# Easier way to implement a large look-up table in Verilog?

I am designing the FPGA-based control for a power-electronic AC/DC converter. This converter has five output voltage levels, so it has 8 switching instances. The levels are given by h, mh, z, ml, l, for high, mid-high, zero, mid-low, and low, respectively. I am using a time-limited counter as the theta input to reset every 0.02-s, for a 50-Hz reset (so for the 1e-8-s time-step, there are 2e6 time-steps for the 50-Hz cycle). So, these 8 switching instances are also in terms of time-steps. Moreover, I am using 3 toggle-switch inputs to determine the AC voltage amplitude, i.e., lvl_select. Therefore, there are 2^3=8 total operating modes, each requiring the 8 switching instances. These switching angles are labelled angle1, angle2,.., angle8

Sorry for the background, as it isn't quite necessary for my question.

The way I have implemented this code (in short-hand) is:

input [31:0] theta;
input [2:0] lvl_select;
wire [31:0] angle1, angle2, ..., angle8 [0:7];
output reg h, mh, z, ml, l;
integer i;

assign angle1[0] = 95549;
assign angle2[0] = 904451;
.
.
.
assign angle8[0] = 1914451;

.
.
.

assign angle1[7] = 72345;
assign angle2[7] = 186932;
.
.
.
assign angle8[7] = 1574124;

always @ (posedge clk)
begin
h = 0;
mh = 0;
z = 0;
ml = 0;
l = 0;
for(i = 0; i < 8; i = i+1)
begin
if (i == lvl_select)
begin
if(theta < angle1[i])
z = 1;
else if (theta < angle2[i])
mh = 1;
else if (theta < angle3[i])
h = 1;
else if (theta < angle4[i])
mh = 1;
else if (theta < angle5[i])
z = 1;
else if (theta < angle6[i])
ml = 1;
else if (theta < angle7[i])
l = 1;
else if (theta < angle8[i])
ml = 1;
else
z = 1;
end
end
end


This is the code in a nutshell. I haven't verified that it works yet, but I think it should (unless you can see why it shouldn't?).

My question is: is there a way to add all of the 64 angle values in a smarter way? Right now there are 64 inputs, which I may want to expand in the future. I have the values in an excel sheet/MATLAB array right now. Please let me know if you have dealt with similar things in the past, thanks a lot!

• Are the angles equally spaced? Is there a way to calculate the angles in Verilog code rather than store them as constants? Dec 16, 2020 at 18:13
• An 8*8 array of integers is not a very large lookup table, so is this a case of premature optimisation?
– user16324
Dec 16, 2020 at 18:17
• This question has a nice answer showing how to make a large fixed look-up table. I'd suggest that if you're making a bunch of look-up tables from something like Matlab that you write a script that generates the appropriate chunk of verilog, or even an entire .v file. That'll cut down on the hand work as you tune your design. Dec 16, 2020 at 18:36
• Actually, that's a good point. I can probably easily make a script in matlab... Thanks a lot for the idea. Dec 16, 2020 at 19:00
• Does this answer your question? Creating a verilog code for 4-bit multiplier using lookup table Dec 16, 2020 at 23:22

Since the angle data exists in excel/MATLAB, you could export it as a file of hex/bin values, then load it with $readmemh()/$readmemb(). For example:

angle1.hex :

1753D // hex for 95549
DCD03 // hex for 904451
...


Then load the file in Verilog:

reg [31:0] angle1, angle2, ..., angle8 [0:7];
...
initial begin
$readmemh("angle1.hex", angle1);$readmemh("angle2.hex", angle2);
// ...
$readmemh("angle8.hex", angle8); end  You could put all 64 entries into one file in a single hex file. Doing thins means you will need to adjust your indexing to map correctly (ex:angle3[i] would probably become angle[16+i]). EDIT 19 Dec 2020: If the synthesizer does not support initial or $readmemh then modify the excel/MATLAB export to prefix the assign angel#[#] = and use the include feature. This will allow you to separate the file management of your angel values and RTL with the compiler seeing it as unified. If the synthesizer support SystemVerilog, you use reduce the line of code of the generated file with the unpacked array construct '{}, but it is basically visual differnce.

angles.txt:

assign angel1[0] = 95549;
assign angle1[1] = 904451;
...


Verilog:

...
wire [31:0] angle1, angle2, ..., angle8 [0:7];
...
include "angles.txt";
...


)ther alternative: Emacs has verilog-mode which has a AUTO_LISP and AUTO_PERL expansion templates. There also an embedded Perl, Ruby, Python, and probable other languages that can be used to read excel/MATLAB and generate/expand the necessary code, All depended on what language(s) you are accustom to. Here is a link to an old answer I did on Stack Overflow related to these options https://stackoverflow.com/a/34230970/1959732

Synchronous logic should be assigned with non-blocking (<=) assignments. This will help with simulation vs synthesis mismatches.

Having if (i == lvl_select) in the for-loop eliminates the need for the for-loop. You can just get the index of the angle from lvl_select directly. Depending on your synthesizer, this might reduce the overall area/gate-count.

always @ (posedge clk)
begin
h <= 0;
mh <= 0;
z <= 0;
ml <= 0;
l <= 0;
if (theta < angle1[lvl_select])
z <= 1;
else if (theta < angle2[lvl_select])
mh <= 1;
else if (theta < angle3[lvl_select])
h <= 1;
else if (theta < angle4[lvl_select])
mh <= 1;
else if (theta < angle5[lvl_select])
z <= 1;
else if (theta < angle6[lvl_select])
ml <= 1;
else if (theta < angle7[lvl_select])
l <= 1;
else if (theta < angle8[lvl_select])
ml <= 1;
else
z <= 1;
end

• Thanks a lot for the answer! BTW, is the "assign" nomenclature I used for the defining the angles proper? Does it matter? Additionally, why exactly should non-blocking be used here? Would the values of h,.., l not be getting assigned two separate values at the same time? Dec 18, 2020 at 16:51
• @CaptainFantastic Verilog assign is used to drive net types (ex wire). Variable types (ex reg & integer) are assigned within procedural blocks (ex always, initial, withinbegin-end). Do not use assign inside a procedural blocks; it is a legacy feature for a corner case behavior not used by typical RTL. FYI assign and always are executed concurrently, meaning the order of does not matter and it is up to the designer (aka you) to make sure there isn't a non-resolving feedback loop between them. The content within a procedural block (begin-end) is executed sequentially.
– Greg
Dec 20, 2020 at 0:25
• @CaptainFantastic for blocking vs non-blocking assignments, read sunburst-design.com/papers/CummingsSNUG2000SJ_NBA.pdf
– Greg
Dec 20, 2020 at 0:28
• Hi Greg, Thanks again for the answer. Apparently my simulation tools doesn't support the "initial" block. So, it works in simulation but not when compiled and uploaded to the FPGA. Do you have any idea how i can initialize these values in a "clever" way? Dec 20, 2020 at 4:51
• @CaptainFantastic interesting usually IC synthesis tools reject/ignore initial and FPGA tools allow initial for variable initialization and setting ROM images. I expanded my answer with other possible strategies.
– Greg
Dec 20, 2020 at 5:33

Some example

      module fixedsize_array;//declaration of array’s

int array2[5:0];             //single dimension array
int array3[2:0][3:0];        //multi dimension array

initial begin
//array initialization
array2 = '{0,1,2,3,4,5};
array3 = '{'{0,1,2,3},'{4,5,6,7},'{8,9,10,11}};
end
endmodule


• The '{} syntax is SystemVerilog and not supported in Verilog. But any modern Verilog simulator/synthesizer should support SystemVerilog by simply changing the file extension from .v to .sv