- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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...
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
--- 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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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)- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi,
Thanks! Seems I have indeed advertized my ignorance!:o Regards, Niki- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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?- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page