S.SaeedJazaeri
S.SaeedJazaeri

Reputation: 13

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?

Upvotes: 0

Views: 1360

Answers (2)

Serge
Serge

Reputation: 12344

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

Upvotes: 1

Mikef
Mikef

Reputation: 2508

For non-blocking assignments in the same process, the first assignment is overwritten; the last assignment wins.

Use two registers to accomplish the functionality of both assignments.

Variable buffer_2 will contain the aggregate result of both operations.

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

Upvotes: 0

Related Questions