2
\$\begingroup\$

For learning purposes, I am trying to implement a very simple processor in verilog. The idea is that every clock cycle the machine either fetches the next 4 instructions from memory (each instruction is 8-bits) or executes an instruction. However, I am having a lot of trouble with the PC (I call it iptr). My iptr is an edge triggered flipflop, I think that when there is a rising edge of the clock and the enable signal is high the iptr should change the value. Also, this new value should be immediately propagated (through a wire) to the fetch unit to decide whether it is already loaded or whether it should force an instruction fetch. The problem is that the fetch unit sees the previous value of the iptr and not the new value (or at least that is what I think the problem is), its like the new value that is being stored in the iptr in the register file is not propageted quickly enough. I am not sure how to fix this, but help is greatly appreciated. This is the relevant peace of code:

Note: The processor is a stack machine. I have copied the relevant parts of the code and patched up a bit the module definitions because they are a bit long.

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);

 ....

Thanks!

\$\endgroup\$
  • \$\begingroup\$ Can you post a simulation plot of the relevant signals, like all I/Os to iptr register, etc., and identify the moment when something unexpected first happens? IMO, your question is too complex for a Q&A format now. Also, I suspect that by narrowing down the problem you have a good chance of solving it yourself. \$\endgroup\$ – Justin Feb 18 '15 at 14:40
  • \$\begingroup\$ Hi Justin, thanks a lot for your reply. I figured out the problem, I have posted an answer with simulation plots and explanation. \$\endgroup\$ – Andrés AG Feb 18 '15 at 15:20
3
\$\begingroup\$

So after a lot of digging I found the answer to the problem. I am not sure if it helps anyone but I am going to post the answer anyways.

Here are the signals of the code above failing.

Processor failing

The problem is actually very simple and I only spotted it when someone told me this: "At the positive edge of the clock something should change, otherwise your design is stuck!". If you look at the signals above, at 15ns something is wrong because none of the registers change when the processor should have executed the first instruction! The reason is because the signel instr[7:0] didnt get assigned the value of the next instruction (which was correctly fetched). The problem is because of this section of code:

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 

This is wrong because it means that 'instr' is driven when the clock goes high, which means that there are some cycles that the processor does nothing and the PC is not updated in time...

The solution is to NOT make 'instr' a registered output rather constantly assign it using a multiplexer. Something like:

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

After all, if it contains the wrong instruction it doesnt matter because that means that we would go into a fetch state and the processor would ignore the values. So there is no reason to put this assignment in the always block. With this change the signals look like this:

correct

Clearly, everything now changes at the appropriate time!!!! :) :D

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.