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

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.

Related

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

Is there a way to toggle an output from a top level file with a single input?

I am designing a system that has two separate counters performing simultaneously that both output a 4-bit binary signal to a set of 4 LEDs. One counter's output is always being displayed by the LEDs while the other counter is still counting (just without displaying an output to the LEDs). The toggle is to be activated by a single button press that will switch the output showing on the LEDs from one counter to the other.
I do not know where to even begin including this function in my design. I am not sure if I could include the logic into my top level file and toggle which signal is sent to the LEDs (I am not even sure if this is possible) or if I could include the logic necessary into the counter files. My wrapper file for the program is located below, showing the instantiation of the top level component. The outputs of the counters are tied to the same set of LEDs, one counter has the output of oQ1 and the other oQ2.
component top_level is
Port ( cnt_tog :in STD_LOGIC;
iReset : in STD_LOGIC;
iUp : in STD_LOGIC;
iClk : in STD_LOGIC;
iCnt_en : in STD_LOGIC;
-- oQ1 : out STD_LOGIC_vector(3 downto 0)
oQ2 : out STD_LOGIC_vector(3 downto 0)
);
end component;
BEGIN
-- INSTANTIATION OF THE TOP LEVEL COMPONENT
Inst_top_level: top_level
port map(
cnt_tog => BTN(3),
iReset => BTN(0),
iClk => clk,
iCnt_en => BTN(2),
iUp => BTN(1),
-- oQ1 => LD(3 downto 0)
oQ2 => LD(3 downto 0)
);
END structural;

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

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.

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;

Wrapping and switching between similar entities in VHDL

I want to describe an entity that can either function normally or be put in a test mode. The general design I have is a top level entity that wraps the "real" entity and a test entity.
I am trying to figure out the best way to express this in VHDL, but I get the feeling I'm overcomplicating things.
Consider a small top-level entity (realistically, there are many more I/Os):
entity toplevelobject is
port (
in1 : inout std_logic;
in2 : inout std_logic;
out1 : out std_logic;
out2 : out std_logic;
testline : in std_logic;
testclk : in std_logic;
);
end toplevelobject;
This is supposed to switch between the real functionality and the test mode depending on the state of "testline" (high means test). Note that the test module actually uses everything but clk as an output, even in_*.
architecture test_passthrough of toplevelobject is
-- This is the actual module
component real_module
port (
in1 : in std_logic;
in2 : in std_logic;
out1 : out std_logic;
out2 : out std_logic;
clk : in std_logic;
-- Note absence of "testline"
);
end component;
-- This is the test module, which will just put the clk
-- signal out on all pins, or play a tune, or something
component test_module
port (
in1 : out std_logic;
in2 : out std_logic;
out1 : out std_logic;
out2 : out std_logic;
testclk : in std_logic;
-- Note absence of "testline"
);
end component;
signal real_in1, real_in2 : std_logic;
signal real_out1, real_out2 : std_logic;
signal test_in1, test_in2 : std_logic;
signal test_out1, test_out2 : std_logic;
begin
real_0 : real_module port map (
in1 => real_in1,
in2 => real_in2,
out1 => real_out1,
out2 => real_out2,
clk => clk,
);
test_0 : test_module port map (
in1 => test_in1,
in2 => test_in2,
out1 => test_out1,
out2 => test_out2,
testclk => clk,
);
-- Ports that are outputs on both don't need
-- much special attention
out1 <= real_out1 when testline = '0' else test_out1;
out2 <= real_out2 when testline = '0' else test_out2;
end test_passthrough;
So I have a few questions:
For the inout ports, should I have one big process with a case ... when statement that switches on testline? Or a process for each I/O with an if statement? Theoretically I figure that many smaller processes are executed concurrently instead of sequentially, but will it actually make a difference to simulation or synthesis? For example:
passthrough_in1 : process(testline, in1, test_in1) is
begin
if testline = '0' then
real_in1 <= in1;
else
in1 <= test_in1;
end if;
end process passthrough_in1;
...vs...
passthrough_all : process(in1, test_in1, in2, test_in2, testline) is
case testline is
when '0' =>
real_in1 <= in1;
real_in2 <= in2;
when '1' =>
in1 <= test_in1;
in2 <= test_in2;
end case;
end process passthrough_all;
Is this a sane approach or is there something simpler?
I'm confused about sensitivity — do I need passthrough_in1 (or even passthrough_all to be sensitive to anything other than testline?
Do I need the real_in1/test_in1 to select between the two wrapped entities? Or is there another way to say "if testline is high, connect test_module output in_1 to the toplevelobject I/O in_1?
If I understand you correctly your testmodule drives the (badly named - I assume in the real code they make more sense :) in1,2 ports?
If so, you need to do something like this:
real_in1 <= in1;
in1 <= test_in1 when testline = '1' else 'Z';
That way in non-test-mode the in1 signal will be driven by a 'Z', so the external proper in1 signal can override it.
You can represent this in various other ways (like the processes that you described), all of which should come out being equal in the simulator. The downside of the "all in one process" option is that you'll need to keep what might end up as an enormous sensitivity list up to date. Doing it one process per signal is just a long winded way of what I did above.
To save some code and copy/paste effort, you could potentially push those two lines of code into an entity of their own and then instance it many times - in that case, I'd put the instance and port map all on one line, but that would offend some coding standards...
This all assumes I've understood the problem correctly!
Maybe I don't fully understand what you're trying to do, but in a typical VHDL testbench:
Your "real_module" code is left as it is. No changes are made to it when it is tested.
A second module, akin to your "toplevelobject" is made. This is typically called a testbench module.
The toplevelobject testbench instantiates real_module.
The testbench usually has no inputs, and doesn't really need outputs (depending on the situation and the testing software used).
The testbench has sequential logic that drives the inputs of real_module.
If you're using testing software such as ModelSim, the inputs and outputs of real_module can be plotted over time to watch the behaviour of real_module as it is driven by your testbench.
What software are you using? I can dig up an old testbench example from a university project a couple of years ago if it would be of any help to you.

Resources