Search code examples
veriloghdl

non-blocking assignment to a variable twice in a always block gives unexpected answer


I'm trying to write a Verilog module and a section of code is something like this:

input wire [7:0] data_in
reg [199:0] buffer;
.
.
.
always @(posedge clk) begin
    buffer[119:112] <= data_in; //place data_in to a specific part of buffer
    buffer <= buffer << 8; //logical left shift the buffer
end

but the problem is the first line of always block is ignored (after each clk posedge)! in another word, data_in doesn't place in buffer[119:112] and buffer always remain 200'b0!

the first question is what is the the cause? and the second question is how can i do that what i want?


Solution

  • You have to look at this from the point of view of the hardware design. always @(posedge clk) represents a flop. The only thing it does, is saving values at the clock edge. The data itself is prepared with some combinatorial logic before. In your case, assigning data_in to some bits as well, as the shift are the parts of the combinatorial world. Saying that, you can write your model as the following:

       reg [199:0] buffer_in, buffer_shifted;
       reg [7:0]   data_in;
       reg         clk;   
    
       always @* begin
          buffer_in = buffer;
          buffer_in[119:112] = data_in;
          buffer_shifted = buffer_in << 8;
       end
       always @(posedge clk)
         buffer <= buffer_shifted;
    

    First two statements in the above always block are really the same as the following

          buffer_in = {buffer[199:120], data_in[7:0], buffer[111:0]};
    

    And now you can collapse all of this and even move it to the statement which models the flop itself. The nature of these statements will still be the same, combinatorial logic, but you can get rid of intermediate variables:

       always @(posedge clk)
         buffer <= {buffer[199:120], data_in[7:0], buffer[111:0]} << 8;
    

    Adding a testcase

    module top;
       reg [7:0]   data_in;
       reg         clk;
       reg [199:0] buffer;
       
       always @(posedge clk)
         buffer <= {buffer[199:120], data_in[7:0], buffer[111:0]} << 8;
       
       initial begin
          clk = 0;
          buffer = 0;
          data_in = 4'h1;
          #13
            data_in = 4'h2;
       end
       
       always 
         #5 clk = ~clk;
       
       initial 
         #20 $finish;
       
       always @*
         $display("%3t> %h_%h", $time, buffer[199:112], buffer[111:0]);
    endmodule