- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Tags:
- Programmable Devices
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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).
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
--- 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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
--- 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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
--- 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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
--- 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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
--- Quote Start --- Exactly. --- Quote End --- I was referring to Tricky, not you. Unfortunately you did not understand the question. Thanks for trying though.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page