Forum Discussion

Altera_Forum's avatar
Altera_Forum
Icon for Honored Contributor rankHonored Contributor
9 years ago

Force synthesis to fail if RAM cannot be inferred?

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.

9 Replies

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    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.

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    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
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    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).

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    --- 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.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    --- 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.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    --- 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.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    --- 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.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    --- Quote Start ---

    Exactly.

    --- Quote End ---

    I was referring to Tricky, not you.

    Unfortunately you did not understand the question. Thanks for trying though.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    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.