Verilog, registrar valores

2

Para fines de aprendizaje, estoy tratando de implementar un procesador muy simple en verilog. La idea es que en cada ciclo de reloj, la máquina extraiga las siguientes 4 instrucciones de la memoria (cada instrucción es de 8 bits) o ejecute una instrucción. Sin embargo, estoy teniendo muchos problemas con la PC (yo lo llamo iptr). Mi iptr es un flip-flop disparado por flanco, creo que cuando hay un flanco ascendente del reloj y la señal de habilitación es alta, el iptr debería cambiar el valor. Además, este nuevo valor debe propagarse inmediatamente (a través de un cable) a la unidad de recuperación para decidir si ya está cargado o si debe forzar una búsqueda de instrucciones. El problema es que la unidad de recuperación ve el valor anterior del iptr y no el nuevo valor (o al menos eso creo que es el problema), es como el nuevo valor que se almacena en el iptr en el archivo de registro es No se propaga lo suficientemente rápido. No estoy seguro de cómo solucionar este problema, pero la ayuda es muy apreciada. Esta es la clave de código relevante:

Nota: El procesador es una máquina de pila. He copiado las partes relevantes del código y he revisado un poco las definiciones del módulo porque son un poco largas.

regfile.v:

module regfile(input wareg, wbreg, wcreg, woreg, wwptr, wiptr,
           input enareg, enbreg, encreg, enoreg, enwptr, eniptr,
           input clk,
           output rareg, rbreg, rcreg, roreg, rwptr, riptr);
....
// drive the outputs
assign rareg = areg;
assign rbreg = breg;
assign rcreg = creg;
assign roreg = oreg;
assign rwptr = wptr;
assign riptr = iptr;

initial begin
    areg = 32'b0;
    breg = 32'b0;
    creg = 32'b0;
    oreg = 32'b0;
    wptr = 32'b0;
    iptr = 32'b0;
end

always @ (posedge clk) begin
    // If any of the registers are enabled then write
    if (enareg) begin
        areg <= wareg;
    end

    if (enbreg) begin
        breg <= wbreg;
    end

    if (encreg) begin
        creg <= wcreg;
    end

    if (enoreg) begin
        oreg <= woreg;
    end

    if (enwptr) begin
        wptr <= wwptr;
    end

    if (eniptr) begin
        iptr <= wiptr;
    end
end

fetch.v:

module fetch(input iptr, data, clear_buff, clk,
             output reg instr, exec_stat, ack_clear);
....

initial begin
    // Initially the buffer is empty
    // when exec_stat == 1 then we are fetching an instruction
    // when exec_stat == 0 then we are executing the instruction at address iptr
    empty_instr = 4'b1111;
    exec_stat = 1'b1;
end

always @ (posedge clk) begin
    // NOTE: in this block is where the problem occurs. The processor fetched the instruction
    // just fine when the buffer is empty, then it executed the instruction at 'iptr[1:0] == 0', the
    // iptr is incremented correctly. However, in this module the condition 'iptr[1:0] == 0' is still
    //true in the next clock cycle and causes theprocessor to go into a fetch, which is clearly wrong
    //since I expected "iptr[1:0] == 2'b1" 
    if (exec_stat == 1'b1) begin
            // If we are fetching, store the instruction in the 'data' buffer and set exec_stat low
            exec_stat <= 1'b0;
            empty_instr[3:0] <= 4'b0000;
            instr0 <= data[7:0];
            instr1 <= data[15:8];
            instr2 <= data[23:16];
            instr3 <= data[31:24];
    end else if (iptr[1:0] == 2'b00) begin
        // the last two bits of the iptr address define which 8-bits from the buffer we need to use as instruction
        if (empty_instr[0]) begin
            // if there is no instruction at this address then
            // go to fetch state
            exec_stat <= 1'b1;
        end else begin
            instr <= instr0;
            empty_instr[0] <= 1'b1;
        end
    end else if (iptr[1:0] == 2'b01) begin
        if (empty_instr[1]) begin
            exec_stat <= 1'b1;
        end else begin
            instr <= instr1;
            empty_instr[1] <= 1'b1;
        end
    end else if (iptr[1:0] == 2'b10) begin
        if (empty_instr[2]) begin
            exec_stat <= 1'b1;
        end else begin
            instr <= instr2;
            empty_instr[2] <= 1'b1;
        end
    end else if (iptr[1:0] == 2'b11) begin
        if (empty_instr[3]) begin
            exec_stat <= 1'b1;
        end else begin
            instr <= instr3;
            empty_instr[3] <= 1'b1;
        end
    end
end

datapath.v:

 module datapath(clk);
 //this seems a bit odd to me because the iptr input to the nextpc is the same than that to fetch,
 //which is connected to the read port of the regfile. I was hoping that the value that fetch would
 //see is the updated one and not the old one, but maybe that is too much to hope for. i.e. the value in the regfile is not propagated quickly enough...

 // This is the module that increments the iptr, there is also some other
 // logic that handles adding offsets for jumps, but that is not being used
 // at the momment.
 nextpc nextpc(ack_clear, riptr, instr_oreg, jump_type, alu_bool, clk, wiptr, clear_buff);

 regfile regfile(wareg, wbreg, wcreg, woreg, wwptr, wiptr,
                mux_enareg, mux_enbreg, encreg, enoreg, enwptr, eniptr,
                clk,
                rareg, rbreg, rcreg, roreg, rwptr, riptr);

 // Input iptr is the ouutput from read port of the regfile
 fetch fetch(riptr, mem_rdata, clear_buff, clk, instr, exec_stat, ack_clear);

 ....

¡Gracias!

    
pregunta Andrés AG

1 respuesta

3

Entonces, después de mucho excavar, encontré la respuesta al problema. No estoy seguro de si ayuda a alguien pero voy a publicar la respuesta de todos modos.

Aquí están las señales del código anterior que falla.

Elproblemaesenrealidadmuysimpleysololodetectécuandoalguienmedijoesto:"En el borde positivo del reloj, algo debería cambiar, de lo contrario, su diseño está atascado". Si observa las señales anteriores, en 15ns algo está mal porque ninguno de los registros cambia cuando el procesador debería haber ejecutado la primera instrucción. La razón es porque al signel instr [7: 0] no se le asignó el valor de la siguiente instrucción (que se detectó correctamente). El problema se debe a esta sección del código:

fetch.v:

always @ (posedge clk) begin
    if (exec_stat == 1'b1) begin
            // If we are fetching, store the instruction in the 'data' buffer and set exec_stat low 
            exec_stat <= 1'b0;
            empty_instr[3:0] <= 4'b0000;
            instr0 <= data[7:0];
            instr1 <= data[15:8];
            instr2 <= data[23:16];
            instr3 <= data[31:24];
    end else if (iptr[1:0] == 2'b00) begin
        // the last two bits of the iptr address define which 8-bits from the buffer we need to use as instruction
        if (empty_instr[0]) begin
            // if there is no instruction at this address then
            // go to fetch state
            exec_stat <= 1'b1;
        end else begin
            instr <= instr0;
            empty_instr[0] <= 1'b1;
        end 
    end else if (iptr[1:0] == 2'b01) begin
        if (empty_instr[1]) begin
            exec_stat <= 1'b1;
        end else begin
            instr <= instr1;
            empty_instr[1] <= 1'b1;
        end 
    end else if (iptr[1:0] == 2'b10) begin
        if (empty_instr[2]) begin
            exec_stat <= 1'b1;
        end else begin
            instr <= instr2;
            empty_instr[2] <= 1'b1;
        end 
    end else if (iptr[1:0] == 2'b11) begin
        if (empty_instr[3]) begin
            exec_stat <= 1'b1;
        end else begin
            instr <= instr3;
            empty_instr[3] <= 1'b1;
        end 
    end 
end 

Esto es incorrecto porque significa que 'instr' se activa cuando el reloj va alto, lo que significa que hay algunos ciclos en los que el procesador no hace nada y la PC no se actualiza a tiempo ...

La solución es NO hacer que 'instr' sea una salida registrada sino asignarla constantemente utilizando un multiplexor. Algo como:

mux8_4 mux0_4(iptr[1:0], instr0, instr1, instr2, instr3, instr);

Después de todo, si contiene la instrucción incorrecta no importa porque eso significa que entraríamos en un estado de recuperación y el procesador ignoraría los valores. Así que no hay razón para poner esta asignación en el bloque siempre. Con este cambio las señales se ven así:

Claramente, todo cambia ahora en el momento apropiado !!!! :): D

    
respondido por el Andrés AG

Lea otras preguntas en las etiquetas