Lattice ICE40 SB_IO VHDL syntax query - vhdl

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?

Related

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.

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.

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;

How to detect compiler

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.

Resources