Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Verilog hieararchical names with $readmemh #344

Closed
niels-moller opened this issue May 13, 2017 · 3 comments
Closed

Verilog hieararchical names with $readmemh #344

niels-moller opened this issue May 13, 2017 · 3 comments
Assignees

Comments

@niels-moller
Copy link

niels-moller commented May 13, 2017

I'm trying to initialize a memory block using $readmemh, and I don't want to do the initialization in the module defining the ram, but in my top module. Here's a complete but cut down example:

module ram_unit(input clk, input rst,
		input cyc, input stb,
		input we, output ack,
		input [SIZE-1:0]  adr,
		input [63:0] 	  dat_write,
		output reg [63:0] dat_read);
   parameter  SIZE = 3; // Address size
   reg [63:0] mem [0:(1 << SIZE)-1];
   integer    i;

   // Always completes in a single cycle.
   assign ack = 1;

   // Written this way due to
   // https://github.com/cliffordwolf/yosys/issues/335
   always @(posedge clk)
     if (cyc & stb & ~we) begin
	dat_read <= mem[adr];
     end
   always @(posedge clk)
     if (cyc & stb & we) begin
	mem[adr] <= dat_write;
     end
endmodule

module main(input clk);
   parameter SIZE = 3;

   reg rst;
   reg [SIZE-1:0] adr;
   reg cyc;
   reg stb;
   reg we;
   wire ack;
   reg [63:0] dat_write;
   wire [63:0] dat_read;
          
   ram_unit #(SIZE) 
   ram(clk, rst, cyc, stb, we, ack,
       adr, dat_write, dat_read);

   initial begin
      $readmemh("/dev/null", ram.mem);
   end
endmodule

This is accepted by iverilog -Wall, but not by yosys:

$ yosys -q -f verilog -p synth_ice40 readmemh-1.vl 
ERROR: Failed to evaluate system function `\$readmemh' with non-memory 2nd argument at readmemh-1.vl:43.

As a work-around, I tried changing the initial block to read into a locally defined memory, and then copying it to the target memory, like this:

module ram_unit(input clk, input rst,
		input cyc, input stb,
		input we, output ack,
		input [SIZE-1:0]  adr,
		input [63:0] 	  dat_write,
		output reg [63:0] dat_read);
   parameter  SIZE = 3; // Address size
   reg [63:0] mem [0:(1 << SIZE)-1];
   integer    i;

   // Always completes in a single cycle.
   assign ack = 1;

   // Written this way due to
   // https://github.com/cliffordwolf/yosys/issues/335
   always @(posedge clk)
     if (cyc & stb & ~we) begin
	dat_read <= mem[adr];
     end
   always @(posedge clk)
     if (cyc & stb & we) begin
	mem[adr] <= dat_write;
     end
endmodule

module main(input clk);
   parameter SIZE = 3;

   reg rst;
   reg [SIZE-1:0] adr;
   reg cyc;
   reg stb;
   reg we;
   wire ack;
   reg [63:0] dat_write;
   wire [63:0] dat_read;

   reg [63:0]  init_mem [0:(1 << SIZE)-1];
   integer     i;

   ram_unit #(SIZE) 
   ram(clk, rst, cyc, stb, we, ack,
       adr, dat_write, dat_read);

   initial begin
      $readmemh("/dev/null", init_mem);
      for (i = 0; i < (1<<SIZE); i = i + 1)
	ram.mem[i] = init_mem[i];
   end
endmodule

This is also accepted by iverilog -Wall, with yosys I get warnings making me think it doesn't quite work:

$ yosys -q -f verilog -p synth_ice40 readmemh-2.vl
Warning: Identifier `\ram.mem' is implicitly declared at readmemh-2.vl:48.
Warning: Range select out of bounds on signal `\ram.mem' at readmemh-2.vl:48: Setting result bit to undef.
Warning: Range select out of bounds on signal `\ram.mem' at readmemh-2.vl:48: Setting result bit to undef.
Warning: Range select out of bounds on signal `\ram.mem' at readmemh-2.vl:48: Setting result bit to undef.
Warning: Range select out of bounds on signal `\ram.mem' at readmemh-2.vl:48: Setting result bit to undef.

Suggestions for better workarounds appreciated.

Best regards,
/Niels Möller

(I tried the "insert code" button this time, but indentation still looks very bad in the preview. Sorry about that).

@cliffordwolf
Copy link
Collaborator

I'm trying to initialize a memory block using $readmemh, and I don't want to do the initialization in the module defining the ram, but in my top module.

This only works in simulation. In synthesizable verilog hierarchical references are forbidden. No synthesis tool would (or should) accept this code.

(I tried the "insert code" button this time, but indentation still looks very bad in the preview. Sorry about that).

See https://help.github.com/articles/basic-writing-and-formatting-syntax/ for Github Markdown syntax. Put tripple backquotes before and after a code block.

@cliffordwolf cliffordwolf self-assigned this May 13, 2017
@niels-moller
Copy link
Author

Thanks for the quick response. What's the recommended way to separate initial contents of the memory from the module implementing it, then?

I could try passing the file name (string) as a module parameter (but that's not good enough for simulation, where I want to use a command line argument (iverilog $value$plusargs) to specify the file to read), or I could use the preprocessor.

I'm aware of the icebram tool, but at the moment I'm looking for something simpler, and ideally, some way which works for both synthesis and simulation.

@cliffordwolf
Copy link
Collaborator

Filenames in module parameter or using a preprocessor define are usual methods to do that.

For simulation you can also "overwrite" the default contents by running initial $readmemh(...); in the module itself for the default memory contents, and then run another $readmemh() in your test bench slightly delayed so it will overwrite the contents set by the initial block in the module.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants