Parameters and generate - Page 1
Forum section: electronics / general

author: Foxxz
author_rank: Regular Contributor
timestamp: January 23, 2021, 01:50:22 am
content: Alright I'm new at FPGAs and I'm going to be this way a while.So I have the code segment below. Basically this is taking in 1 clock pulses from a button debouncer that only sends 1 pulse every time its pressed. I want this module to hold the last sent signal high. This code works how I want.Code:module selector(input i_clk,input [4:0] i_pulse,output [4:0] o_select);reg [4:0] select = 1;always @(posedge i_clk) begincase (i_pulse)5'b10000: select <= i_pulse;5'b01000: select <= i_pulse;5'b00100: select <= i_pulse;5'b00010: select <= i_pulse;5'b00001: select <= i_pulse;default: select <= select;endcaseendassign o_select = select;endmoduleWhat I'm trying to do is parameterize it but it seems like verilog was written by hardware guys with limited forethought for preprocessing and macros. They put in generate but it seems of very limited use. I was trying to use it to create the case statements. Basically I'm just trying to ensure only one bit is set to 1 on the i_pulse input. So maybe theres a better way to do this as this doesn't work at all or the way I wish it did.Code:module selector #(parameter WIDTH = 2)(input i_clk,input [WIDTH - 1:0] i_pulse,output [WIDTH - 1:0] o_select);reg [WIDTH - 1:0] select = 1;generategenvar i;always @(posedge i_clk) begincase (i_pulse)for (i = 0; i < WIDTH; i = i + 1)2**i : select <= i_pulse;default: select <= select;endcaseendendgenerateassign o_select = select;endmodule

author: BrianHG
author_rank: Super Contributor
timestamp: January 23, 2021, 04:17:45 am
content: Quote from: Foxxz on January 23, 2021, 01:50:22 amHow about:Code:module selector #(parameter WIDTH = 2)(input i_clk,input [WIDTH - 1:0] i_pulse,output reg [WIDTH - 1:0] o_select);always @(posedge i_clk) beginfor (int i = 0; i < WIDTH; i = i + 1)if ( (WIDTH)'(2**i) == i_pulse) o_select <= i_pulse;end // ...always @...endmodule

author: Foxxz
author_rank: Regular Contributor
timestamp: January 23, 2021, 05:26:42 am
content: Thank you for taking the time to show me the way.Here is where I landed. I added the select register back in so I could set an initial condition. The module worked without it except the output was all 0s until a button was pressed.Code:module selector #(parameter WIDTH = 2)(input i_clk,input [WIDTH - 1:0] i_pulse,output [WIDTH - 1:0] o_select);reg [WIDTH - 1:0] select = 1;always @(posedge i_clk) beginfor (integer i = 0; i < WIDTH; i = i + 1)if ((2**i) == i_pulse) select <= i_pulse;endassign o_select = select;endmoduleI guess my only remaining question is what does generate do then? I thought I had to wrap the for loop inside of generate like a preprocessing macro or inside of an initial statement only to be used in a testbench. And I assume that the for loop is simply generating a bunch of those if statements in hardware (unrolling the loop essentially) and not actually implementing a loop of some sort in hardware?In any case the above dynamic code utilized just a bit less of the FPGA resources than the unparameterized case statement module. Again, I appreciate the help.

author: hamster_nz
author_rank: Super Contributor
timestamp: January 23, 2021, 07:16:08 am
content: Not really answering your question, but this is quite an efficient way to do it.Code:if(input != 0 && (input &(input-1)) == 0) {output <= input;}- Two n-bit comparison against 0 (each is an n-input OR)- An n-bit subtraction- An n-bit AND- A 2-bit AND.- A n-bit register.Most likely the most efficient way for longer lengths.

author: NorthGuy
author_rank: Super Contributor
timestamp: January 23, 2021, 03:34:02 pm
content: Quote from: Foxxz on January 23, 2021, 05:26:42 amThe amount of logic has nothing to do with how you write it, but depends heavily on the WIDTH.Quote from: Foxxz on January 23, 2021, 05:26:42 amThere will be no statements in hardware. Basically, the hardware needs to produce a signal which is '1' if there's only one bit set in the inputs and '0' otherwise. Then this signal may be used as a selector to a mux which selects either the old value or the input.The signal is produced by combinatorial logic, which consists of LUTs in FPGA. LUT isLookUpTable. LUT has n inputs and 1 output. LUT can be configured to produce any conceivable n-bit logical operation. Some FPGA have 4-niput LUTs, others have 6-input LUTs.If WIDTH is small enough, you only need a single LUT to produce the selector signal for the muxes. For example, if your FPGA has 6-input LUTs, then all the logic with WIDTH 6 or less will be done in a single LUT.If WIDTH is larger, then many LUTs should be chained - outputs of some LUTs are fed to inputs of other LUTs. At large WIDTH this becomes complicated. The speed of the resulting logic will depends on the number of logic layers (that is the maximum number of LUTs a signal would need to transverse on its way from input to the result). You can optimize for speed (minimizing the number of layers) or for area (minimizing the number of LUTs).As WIDTH becomes even larger (e.g. 100), tools may not be able to do a good optimizing job.

author: NorthGuy
author_rank: Super Contributor
timestamp: January 23, 2021, 03:49:50 pm
content: Quote from: hamster_nz on January 23, 2021, 07:16:08 amI think if you choose the "carry" pathway (that is the processing for n-th bit depends on the (n-1)-th bit), then it will be more efficient if you specialize, as opposed to use stock subtraction. Say, for every bit you do the following:Code:// C-like pseudocodehad_too_many_ones ||= current_bit && had_ones;had_ones ||= current_bit;However, for big WIDTH, you would get faster results with hierarchical tree-like approach, where you split the vector in multiple parts (halves?), calculate "had_too_many_ones" and "had_ones" for each part and aggregate.

author: BrianHG
author_rank: Super Contributor
timestamp: January 23, 2021, 04:44:22 pm
content: Quote from: hamster_nz on January 23, 2021, 07:16:08 amYou are assuming how the compiler interprets the IF and does no work of it's own or optimizes for the FPGA fabric.The code I wrote generates a tree of XOR gates on the bits of the input to drive the clock enable.

author: BrianHG
author_rank: Super Contributor
timestamp: January 23, 2021, 04:50:19 pm
content: Quote from: Foxxz on January 23, 2021, 05:26:42 amFor me anyways, I use the generate for 2 things, to either include or exclude complete sections of code, or, to initiate multiple functions/modules of code multiple times in a loop. A use case may be to select between the number of parallel floating point processor instances I wish to call. Or, select between a serializer module dedicated for Xilinx VS Altera at compile time for support between 2 different FPGA vendors.Ezxaple:Code:genvar x;generatefor (x=0 ; x<NUM_LAYERS ; x=x+1) begin : bart_instbart bart_inst(.clk ( clk ),.pc_ena ( pc_ena[3:0] ),.bp_2_rast_cmd ( maggie_to_bp2r ),.cmd_in ( GPU_ram_cmd_out ),.ram_byte_in ( GPU_ram_data_out ),.pixel_out ( BART_to_PAL_MIXER ));endendgenerateThe parameter 'NUM_LAYERS' allows me to call multiple parallel 'bart_inst' modules with multiple IOs packed into dedicated arrays using the genvar 'x'.

author: NorthGuy
author_rank: Super Contributor
timestamp: January 23, 2021, 05:50:33 pm
content: Quote from: BrianHG on January 23, 2021, 04:44:22 pmSure with WIDTH = 2 this is a single XOR gate (I wouldn't call it a tree though), and with WIDTH = 1 this is a straight wire. Try WIDTH = 8, or WIDTH = 100 for that matter.

author: BrianHG
author_rank: Super Contributor
timestamp: January 23, 2021, 06:51:43 pm
content: Quote from: NorthGuy on January 23, 2021, 03:34:02 pmWell, you should always have at the top of your code something like this (Here is another use for generate):Code:generateif ( WIDTH <1 || WIDTH >16 ) initial begin // 'initial' required for Quartus.$warning("*** Module selector's parameter WIDTH is %d. It must be between 1 and 16. ***",(WIDTH)); // Displays a warning message.$error(" Incorrect parameter setting."); // In FPGA compilers, this displays the message in quotes and halts compilation.$stop; // For simulators like Modelsim, who continue after the $error(), this will prevent Modelsim from continuing and attempting to run a full simulation. This is why we have both $error() and $stop().endendgenerate

author: NorthGuy
author_rank: Super Contributor
timestamp: January 23, 2021, 11:28:34 pm
content: Quote from: BrianHG on January 23, 2021, 06:51:43 pmI never do such things, but if you must you must.Nonetheless, it is interesting to see how tools scale. Here's the number of 6-input LUTs I got in Vivado 2018.1 for 7-series FPGA depending on the WIDTH parameter. The number in parentheses is how many LUTs I would use if I wrote this manually and my sole goal would be to minimize the number of LUTs:WIDTH = 8 - 2(2)WIDTH = 16 - 8(5)WIDTH = 32 - 23(10)WIDTH = 64 - 53(21)WIDTH = 128 - 5530(42)WIDTH = 256 - 22066(85)It's quite interesting to see how it works decently, but then wheels completely come off at WIDTH = 128.Would be interesting if anyone would repeat the experiment with their favourite tool. Here's the exact code I used in Vivado:Code:module selector #(WIDTH = 256)(input i_clk,input [WIDTH - 1:0] i_pulse,output reg o_select);integer i;always @(posedge i_clk) begino_select <= 1'b0;for (i = 0; i < WIDTH; i = i + 1)if ( (2**i) == i_pulse) o_select <= 1'b1;end // ...always @...endmodule

author: BrianHG
author_rank: Super Contributor
timestamp: January 23, 2021, 11:43:33 pm
content: Quote from: NorthGuy on January 23, 2021, 11:28:34 pmSo, if you distribute your IP cores, and let's say the 'WIDTH' is used as a dividend somewhere, or it can only be 10 different specific numbers. Your not going to tell your user's that they assigned an illegal value?I've had parameter typos mess up the way a ram controller is being clocked which left alone can lead to unforeseen data errors in the field.

author: Foxxz
author_rank: Regular Contributor
timestamp: January 24, 2021, 12:53:30 am
content: I wrote a parameterized mux that is taking the selection signal from the code above. I was having trouble getting the tools to build it and getting indiscriminate errors so I took it over to EDA playground and ran it through the simulators. They at least gave me more helpful error messages.The jist of the error messages seemed to be that, in the code I have commented out below, the tools can't seem to calculate the offsets. They seem not to understand what 'i' is. However, the line using a bit shift seems to work fine.Code:module mux #(parameter WORD_SIZE = 8,parameter INPUTS = 2)(input [INPUTS - 1:0] i_sel,input [(WORD_SIZE * INPUTS) - 1:0] i_bus,output reg [WORD_SIZE - 1:0] o_bus);always @* beginfor (integer i = 1; i <= INPUTS; i = i + 1)beginif (2**(i - 1) == i_sel)//o_bus <= i_bus[(WORD_SIZE * i) - 1:(WORD_SIZE * i) - WORD_SIZE];o_bus <= i_bus >> (WORD_SIZE * (i - 1));endendendendmodule

author: NorthGuy
author_rank: Super Contributor
timestamp: January 24, 2021, 01:33:42 am
content: Quote from: BrianHG on January 23, 2021, 11:43:33 pmI'm not in a business of selling IPs. I cannot tell what I would do if I was.

author: BrianHG
author_rank: Super Contributor
timestamp: January 24, 2021, 02:13:01 am
content: Why not:Code:module mux #(parameter WORD_SIZE = 8,parameter INPUTS = 2)(input clk, // ********************** Without a clock, there is no latch or holding of state.input [INPUTS - 1:0] i_sel,input [WORD_SIZE-1:0] i_bus [0:(INPUTS - 1)],output reg [WORD_SIZE - 1:0] o_bus);always @(posedge clk) beginfor (integer i = 0; i < WORD_SIZE; i = i + 1)beginif ( (2**i) == i_bus) o_bus <= i_bus;end // for loop.end // always @posedge clk...endmoduleOoops, you need to be in SystemVerilog which supports addressable arrays and buses.I have never used an array in this manner with an IF inside a for loop, so I have no clue what it will do.

author: Foxxz
author_rank: Regular Contributor
timestamp: January 24, 2021, 02:59:09 am
content: So right now I'm not trying to address an array. I'm just trying to select some bits and use the for loop to generate the statements like...In the event I have 32 bit words and 5 inputs this would be for the top endCode:if (5 == i_sel) o_bus <= i_bus[159:128];So theres no dynamic address here. Its just the i_sel input changing which set of bits I'm outputting on the mux (but maybe that still counts). My i_bus is coming from clocked registers. I guess I don't need an output register since I'm just routing signals?I'm not a professional software dev but I do occasionally write software. And verilog is giving me fits as it and its tools are so different.I appreciate yours and everyones patience in helping me out.Quote from: BrianHG on January 24, 2021, 02:13:01 am

author: Foxxz
author_rank: Regular Contributor
timestamp: January 24, 2021, 04:56:04 am
content: Okay... I'm putting this here for posterity in the hopes maybe it will help others...I was trying to use a for loop to generate this code using WORD_SIZE = 8 as a parameter and 'i' being the loop variableCode:if (2**(i - 1) == i_sel) o_bus <= i_bus[(WORD_SIZE * i) - 1:(WORD_SIZE * i) - WORD_SIZE];However, after some googling of error messages I have learned that the left land size of the part select will be the starting index of the part select. Instead of using a colon ':' to separate and then specify the ending index you use '-:' or '+:' and a number to specify how many bits you want to include. That's useful but it kind of sucks to force that language construct in this situation. So fixing my example above it becomes...Code:if (2**(i - 1) == i_sel) o_bus <= i_bus[(WORD_SIZE * i) - 1 -: WORD_SIZE];Stack overflow to the rescue again:https://stackoverflow.com/questions/60268113/im-getting-this-error-for-my-verilog-code-illegal-operation-for-constant-exprVerilog is a strange language...

author: Someone
author_rank: Super Contributor
timestamp: January 24, 2021, 07:26:54 am
content: Some people seem to have short memories:https://www.eevblog.com/forum/fpga/determine-whether-a-binary-number-is-of-power-of-two-in-verilog-code/?all

author: BrianHG
author_rank: Super Contributor
timestamp: January 24, 2021, 09:17:10 am
content: Quote from: Someone on January 24, 2021, 07:26:54 amI don't know about the other examples on that thread, but my examples in that thread weren't parameter sizable.The for-loop & IF() example listed here does the equivilant to the summing of bits except it automatically sizes to the parameter 'WIDTH' and the compiler builds the XOR tree, or summing of bits to trigger the clock enable. (Or equivalent there of...)

author: NorthGuy
author_rank: Super Contributor
timestamp: January 24, 2021, 02:33:38 pm
content: Quote from: Foxxz on January 24, 2021, 04:56:04 amConsider using a memory block to store the i -> o_bus translations. i will be fed to memory as an address, o_bus will be read from memory as data. You can make the memory static, or you can make it writeable from a different process.

author: NorthGuy
author_rank: Super Contributor
timestamp: January 24, 2021, 02:38:52 pm
content: Quote from: Someone on January 24, 2021, 07:26:54 amWow! This was less than two years ago. I felt I faced the problem before, even tried to remember where ...