1

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? – The Photon Dec 16 '20 at 18:13
  • An 8*8 array of integers is not a very large lookup table, so is this a case of premature optimisation? –  Dec 16 '20 at 18:17
  • 1
    [This question](https://electronics.stackexchange.com/questions/163961/creating-a-verilog-code-for-4-bit-multiplier-using-lookup-table) 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. – TimWescott Dec 16 '20 at 18:36
  • Actually, that's a good point. I can probably easily make a script in matlab... Thanks a lot for the idea. – CaptainFantastic Dec 16 '20 at 19:00
  • Does this answer your question? [Creating a verilog code for 4-bit multiplier using lookup table](https://electronics.stackexchange.com/questions/163961/creating-a-verilog-code-for-4-bit-multiplier-using-lookup-table) – dave_59 Dec 16 '20 at 23:22

2 Answers2

1

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


Other thing with your code:

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
Greg
  • 4,270
  • 1
  • 21
  • 32
  • 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? – CaptainFantastic Dec 18 '20 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, within`begin`-`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 '20 at 00:25
  • @CaptainFantastic for blocking vs non-blocking assignments, read http://www.sunburst-design.com/papers/CummingsSNUG2000SJ_NBA.pdf – Greg Dec 20 '20 at 00: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? – CaptainFantastic Dec 20 '20 at 04: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 '20 at 05:33
  • Thanks again, Greg. I actually already tried the wire/assign method. With the Vivado synthesizer, it appears I could not assign values as an array. I had to make the "angle" reg/wire a 32*8-bit word and assign values as reg [32*8-1] angle1 ={32'dnum1,...,32'dnum8}, etc. Quite the headache, especially since I don't know what I did wrong. Apparently the array structure didn't work when compiled onto the Artix-7/BASYS 3. Any idea why that would be? It works at this point, so I don't need to put more time into it, but I would like to learn from it! – CaptainFantastic Dec 20 '20 at 22:38
0

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

Please check this treat for answer with text files, contained values

https://stackoverflow.com/questions/30000646/easy-way-to-assign-values-to-an-array-in-verilog

Also read: https://stackoverflow.com/questions/23163042/when-to-use-the-tick-for-verilog-array-initialization/23167420

Pinelab
  • 36
  • 4
  • 1
    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 – Greg Dec 17 '20 at 19:00