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

ramstyle attribute in Quartus synthesis

Altera_Forum
Honored Contributor II
2,371 Views

I am trying to create some verilog that will receive a message with a set of sample data from a Nios processor and send the samples out on digital i/o pins sequentially. The message size is 512 bytes and this takes up too many LEs in my design if I implement the message buffer as a register. I have been looking into ways to use M9K blocks instead as the design has plenty of those available. I've tried to use: 

 

(* ramstyle="M9K" *) reg act_message;  

instead of  

 

reg act_message;  

 

but that doesn't affect the LE usage. Am I using the command incorrectly? Is there a Quartus setting that needs to be changed to use this? Is there a better way to solve the problem?  

 

The full verilog file is below: 

module dio_bus_interface ( //AVALON memory mapped interface SIGNALS clk, reset_n, chipselect_n, address, read, readdata, write, writedata, //internal signals to the dio control module w_dio_output_bank1, w_dio_input_bank1, w_dio_dir_bank1, ); input clk; input reset_n; input chipselect_n; input address; input read; output readdata; reg readdata; input write; input writedata; output w_dio_output_bank1; reg w_dio_output_bank1; input w_dio_input_bank1; output w_dio_dir_bank1; reg w_dio_dir_bank1; //Register addresses parameter WRITE_DIO_REG_ADDRESS_BANK1 = 4'b 0000; parameter READ_DIO_REG_ADDRESS_BANK1 = 4'b 0001; parameter SET_DIO_INPUT_REG_ADDRESS_BANK1 = 4'b 0010; parameter SET_DIO_OUTPUT_REG_ADDRESS_BANK1 = 4'b 0011; parameter SETUP_DIO_SEQUENCE_REG_ADDRESS_BANK1 = 4'b 1000; //Set DIO CH1 as input or output wire set_dio_input_register_ch1; wire set_dio_output_register_ch1; assign set_dio_input_register_ch1 = ((chipselect_n == 1'b0) & (write == 1'b1) & (address == SET_DIO_INPUT_REG_ADDRESS_BANK1)); assign set_dio_output_register_ch1 = ((chipselect_n == 1'b0) & (write == 1'b1) & (address == SET_DIO_OUTPUT_REG_ADDRESS_BANK1)); always @ (posedge clk or negedge reset_n) begin if(~reset_n) w_dio_dir_bank1 <= 1'b1; else if (set_dio_output_register_ch1) //set ch1 as output w_dio_dir_bank1 <= 1'b0; else if(set_dio_input_register_ch1) //set ch1 as input w_dio_dir_bank1 <= 1'b1; end // ************************************************ // Setup digital output sequence // ************************************************ (* ramstyle="M9K" *) reg act_message; reg byte_counter = 0; reg samples_to_send = 0; reg samples_sent = 0; reg clocks_per_sample = 0; reg clock_counter = 0; reg dio_output_sequence_state = 2'b00; // State defines parameter STATE_IDLE = 2'b00; parameter STATE_OUPUTTING = 2'b01; wire setup_dio_sequence_register_ch1; assign setup_dio_sequence_register_ch1 = ((chipselect_n == 1'b0) & (write == 1'b1) & (address == SETUP_DIO_SEQUENCE_REG_ADDRESS_BANK1)); always @ (posedge clk or negedge reset_n) begin if(~reset_n) begin // Initialise variables byte_counter <= 0; samples_to_send <= 0; samples_sent <= 0; clocks_per_sample <= 0; clock_counter <= 0; dio_output_sequence_state <= STATE_IDLE; end else if(setup_dio_sequence_register_ch1) // Receive and store sequence message begin // Store message byte act_message <= writedata; if (byte_counter < 511) begin // Message not complete yet, increment byte counter byte_counter <= byte_counter + 1; end else begin // Complete message received byte_counter <= 0; samples_to_send <= {act_message, act_message, act_message, act_message}; clocks_per_sample <= 50*({act_message, act_message, act_message, act_message}); // Minimum sample time = 50 clock cycles = 1uS // Start sending output samples clock_counter <= 0; samples_sent <= 0; dio_output_sequence_state <= STATE_OUPUTTING; end end else // Send sequence message output begin case (dio_output_sequence_state) STATE_IDLE: begin clock_counter <= 0; samples_sent <= 0; end STATE_OUPUTTING: begin if (samples_to_send == 0) begin // Empty sequence dio_output_sequence_state <= STATE_IDLE; end else if (clock_counter == 0) begin // Send sample w_dio_output_bank1 <= act_message; // Check for end of sequence if (samples_sent < (samples_to_send-1)) begin // Increment samples_sent and reload counter samples_sent <= samples_sent + 1; clock_counter <= (clocks_per_sample - 1); end else begin // End sequence //dio_output_sequence_state <= STATE_IDLE; // Restart sequence samples_sent <= 0; clock_counter <= (clocks_per_sample - 1); end end else begin // Decrement reload counter clock_counter <= clock_counter - 1; end end endcase end end endmodule
0 Kudos
10 Replies
Altera_Forum
Honored Contributor II
961 Views

Hi, 

 

Unfortunately I do not know Verilog well, but the more typical way of using a RAM would be to instantiate a RAM component. You can do this with the Plug-In Megawizard, or by hand if you know the model name and parameters.  

 

There has been a post some time ago about the ability of Quartus to recognise a RAM from behavoural code and then to automatically instantiate a RAM component, but that was for VHDL. Your syntax needs to be correct for the compiler to recognise the RAM. Personally I prefer to instantiate the RAM component explicitly since then I have control over the use of my RAM resources. 

 

Regards, 

Niki
0 Kudos
Altera_Forum
Honored Contributor II
961 Views

Hi, 

 

Not knowing Verilog, something did strike me as odd in your code. Your always process is defined as:  

always @ (posedge clk or negedge reset_n) 

It seems like you want the process to trigger on a rising clock edge or a falling reset_n edge. Normally you want the process to trigger on the clock edge and on the reset level. Having two edges like this is generally not synthesizable - or at least not to the logic you intended. I may just be advertising my ignorence, and maybe this is the way you specify it in Verilog, it just lokked odd to me.  

 

Regards, 

Niki
0 Kudos
Altera_Forum
Honored Contributor II
961 Views

Hi Bras0463, 

 

in VHDL as well as in Verilog, Quartus will synthesize a ram block that is actually used as a normal ram (in: address, in & out: data). It will normally use on-chip block ram for that. 

 

In your description however you are using specific addresses to your act_message ram. This is illustrated (marked in red) in the code-snippet below: 

 

 

--- Quote Start ---  

... begin // Complete message received byte_counter <= 0; samples_to_send <= {act_message, act_message, act_message, act_message}; clocks_per_sample <= 50*({act_message, act_message, act_message, act_message}); // Minimum sample time = 50 clock cycles = 1uS // Start sending output samples clock_counter <= 0; samples_sent <= 0; dio_output_sequence_state <= STATE_OUPUTTING; end ... 

--- Quote End ---  

 

 

Remember designing hardware is NOT like writing software! 

 

When you use specific indices to your hardware like act_message[10] it means that the harware always needs access with wires from this memory location. This is also true for other memory addresses with specific indexes. There is no way that you will have instantaneous access to several different places in your memory when you would only be able to use an on-chip block RAM. Therefore Quartus correctly synthesizes a ram based on LE's. This is only to enable the access to the words on different addresses. 

 

To solve this problem you should define a multiplexor as input to the address of your act_message RAM. Depending on the control signals of your multiplexor you will be able to determine what word you want to read at a specific time. At that time Quartus will also automatically be able to infer an on-chip block RAM. 

 

Hope this helps...
0 Kudos
Altera_Forum
Honored Contributor II
961 Views

 

--- Quote Start ---  

Not knowing Verilog 

--- Quote End ---  

That's excadtly the point. Although always @ (posedge clk or negedge reset_n) may look strange to a VHDL programmer, it's correct synthesizable Verilog syntax. It's simply the equivalent of the VHDL construct 

 

--- Quote Start ---  

if reset_n ='0' 

elsif rising_edge(clk) 

end if; 

--- Quote End ---  

 

You may want to take a look a the Quartus Verilog templates to get an idea of it, 

 

The "ignoring ramstyle" problem is rather obvious from this lines 

samples_to_send <= {act_message, act_message, act_message, act_message}; clocks_per_sample <= 50*({act_message, act_message, act_message, act_message}); 

They would already require an 8-port RAM, because 8 values are read simultaneuosly.
0 Kudos
Altera_Forum
Honored Contributor II
961 Views

Hi Niki, 

 

 

--- Quote Start ---  

Hi, 

 

Not knowing Verilog, something did strike me as odd in your code. Your always process is defined as:  

always @ (posedge clk or negedge reset_n) 

It seems like you want the process to trigger on a rising clock edge or a falling reset_n edge. Normally you want the process to trigger on the clock edge and on the reset level. Having two edges like this is generally not synthesizable - or at least not to the logic you intended. I may just be advertising my ignorence, and maybe this is the way you specify it in Verilog, it just lokked odd to me.  

--- Quote End ---  

 

 

always @ (posedge clk or negedge reset_n)is perfectly OK to design synchronous circuits with asynchronous reset.
0 Kudos
Altera_Forum
Honored Contributor II
961 Views

Also, don't forget when you have your Verilog/VHDL file open in Quartus, go to Edit -> Insert Template. They've got templates for RAMs that you can look at or paste directly into your code. I also like this a lot for the altera attributes and synthesis attributes. 

(But Sanmao nailed the problem on why synthesis won't infer it to begin with)
0 Kudos
Altera_Forum
Honored Contributor II
961 Views

Hi, 

 

Thanks! Seems I have indeed advertized my ignorance!:o  

 

Regards, 

Niki
0 Kudos
Altera_Forum
Honored Contributor II
961 Views

Thanks for all the responses. 

 

I think I understand the problem now. Am I right in thinking that if I had only accessed one location in act_message[] per clock cycle then it would have been implemented in RAM?
0 Kudos
Altera_Forum
Honored Contributor II
961 Views

All FPGA internal RAM blocks are dual-ported, so you can basically access two locations simultaneously. But there are additional requirements to allow RAM inference. So it's best to start with Quartus editor templates for RAM inference and modify them stepwise according to your requirements.

0 Kudos
Altera_Forum
Honored Contributor II
961 Views

i agree with starting from Quartus II templates or the Recommended HDL Coding Styles manual. if you deviate too much from the template all of the features may not be inferred. for example i just noticed on one of my designs that the M9K's output registers are not being used, so i will have to simplify my code or maybe just use the explicit template and keep my logic in a higher level file. 

 

there was also a post about RAM coding styles on comp.arch.fpga today.
0 Kudos
Reply