VDHL design flow
Source files:
For this hello world example we are going to use three VHDL files:
We start with an edge-detector (edgeDetector.vhdl):
library ieee;
use ieee.std_logic_1164.all;
entity edgeDetector is
port ( clock : in std_logic;
reset : in std_logic;
enable : in std_logic;
inputValue : in std_logic;
risingEdge : out std_logic;
fallingEdge : out std_logic);
end edgeDetector;
architecture hello of edgeDetector is
signal edgeRegisters : std_logic_vector( 2 downto 0 );
begin
risingEdge <= not( edgeRegisters(2) ) and edgeRegisters(1);
fallingEdge <= edgeRegisters(2) and not ( edgeRegisters(1) );
makeRegs : process( clock ) is
begin
if (rising_edge(clock)) then
if (reset = '1') then
edgeRegisters <= inputValue&inputValue&inputValue;
else
edgeRegisters(2 downto 1) <= edgeRegisters(1 downto 0);
if (enable = '1') then
edgeRegisters(0) <= inputValue;
end if;
end if;
end if;
end process makeRegs;
end hello;
This module will take the value of the inputValue when the enable input is active. This is done to make it possible to debounce the button used. It will detect the pressing on the button by the output risingEdge, and the releasing of the button by the output fallingEdge.
The next module is a generic clock divider (genClockDiv.vhdl):
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.log2;
use ieee.math_real.ceil;
entity genClockDiv is
generic ( clockFrequency : integer := 50000000;
tickFrequency : integer := 1000);
port ( clock : in std_logic;
reset : in std_logic;
tick : out std_logic);
end genClockDiv;
architecture hello of genClockDiv is
constant reloadValue : integer := clockFrequency / tickFrequency;
constant nrOfBits : integer := integer(ceil(log2(real(reloadValue))));
signal counterRegister : unsigned( nrOfBits-1 downto 0 );
begin
tick <= '1' when counterRegister = to_unsigned(0, nrOfBits) else '0';
makeCounter : process( clock ) is
begin
if (rising_edge(clock)) then
if (reset = '1' or counterRegister = to_unsigned(0, nrOfBits)) then
counterRegister <= to_unsigned( reloadValue - 1, nrOfBits);
else
counterRegister <= counterRegister - to_unsigned(1, nrOfBits);
end if;
end if;
end process makeCounter;
end hello;
This module will create tickFrequency number of ticks on the output tick given the clock frequency clockFrequency in Hz. We use this to debounce the button.
The final module is the top-level (helloWorld.vhdl):
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity helloWorld is
port ( clock : in std_logic;
nReset : in std_logic;
nButton : in std_logic;
colSelect : out std_logic_vector( 3 downto 0 );
nRedLeds : out std_logic_vector( 9 downto 0 );
nGreenLeds : out std_logic_vector( 9 downto 0 );
nBlueLeds : out std_logic_vector( 9 downto 0 ));
end helloWorld;
architecture hello of helloWorld is
signal s_reset, s_button : std_logic;
signal s_dividedClockTick : std_logic;
signal s_buttonPressed : std_logic;
signal colorSelectReg : unsigned( 2 downto 0 );
begin
s_reset <= not(nReset); -- the user reset button is active low
s_button <= not(nButton); -- the buttons are active low
colSelect <= X"0"; -- we use column 0
-- first we instantiate the debounce counter
divider1 : entity work.genClockDiv(hello)
generic map ( clockFrequency => 74250000, -- we use the 74.25MHz clock
tickFrequency => 1000) -- we use a scanning of 1 kHz
port map ( clock => clock,
reset => s_reset,
tick => s_dividedClockTick );
-- here is the edgedetection placed
isPressed : entity work.edgeDetector(hello)
port map ( clock => clock,
reset => s_reset,
enable => s_dividedClockTick,
inputValue => s_button,
risingEdge => s_buttonPressed,
fallingEdge => open );
-- here we do the color selection
makeColor : process( clock ) is
begin
if (rising_edge(clock)) then
if (s_reset = '1') then colorSelectReg <= "111"; -- after reset we show white
elsif (s_buttonPressed = '1') then
colorSelectReg <= colorSelectReg - to_unsigned(1, 3);
end if;
end if;
end process makeColor;
-- finally we assign the Leds, note they are active low
genleds : for n in 9 downto 0 generate
nRedLeds(n) <= not(colorSelectReg(0));
nGreenLeds(n) <= not(colorSelectReg(1));
nBlueLeds(n) <= not(colorSelectReg(2));
end generate genleds;
end hello;
You can download these three VHDL files here.
Now that we have our design, we can create the files required for the CAD-tools.
Note
This page assumes an automated design flow, hence we will execute everything in a terminal.
Synthesis:
We are going to use yosys as synthesis tool. For VHDL, we need also need ghdl and the yosys-ghdl-plugin. We can use for yosys a script file (yosys.script) that describes the source files, and which operation it should be performed:
ghdl edgeDetector.vhdl genClockDiv.vhdl helloWorld.vhdl -e helloWorld
synth_ecp5 -json helloWorld.json
The first line in this file indicates the analysis part done by ghdl, it requires the list of all the VHDL-files followed by the option -e <toplevel entity name>. The second line instructs yosys to perform a synthesis for the ECP5-family of FPGA’s and that it should create a json file with the results (-json helloWorld.json).
Once this file is created a synthesis can be performed by:
yosys -m ghdl.so -s ./yosys.script
Important
During the writing of this page the MSYS2 packages of Windows native were broken (see this issue).
After execution you will have a file called helloWorld.json. This file is required for the next step.
Place and Route:
For place and route we are going to use nextpnr-ecp5. For the place and route tool to have information on which pins of the FPGA the ports are mapped, we require an lpf-file (gecko5.lpf):
BLOCK RESETPATHS;
BLOCK ASYNCPATHS;
SYSCONFIG CONFIG_IOVOLTAGE=3.3 COMPRESS_CONFIG=ON MCCLK_FREQ=62 SLAVE_SPI_PORT=DISABLE MASTER_SPI_PORT=ENABLE SLAVE_PARALLEL_PORT=DISABLE;
LOCATE COMP "clock" SITE "G2";
IOBUF PORT "clock" PULLMODE=NONE IO_TYPE=LVCMOS33;
FREQUENCY PORT "clock" 74.25 MHZ;
LOCATE COMP "nReset" SITE "G1";
IOBUF PORT "nReset" PULLMODE=NONE IO_TYPE=LVCMOS33;
LOCATE COMP "nButton" SITE "M18";
IOBUF PORT "nButton" PULLMODE=UP IO_TYPE=LVCMOS18;
LOCATE COMP "colSelect[0]" SITE "B6";
LOCATE COMP "colSelect[1]" SITE "J3";
LOCATE COMP "colSelect[2]" SITE "K3";
LOCATE COMP "colSelect[3]" SITE "B17";
LOCATE COMP "nRedLeds[0]" SITE "A7";
LOCATE COMP "nRedLeds[1]" SITE "C6";
LOCATE COMP "nRedLeds[2]" SITE "D13";
LOCATE COMP "nRedLeds[3]" SITE "B12";
LOCATE COMP "nRedLeds[4]" SITE "C11";
LOCATE COMP "nRedLeds[5]" SITE "C10";
LOCATE COMP "nRedLeds[6]" SITE "E9";
LOCATE COMP "nRedLeds[7]" SITE "B9";
LOCATE COMP "nRedLeds[8]" SITE "C8";
LOCATE COMP "nRedLeds[9]" SITE "E7";
LOCATE COMP "nGreenLeds[0]" SITE "C7";
LOCATE COMP "nGreenLeds[1]" SITE "D6";
LOCATE COMP "nGreenLeds[2]" SITE "E13";
LOCATE COMP "nGreenLeds[3]" SITE "C12";
LOCATE COMP "nGreenLeds[4]" SITE "E11";
LOCATE COMP "nGreenLeds[5]" SITE "A11";
LOCATE COMP "nGreenLeds[6]" SITE "A10";
LOCATE COMP "nGreenLeds[7]" SITE "A9";
LOCATE COMP "nGreenLeds[8]" SITE "D8";
LOCATE COMP "nGreenLeds[9]" SITE "B8";
LOCATE COMP "nBlueLeds[0]" SITE "E6";
LOCATE COMP "nBlueLeds[1]" SITE "J4";
LOCATE COMP "nBlueLeds[2]" SITE "D12";
LOCATE COMP "nBlueLeds[3]" SITE "D11";
LOCATE COMP "nBlueLeds[4]" SITE "B11";
LOCATE COMP "nBlueLeds[5]" SITE "B10";
LOCATE COMP "nBlueLeds[6]" SITE "D9";
LOCATE COMP "nBlueLeds[7]" SITE "E8";
LOCATE COMP "nBlueLeds[8]" SITE "A8";
LOCATE COMP "nBlueLeds[9]" SITE "D7";
IOBUF PORT "colSelect[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "colSelect[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "colSelect[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "colSelect[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nRedLeds[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nRedLeds[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nRedLeds[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nRedLeds[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nRedLeds[4]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nRedLeds[5]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nRedLeds[6]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nRedLeds[7]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nRedLeds[8]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nRedLeds[9]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nGreenLeds[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nGreenLeds[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nGreenLeds[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nGreenLeds[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nGreenLeds[4]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nGreenLeds[5]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nGreenLeds[6]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nGreenLeds[7]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nGreenLeds[8]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nGreenLeds[9]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nBlueLeds[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nBlueLeds[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nBlueLeds[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nBlueLeds[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nBlueLeds[4]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nBlueLeds[5]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nBlueLeds[6]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nBlueLeds[7]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nBlueLeds[8]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "nBlueLeds[9]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
The first section of this file contains some generic assignments that you can copy directly. The next sections contain to definitions for the clock input (we use the 74.25MHz clock as described here), the nReset input (see also here), the nButton input (we use button 1 as described here), and finally the RGB-array (as described here).
Important
The names in this file are case-sensitive, hence they should equal the case as in your top-level entity!
Once this file is present, the place-and-route can be performed by:
nextpnr-ecp5 --threads 12 --timing-allow-fail --85k --package CABGA381 --json helloWorld.json --lpf gecko5.lpf --textcfg helloWorld.config
The parameters given represent:
–threads 12: the maximum number of threads used.
–timing-allow-fail: continue even if the timing is not met.
–85k: the size of the FPGA mounted on the board.
–package CABGA381: the package of the FPGA mounted on the board.
–json helloWorld.json: the synthesized design by yosys.
–lpf gecko5.lpf: the pin definition file.
–textcfg helloWorld.config: the output file that is going to be used to generate the bit-file.
Bit-file generation:
For the bit-file (that we can use to configure the FPGA) generation, ecppack is used. To generate the bit file:
ecppack --compress --freq 62.0 --input helloWorld.config --bit helloWorld.bit
The parameters given represent:
–compress: compress the bit-file such that is takes lesser space.
–freq 62.0: The download frequency when reading from external SPI-flash.
–input helloWorld.config: the place-and-routed design.
–bit helloWorld.bit: The bit-file that we can use to configure the FPGA.
Configuring the FPGA:
To configure your GECKO5Education/Modular with your design, the tool openFPGALoader is used. To configure your FPGA, use:
openFPGALoader helloWorld.bit
You should now see that the first column of LED’s will light up white, and each time you press the button their colors will change.
Note
You can also put your design into the flash on the board by using:
openFPGALoader -f helloWorld.bit
However, this will wear off your flash, so only use it, for example, if you want to prepare yourself for a project presentation where you do not want to download the design by hand.
Complete automation:
To not have to type in the commands you can also create a command-file (synthesize.sh):
yosys -m ghdl.so -s yosys.script
nextpnr-ecp5 --threads 12 --timing-allow-fail --85k --package CABGA381 --json helloWorld.json --lpf gecko5.lpf --textcfg helloWorld.config
ecppack --compress --freq 62.0 --input helloWorld.config --bit helloWorld.bit
openFPGALoader helloWorld.bit
and make it executable by:
chmod +x synthesize.sh
All the files can be found here.