Editor’s Note: As advanced algorithms continue to emerge for smart product designs, developers often find themselves struggling to implement embedded systems able to meet the associated processing demands of these algorithms. FPGAs can deliver the required performance, but designing with FPGAs has long been considered limited to the purview of FPGA programming experts. Today, however, the availability of more powerful FPGAs and more effective development environments has made FPGA development broadly accessible. In this excerpt, Chapter 4 from the book Architecting High-Performance Embedded Systems, the author offers a comprehensive review of FPGA devices, implementation languages, and the FPGA development process as well as a detailed walkthrough of how to get started implementing FPGAs in your own design. The complete excerpt is presented in the following series of installments:
1: Hardware resources
2: Implementation languages
3: Development process
4: Building a project
5: Implementation (this article)
Adapted from Architecting High-Performance Embedded Systems, by Jim Ledin.
Testing the logic behavior
It is important to test the behavior of logic using simulation before trying to run it in the FPGA. This is because it is much easier to detect and fix problems in the simulation environment than it is with the logic running inside the FPGA. The Vivado simulation tools do a very good job of representing circuit behavior:
- In the Sources sub-window, right-click on Simulation Sources and select Add Sources…:
Figure 4.18 – Add Sources… menu selection for Simulation Sources
- In the Add Sources dialog, ensure Add or create simulation sources is selected, then click Next.
- In the Add or Create Simulation Sources dialog, click Create File.
- Enter the filename vhdl and click OK.
- Click Finish to dismiss the Add or Create Simulation Sources dialog, then click OK in the Define Module dialog and click Yes when asked if you are sure you want to use these
- Double-click Adder4 TestBench (Behavioral) (Adder4TestBench.vhdl) under Simulation Sources. Delete the automatically populated contents of the vhdl editor window and enter the following code into the Adder4TestBench.vhdl editor:
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; entity ADDER4_TESTBENCH is end entity ADDER4_TESTBENCH; architecture BEHAVIORAL of ADDER4_TESTBENCH is component ADDER4 is port ( A4 : in std_logic_vector(3 downto 0); B4 : in std_logic_vector(3 downto 0); SUM4 : out std_logic_vector(3 downto 0); C_OUT4 : out std_logic ); end component; signal a : std_logic_vector(3 downto 0); signal b : std_logic_vector(3 downto 0); signal s : std_logic_vector(3 downto 0); signal c_out : std_logic; signal expected_sum5: unsigned(4 downto 0); signal expected_sum4: unsigned(3 downto 0); signal expected_c : std_logic; signal error : std_logic; begin TESTED_DEVICE: ADDER4 port map ( A4 => a, B4 => b, SUM4 => s, C_OUT4 => c_out ); TEST: process begin -- Test all combinations of two 4-bit addends (256 total tests) for a_val in 0 to 15 loop for b_val in 0 to 15 loop -- Set the inputs to the ADDER4 component a <= std_logic_vector(to_unsigned(a_val, a'length)); b <= std_logic_vector(to_unsigned(b_val, b'length)); wait for 1 ns; -- Compute the 5-bit sum of the two 4-bit values expected_sum5 <= unsigned('0' & a) + unsigned('0' & b); wait for 1 ns; -- Break the sum into a 4-bit output and a carry bit expected_sum4 <= expected_sum5(3 downto 0); expected_c <= expected_sum5(4); wait for 1 ns; -- The 'error' signal will only go to 1 if an error occurs if ((unsigned(s) = unsigned(expected_sum4)) and (c_out = expected_c)) then error <= '0'; else error <= '1'; end if; -- Each pass through the inner loop takes 10 ns wait for 7 ns; end loop; end loop; wait; end process TEST; end architecture BEHAVIORAL;
This code exercises the 4-bit adder functionality by presenting all combinations of 4-bit numbers to each of the A4 and B4 inputs to the Adder4 component. It compares the SUM4 and C_OUT4 outputs of the Adder4 component to independently computed values for the same inputs. After each addition operation, the error signal is set to 0 if the Adder4 outputs matched the expected values, or it is set to 1 if there is a mismatch.
The code in Adder4TestBench.vhdl resembles traditional software code in the way it uses nested for loops to apply all of the test input combinations to the Adder4 component under test. Code that runs tests in simulation mode is non-synthesizable, which means it does not purely represent a hardware logic circuit and is capable of traditional software-like operations, such as the iterative execution of for loops.
However, as in physical circuits, signals being assigned values in the test bench code using the <= operator cannot be used at the same instant in time in subsequent expressions. This is because the simulation environment represents the real-world effects of propagation delay, which is significant even within tiny FPGA devices. The three wait for 1 ns; statements in the test bench code pause circuit operations to allow for propagation delay. These 1 ns delays provide time for the signal values computed just before the wait statement to propagate so they can be used in the following statement. The final wait for 7 ns; statement in the inner loop is a pause that allows us to clearly see the results of each iteration of the simulation loops in the signal trace display.
- Right-click Adder4 TestBench (Behavioral) (Adder4TestBench.vhdl) under Simulation Sources and select Automatic Update and Compile Order. This sets ADDER4_TESTBENCH as the top-level object for the simulation run:
Figure 4.19 – Menu selection to set Automatic Update and Compile Order
- Click Run Simulation, then Run Behavioral Simulation in the Flow Navigator window to enter simulation mode. If you haven’t already saved the editor files, you will be prompted to do so. Click Save. The simulation will then run:
Figure 4.20 – Run Behavioral Simulation menu selection
- When the SIMULATION window opens, click the maximize button in the simulation output window with the title Untitled 1:
Figure 4.21 – Simulation results window
The total simulated time of each pass through the inner loop is 10 ns. Because there are 256 passes through the loop in Adder4TestBench.vhdl, the time to run the simulation is 2560 ns.
- Set the simulation run time to 2560 ns in the top toolbar (step 1 in the following figure), press the left-pointing restart button (step 2), then press the right-facing button to run the simulation for 2560 ns (step 3), and, finally, press the Zoom Fit button (step 4) to scale the simulation output data range to fit the window:
Figure 4.22 – Simulation results from the start to the end of the run
You can use the magnifier icons to zoom in on any point of the trace and observe the results of each addition operation performed during testing. For example, the following figure shows the decimal values 6 and 2 were added to produce the result 8 with a carry of 0. These values match the expected values, which caused error to be set to 0. The error signal is 0 for all 256 test cases, indicating our logic circuit passed all of the tests:
Figure 4.23 – Zoomed-in view of the simulation results
- Close simulation mode by clicking the X in the blue SIMULATION bar above the data trace Click OK when asked if you want to close the simulation.
Having passed behavioral testing, we will define the I/O signals used in the design.
Defining I/O signals
Our next step is to connect the inputs and outputs of our circuit to hardware devices on the Arty board. The inputs will be the board switches and pushbuttons, and the outputs will be LEDs.
The following steps will create a constraints file that describes the I/O pins we will use on the FPGA device and the functions connected to those pins on the Arty board. Constraint files have the xdc extension:
- In the Sources sub-window, right-click Constraints and select Add Sources….
- In the Add Sources dialog, ensure Add or create constraints is selected, then click Next.
- In the Add or Create Constraints dialog, click Create File.
- Enter the filename Arty-A7-100.xdc (or Arty-A7-35.xdc if appropriate for your device) and click OK.
- Click Finish to dismiss the Add or Create Constraints
- Expand the Constraints source tree and double-click Arty-A7-35.xdc.
- Digilent provides pre-populated constraint files for the Arty A7 boards Visit https://raw.githubusercontent.com/Digilent/digilent-xdc/master/Arty-A7-35-Master.xdc and copy the entire content of the browser window into the Arty-A7-35.xdc editor window in Vivado. If appropriate for your device, use the file at https://raw.githubusercontent.com/Digilent/digilent-xdc/master/Arty-A7-100-Master.xdc instead.
- All of the I/O pins are commented out in the constraints file by Uncomment the appropriate lines in the file by removing the # character from the beginning of each line. We will be using the pins listed in the following sections in the Arty- A7-100.xdc file: Switches, RGB LEDs (but only led0_g, the first green LED), LEDs, and Buttons. The following figure shows these lines after they have been uncommented:
Figure 4.24 – Constraints editor window
In the next section, we will create a top-level VHDL file that interfaces the adder code with the I/O devices.
Creating a top-level VHDL file
We will next create a top-level VHDL file that connects our 4-bit adder component to the corresponding board I/O signals:
- In the Sources sub-window, right-click on Design Sources and select Add Sources….
- In the Add Sources dialog, ensure Add or create design sources is selected, then click Next.
- In the Add or Create Design Sources dialog, click Create File.
- Enter the filename vhdl and click OK.
- Click Finish to dismiss the Add or Create Design Sources dialog, then click OK in the Define Module dialog and click Yes when asked if you are sure you want to use these
- Double-click vhdl under Design Sources. Delete the automatically populated contents of the ArtyAdder.vhdl editor window and enter the following code into the ArtyAdder.vhdl editor:
-- Load the standard libraries library IEEE; use IEEE.STD_LOGIC_1164.ALL; entity ARTY_ADDER is port ( sw : in STD_LOGIC_VECTOR (3 downto 0); btn : in STD_LOGIC_VECTOR (3 downto 0); led : out STD_LOGIC_VECTOR (3 downto 0); led0_g : out STD_LOGIC ); end entity ARTY_ADDER; architecture BEHAVIORAL of ARTY_ADDER is -- Reference the previous definition of the 4-bit adder component ADDER4 is port ( A4 : in std_logic_vector(3 downto 0); B4 : in std_logic_vector(3 downto 0); SUM4 : out std_logic_vector(3 downto 0); C_OUT4 : out std_logic ); end component; begin ADDER : ADDER4 port map ( A4 => sw, B4 => btn, SUM4 => led, C_OUT4 => led0_g ); end architecture BEHAVIORAL;
This code maps the signal names for the I/O devices named in Arty-A7-100.xdc as sw (4 switches), btn (4 pushbuttons), led (4 single-color LEDs), and led0_g (the green channel of the first multi-color LED) to the ADDER4 inputs and outputs.
While VHDL is not case-sensitive, the processing of xdc constraint files in Vivado is case-sensitive. The case used in I/O device names defined in the xdc file must be identical when referenced in a VHDL file. Specifically, the I/O signal names in VHDL must be lowercase in this file because they are lowercase in the constraints file.
We are now ready to synthesize, implement, and program our design for the Arty board.
Synthesizing and implementing the FPGA bitstream
Alternatively, you can select Generate Bitstream and Vivado will perform all of the required steps, including synthesis, implementation, and bitstream generation without further user intervention. If a fatal error occurs, the process will stop and error messages will be displayed. Perform the following steps to generate the bitstream:
- Click Generate Bitstream to start the build You may be asked if you want to save text editors. Click Save. You may be informed that there are no implementation results available and asked if it is OK to launch synthesis and implementation. Click Yes:
Figure 4.25 – Generate Bitstream menu selection
- The Launch Runs dialog will then You can select a value for Number of jobs up to the number of processor cores in your computer. Using more cores makes the process go faster, but it can bog down your machine if you want to continue using it during a lengthy build process. Click OK to start the build:
Figure 4.26 – Launch Runs dialog
- During the build process, Vivado will display the status in the upper-right corner of the main window. If necessary, you can cancel the build process by clicking Cancel next to the status display:
Figure 4.27 – Compilation status display
- When the build process completes, assuming there were no fatal errors, a Bitstream Generation Completed dialog will Although other options are offered, we will proceed directly to downloading the bitstream to the Arty board. Select Open Hardware Manager and click OK:
Figure 4.28 – Bitstream Generation Completed dialog
Next, we will download the bitstream into the FPGA.
Downloading the bitstream to the board
Perform the following steps to download the bitstream to an Arty A7 board:
- The HARDWARE MANAGER dialog will appear and indicate No hardware target is open.
- Connect your Arty A7-35 or A7-100 board to the computer with a USB Wait a few seconds for the board to be recognized, then click Open target, then Auto Connect:
Figure 4.29 – Open target and Auto Connect selections
- After a few seconds, Vivado should indicate that the board is connected. Click Program device to download the FPGA bitstream to the Arty You will be prompted to select a bitstream file. If you’ve used the same directory structure as this example, the file will be located at C:/Projects/ArtyAdder/ArtyAdder.runs/impl_1/ARTY_ADDER.bit:
Figure 4.30 – Program Device dialog
- Click Program to download the program to the FPGA device and start it
- You can now test the operation of the program with the Arty I/O Place all of the four switches in the off position (move the switches toward the adjacent board edge) and do not press any of the four pushbuttons. All of the four green LEDs should be off.
- If you turn on any individual switch or press any one pushbutton, the corresponding green LED should turn on. Turning on any combination of switches while pressing any number of pushbuttons will add the corresponding 4-bit numbers and light the LEDs with the If there is a carry (for example, turn on SW3 and press BTN3 simultaneously), the green carry LED will illuminate.
The programming process performed here stored the program in FPGA RAM. If you cycle power on the FPGA board, you will need to repeat the programming process to reload the program. Alternatively, you can store the FPGA configuration file in onboard flash memory as described in the following section.
Programming the bitstream to onboard flash memory
To configure the FPGA each time power is applied to the Arty board, the FPGA configuration file must be stored to flash memory on the board. If the MODE jumper is installed, the FPGA will attempt to download a configuration file from onboard flash memory at power-on. This memory is located in a separate chip adjacent to the Artix-7 FPGA. Follow these steps to program the configuration file to flash memory:
- Install the MODE jumper on the Arty board if it is not already in
- Right-click Generate Bitstream and select Bitstream Settings….
- In the Settings dialog, check the box next to -bin_file and click OK:
Figure 4.31 – Bitstream settings dialog
- In the main Vivado dialog, click Generate Bitstream and repeat the bitstream generation process. Click Cancel when the Bitstream Generation Completed dialog
- In the Hardware dialog, right-click the FPGA part number (xc7a100t_0) and select Add Configuration Memory Device…:
Figure 4.32 – Add Configuration Memory Device… menu selection
- Type s25fl127 into the Search This should bring up one matching part number. Select the part and click OK:
Figure 4.33 – Add Configuration Memory Device dialog
- You will be presented with a dialog asking Do you want to program the configuration memory device now? Click OK.
- This will bring up a Program Configuration Memory Device dialog requesting the configuration filename. Click the … button to the right of Configuration file and select C:/Projects/ArtyAdder/ArtyAdder.runs/impl_1/ARTY_ADDER.bin. Click OK:
Figure 4.34 – Program Configuration Memory Device dialog
- The programming process will take several seconds to You should receive a message indicating success after the file has been programmed into the board flash memory:
Figure 4.35 – Program Flash complete dialog
After this, each time you cycle the board power, the 4-bit adder program will load and run. It will take a long time for the program to load with the settings that we used for configuration file loading. To avoid waiting for the FPGA to load the program, you can improve the speed of configuration file loading by performing the following steps:
- Select Open Synthesized Design in Flow Navigator.
- In the Vivado main menu, select Tools/Edit Device Properties….
- In the General tab, set Enable Bitstream Compression to TRUE.
- In the Configuration tab, set Configuration Rate (MHz) to 33, then click OK.
- Generate the bitstream again, and program the flash memory as described You will need to remove the configuration memory device and add it back again to display the option for reprogramming.
- Close Hardware Manager.
- Unplug the Arty board USB cable and plug it in The program should begin running virtually instantaneously at power-on.
This section presented an example of simple combinational logic interacting with signals on the FPGA I/O pins. The intent here has been to familiarize you with the Vivado
tool suite and to demonstrate how the tools are used to perform a complete FPGA development cycle.
This chapter began with a discussion on the effective use of FPGAs in real-time embedded system architectures and continued with a description of standard FPGA devices and the low-level components they contain. The range of FPGA design languages, including HDLs, block diagram methods, and popular software programming languages such as C/C++, was introduced. An outline of the FPGA development process was presented. The chapter concluded with a complete example of an FPGA development cycle, starting with a statement of requirements and ending with a functional system implemented on a low-cost FPGA development board.
Having completed this chapter, you should know how FPGAs can be applied effectively in real-time embedded system architectures and understand the components inside an FPGA integrated circuit. You have learned about the programming languages used in the design of FPGA algorithms, the steps in the FPGA development cycle, and understand the sequence of steps in the FPGA development process.
The next chapter will expand on the FPGA development process to provide a complete approach to architecting real-time embedded systems containing FPGAs. It will also begin the development of a prototype high-performance embedded system, a digital oscilloscope, that will serve as an example for the following chapters.
Reprinted with permission from Packt Publishing. Copyright © 2021 Packt Publishing
|Jim Ledin is the CEO of Ledin Engineering, Inc. Jim is an expert in embedded software and hardware design, development, and testing. He is also accomplished in embedded system cybersecurity assessment and penetration testing. He has a B.S. degree in aerospace engineering from Iowa State University and an M.S. degree in electrical and computer engineering from Georgia Institute of Technology. Jim is a registered professional electrical engineer in California, a Certified Information System Security Professional (CISSP), a Certified Ethical Hacker (CEH), and a Certified Penetration Tester (CPT).|
- Embedded design with FPGAs: Hardware resources
- Embedded design with FPGAs: Implementation languages
- Embedded design with FPGAs: Development process
- Open-source tools help simplify FPGA programming
- Implementing floating-point algorithms in FPGAs or ASICs
- Leveraging FPGAs for deep learning
- Software tools migrate GPU code to FPGAs for AI applications
For more Embedded, subscribe to Embedded’s weekly email newsletter.