Forum Discussion

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

ramstyle attribute in Quartus synthesis

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 

10 Replies

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

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

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

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

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

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

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

    Hi,

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

    Regards,

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

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

    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.

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

    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.