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.
Related
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?
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
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.
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.
I have to load ROM from file. Quartus can use .mif files directly and for the simulator I have written (a quick and dirty) .mif file parser with the help of textio. Is there a way to detect Synthesizer tools (Quartus in my case) and to generate textio file loader process only if it is not being compiled by the Synthesizer tool?
(the source question)
You can detect the synthesis tool by using a pragma that is only accepted by your specific synthesis tool. For example, you can write:
constant isQuartus : std_logic := '1'
-- altera translate_off
and '0'
-- altera translate_on
;
This constant will be '1' only if you synthesize this using Altera's Quartus.
More about the various VHDL metacomment pragma's at: http://www.sigasi.com/content/list-known-vhdl-metacomment-pragmas
Are you trying to infer ROMs from your code? If you're using the LPM functions, everything should "just work".
I use ROMs in Quartus and ModelSim without issue. Just create a custom VHDL file for your ROM using the MegaWizard plugin and direct it to the appropriate initialization file, or directly instantiate an altsyncram component and include the init_file generic.
Then compile or simulate as normal. I have had no problems running simulations using ModelSim (Altera version or stand-alone version) with initialized ROMs, and have not had to write any initialization code to read *.mif or *.hex files.
For reference, here is a directly instantiated ROM megafunction from a bit of my code that simulates properly in ModelSim (note the init_file generic):
-- Sine lookup table
sine_lut : altsyncram
generic map (
clock_enable_input_a => "BYPASS",
clock_enable_output_a => "BYPASS",
init_file => "./video/sine_rom-512x8.mif",
intended_device_family => "Arria GX ",
lpm_hint => "ENABLE_RUNTIME_MOD=NO",
lpm_type => "altsyncram",
numwords_a => 512,
operation_mode => "ROM",
outdata_aclr_a => "NONE",
outdata_reg_a => "UNREGISTERED",
widthad_a => 9,
width_a => 8,
width_byteena_a => 1
)
port map (
-- Read port
clock0 => clk,
address_a => sine_addr,
q_a => sine_do
);
If you really need to do something different when simulating vs. synthesizing, there are a several ways to go about it. For simple things, you can use something like the following directives:
--synthesis translate-off
<code for simulation only>
--synthesis translate-on
http://quartushelp.altera.com/11.1/master.htm#mergedProjects/hdl/vhdl/vhdl_file_dir_translate.htm?GSA_pos=5&WT.oss_r=1&WT.oss=--%20synthesis%20translate_off
You should be able to find a lot of real-world examples of these directives via internet search, and I've included one example below (power-on reset is shorter when simulating vs. when running in real hardware):
-- Async. Power-on Reset, with de-assertion delay
process (clk)
begin
if rising_edge (clk) then
-- Create a delay at power-up
if rst_PowerOn='1' then
rst_pora <= '1';
rst_por_cnt <= (others=>'0');
-- synthesis translate_off
elsif rst_por_cnt(5)='1' then -- 256 nS in simulation
rst_pora <= '0';
-- synthesis translate_on
elsif rst_por_cnt(19)='1' then -- 4ms in real hardware
rst_pora <= '0';
else
rst_por_cnt <= rst_por_cnt + 1;
end if;
end if;
end process;
For more complex situations, use your imagination. Some folks use the C pre-processor, the M4 macro language, or something similar as a preliminary build step prior to synthesis/simulation. I have makefiles and scripts that convert FileName.sim.vhdl (for simulation) into FileName.vhdl (for synthesis), processing it with some text utilities to comment/un-comment various blocks of code based on rules I crafted so I could maintain both versions in a single file (think something like an enhanced C pre-processor tweaked for use with VHDL). Just do something that fits with your workflow and team dynamics/culture.