Intel® Quartus® Prime Software
Intel® Quartus® Prime Design Software, Design Entry, Synthesis, Simulation, Verification, Timing Analysis, System Design (Platform Designer, formerly Qsys)
17255 Discussions

Force synthesis to fail if RAM cannot be inferred?

Altera_Forum
Honored Contributor II
2,292 Views

Hi, we have a design where there are a few large memories, declared as verilog reg[][]'s, which must be implemented as M9Ks. The design can't possibly fit the device or meet timing if they are implemented using LAB registers. 

 

The declarations look like this: 

 

reg mem /* synthesis syn_ramstyle = "M9K,no_rw_check" */;  

 

Is there any way to force Quartus to fail if it can't infer an M9K for these? 

 

As we all know, there an enormous number of conditions necessary for RAM inference to succeed, and some of them are quite subtle. It's very easy to trip over one of these conditions, and several times now I've spent a few hours tracking down a timing violation or fit failure that ended up being nothing more than accidentally violating one of these conditions. 

 

In theory we could rewrite the code to use altsyncram, and in other places we've actually done that. But there are a few cases where that would make the code extremely hard to read and understand. 

 

FWIW this is all command-line, we don't use the GUI.
0 Kudos
9 Replies
Altera_Forum
Honored Contributor II
1,360 Views

Quartus has several templates designed to infer memory. If you use one of them you will get memory unless there is a problem. You should pick the template that suits your needs and you will automatically have memory inferred if at all possible. You will need to use the GUI because the templates are in the editor.

0 Kudos
Altera_Forum
Honored Contributor II
1,360 Views

Put the desired verilog ram template inside a module, and instantiate the module where you need a ram inferred. Parameterizze the module definition to select the desired width and depth. By doing this you should always be able to get Quartus to generate actual SRAM blocks vs separate LABs. 

 

For example, I use the following for a dualport memory implementation, and then just instantiate a new instance of dualport () when needed. 

 

module dualport # ( // external parameters parameter TPD = 0, // simulation delay parameter DATA = 32, // data bit width parameter ADDRESS = 5, // number of address bits parameter MODE = "NEW" // on read/write collision, data output ) ( // port definitions input wire clk, // master clock input wire addr_a, // port A address input input wire wren_a, // port A write enable input input wire din_a, // port A data input output reg dout_a, // port A data output input wire addr_b, // port B address input input wire wren_b, // port B write enable input input wire din_b, // port B data input output reg dout_b // port B data output ); // internal parameters localparam SIZE = (1<<ADDRESS); // memory size based on address size // internal signals reg r_addr_a; // a side address register reg r_wren_a; // a side write enable register reg r_din_a; // a side data in register reg r_addr_b; // b side address register reg r_wren_b; // b side write enable register reg r_din_b; // b side data in register reg mem /* synthesis ramstyle = "no_rw_check" */; `ifdef SIMULATION // init memory contents initial begin : init_mem integer i; for (i = 0; i < SIZE; i = i+1) mem = 0; end reg d_addr_a; reg d_addr_b; reg d_wren_a; reg d_wren_b; always @(posedge clk) begin // output write data if (r_wren_a) $write("%m wren:a addr=%h data=%h\n",r_addr_a,r_din_a); if (r_wren_b) $write("%m wren:b addr=%h data=%h\n",r_addr_b,r_din_b); // output read data if (d_addr_a != r_addr_a && !d_wren_a) $write("%m read:a addr=%h data=%h\n",d_addr_a,dout_a); if (d_addr_b != r_addr_b && !d_wren_b) $write("%m read:b addr=%h data=%h\n",d_addr_b,dout_b); // delay address/wren for reads d_addr_a <=# TPD r_addr_a; d_addr_b <=# TPD r_addr_b; d_wren_a <=# TPD r_wren_a; d_wren_b <=# TPD r_wren_b; end `endif // SIMULATION // port A access always @(posedge clk) begin r_addr_a <=# TPD addr_a; r_wren_a <=# TPD wren_a; r_din_a <=# TPD din_a; end always @(posedge clk) begin // write data if (r_wren_a) mem <=# TPD r_din_a; // read data if (r_wren_a && MODE == "NEW") dout_a <=# TPD r_din_a; else dout_a <=# TPD mem; end // port B access always @(posedge clk) begin r_addr_b <=# TPD addr_b; r_wren_b <=# TPD wren_b; r_din_b <=# TPD din_b; end always @(posedge clk) begin // write data if (r_wren_b) mem <=# TPD r_din_b; // read data if (r_wren_b && MODE == "NEW") dout_b <=# TPD r_din_b; else dout_b <=# TPD mem; end endmodule // dualport
0 Kudos
Altera_Forum
Honored Contributor II
1,360 Views

Why don't use the MegaWizard? It is easy and confident. You can easily force Quartus to infer Block RAM for your memory. There is a check box when you generate the ip core which forces Quartus to use M9K (If your device has M9K, it may be M4K, etc).

0 Kudos
Altera_Forum
Honored Contributor II
1,360 Views

 

--- Quote Start ---  

Put the desired verilog ram template inside a module, and instantiate the module where you need a ram inferred. Parameterizze the module definition to select the desired width and depth. By doing this you should always be able to get Quartus to generate actual SRAM blocks vs separate LABs. 

 

For example, I use the following for a dualport memory implementation, and then just instantiate a new instance of dualport () when needed. 

 

module dualport # ( // external parameters parameter TPD = 0, // simulation delay parameter DATA = 32, // data bit width parameter ADDRESS = 5, // number of address bits parameter MODE = "NEW" // on read/write collision, data output ) ( // port definitions input wire clk, // master clock input wire addr_a, // port A address input input wire wren_a, // port A write enable input input wire din_a, // port A data input output reg dout_a, // port A data output input wire addr_b, // port B address input input wire wren_b, // port B write enable input input wire din_b, // port B data input output reg dout_b // port B data output ); // internal parameters localparam SIZE = (1<<ADDRESS); // memory size based on address size // internal signals reg r_addr_a; // a side address register reg r_wren_a; // a side write enable register reg r_din_a; // a side data in register reg r_addr_b; // b side address register reg r_wren_b; // b side write enable register reg r_din_b; // b side data in register reg mem /* synthesis ramstyle = "no_rw_check" */; `ifdef SIMULATION // init memory contents initial begin : init_mem integer i; for (i = 0; i < SIZE; i = i+1) mem = 0; end reg d_addr_a; reg d_addr_b; reg d_wren_a; reg d_wren_b; always @(posedge clk) begin // output write data if (r_wren_a) $write("%m wren:a addr=%h data=%h\n",r_addr_a,r_din_a); if (r_wren_b) $write("%m wren:b addr=%h data=%h\n",r_addr_b,r_din_b); // output read data if (d_addr_a != r_addr_a && !d_wren_a) $write("%m read:a addr=%h data=%h\n",d_addr_a,dout_a); if (d_addr_b != r_addr_b && !d_wren_b) $write("%m read:b addr=%h data=%h\n",d_addr_b,dout_b); // delay address/wren for reads d_addr_a <=# TPD r_addr_a; d_addr_b <=# TPD r_addr_b; d_wren_a <=# TPD r_wren_a; d_wren_b <=# TPD r_wren_b; end `endif // SIMULATION // port A access always @(posedge clk) begin r_addr_a <=# TPD addr_a; r_wren_a <=# TPD wren_a; r_din_a <=# TPD din_a; end always @(posedge clk) begin // write data if (r_wren_a) mem <=# TPD r_din_a; // read data if (r_wren_a && MODE == "NEW") dout_a <=# TPD r_din_a; else dout_a <=# TPD mem; end // port B access always @(posedge clk) begin r_addr_b <=# TPD addr_b; r_wren_b <=# TPD wren_b; r_din_b <=# TPD din_b; end always @(posedge clk) begin // write data if (r_wren_b) mem <=# TPD r_din_b; // read data if (r_wren_b && MODE == "NEW") dout_b <=# TPD r_din_b; else dout_b <=# TPD mem; end endmodule // dualport 

--- Quote End ---  

 

 

If you're going to do that, you might as well just instantiate the ALTSYNCRAM directly. 

The only time I see using a parameterised module like this being useful, that simply infers a ram internally, is when you need cross vendor compatibility.
0 Kudos
Altera_Forum
Honored Contributor II
1,360 Views

 

--- Quote Start ---  

Why don't use the MegaWizard? It is easy and confident. You can easily force Quartus to infer Block RAM for your memory. There is a check box when you generate the ip core which forces Quartus to use M9K (If your device has M9K, it may be M4K, etc). 

--- Quote End ---  

 

 

Because using the megawizard doesnt let you paramterise your block, or use your design in another vendor's tools.
0 Kudos
Altera_Forum
Honored Contributor II
1,360 Views

 

--- Quote Start ---  

If you're going to do that, you might as well just instantiate the ALTSYNCRAM directly. 

The only time I see using a parameterised module like this being useful, that simply infers a ram internally, is when you need cross vendor compatibility. 

 

Because using the megawizard doesnt let you paramterise your block, or use your design in another vendor's tools. 

--- Quote End ---  

 

 

I'm glad at least one person here understood my question.
0 Kudos
Altera_Forum
Honored Contributor II
1,360 Views

 

--- Quote Start ---  

I'm glad at least one person here understood my question. 

--- Quote End ---  

 

 

Exactly. That is why I posted the dual-port implementation above that does NOT use any vendor proprietary functions. It is multi vendor.
0 Kudos
Altera_Forum
Honored Contributor II
1,360 Views

 

--- Quote Start ---  

Exactly. 

--- Quote End ---  

 

 

I was referring to Tricky, not you. 

 

Unfortunately you did not understand the question. Thanks for trying though.
0 Kudos
Altera_Forum
Honored Contributor II
1,360 Views

I dont know whether you can force synthesis failure if a ram is not infered. But the easiest way to make sure is simply to test your blocks in isolation, not as part of the whole system. If they synthesise to M9Ks on their won then there should be no problems later on. 

 

steps to test: 

1. Create a new project with only your "test" module as the top level 

2. in assignments editor, assign virtual pins to * 

 

Now it will synthesise your module and hopefully wont take too long. 

 

To avoid problems in the first place - try and follow the templates as close as possible: 

1. All addresses should be registered and not read by another process (as this would create a wire that doesnt exist on the hardware) 

2. Write data should be registered 

 

In some cases - failure to include a write-enable of some sort can cause failure to infer a ram - if this is the case raise an enhancement request.
0 Kudos
Reply