Instantiating a LUT and Initialising with a .coe for ModelSim/QuestaSim - vhdl

The Background
This LUT needs a width of 32 and a depth of 256.
So I have a LUT which was created by an IP core. Now I want to instantiate it myself to get it working in the sim (this also helps me learn all of the parameters myself). I've done this many times for FIFOs but never created a LUT before so please check what I've done looks correct. I just want to create a LUT of values and to be able to read them back. I used a block of RAM for this.
I've tried on two different computers with:
QuestaSim-64 10.2c_5
ModelSim SE-64 10.1b
The Problem
So I can compile the code. When I try to open it:
vsim work.top
It opens the IDE and freezes on:
# Loading unisim.rb36_internal_vhdl(rb36_internal_vhdl_v)#1
If I remove:
INIT_FILE => "lut.coe",
Then it loads up fine. So I know that line crashes it.
The LUT:
So I have a LUT, does this look correct to you? Is there any other ways to instantiate a LUT with a .coe file?
lut : RAMB36E1
generic map(
INIT_FILE => "lut.coe",
READ_WIDTH_A => 36
)
port map
(
addrardaddr => addr_lut,
addrbwraddr => X"0000",
cascadeina => '0',
cascadeinb => '0',
clkardclk => clk_i,
clkbwrclk => clk_i,
diadi => X"00000000",
dibdi => X"00000000",
dipadip => X"0",
dipbdip => X"0",
doado => data_lut,
enarden => '1',
enbwren => '0',
injectdbiterr => '0',
injectsbiterr => '0' ,
regceb => '0',
regcearegce => '1',
rstramarstram => rst_i,
rstramb => rst_i,
rstregarstreg => rst_i ,
rstregb => rst_i,
wea => X"0",
webwe => X"00"
);
Tried swapping the above out for 18kb RAM, same error:
# Loading unisim.rb18_internal_vhdl(rb18_internal_vhdl_v)#2
LUT:
lut : RAMB18E1 -- Simple Duel Port mode, 512 deep
generic map(
INIT_FILE => "lut.coe",
RAM_MODE => "SDP"
)
port map
(
addrardaddr => addr_lut,
addrbwraddr => "00000000000000",
clkardclk => clk_i,
clkbwrclk => clk_i,
diadi => X"0000",
dibdi => X"0000",
dipadip => "00",
dipbdip => "00",
doado => data_lut_b,
dobdo => data_lut_a,
enarden => '1',
enbwren => '0',
regceb => '0',
regcearegce => '1',
rstramarstram => rst_i,
rstramb => rst_i,
rstregarstreg => rst_i ,
rstregb => rst_i,
wea => "00",
webwe => X"0"
);

Seriously. Throw away the IP core and the COE file. ((If that's the only place your data is, don't actually throw it away!)
Subtype Data_Word is std_logic_vector(31 downto 0);
Type Lut_Type is Array(0 to 255) of Data_Word;
Constant Lut : Lut_Type := (
0 => X"00000001",
1 => X"00000002",
...
17 => X"DEADBEEF",
others => (others => 'X') );
Substitute your own coefficient of course. For bonus points, use a script or even a C or VHDL program to read the COE file and write the above chunk of VHDL.
Job done.
It's synthesisable, simulatable, and portable to other FPGAs.
(IMO the portability issue is the real reason for most vendors' IP cores. But I'll make an exception for complex cores like PCIe or DDR memory interfaces.)

There is a simple and short way for Xilinx tools to read *.mem or *.hex files directly in VHDL. The file content can be use in simulation and synthesis as BlockRAM initialization.
Xilinx offers a coding example in UG901 on page 124:
type RamType is array(0 to 7) of bit_vector(31 downto 0);
impure function InitRamFromFile (RamFileName : in string) return RamType is
FILE RamFile : text is in RamFileName;
variable RamFileLine : line;
variable RAM : RamType;
begin
for I in RamType'range loop
readline (RamFile, RamFileLine);
read (RamFileLine, RAM(I));
end loop;
return RAM;
end function;
signal RAM : RamType := InitRamFromFile("rams_20c.data");
The example is for a RAM but it can easily be converted to a ROM (LUT) when you remove the write port and replace the signal with a constant.
A more advanced implementation can be found for example in PoC.mem.ocrom.sp. This implementation also works with Altera's sltsyncrams, which csn read *.mif files.

Related

Lattice ICE40 SB_IO VHDL syntax query

To be able to register inputs and outputs at the pin in VHDL you need to define ...
library sb_ice40_components_syn;
use sb_ice40_components_syn.components.all;
and
-- SB_IO: io_ftdi_data INOUT, registered input, falling edge of "i_ftdi_clk" clocked output ...
SB_FTDI_DATA0: SB_IO generic map (
PIN_TYPE => "100100", -- 100100: PIN_OUTPUT_REGISTERED_ENABLE, PIN_INPUT_REGISTERED
PULLUP => '0',
NEG_TRIGGER => '0',
IO_STANDARD => "SB_LVCMOS"
) port map (
PACKAGE_PIN => io_ftdi_data(0),
LATCH_INPUT_VALUE => '0',
CLOCK_ENABLE => '1',
INPUT_CLK => i_ftdi_clk,
OUTPUT_CLK => NOT i_ftdi_clk,
OUTPUT_ENABLE => r_ftdi_oe_n,
D_OUT_0 => r_ftdi_data_out(0),
D_OUT_1 => open,
D_IN_0 => r_ftdi_data_in(0),
D_IN_1 => open
);
This describes a single pin "io_ftdi_data(0)". You then have this repeated 15 times (it's part of a 16bit bus) which seems a bit verbose.
In the Verilog world, it would appear that you can collate all the pins in one SB_IO statement, for example
SB_IO #(
.PIN_TYPE(6'b 1010_01),
.PULLUP(1'b 0)
) flash_io_buf [3:0] (
.PACKAGE_PIN({flash_io3, flash_io2, flash_io1, flash_io0}),
.OUTPUT_ENABLE({flash_io3_oe, flash_io2_oe, flash_io1_oe, flash_io0_oe}),
.D_OUT_0({flash_io3_do, flash_io2_do, flash_io1_do, flash_io0_do}),
.D_IN_0({flash_io3_di, flash_io2_di, flash_io1_di, flash_io0_di})
);
Can anyone translate this Verilog syntax into VHDL?
I have tried wrapping pins in braces etc, but I've so far failed to fall over the correct syntax. VHDL documentation for ICE40/ICECUBE2 seems poor to non-existent and I'm obviously not smart enough do this myself. Anyone?

Where does Vivado reference primitives when building the implemented design?

I'm trying to figure out exactly what file vivado uses to create the attributes of a primitive block. I'm trying to do an experiment with removing some of the properties but no matter what file I edit they always reappear after implementation. Specifically, I'm using an Artix-7 and I'm trying to modify the attributes of the block ram.
Vivado has two different methods for instantiating primitives:
Direct instantiation: You explicitly write a macro and you connect all the signals accordingly. In this case the macro is parametrizable and the properties you write during instantiation are 1:1 matching with the primitive instantiation in the netlist.
Inferring: you write some templated code which then the synthesizer recognize and transform in the equivalent primitive. Controlling primitives here is more complicated and depends on a mix of instantiation style and RTL attributes.
You can find more information in the 7series library guide.
For your question, let's take a look at the BRAM_TDP_MACRO. Here's the direct instantiation template:
BRAM_TDP_MACRO_inst : BRAM_TDP_MACRO
generic map (
BRAM_SIZE => "18Kb", -- Target BRAM, "18Kb" or "36Kb"
DEVICE => "7SERIES", -- Target Device: "VIRTEX5", "VIRTEX6", "7SERIES", "SPARTAN6"
DOA_REG => 0, -- Optional port A output register (0 or 1)
DOB_REG => 0, -- Optional port B output register (0 or 1)
INIT_A => X"000000000", -- Initial values on A output port
INIT_B => X"000000000", -- Initial values on B output port
INIT_FILE => "NONE",
READ_WIDTH_A => 0, -- Valid values are 1-36 (19-36 only valid when BRAM_SIZE="36Kb")
READ_WIDTH_B => 0, -- Valid values are 1-36 (19-36 only valid when BRAM_SIZE="36Kb")
SIM_COLLISION_CHECK => "ALL", -- Collision check enable "ALL", "WARNING_ONLY",
-- "GENERATE_X_ONLY" or "NONE"
SRVAL_A => X"000000000", -- Set/Reset value for A port output
SRVAL_B => X"000000000", -- Set/Reset value for B port output
WRITE_MODE_A => "WRITE_FIRST", -- "WRITE_FIRST", "READ_FIRST" or "NO_CHANGE"
WRITE_MODE_B => "WRITE_FIRST", -- "WRITE_FIRST", "READ_FIRST" or "NO_CHANGE"
WRITE_WIDTH_A => 0, -- Valid values are 1-36 (19-36 only valid when BRAM_SIZE="36Kb")
WRITE_WIDTH_B => 0, -- Valid values are 1-36 (19-36 only valid when BRAM_SIZE="36Kb"),
port map (
DOA => DOA, -- Output port-A data, width defined by READ_WIDTH_A parameter
DOB => DOB, -- Output port-B data, width defined by READ_WIDTH_B parameter
ADDRA => ADDRA, -- Input port-A address, width defined by Port A depth
ADDRB => ADDRB, -- Input port-B address, width defined by Port B depth
CLKA => CLKA, -- 1-bit input port-A clock
CLKB => CLKB, -- 1-bit input port-B clock
DIA => DIA, -- Input port-A data, width defined by WRITE_WIDTH_A parameter
DIB => DIB, -- Input port-B data, width defined by WRITE_WIDTH_B parameter
ENA => ENA, -- 1-bit input port-A enable
ENB => ENB, -- 1-bit input port-B enable
REGCEA => REGCEA, -- 1-bit input port-A output register enable
REGCEB => REGCEB, -- 1-bit input port-B output register enable
RSTA => RSTA, -- 1-bit input port-A reset
RSTB => RSTB, -- 1-bit input port-B reset
WEA => WEA, -- Input port-A write enable, width defined by Port A depth
WEB => WEB -- Input port-B write enable, width defined by Port B depth
);
And here (page 132) instead a possible way to have it inferred.

How to use only one DUT for different test cases in a VHDL testbench and how to detect SPI master's mode on the slave side?

First I would like to mention that I'm still new to the VHDL World so please bear with me.
Currently, I'm working on an SPI project where I created a generic master module that can send and receive different data widths ranging from 8 to 64 bits. Everything is working fine in my testbench regarding sending and receiving data between the master and the slave.
P.S. The master is synthesizable while the slave is just for simulation purposes.
In the testbench I instantiated many DUTs from the master and the slave, for instance, I created DUT_00 for testing 8bit data width, DUT_01 for 11bit data with, etc. As shown below:
Signal declaration for DUT_00 in my testbench
---------------------------------------------------------------------------------------------------------------------
-- DUT0 Signals
---------------------------------------------------------------------------------------------------------------------
constant data_width_8bit : positive range 8 to 64 := 8; --! data width from the master to the slave
--shared variable s_data_width : positive range 8 to 64 := data_width_00; --! data width from the master to the slave
constant bw_g : positive range 8 to 24 := 24; --! width of baud rate configuration port cbr_i
constant inv_g : natural range 0 to 1 := 0; --! 0: master and slave sample on same edge, 1: master samples on opposite edge as slave
signal clk_tb : std_logic:= '0'; --! system clock
signal rst_tb : std_logic:= '0'; --! system reset synchronous, high activsen_i
signal cbr_0tb :std_logic_vector(bw_g-1 downto 0):= x"00_0001"; --! clock baud rate configuration
signal cpol_0tb :std_logic:= '0'; --! clock polarity selection
signal cpha_0tb :std_logic :='0'; --! clock phase selection
signal sen_0tb :std_logic:='0'; --! slave enable : 1 => slave enabled, 0 => slave disabled
signal sts_0tb :std_logic:='0'; --! slave transfer start (unit pulse)
signal sbusy_0tb :std_logic; --! slave status: 1 => SPI transaction ongoing, 0 => idle
signal stv_0tb :std_logic; --! slave transfer valid (unit pulse)
signal dts_0tb :std_logic_vector(data_width_8bit-1 downto 0):= (others => '0'); --! parallel data to slave
signal dfs_0tb :std_logic_vector(data_width_8bit-1 downto 0); --! parallel data from slave
signal ss_0tb :std_logic; --! slave selection
signal spi_clk_0tb :std_logic; --! master clock
signal mosi_0tb :std_logic; --! serial data to slave (Master Output Slave Input)
signal miso_0tb :std_logic; --! serial data from slave (Master Input Slave Output)
---------------------------------------------------------------------------------------------------------------------
-- Slave Signals
---------------------------------------------------------------------------------------------------------------------
signal dfm_0tb :std_logic_vector(data_width_8bit-1 downto 0); --! parallel data from master
signal dfm_val_0tb :std_logic; --! valid pulse for data from master
signal dtm_0tb :std_logic_vector(data_width_8bit-1 downto 0):= (others => '0'); --! parallel data to master
signal busy_tx_0tb :std_logic; --! slave busy,Do not write data while high
signal dtm_val_0tb :std_logic; --! valid pulse for data to the master
signal sum_0tb :std_logic_vector(data_width_8bit-1 downto 0):= (others => '0'); --! bit counter for the data to the master
DUT_00 instantiation
DUT_Gen: for i in 0 to 4 generate
--! #brief instantiation of SPI master entity
DUT_00: if i = 0 generate
master_00: entity work.SPI_MASTER(rtl_SPI_MASTER)
generic map (
nb_g => data_width_8bit,
bw_g => bw_g,
inv_g => inv_g)
-- Master Ports Mapping
port map(
-------------------------------Master I/O pins------------------------------------------------------
clk_i => clk_tb,
rst_i => rst_tb,
-- HOST IF
cbr_i => cbr_0tb,
cpol_i => cpol_0tb,
cpha_i => cpha_0tb,
-- Control
sen_i => sen_0tb,
sts_i => sts_0tb,
sbusy_o => sbusy_0tb ,
stv_o => stv_0tb,
-- Data
dts_i => dts_0tb,
dfs_o => dfs_0tb,
-- SPI IF
spi_ss_o => ss_0tb,
spi_clk_o => spi_clk_0tb,
spi_mosi_o => mosi_0tb,
spi_miso_i=> miso_0tb );
slave_00: entity work.SPI_SLAVE(beh_SPI_SLAVE)
generic map (
nb_g => data_width_8bit
-- bw_g => bw_g
)
-- Slave Ports Mapping
port map(
-------------------------------Slave I/O pins------------------------------------------------------
clk_i => clk_tb,
rst_i => rst_tb,
cpol_i => cpol_0tb,
cpha_i => cpha_0tb,
sck_i => spi_clk_0tb,
mosi_i => mosi_0tb,
miso_o => miso_0tb,
cs_i => ss_0tb,
dfm_o => dfm_0tb,
dfm_val_o => dfm_val_0tb,
dtm_i => dtm_0tb,
busy_tx_o => busy_tx_0tb,
dtm_val_o => dtm_val_0tb,
sum_o => sum_0tb
-- sck_frq_o => sck_frq_0tb
);
end generate DUT_00;
DUT_01 instanstation
--! #brief instantiation of SPI master entity
DUT_01: if i = 1 generate
master_01: entity work.SPI_MASTER(rtl_SPI_MASTER)
generic map (
nb_g => data_width_11bit,
bw_g => bw_g,
inv_g => inv_g)
-- Master Ports Mapping
port map(
-------------------------------Master I/O pins------------------------------------------------------
clk_i => clk_tb,
rst_i => rst_tb,
-- HOST IF
cbr_i => cbr_1tb,
cpol_i => cpol_1tb,
cpha_i => cpha_1tb,
-- Control
sen_i => sen_1tb,
sts_i => sts_1tb,
sbusy_o => sbusy_1tb ,
stv_o => stv_1tb,
-- Data
dts_i => dts_1tb,
dfs_o => dfs_1tb,
-- SPI IF
spi_ss_o => ss_1tb,
spi_clk_o => spi_clk_1tb,
spi_mosi_o => mosi_1tb,
spi_miso_i=> miso_1tb );
slave_01: entity work.SPI_SLAVE(beh_SPI_SLAVE)
generic map (
nb_g => data_width_11bit
-- bw_g => bw_g
)
-- Slave Ports Mapping
port map(
-------------------------------Slave I/O pins------------------------------------------------------
clk_i => clk_tb,
rst_i => rst_tb,
cpol_i => cpol_1tb,
cpha_i => cpha_1tb,
sck_i => spi_clk_1tb,
mosi_i => mosi_1tb,
miso_o => miso_1tb,
cs_i => ss_1tb,
dfm_o => dfm_1tb,
dfm_val_o => dfm_val_1tb,
dtm_i => dtm_1tb,
busy_tx_o => busy_tx_1tb,
dtm_val_o => dtm_val_1tb,
sum_o => sum_1tb
-- sck_frq_o => sck_frq_1tb
);
end generate DUT_01;
My first question is, instead of creating many DUTs for every different data width, how can I only use one DUT that can handle different data sizes also different baud rates?
Second, how can I make the slave detect the mode that has been used by the master when it sends data without telling the slave explicitly which Phase and polarity have been used?
Sorry if my question wasn't well-stated at the beginning.
Have you written a test plan for your SPI project? How many test cases are required to test a single data width of your SPI project? Once you determine that, you can decide how to solve the problem of having to test multiple data widths.
How many of your data widths do you want to test?
One approach is to create a testbench that instantiates a single SPI module and then use a configuration to specify the generic that sizes the SPI module. Your test cases would also need to have this generic so they could adapt what they do for different sizes.
Unfortunately when there are a large number of test cases, configurations tend to explode - meaning you end up with alot of them. Here the number of configurations you would need is #DataWidthsTested * #TestCases.
So if you restructure your testbench to separate your test cases from your test harness, you can instead create #DataWidthsTested variations of your test harness and have run each of the test cases against that.
If you would like a more detailed example of this, OSVVM has two variations of our verification components - AxiStreamTransmitter and AxiStreamTransmitterVti. They are the same inside. The only thing that is different is how the external connections are done. As a result, we create two separate directories for the test harnesses, testbench (which instantiates AxiStreamTransmitter) and testbenchVti (which instantiates AxiStreamTransmitterVti). Care is taken so that both instances have the same instance label and same port names.
The test cases are written in a way that either component can be used. There are 63 test cases. In our methodology we prefer to use configurations to run our test cases - that means we have 63 configurations. Note the configurations only select the test case to be run and not which version of the AxiStreamTransmitter to be use (it is left to default bind). We have a script that compiles and runs each test case.
At a higher level, we have a script that compiles (RunAllTests.pro vs RunAllTestsVti.pro) that calls the script to compile the test harness and then calls the script to compile and run the test cases. By putting each test harness in a separate library we are able to safely run each test case with the intended test harness.
For more see the OSVVM repository on GitHub. It starts at: https://github.com/OSVVM/OsvvmLibraries
It needs to be cloned recursively as there are submodules. The AxiStream examples are in the directory OsvvmLibraries/AXI4/AxiStream. To get started with OSVVM, see the README.rst which is displayed at the bottom of the page of https://github.com/OSVVM/Documentation

VHDL - Why are you not allowed to use variables in generate loops

I know, variables are only allowed in processes, but why are you not allowed to use them in generate loops.
There are no problems for synthesizing such constructs, because they are evaluated before.
The code could get more readable without this restriction.
lbl1: for i in data_out'range generate
lbl2a: component comp_a
port map(
clk => clk,
out => out(0)(i)
in_a => data_in(i*w + offset to i*w + w + offset));
lbl2b: component comp_b
port map(
clk => clk,
out => out(1)(i)
in_b => data_in(i*w + offset to i*w + w + offset));
.
.
.
lbl2n: component comp_n
port map(
clk => clk,
out => out(n)(i)
in_n => data_in(i*w + offset to i*w + w + offset));
end generate lbl1;
Or just write
lbl1: for i in data_out'range generate
variable lower : integer := i*w + offset;
variable upper : integer := i*w + w + offset;
lbl2a: component comp_a
port map(
clk => clk,
out => out(0)(i)
in_a => data_in(lower to upper));
lbl2b: component comp_b
port map(
clk => clk,
out => out(1)(i)
in_b => data_in(lower to upper));
.
.
.
lbl2n: component comp_n
port map(
clk => clk,
out => out(n)(i)
in_n => data_in(lower to upper));
end generate lbl1;
The code is not from any example, it could fail at any point, but i think you get my point. It would be easier to read and maintain. The generated variable could get out of scope outside the generate process.
So is there any reason WHY this isn't allowed or is it just a historical artefact from previous vhdl standards?
If you would like to declare something, it needs to be in a declarative region. A generate statement is a bit special in that it does not need a begin; you can (as you have) use:
GenerateLoop : for i in data_out'range generate
-- Code here
end generate;
If you would like to declare things as part of the loop, you must have a declarative region, which, with a generate statement, would look like this:
GenerateLoop : for i in data_out'range generate
-- Declarative region
begin
-- Code here
end generate;
You should be able to declare things in this declarative region. Note that you will need to use a signal or constant (it looks like a constant will be more appropriate) instead of a variable.

using when...else statment in port map

i can't find anything about using when...else statment in port map. It seems to be a correct form but when i compile i see a error like this :
Error (10500): VHDL syntax error at Device.vhd(68) near text "when";
expecting ")", or ","
It's probably a silly mistake cause i'm still fresh in vhdl. Could you give me a hint about this? I would be very thankful for any help.
Here is the code and the SDA port is inout type:
com : I2C_com port map (
reset_en => reset_en,
I2C_clock_port => SCL,
clk => clk,
sda_read_data <= SDA when RD ='1' else 'Z',
sda_write_data => SDA
);
For one, your arrow points in the wrong direction. For port associations, always use =>, regardless of input or output ports.
Second: the when/else construct is not an expression like a?x:y is in C. It is specific to a when/else signal assignment: http://www.sigasi.com/content/signal-assignments-vhdl-withselect-whenelse-and-case
You need to either use an intermediate signal, or use an adaptor function:
com : I2C_com port map(
reset_en => reset_en,
I2C_clock_port => SCL,
clk => clk,
sda_read_data => SDA_or_z,
sda_write_data => SDA
);
SDA_or_z <= SDA when RD = '1' else 'Z';
or:
com : I2C_com port map(
reset_en => reset_en,
I2C_clock_port => SCL,
clk => clk,
sda_read_data => myFunction(SDA,RD),
sda_write_data => SDA
);
Take one signal temp<= SDA when RD='1' else 'Z';
then assign sda_read_data <= temp;

Resources