Goal: The main goal of this project is demonstrating the power and capability of high-level synthesis design flow in implementing digital systems.
As this is my first project here in hackster, I have tried to explain all the details step-by-step. If you are interested in learning high-level synthesis techniques to design a digital system, please refer to here.
Project description: In this project, I am going to design a 0-9 Up/Down counter with initialisation signal. Figure 1 shows an overview of the project.
For implementing this project, I am using the Xilinx HLS platform provided by Vivado Design Suite – HLx Editions. Also, I will use the Basys 3 Evaluation board as the target FPGA platform. Figure 2 shows the layout of the final up/down counter on the board. The right-hand side 7-segment shows the counter. The UP push-button is used for up counting, and the DOWN push-button is used for the down counting. INIT push-button initialises the counter. Four slide switches denoted by “INIT value” will be used for entering the initialisation number. Also, four LEDs show this value.
We have three phases to implement this project: C/C++ description, logic synthesis and board programming.
Phase 1: C/C++ Description (Vivado-HLS Project)1- Run the Vivado-HLS IDE and create a new project.
2- In the first page of the create new project wizard insert “counter-vhls” as the project name and choose a proper location for the project files.
3- Choose “counter” as the top-function name.
4- In the “Solution Configuration” page, click on the ellipsis button to select the FPGA platform.
5- The Device Selection Dialog will be opened. Click on Boards, find the digilent vendor, select the Basys3 board and press OK.
6- Create two new files under the Source folder with the names of “counter.cpp” and “counter.h”.
7- Create two new files under the Test Bench folder with the names of “counter-tb.cpp” and “counter-tb.h”.
8- Let’s write the design header file as follows. As shown in the following figure, the code in this file has three sections:
1- Firstly, As we are going to use the HLS arbitrary-precision data types, we should include the “ap_int.h” header file. 2- Secondly, We define a few data types to be used later in the design 3- Finally, we define a constant array that saves the 7-segment codes corresponding to the digits from 0 to 9.
9- The design source file contains the top-function description. It consists of eight sections.
1- The top-function arguments: the function contains three input and two output arguments.
The init_value variable contains the counter initial value.
The reset_counter variable determines when the counter should be initialised. When it is 1 the counter gets the value in the init_value argument.
The push_buttons variable determines the UP or DOWN counting.
The seven_segments_data variable contains the 7-segment code corresponding to the state of the counter
The seven_segments_enable variable enables the target 7-segment.
2- Port interfaces: This section defines the port interfaces corresponding to top-function arguments. These interfaces define the hardware structures implementing arguments data transactions. As we have a simple design and we expect that simple wires implement the arguments, I have selected the ap_none as the interface mode.
3- Declaring variables: Here, I have declared a few variables to be used in the design. The up_count variable will be 1 if the UP push-button has been pressed. The down_count will be 1 if the DOWN push-button has been pressed. up_pressed and down_pressed are declared as static variables to save the history of the push-buttons. The number variable keeps the counter state, so it is defined as a static variable.
4- Set Outputs: This section send the INIT value to LEDs and enables the right-hand side 7-segment.
5- Initialisation: This if-statement initialises the counter state if reset_counter signal is activated.
6- Up Counting: This section consists of two if-statements. The first one checks if the UP push-button is pressed. The second if checks if the UP push-button has been released.
7- Down Counting: This section is very similar to the Up Counting section.
8- 7-segment code: The last section sends out the 7-segment code corresponding to the counter state.
10- Now, we need a C/C++ test-bench to test the design. The test bench files can be found here. The test bench has three steps:
— Generating the test vectors, — Applying the test vectors to the design, — Finally, comparing the hardware outputs with that of the golden model and reporting any discrepancy
11- Now we can run the C-simulation by clicking on its icon in the toolbar.
12- After finishing the C-Simulation successfully, we can run the high-level synthesis.
Figure 13 shows parts of the synthesis report. It has three main parts:
1- Timing information 2- Resource utilisation 3- Port interfaces
13- Finally, we should generate and export the design RTL-IP by clicking on its icon.
Now, we are ready for logic synthesis and generating the FPGA bitstream.
1- Create a new Vivado project with the name of counter-vivado. Note that the project does not have any source file.
2- Do not forget to choose the Basys-3 board as the target FPGA.
3- Create a new block design
4- Right-click somewhere inside the Diagram area and select the “IP Setting…” option
5- Then click on the Repository option under IP in the setting dialog. Then click on the plus sign on the right and browse into the counter HLS project folder. Vivado searches the folder for any possible IP and adds that to its repository.
6- Right-click somewhere inside the Diagram area and select the “Add..” option. Then search for the counter IP and add that to the vivado project.
17- Click on the “Run Connection Automation” on the top of the diagram area.
18- Click again on the “Run Connection Automation” on the top of the diagram area.
9- Select the unconnected ports on our counter IP and make them external (right-click on the port names, and you will see the “Make External” option in the dropdown menu).
10- You can rename the ports by selecting them and using the “External Port Properties” window.
init_counter_V_0 -----------> init_counter
push_buttons_V_0------------> push_buttons
init_value_V_0--------------> init_value
init_counter----------------> init_counter
leds_V_0--------------------> leds
seven_segments_data_V_0 ----> seven_segments_data
seven_segments_enable_V_0---> seven_segments_enable
11- Now create a constraint file, and add the following constraints to connect the IP ports to the FPGA pins with proper I/O standard.
## Switches
set_property PACKAGE_PIN V17 [get_ports {init_value[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {init_value[0]}]
set_property PACKAGE_PIN V16 [get_ports {init_value[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {init_value[1]}]
set_property PACKAGE_PIN W16 [get_ports {init_value[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {init_value[2]}]
set_property PACKAGE_PIN W17 [get_ports {init_value[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {init_value[3]}]
# LEDs
set_property PACKAGE_PIN U16 [get_ports {leds[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {leds[0]}]
set_property PACKAGE_PIN E19 [get_ports {leds[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {leds[1]}]
set_property PACKAGE_PIN U19 [get_ports {leds[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {leds[2]}]
set_property PACKAGE_PIN V19 [get_ports {leds[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {leds[3]}]
#7 segment display
set_property PACKAGE_PIN W7 [get_ports {seven_segments_data[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seven_segments_data[0]}]
set_property PACKAGE_PIN W6 [get_ports {seven_segments_data[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seven_segments_data[1]}]
set_property PACKAGE_PIN U8 [get_ports {seven_segments_data[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seven_segments_data[2]}]
set_property PACKAGE_PIN V8 [get_ports {seven_segments_data[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seven_segments_data[3]}]
set_property PACKAGE_PIN U5 [get_ports {seven_segments_data[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seven_segments_data[4]}]
set_property PACKAGE_PIN V5 [get_ports {seven_segments_data[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seven_segments_data[5]}]
set_property PACKAGE_PIN U7 [get_ports {seven_segments_data[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seven_segments_data[6]}]
set_property PACKAGE_PIN V7 [get_ports seven_segments_data[7]]
set_property IOSTANDARD LVCMOS33 [get_ports seven_segments_data[7]]
set_property PACKAGE_PIN U2 [get_ports {seven_segments_enable[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seven_segments_enable[0]}]
set_property PACKAGE_PIN U4 [get_ports {seven_segments_enable[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seven_segments_enable[1]}]
set_property PACKAGE_PIN V4 [get_ports {seven_segments_enable[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seven_segments_enable[2]}]
set_property PACKAGE_PIN W4 [get_ports {seven_segments_enable[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seven_segments_enable[3]}]
#Buttons
set_property PACKAGE_PIN T18 [get_ports push_buttons[0]]
set_property IOSTANDARD LVCMOS33 [get_ports push_buttons[0]]
set_property PACKAGE_PIN W19 [get_ports init_counter[0]]
set_property IOSTANDARD LVCMOS33 [get_ports init_counter[0]]
set_property PACKAGE_PIN U17 [get_ports push_buttons[1]]
set_property IOSTANDARD LVCMOS33 [get_ports push_buttons[1]]
12- Now right-click somewhere inside the Diagram area and select the “Validate Design” option.
13- After validating the design successfully, right-click on the “design_1” under the “Design Sources” folder and select “Generate Output Products…”
14- Then, right-click again on the “design_1” under the “Design Sources” folder and select “Create HDL Wrapper…”
15- Now click on the “Generate Bitstream” option under the PROGRAM and DEBUG” in the Flow Navigator on the left.
1- Finally, program the board and examine the design.
If you are interested in designing with HLS please have a look at Digital System Design with High-Level Synthesis for FPGA: Combinational Circuits
Comments