2

Pretty simple problem. Given the following code:

module main(
    output reg  [1:0][DATA_WIDTH-1:0] dOut,
    input  wire [1:0][DATA_WIDTH-1:0] dIn,
    input  wire [1:0][ADDR_WIDTH-1:0] addr,
    input  wire [1:0] wren,
    input  wire clk
);
    parameter DATA_WIDTH = 16;
    parameter ADDR_WIDTH = 6;

    reg [DATA_WIDTH-1:0] ram [2**ADDR_WIDTH-1:0];

    generate
        genvar k;
        for(k=0; k<2; k=k+1) begin: m
            always @(posedge clk) begin
                if(wren[k])
                    ram[addr[k]] <= dIn[k];
                dOut[k] <= ram[addr[k]];
            end
        end
    endgenerate
endmodule

quarus 13.0sp1 gives this error (and its 20 other ill-begotten fraternally equivalent siblings):

Error (10028): Can't resolve multiple constant drivers for net "ram[63][14]" at main.v(42)

But if I manually un-roll the generate loop:

module main(
    output reg  [1:0][DATA_WIDTH-1:0] dOut,
    input  wire [1:0][DATA_WIDTH-1:0] dIn,
    input  wire [1:0][ADDR_WIDTH-1:0] addr,
    input  wire [1:0] wren,
    input  wire clk
);
    parameter DATA_WIDTH = 16;
    parameter ADDR_WIDTH = 6;

    reg [DATA_WIDTH-1:0] ram [2**ADDR_WIDTH-1:0];

    always @(posedge clk) begin
        if(wren[0])
            ram[addr[0]] <= dIn[0];
        dOut[0] <= ram[addr[0]];
    end

    always @(posedge clk) begin
        if(wren[1])
            ram[addr[1]] <= dIn[1];
        dOut[1] <= ram[addr[1]];
    end
endmodule

It all becomes okay with the analysis & synthesis step.

What's the cure to get the generate loop running?

Kraken
  • 189
  • 5
  • 2
    It is absolutely correct. You generate multiple `always` statements which drive `ram[addr[whatever]]`; `addr` is a var array and cannot be resolved at compilation time. So, it is multiply driven. – Serge Jun 02 '20 at 15:42
  • @Serge I fail to understand your comment. – Kraken Jun 02 '20 at 15:44
  • 1
    what i am saying that 'ram' is multiply driven in both your cases. Synthesis should give some warning/error in any case, unless it could resolve 'addr' to a constant. – Serge Jun 02 '20 at 15:47
  • Serge and toolic Thanks. I see. But why does the unrolled version work. Also, a very similar code is used by the template to infer true dual-port-RAM by Quartus. Does it not see the same address write conflict there? – Kraken Jun 02 '20 at 16:04
  • @toolic also, no cigar if I force the update condition as ```addr[0]!=addr[1] && wren[k]``` – Kraken Jun 02 '20 at 16:07

2 Answers2

4

I think the correct way is in the lines of what it's explained in this question: Using a generate with for loop in verilog

Which would be transferred to your code as this:

module main(
    output reg  [1:0][DATA_WIDTH-1:0] dOut,
    input  wire [1:0][DATA_WIDTH-1:0] dIn,
    input  wire [1:0][ADDR_WIDTH-1:0] addr,
    input  wire [1:0] wren,
    input  wire clk
);
    parameter DATA_WIDTH = 16;
    parameter ADDR_WIDTH = 6;

    reg [DATA_WIDTH-1:0] ram [2**ADDR_WIDTH-1:0];    
    integer k;

    always @(posedge clk) begin
      for(k=0; k<2; k=k+1) begin:
        if(wren[k])
          ram[addr[k]] <= dIn[k];
        dOut[k] <= ram[addr[k]];
      end
    end
endmodule

Keeping all accesses to your dual port RAM in one always block is convenient so the synthesizer can safely detect that you are efefctively using a dual port RAM at register ram.

mcleod_ideafix
  • 11,128
  • 2
  • 24
  • 32
  • No cigar. New error: ```Error (10049): Verilog HDL error at main.v(42): value must not be assigned to nonvariable "k"``` – Kraken Jun 02 '20 at 15:41
  • 2
    should be `integer k` – Serge Jun 02 '20 at 15:43
  • 2
    Read the last parragraph from my answer. Your approach generated multiple always blocks. A good design style (less prone to errors) mandates that all writes to the same signal (in this case, ram) should be performed within the same always block. For simulation, you approach most probably work, but for synthesis, most probably won't. – mcleod_ideafix Jun 03 '20 at 14:42
  • @Kraken I explained why your approaches do not work in my answer. mcleod_ideafix single always block solution is considered best practice for this situation. – Greg Jun 03 '20 at 17:03
1

Both the generate loop and unrolled versions should not have passed synthesis. In both cases the same address in ram can be assigned by both always blocks. Worse, if both bits of wren are high with both addresses being the same and data being different, then the result is indeterminable. The Verilog LRM states last assignment on a register wins and always blocks with the same trigger could be evaluated in any order.

Synthesis requires assignments to registers to be deterministic. Two (or more) always blocks having write access to the same bit is illegal because nondeterministic. If the unrolled is synthesizing correctly, then that means there are constants on wren and addr outside of the shown module that make it logically impossible for write conflict; for some reason the generate loop version is not getting the same optimization. Example of constraints that would allow optimization to prevent multi-always block write access:

  1. One wren is hard coded to 0. Therefore only one block has exclusive access
  2. Address have non overlapping sets of possible values. Ex addr[0] can only be even while addr[1] can only be odd, or addr[0] < 2**(ADDR_WIDTH/2) and addr[1] >= 2**(ADDR_WIDTH/2).

Synthesis is okay with dOut being assigned by two always blocks because each block has exclusive write access to its target bits (non overlapping sets of possible address values).

The single always block in mcleod_ideafix answer is the preferred solution. If both bits of wren are high with both addresses being the same, then wren[1] will always win. If wren[0] should have priority, then make the for-loop a count down.

Greg
  • 18,111
  • 5
  • 46
  • 68
  • "then that means there are constants on wren and addr outside of the shown module that make it logically impossible for write conflict"... Not so, the module shown in questions are my top modules. Also, the two always approach is used by quartus for inferring ALT_SYNC_RAM and is stated in Altera Docs as well. I understand that multi-assign to same variables is bad, but I guess Altera made a "Hard-Coded" exception in this case. – Kraken Jun 04 '20 at 05:04
  • 1
    @Kraken Altera could have allowed special conditions. That would also explain they the generate loop didn't work (didn't fit a template) and the manual did (did fit their template). I'm not sure what Altera expects to happen if two writes occur on the same address. Since writing to the same register with two or more always blocks is non-standard, don't expect the code to work with another synthesizer. – Greg Jun 04 '20 at 23:12
  • 1
    I prefer to place all my writes to a single variable in a single block. That way, in the case of conflict, the last statement gets to drive the net. Wonder why Altera don't prefer that. – Kraken Jun 05 '20 at 16:28