How to instantiate a submodule in Verilog

Status
Not open for further replies.
inferring rams/roms still can be a tricky subject. you should read the synthesis coding guide and also look at the logs and implementation results. seemingly minor details can cause the tools to do unexpected things.
 

Hello!

Thanks for your reply.

inferring rams/roms still can be a tricky subject. you should read the synthesis coding guide and also look at the logs and implementation results. seemingly minor details can cause the tools to do unexpected things.

I'm aware this might be a tricky subject. But although it cannot really be compared, writing
in a CPU flash can be tricky. But it can be done, and for example TI gives examples on how to use
the flash which is inside of the processor (unlock the flash, and if you want to alter one byte for
instance, save the page that contains it, erase the flash page, change the byte in the saved buffer
and save it, while being aware that you cannot set breakpoints inside of this process).

So, RAM being internal to the FPGA, I don't think I'm expecting too much to have a piece of
verilog or VHDL that would make a simple demonstration of what it works. So to rephrase
my question: can anybody point me to a short piece of verilog that explains how to use
MAX10's internal RAM?

Thanks!

Pastel
 

So, RAM being internal to the FPGA, I don't think I'm expecting too much to have a piece of verilog or VHDL that would make a simple demonstration of what it works.
It's tricky. best to read the docs for whatever synthesis tool you're using. More reliable than people guessing. You may also be expecting too much, sadly.
 


You didn't yet answer which MAX10 type you are using. Probably you overlooked the comment in post #17 about "compact" flash types not supporting ROM inference.

It makes little sense to explain RAM/ROM coding style in detail and then find out, that it's not supported by your MAX10 device.

Initializing the RAM table at runtime through external CPU will work with any MAX10 however.
 

Hello!

Thanks for your reply.
The MAX10 I'm using is MAX10M50DAF484C8G.
One of the largest.
I'm looking for a verilog method to specify that I want to use the internal RAM.
Currently looking at the output of the IP included in Quartus.
I don't want to use a standard IP for the moment, I just want to use one memory block
and more than one if it works.

Thanks,

Pastel
 

O.K. MAX10xxDA supports RAM initialization (ROM inference).

As a starting point, you can use the RAM and ROM designs in Quartus Verilog templates, accessible through context menu in editor window.

- - - Updated - - -

There's a little trick with MAX10. You need to select a configuration scheme with memory initialization to allow ROM inference

 

Previous example supplemented with $readmemh initialization. Other initialization methods are possible, but not all are compatible with dual port ROM inference.


Code Verilog - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
module test190803(DA, DB, max_clk, clk);
    output reg[15:0] DA, DB; // Data of the DAC
    output reg max_clk;  // Clock of the DAC
    input clk; // FPGA 100 MHz input clock
    // Some variables
    reg[15:0] sine[0:1023];
    reg[9:0] sinindex;
    reg[9:0] cosindex;
 
    initial begin
        sinindex = 0;
        cosindex = 256;
        $readmemh("sinetable.txt", sine);
    end
    always@(posedge clk) begin
        if(max_clk == 0) begin
            max_clk <= 1;
          sinindex <= sinindex+1;
          cosindex <= cosindex+1;
        end
        else begin
            max_clk <= 0;
        end
        // ROM must be read unconditionally to infer dual port ROM
        DA <= sine[sinindex];
        DB <= sine[cosindex];
    end
endmodule

 

Attachments

  • sinetable.txt
    5.9 KB · Views: 89

Hello!

Thanks for your reply!
I tried to do something... But I don't understand the compilation errors.
Just in case, I took your code although mine was basically the same.
See attached verilog.
So basically, I have used the context menu to insert a dual port ROM, and a
template was created.I changed the data and address widths (I suppose there
is nothing wrong in the principle).
I tried to instantiate one dual_port_rom in my code...
But I got this error message
Code:
Info (276014): Found 1 instances of uninferred RAM logic
	Info (276013): RAM logic "dual_port_rom:SineRom|rom" is uninferred because MIF is not supported for the selected family
Error (276003): Cannot convert all sets of registers into RAM megafunctions when creating nodes. The resulting number of registers remaining in design exceeds the number of registers in the device or the number specified by the assignment max_number_of_registers_from_uninferred_rams. This can cause longer compilation time or result in insufficient memory to complete Analysis and Synthesis
Error: Quartus Prime Analysis & Synthesis was unsuccessful. 1 error, 6 warnings
	Error: Peak virtual memory: 652 megabytes
	Error: Processing ended: Fri Aug 09 16:52:29 2019
	Error: Elapsed time: 00:00:09
	Error: Total CPU time (on all processors): 00:00:18
Error (293001): Quartus Prime Full Compilation was unsuccessful. 3 errors, 6 warnings

At that point, it looks like I have exactly the same message as before without inserting a template,
so the ROM was not inferred.

Now you were writing:
There's a little trick with MAX10. You need to select a configuration scheme with memory initialization to allow ROM inference

I couldn't get the setting window of your post below. Don't forget
I'm an absolute beginner, so if you don't tell me the menu path from the root
window, the odds of finding it are quite low.

Another question: this is a dual port ROM, which is fine for the current design,
but is it possible to make a 8-port ROM? For example by adding addr_a to addr_h,
q_a to q_h?

One more thing: I suppose the ROM will do the job as is, and therefore I don't need
the DA <= sine[sinindex]; (and same for DB) that I used before, right?

Thanks,

Pastel


Code Verilog - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// Quartus Prime Verilog Template
// Dual Port ROM
 
module dual_port_rom
        #(parameter DATA_WIDTH=16, parameter ADDR_WIDTH=13) (
    input [(ADDR_WIDTH-1):0] addr_a, addr_b,
    input clk, 
    output reg [(DATA_WIDTH-1):0] q_a, q_b);
    // Declare the ROM variable
    reg [DATA_WIDTH-1:0] rom[2**ADDR_WIDTH-1:0];
    // Initialize the ROM with $readmemh instead of readmemb.
    initial begin
        $readmemh("sine8192.hex", rom);
    end
    always @ (posedge clk)
    begin
        q_a <= rom[addr_a];
        q_b <= rom[addr_b];
    end
endmodule
 
module Test190801(DA, DB, max_clk, clk);
    output reg[15:0] DA, DB; // Data of the DAC
    output reg max_clk;  // Clock of the DAC
    input clk; // FPGA 100 MHz input clock
    // Some variables
    reg[12:0] sinindex;
    reg[12:0] cosindex;
    //  Instanciation of dual port ROM
    dual_port_rom SineRom(
        .addr_a(sinindex), .addr_b(sinindex),
        .clk(clk),
        q_a(DA), q_b(DB)
    );
    //  Index calculation
    always@(posedge clk) begin
        if(max_clk == 0) begin
            max_clk <= 1;
            sinindex <= sinindex+1;
            cosindex <= cosindex+1;
        end
        else begin
            max_clk <= 0;
        end
    end
endmodule

 

The error message shows that Quartus is trying to infer ROM, however the device type or configuration mode isn't appropriate.

Go for "Assignments/Device/Device and Pin Options"
 

Hello!

Thanks for your mail!
I'm getting closer, this time it compiled. Now I would need some explanation.
Here is the whole code. So I used the contextual menu to get a template. Now from
my understanding, the submodule dual_port_rom, receives from the main module
everything necessary to send the data I want to output ports.
- It knows the outputs that were passed from the main module. (sorry, I use C / C++ vocabulary,
I know that the variables don't move across the program, but anyway it looks like that).
- It gets also the clock, and the 2 indexes, sinindex and cosindex. So its always@ block
uses the internal data of this object to set data where it has to go, and therefore
I don't need to have DA <= something in the top object.
So I thought the simple fact that I declare a dual_port_rom SineRom in the top object
would make it work.

Now it doesn't work, and I would appreciate if anybody could explain me what's wrong.

NB: When I say it doesn't work: the previous version used to work, to send data to my
MAX5875 ADC and I could see a sine wave on the scope. Now I get absolutely nothing.
It's the same file, nothing has changed (pin assignment is the same).

Thanks for any hint.

Pastel


Code Verilog - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// Quartus Prime Verilog Template
// Dual Port ROM
 
module dual_port_rom
        #(parameter DATA_WIDTH=16, parameter ADDR_WIDTH=13) (
    input [(ADDR_WIDTH-1):0] addr_a, addr_b,
    input clk, 
    output reg [(DATA_WIDTH-1):0] q_a, q_b);
    // Declare the ROM variable
    reg [DATA_WIDTH-1:0] rom[2**ADDR_WIDTH-1:0];
    // Initialize the ROM with $readmemh instead of readmemb.
    initial begin
        $readmemh("sine8192.hex", rom);
    end
    always @ (posedge clk)
    begin
        q_a <= rom[addr_a];
        q_b <= rom[addr_b];
    end
endmodule
 
module Test190801(DA, DB, max_clk, clk);
    output wire[15:0] DA, DB; // Data of the DAC
    output reg max_clk;  // Clock of the DAC
    input clk; // FPGA 100 MHz input clock
    // Some variables
    reg[12:0] sinindex;
    reg[12:0] cosindex;
    //  Instanciation of dual port ROM
    dual_port_rom SineRom(
        .addr_a(sinindex), .addr_b(sinindex),
        .clk(clk),
        .q_a(DA), .q_b(DB)
    );
    //  Index calculation
    always@(posedge clk) begin
        if(max_clk == 0) begin
            max_clk <= 1;
            sinindex <= sinindex+1;
            cosindex <= cosindex+1;
        end
        else begin
            max_clk <= 0;
        end
    end
endmodule

 

It's not necessary to implement the ROM as separate module, it can be also embedded in the main code as in my example, but this shouldn't change anything.

I don't find a reason for non-function of your code at first sight. Did you check data and clock signals with oscilloscope?
 

Ignoring the lack of any initialization of the sinindex and cosindex using a reset of some sort (don't know about max10, but some FPGAs have no guaranteed power up state for flip-flops) the code you posted in 31 simulates properly, so the problem is either in the synthesis or the p&r.

Does the design still have the expect resource utilization counts? Did you verify the pins were assigned correctly by p&r? Did the design pass all DRC check during the compilation.

FYI, just because a design finishes compilation doesn't mean it was compiled, placed, and routed correctly, GIGO applies.

- - - Updated - - -

Another question: this is a dual port ROM, which is fine for the current design,
but is it possible to make a 8-port ROM? For example by adding addr_a to addr_h,
q_a to q_h?

Yes, but it will require replicating the block RAM "ROM" 4 times to get to 8 as the physical block that comprises a memory in the device has only two physical memory array interface ports.
 

is "sine8192.hex" something that works in simulation? I suspect readmemh failure is just a warning or silent failure.

IIRC the last time I used readmemh with quartus I needed to add a sanity check to the build script because it wouldn't always work. maybe this was something I did wrong generating the data, but I don't trust the tools either way.
 

is "sine8192.hex" something that works in simulation? I suspect readmemh failure is just a warning or silent failure.
Simulation wise Modelsim handles a file with hex data with $readmemh just fine. I checked that the data was read in using the 1024 size file that was posted earlier in the thread. If Quartus doesn't initialize a ROM properly with the $readmemh system task then that is a Quartus tool problem (Xilinx/Microsemi has no problem doing this).
 

You can verify that Quartus generates a respective *.mif file in the /db directory, the file name is listed in the synthesis report.


If you worry about correct ROM data, inspect the *.mif file.
 

Hello!

Many thanks for all your support and detailed replies.
By going to the basics (led blinker, but using a submodule to divide the clock), I found out
what was wrong in my code. I needed wires, and (shame, the issue was solved by ads-ee very early in this
thread). In the meantime, my code became a real mess, so I will not post it here,
but anyway I can now load quite large files (64k coefficients sine table).

Right now, I will divide the data by 4 by using the symmetries.

I'm really starting to enjoy FPGA programming. It's really like a new world and I think I will spend the
whole weekend on it...

I will certainly have other questions, but again, many thanks fo you all!

Pastel
 

Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…