Search code examples
verilogsystem-verilogfpgaquartusintel-fpga

In Intel Quartus, can I initialize RAM using a string parameter?


I need to initialize several instances of the same ram module with different data files which I would like to do as follows:

module ram #(
  string HEXFILE = "split1.mem"
)
(
  input  logic        clk,
  input  logic [31:0] a,
  input  logic [7:0]  wd,
  input  logic        we,
  output logic [7:0]  rd
);

logic [7:0] mem [3071:0];
integer fd;


initial $readmemh(HEXFILE, mem);

always_ff @(posedge clk) begin
  if (we) mem[a] <= wd;
  rd <= mem[a];
end

endmodule

And in my top level entity, initialize these as follows:

ram #(
  .HEXFILE("split1.mem")
) M0 (
  .clk(clk),
  .a(a0),
  .wd(wd0),
  .we(we),
  .rd(rd0)
);

ram #(
  .HEXFILE("split2.mem")
) M1 (
  .clk(clk),
  .a(a1),
  .wd(wd1),
  .we(we),
  .rd(rd1)
);

// And so on ...

But when I try to do this, I get the following error:

Error (10686): SystemVerilog error at ram.sv(18): HEXFILE has an aggregate value

It works fine if I use a string literal for the file name:

initial $readmemh("split1.mem", mem)

Any ideas on how I can achieve this without creating copies of the same file just to change the input file?

EDIT: I think Verilog treats parameters and string literals differently. It's treating string as an extension of logic which is why it's saying it needs to be extended.

I don't know how to define it as a string literal. The following seems to be working but it's a terrible terrible way in my opinion:

generate
if      (HEXFILE == "split1.mem") initial $readmemh("split1.mem", mem);
else if (HEXFILE == "split2.mem") initial $readmemh("split2.mem", mem);
else if (HEXFILE == "split3.mem") initial $readmemh("split3.mem", mem);
else if (HEXFILE == "split4.mem") initial $readmemh("split4.mem", mem);
endgenerate

Solution

  • The reported error you see is for line 18 in the ram module, which is this line:

    always_ff @(posedge clk) begin
    

    When I run on different simulators, I don't see that exact error message, but with Synopsys VCS, I see:

    Error-[ICPD] Illegal combination of drivers
    ram.sv, 12
      Illegal combination of procedural drivers
      Variable "mem" is driven by an invalid combination of procedural drivers. 
      Variables written on left-hand of "always_ff" cannot be written to by any 
      other processes, including other "always_ff" processes.
      This variable is declared at "ram.sv", 12: logic [7:0] mem[3071:0];
      The first driver is at "ram.sv", 16: $readmemh(HEXFILE, mem);
      The second driver is at "ram.sv", 18: always_ff @(posedge clk) begin
      if (we) begin
       ...
    

    Refer to IEEE Std 1800-2017, section 9.2.2.4 Sequential logic always_ff procedure:

    Variables on the left-hand side of assignments within an always_ff procedure, including variables from the contents of a called function, shall not be written to by any other process.

    The error goes away with:

    always @(posedge clk) begin
    

    Aside from that, I don't see any problem with using different values of HEXFILE passed to different instances. If you continue to see problems, you could try to use parameter instead of string:

    module ram #(
      parameter HEXFILE = "split1.mem"
    )