Search code examples
verilogsystem-verilogfpgayosys

Verilog: mapping an memory array


I'm trying to make a memory in system verilog and it can be synthesised only when I want to write to the memory directly.

Here is a code that DOES work:

module top (
        input logic clk_i,
        output logic data_o
    );

    reg [31:0] counter  = '0;

    (* ram_style="block" *)
    reg [31:0] video_ram_array [0:(2**15)-1];

    always_ff @(posedge clk_i) begin
        counter <= counter + 1;
        // video_ram_array[counter] <= counter;
        video_ram_array[counter] <= 32'h1234;
    end


    always_comb begin
        data_o = video_ram_array[counter][0];
    end

endmodule

And here is what does NOT work:

module top (
        input logic clk_i,
        output logic data_o
    );

    reg [31:0] counter  = '0;

    (* ram_style="block" *)
    reg [31:0] video_ram_array [0:(2**15)-1];

    always_ff @(posedge clk_i) begin
        counter <= counter + 1;
        video_ram_array[counter] <= counter;
        // video_ram_array[counter] <= 32'h1234;
    end


    always_comb begin
        data_o = video_ram_array[counter][0];
    end

endmodule

It maybe a rookie mistake, but I'm not sure what I am doing wrong. The error in the seconds code block is:

2.8. Executing MEMORY_LIBMAP pass (mapping memories to cells).
<suppressed ~2 debug messages>
ERROR: no valid mapping found for memory top.video_ram_array
make: *** [run] Error 1

I'm using Yosys for synthesis.

EDIT: I'm trying to synthesise for Tang nano 4k, which only has 180k Bits of memory if I'm not mistaken. But I get the same error even if make a very small RAM:

module top (
        input logic clk_i,
        output logic data_o
    );

    reg [31:0] counter  = '0;

    (* ram_style="block" *)
    reg [31:0] video_ram_array [0:2];

    always_ff @(posedge clk_i) begin
        counter <= counter + 1;
        video_ram_array[counter & 32'h0000_0003] <= counter;
    end


    always_comb begin
        data_o = video_ram_array[counter][0];
    end

endmodule

It may be working the first time because the synthesiser will optimise the BRAM out, but in the same time when launching Yosys to generate me a output json, visualising it via netlistsvg will show it exactly like I would want to. But that does not really mean it is really happening...netlistsvg output


Solution

    • One of the main jobs for synthesis tools is to optimize or trim unused logic. The first design is probably not inferring BRAM because you are setting every element to the same pattern. The tool probably optimizes the memory away and replaces it with a single hard coded value 16'h1234. You then access only bit [0] which is 0. Most of what is there is optimized away and replaced with single 0 and a register to clock it. I don't think the first design is inferring BRAM.

    • The 2nd may have trouble inferring BRAM because you are asking for more RAM than the targeted part has. You are asking for 32K * 4 = 128K bytes = 1024K bits. Try reducing the size to something much smaller to test if this is the issue:
      reg [31:0] video_ram_array [0:(2 ** 4) - 1];
      in the 2nd design.

    • EDA Playground has a free version of Yosys.
      I observed there is an option called `memory -nomap' which seems to be related to the message you get.
      enter image description here

      Check to see that this option is not set in a GUI or script.

    • SystemVerilog support seems limited on Yosys to the non-free versions of the tool. I made small changes to convert your 2nd design to Verilog and ran it on Yosys 0.9 on EDA Playground (supports Verilog only); the build completed without error. Unfortunately, I don't see a message about inference or non-inference of BRAM.
      Recommend trying the Verilog version in your tools.

    module top (
            input wire clk_i,
            output reg data_o
        );
    
        reg [31:0] counter=0;
    
        (* ram_style="block" *)
        reg [31:0] video_ram_array [0:(2**4)-1];
    
        always @(posedge clk_i) begin
            counter <= counter + 1;
            video_ram_array[counter] <= counter;
            // video_ram_array[counter] <= 32'h1234;
        end
    
    
        always @ * begin
            data_o = video_ram_array[counter][0];
        end
    
    endmodule
    
    • I also tried running a simulation on your code.
      The simulator errors out on the count variable because of multiple drivers. The statement reg [31:0] counter = '0; is considered one driver the statement counter <= counter + 1; is considered the other. The `always_ff' block in SystemVerilog turns on this stronger checking.
      You can try changing 'always_ff' to 'always' to see if its related.