Basic Embedded System Design Tutorial-2022.2
Basic Embedded System Design Tutorial-2022.2
modulator system
www.so-logic.net 2023/01/10 1
2 2023/01/10 www.so-logic.net
Contents
1 INTRODUCTION 5
1.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.2 Purpose of this Tutorial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.3 Objectives of this Tutorial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.4 One Possible Solution for the Modulator Design . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.4.1 Block diagram . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.4.2 Design steps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.5 Embedded Design Process Flow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
5 CONCLUSION 139
6 EXERCISES 143
3
CONTENTS
4 2023/01/10 www.so-logic.net
Chapter 1
INTRODUCTION
1.1 Motivation
"Basic Embedded System Design Tutorial" is a document made for beginners who are entering the embedded
system design world using FPGAs. This tutorial explains, step by step, the procedure of designing a simple
digital system using C language, Xilinx Vivado Design Suite and Sozius development board.
This tutorial is made to introduce you how to create and test an project and run it on your development
board.
Programming Language: C
Create a Zynq-7000 AP SoC processor system project using Vivado IP Integrator tool and Tcl program-
ming interface
Generate the hardware implementation bitstream le and download it to the target development board
5
CHAPTER 1. INTRODUCTION
In this tutorial a PWM signal modulated using the sine wave with two dierent frequencies (1 Hz and 3.5
Hz) will be created.
Frequency that will be chosen depends on the position of the two-state on-board switch.
PWM Signal
Pulse-width modulation (PWM) uses a rectangular pulse wave whose pulse width is modulated by some other signal (in
our case we will use a sine wave) resulting in the variation of the average value of the waveform. Typically, PWM signals
are used to either convey information over a communications channel or control the amount of power sent to a load. To
learn more about PWM signals, please visit http://en.wikipedia.org/wiki/Pulse-width_modulation.
Figure 1.1. illustrates the principle of pulse-width modulation. In this picture an arbitrary signal is used to
modulate the PWM signal, but in our case sine wave signal will be used.
Considering that we are working with digital systems and signals, our task will be to generate an digital
representation of an analog (sine) signal with two frequencies: 1 Hz and 3.5 Hz.
6 2023/01/10 www.so-logic.net
CHAPTER 1. INTRODUCTION
Figure 1.2 is showing the sine wave that will be used to modulate the PWM signal.
8
One period of the sine wave is represented with 256 (2 ) samples, where each sample can take one of 4096 (2
12 )
possible values. Since the sine wave is a periodic signal, we only need to store samples of one period of the
signal.
Note : Pay attention that all of sine signals with the same amplitude, regardless their frequency, look the same
during the one period of a signal. The only thing that is dierent between those sine signals is duration of a
signal period. This means that the sample rate of those signals is dierent.
Now, it is obvious that the sine wave can be generated by reading sample values of one period, that are stored
in one table, with appropriate speed. In our case the values will be generated using the sine function from the
C numerics library (math.h) and will be stored in an array.
Block diagram on the Figure 1.3 shows the structure of one possible system that can be used to generate required
PWM signals.
www.so-logic.net 2023/01/10 7
CHAPTER 1. INTRODUCTION
Block diagram
Figure 1.3: Structure of microprocessor-based embedded system that will be used in tutorial
AXI GPIO core to drive the LED and to read the status of the SWITCH
Zynq-7000 AP Soc Processor - The Zynq-7000 family is based on the Xilinx All Programmable SoC (AP
SoC) architecture. The Zynq-7000 AP SoC is composed of two major functional blocks: Processing System
(PS) and Programmable Logic (PL), see Figure 1.4. The hart of the Processing System block is dual-
core ARM Cortex-A9 MPCore CPU. Beside ARM processor, PS also includes Application Processor Unit
(APU), Memory Interface, I/O Peripherals (IOP) and Interconnect.
8 2023/01/10 www.so-logic.net
CHAPTER 1. INTRODUCTION
Application Processor Unit (APU) - provides an extensive oering of high-performance features and
standards-compliant capabilities. APU contains:
Run time options allows single processor, asymmetrical (AMP) or symmetrical multiprocessing (SMP)
congurations
ARM version 7 ISA: standard ARM instruction set and Thumb-2, Jazelle RCT and Jazelle DBX Java
acceleration
DDR Controller
Quad-SPI Controller
I/O Peripherals - the I/O Peripherals (IOP) are a collection of industry-standard interfaces for external data
communication:
GPIO
www.so-logic.net 2023/01/10 9
CHAPTER 1. INTRODUCTION
PS MIO I/Os
UART Controller - is a full-duplex asynchronous receiver and transmitter that supports a wide range of
programmable baud rates and I/O signal formats. The controller can accommodate automatic parity generation
and multi-master detection mode.
The UART operations are controlled by the conguration and mode registers. The state of the FIFOs, modem
signals and other controller functions are read using status, interrupt status and modem status registers.
The controller is structured with separate Rx and Tx data paths. Each path includes a 64-byte FIFO. The
controller serializes and de-serializes data in the Tx and Rx FIFOs and includes a mode switch to support
various loopback congurations for the RxD and TxD signals. Software reads and writes data bytes using Rx
and Tx data port registers.
Each UART controller (UART 0 and UART 1) has the following features:
Programmable protocol
Line-break generation
Interrupts generation
RxD and TxD modes: Normal/echo and diagnostic loopbacks using the mode switch
The block diagram of the UART module is shown on the Figure 1.5.
10 2023/01/10 www.so-logic.net
CHAPTER 1. INTRODUCTION
Note : The UART Controller will be used in the Sub-chapter 3.3 Creating a C/C++ source les for
socius ARM-based processor system to transmit debug and system status information during application
execution to the attached PC.
If you want to read and learn more about UART Controller, please refer to Chapter 19 UART Controller in
the Zynq-7000 All Programmable SoC Technical Reference Manual .
36 Kb Block RAM
Clock Management
Congurable I/Os
Note : If you want to read and learn more about the Zynq-7000 AP Soc processor core, please refer to "Zynq-7000
All Programmable SoC - Technical Reference Manual".
LogiCORE IP AXI Timer/Counter - The LogiCORE IP AXI Timer/Counter is a 32/64-bit timer module
that interfaces to the AXI4-Lite interface. The AXI Timer is organized as two identical timer modules. Each
timer module has an associated load register that is used to hold either the initial value for the counter for event
generation or a capture value, depending on the mode of the timer.
Two programmable interval timers with interrupt, event generation, and event capture capabilities
www.so-logic.net 2023/01/10 11
CHAPTER 1. INTRODUCTION
Note : If you want to read and learn more about the AXI Timer/Counter core, please refer to "LogiCORE IP
AXI Timer v2.0 Product Guide".
LogiCORE IP AXI Interrupt Controller (INTC) - The LogiCORE IP AXI Interrupt Controller (INTC)
core receives multiple interrupt inputs from peripheral devices and merges them to a single interrupt output
to the system processor. The registers used for storing interrupt vector addresses, checking, enabling and
acknowledging interrupts are accessed through the AXI4- Lite interface.
Priority between interrupt requests is determined by vector position. The least signicant bit (LSB, in
the case bit 0) has the highest priority
Each input is congurable for edge or level sensitivity. Edge sensitivity can be congured for rising or
falling. Level sensitivity can be active-high or active-low
Output interrupt request pin is congurable for edge or level generation. Edge generation is congurable
for rising or falling and level generation is congurable for active-high or active-low
The LogiCORE IP INTC core concentrates multiple interrupt inputs from peripheral devices to a single interrupt
output to the system processor. The registers used for checking, enabling, and acknowledging interrupts are
accessed through the AXI4-Lite interface.
Note : If you want to read and learn more about the AXI Interrupt Controller core, please refer to "LogiCORE
IP AXI Interrupt Controller (INTC) v4.1 Product Guide".
12 2023/01/10 www.so-logic.net
CHAPTER 1. INTRODUCTION
LogiCORE IP AXI General Purpose Input/Output (GPIO) - The LogiCORE IP AXI General Pur-
pose Input/Output (GPIO) core provides a general purpose input/output interface to the AXI interface. This
32-bit soft IP core is designed to interface with the AXI4-Lite interface.
The AXI GPIO design provides a general purpose input/output interface to an AXI4-Lite interface. The AXI
GPIO can be congured as either a single or a dual-channel device. The width of each channel is independently
congurable.
The ports are congured dynamically for input or output by enabling or disabling the 3-state buer. The
channels can be congured to generate an interrupt when a transition on any of their inputs occurs.
Note : If you want to read and learn more about the AXI GPIO core, please refer to "LogiCORE IP AXI GPIO
v2.0 Product Guide".
This tutorial will be realized step by step with the idea to explain the whole procedure of designing an digital
system, using Vivado tool.
First, we will create ("modulator" ) project for Zynq-7000 AP SoC processor system using Vivado IP
Integrator tool. The block diagram of this system is shown on the Figure 1.3. Here we will congure the
selected microprocessor and peripherals, and specify the interconnections between these components.
After we create "modulator" project using Vivado IP Integrator tool, we will perform synthesis, imple-
mentation, bitstream le generation and program target FPGA device.
www.so-logic.net 2023/01/10 13
CHAPTER 1. INTRODUCTION
Then, we will export our hardware platform desscription from Vivado to Vitis software platform. The
exported le has all the necessary information that Vitis requires for software development and debug
work on the hardware platform that we designed.
In the Vitis IDE, we will create and debug the software application for this project. There will be two
dierent software applications, one without and one with the interrupt controller. Source codes for these
two applications will be stored in modulator_socius_no_intc.c and modulator_socius_intc.c
source le respectively.
Now, the design is ready to be implemented. Please notice, that the last step refers only to the MicroBlaze-
based systems where it is necessary to initialize the bitstream with the appropriate ELF le before down-
loading bitstream to the target Xilinx development board.
Design Steps
In case of using MicroBlaze-based processor systems, bitstream le will be initialized with the appropriate .elf
le and will be downloaded into the BRAM memory on the FPGA device.
In case of using ARM-based processor systems, bitstream le will be automatically downloaded in to the
peripheral DRAM memory and bitstream le initialization is not necessary.
14 2023/01/10 www.so-logic.net
CHAPTER 1. INTRODUCTION
www.so-logic.net 2023/01/10 15
CHAPTER 1. INTRODUCTION
16 2023/01/10 www.so-logic.net
Chapter 2
Select Start -> All Programs -> Xilinx Design Tools -> Vivado 2022.2 -> Vivado 2022.2 and the
Vivado Getting Started page will appear, see Figure 2.1.
17
CHAPTER 2. CREATING THE HARDWARE PLATFORM
- On the Getting Started page, choose rst oered Create Project option, under the Quick Start section.
- In the Create a New Vivado Project dialog box click Next and the wizard will guide you through the
creation of a new project.
18 2023/01/10 www.so-logic.net
CHAPTER 2. CREATING THE HARDWARE PLATFORM
- In the Project Name dialog box specify the name and the location of the new project and click Next.
In the Project name eld type modulator as the name of our project
In the Project location eld specify the location where our project data will be stored
- In the Project Type dialog box specify the type of project you want to create. In our case we will choose
RTL Project option. Select Do not specify sources at this time also and click Next.
www.so-logic.net 2023/01/10 19
CHAPTER 2. CREATING THE HARDWARE PLATFORM
As you can see from the gure above, ve dierent types of the project can be created:
RTL Project - The RTL Project environment enables you to add RTL source les and constraints, congure
IP with the Vivado IP catalog, create IP subsystems with the Vivado IP integrator, synthesize and
implement the design, and perform design planning and analysis.
Post-synthesis Project - This type of project enables you to import third-party netlists, implement the
design, and perform design planning and analysis.
I/O Planning Project - With this type of project you can create an empty project for use with early I/O
planning and device exploration prior to having RTL sources.
Imported Project - This type of project enables you to import existing project sources from the ISE Design
Suite, Xilinx Synthesis Technology (XST), or Synopsys Synplify.
Congure an Example Embedded Evaluation Board Design - This type of project enables you to target
the example Zynq-7000 or MicroBlaze embedded designs to the available Xilinx evaluation boards.
- In the Default Part dialog box choose a default Xilinx part or board for your project and click Next.
20 2023/01/10 www.so-logic.net
CHAPTER 2. CREATING THE HARDWARE PLATFORM
The main component of the Sozius development board is Zynq-7000 AP SoC, so in the Default Part dialog
box select Parts option and set the lter parameters as it is shown on the Figure 2.5.
- In the New Project Summary dialog box click Finish if you are satised with the summary of your project.
If you are not satised, you can go back as much as necessary to correct all the questionable issues, see Figure
2.6.
www.so-logic.net 2023/01/10 21
CHAPTER 2. CREATING THE HARDWARE PLATFORM
After we nished with the new project creation, in a few seconds Vivado IDE Viewing Environment will
appear, see Figure 2.7.
When Vivado creates new project, it also creates a directory with the name and at the location that we specied
in the GUI. That means that the all project data will be stored in the project_name (modulator) directory
containing the following:
project_name.xpr le - object that is selected to open a project (Vivado IDE project le)
project_name.srcs directory - contains all imported local HDL source les, netlists, and XDC les
The Vivado IDE allows dierent le types to be added as design sources, including Verilog, VHDL, EDIF, NGC
format cores, SDC, XDC, and TCL constraints les, and simulation test benches. These les can be stored in
variety of ways using the tabs at the bottom of the Sources window: Hierarchy, Library or Compile Order,
see Figure 2.8.
22 2023/01/10 www.so-logic.net
CHAPTER 2. CREATING THE HARDWARE PLATFORM
By default, after launching, the Vivado IDE opens the Default Layout. Each docked window in the Vivado IDE
is called a view, so you can nd Sources View, Properties View, Project Summary View ans so on, see Figure
2.8.
Flow Navigator
The vertical toolbar present on the left side of the Vivado IDE is the Flow Navigator. The Flow Navigator
provides control over the major design process tasks, such as project conguration, synthesis, implementation
and bitstream creation.
Sources View
The Sources view displays the list of source les that has been added in the project.
The Design Sources folder helps you keep track of VHDL and Verilog design source les and libraries.
The Constraints folder helps you keep track of the constraints les.
The Simulation Sources folder helps keep track of VHDL and Verilog simulation sources source les
and libraries.
In the Libraries tab, sources are grouped by le type, while the Compile Order tab shows the le order
used for synthesis.
www.so-logic.net 2023/01/10 23
CHAPTER 2. CREATING THE HARDWARE PLATFORM
The Project Summary view provides a brief overview of the status of dierent processes executed in the
Vivado IDE, see Figure 2.9.
The Project Settings panel displays the project name, product family, project part, and top module name.
Clicking a link in this panel you will open the Project Settings dialog box.
The Messages panel summarizes the number of errors and warnings encountered during the design
process.
The Synthesis panel summarizes the state of synthesis in the active run. The synthesis panel also shows
the target part and the strategy applied in the run.
The Implementation panel summarizes the state of implementation in the active run. The Implemen-
tation panel also shows the target part and the strategy applied in the run.
Tcl Console
Below the Project Summary view, see Figure 2.10, is the Tcl Console which echoes the Tcl commands as
operations are performed. It also provides a means to control the design ow using Tcl commands.
IP
To accelerate the creation of highly integrated and complex designs, Vivado Design Suite is delivered with
Integrator (IPI) which provides a new graphical and Tcl-based IP- and system-centric design development
ow.
The Xilinx Vivado Design Suite IP Integrator feature lets you create complex system designs by instantiating
and interconnecting IP cores from the Vivado IP Catalog onto a design canvas.
24 2023/01/10 www.so-logic.net
CHAPTER 2. CREATING THE HARDWARE PLATFORM
You can create designs interactively through the IP Integrator design canvas GUI, or using a Tcl programming
interface.
Rapid development of smarter systems requires levels of automation that go beyond RTL-level design. The
Vivado IP Integrator accelerates IP- and system-centric design implementation by providing the following:
System-level optimizations
You will typically construct design at the AXI interface level for greater productivity, but you may also manip-
ulate designs at the port level for more precise design control.
In this tutorial you will instantiate a few IPs in the IP Integrator tool and then stitch them together to create
an IP based system design.
While working on this tutorial, you will be introduced to the IP Integrator GUI, run design rule checks (DRC)
on your design, and then integrate the design in a top-level design in the Vivado Design Suite.
Finally, you will run synthesis and implementation process, generate bitstream le and run your design on the
Sozius development board.
Sozius development platform is a small, portable electronic device that can easily be powered from a USB port,
USB charger, Power Over Ethernet or a battery pack.
You can easily develop software and/or digital hardware for it, because it uses an FPGA with an embedded
processors.
Sozius delivers already a well designed board and should help you to focus on the specics of your project and
can be easily extended to meet your needs.
The main system with many interfaces and Linux is already precongured and ready for use.
www.so-logic.net 2023/01/10 25
CHAPTER 2. CREATING THE HARDWARE PLATFORM
The main component of the Sozius development board is Zynq-7000 AP SoC. As we already said, the Zynq-
7000 AP SoC is composed of two major functional blocks: Processing System (PS) and Programmable
Logic (PL). Since existing LEDs and switches on the Sozius board are connected to the PS part of the Zynq
FPGA, it would require programming PS part of the Zynq FPGA. In this design we will not use PL part of the
Zynq FPGA to implement timer and GPIO modules (as it is presented on the Figure 1.3), because we would
not be able to connect them to the Sozius board LEDs and switches. Instead, we must use timer and GPIO
modules from the PS part of the Zynq FPGA. More specically, we will use one Triple Timer Counter (TTC)
module, TTC0, that is present in the PS part of the Zynq FPGA and four General Purpose IO ports from the
GPIO module that is also present in the PS part of the Zynq FPGA, see Figure 2.10.
This sub-chapter will show how to build Zynq-7000 All Programmable (AP) SoC processor "modulator" design
using Vivado IDE and Tcl programming interface. In this sub-chapter, you will instantiate a few IPs in the IP
Integrator tool and then stitch them together to create an IP based system design. At the end, you will run
synthesis and implementation process and generate bitstream le.
The following steps describe how to create ARM-based hardware platform for Sozius development board.
modulator_sozius_arm_rtl.vhd le will hold the top-level module of our design, in which Zynq PS
component congured for Sozius development board will be instantiated
26 2023/01/10 www.so-logic.net
CHAPTER 2. CREATING THE HARDWARE PLATFORM
modulator_sozius_arm_rtl.vhd:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
library unisim;
use unisim.vcomponents.all;
library work;
use work.sozius_components_package.all;
entity modulator_sozius_arm is
port(
-- ps io
ps_ddr3_addr : inout std_logic_vector(14 downto 0);
ps_ddr3_ba : inout std_logic_vector(2 downto 0);
ps_ddr3_cas_n : inout std_logic;
ps_ddr3_ck_n : inout std_logic;
ps_ddr3_ck_p : inout std_logic;
ps_ddr3_cke : inout std_logic;
ps_ddr3_cs_n : inout std_logic;
ps_ddr3_dm : inout std_logic_vector( 3 downto 0);
ps_ddr3_dq : inout std_logic_vector(31 downto 0);
ps_ddr3_dqs_n : inout std_logic_vector( 3 downto 0);
ps_ddr3_dqs_p : inout std_logic_vector( 3 downto 0);
ps_ddr3_odt : inout std_logic;
ps_ddr3_ras_n : inout std_logic;
ps_ddr3_reset_n : inout std_logic;
ps_ddr3_we_n : inout std_logic;
ps_ddr_vrn : inout std_logic;
ps_ddr_vrp : inout std_logic;
ps_clk_i : inout std_logic;
ps_por_n_i : inout std_logic;
ps_srst_n_i : inout std_logic;
ps_phy_mdc_io : inout std_logic;
ps_phy_mdio_io : inout std_logic;
ps_phy_rx_clk_io : inout std_logic;
ps_phy_rx_ctrl_io : inout std_logic;
ps_phy_rxd_io : inout std_logic_vector(3 downto 0);
ps_phy_tx_clk_io : inout std_logic;
ps_phy_tx_ctrl_io : inout std_logic;
ps_phy_txd_io : inout std_logic_vector(3 downto 0);
ps_i2c_scl_io : inout std_logic;
ps_i2c_sda_io : inout std_logic;
ps_led_error_n_io : inout std_logic;
ps_led_front_n_io : inout std_logic_vector(1 downto 0);
ps_led_sdcard_n_io : inout std_logic;
ps_sw0_a_io : inout std_logic;
ps_sw0_b_io : inout std_logic;
ps_sw1_a_io : inout std_logic;
ps_sw1_b_io : inout std_logic;
ps_sw2_a_io : inout std_logic;
ps_sw2_b_io : inout std_logic;
ps_sw3_a_io : inout std_logic;
ps_sw3_b_io : inout std_logic;
ps_uart_rx_io : inout std_logic;
ps_uart_tx_io : inout std_logic;
ps_qspi_cs_n_io : inout std_logic;
ps_qspi_data_io : inout std_logic_vector(3 downto 0);
ps_qspi_clk_io : inout std_logic;
ps_sdio_clk_io : inout std_logic;
ps_sdio_cmd_io : inout std_logic;
ps_sdio_data_io : inout std_logic_vector(3 downto 0);
ps_usb_clk_io : inout std_logic;
ps_usb_data_io : inout std_logic_vector(7 downto 0);
ps_usb_dir_io : inout std_logic;
ps_usb_nxt_io : inout std_logic;
ps_usb_stp_io : inout std_logic
);
end entity;
-- Between architecture and begin is declaration area for types, signals and constants
-- Everything declared here will be visible in the whole architecture
-- ps signals
signal ps_mio_s : std_logic_vector(53 downto 0);
begin
www.so-logic.net 2023/01/10 27
CHAPTER 2. CREATING THE HARDWARE PLATFORM
28 2023/01/10 www.so-logic.net
CHAPTER 2. CREATING THE HARDWARE PLATFORM
end architecture;
sozius_components_package.vhd:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
package sozius_components_package is
component sozius_xz_lab_ps_bd is
port (
pl_clk0 : out std_logic;
pl_reset_n : out std_logic;
ddr3_cas_n : inout std_logic;
ddr3_cke : inout std_logic;
ddr3_ck_n : inout std_logic;
ddr3_ck_p : inout std_logic;
ddr3_cs_n : inout std_logic;
ddr3_reset_n : inout std_logic;
ddr3_odt : inout std_logic;
ddr3_ras_n : inout std_logic;
ddr3_we_n : inout std_logic;
ddr3_ba : inout std_logic_vector ( 2 downto 0 );
ddr3_addr : inout std_logic_vector ( 14 downto 0 );
ddr3_dm : inout std_logic_vector ( 3 downto 0 );
ddr3_dq : inout std_logic_vector ( 31 downto 0 );
ddr3_dqs_n : inout std_logic_vector ( 3 downto 0 );
ddr3_dqs_p : inout std_logic_vector ( 3 downto 0 );
fixed_io_mio : inout std_logic_vector ( 53 downto 0 );
fixed_io_ddr_vrn : inout std_logic;
fixed_io_ddr_vrp : inout std_logic;
fixed_io_ps_srstb : inout std_logic;
fixed_io_ps_clk : inout std_logic;
fixed_io_ps_porb : inout std_logic;
sdio_0_cdn : in std_logic;
usbind_0_port_indctl : out std_logic_vector ( 1 downto 0 );
usbind_0_vbus_pwrselect : out std_logic;
usbind_0_vbus_pwrfault : in std_logic;
pl_iic_1_sda_i : in std_logic;
pl_iic_1_sda_o : out std_logic;
pl_iic_1_sda_t : out std_logic;
pl_iic_1_scl_i : in std_logic;
pl_iic_1_scl_o : out std_logic;
pl_iic_1_scl_t : out std_logic;
pl_spi_0_sck_i : in std_logic;
pl_spi_0_sck_o : out std_logic;
pl_spi_0_sck_t : out std_logic;
pl_spi_0_io0_i : in std_logic;
pl_spi_0_io0_o : out std_logic;
pl_spi_0_io0_t : out std_logic;
pl_spi_0_io1_i : in std_logic;
pl_spi_0_io1_o : out std_logic;
pl_spi_0_io1_t : out std_logic;
pl_spi_0_ss_i : in std_logic;
pl_spi_0_ss_o : out std_logic;
pl_spi_0_ss1_o : out std_logic;
pl_spi_0_ss2_o : out std_logic;
pl_spi_0_ss_t : out std_logic;
pl_uart_1_txd : out std_logic;
pl_uart_1_rxd : in std_logic
);
end component;
component sozius_xz_ps_bd is
www.so-logic.net 2023/01/10 29
CHAPTER 2. CREATING THE HARDWARE PLATFORM
- In the Add or Create Design Sources dialog box, click the + icon and select Add Files... option to
Add Files button.
include the existing source les into the project, or just click
Figure 2.12: Add or Create Design Sources dialog box - Add Files option
Add Source Files dialog box, browse to the project working directory and select the modula-
- In the
tor_sozius_arm_rtl.vhd and sozius_components_package.vhd source les.
30 2023/01/10 www.so-logic.net
CHAPTER 2. CREATING THE HARDWARE PLATFORM
Figure 2.14: Add or Create Design Sources dialog box - with added le
Finish and your source les should appear under the Design Sources in the Sources view in the
- Click
Project Manager window.
www.so-logic.net 2023/01/10 31
CHAPTER 2. CREATING THE HARDWARE PLATFORM
- Now is the time to create constraints le for the Sozius board, sozius_xz_modulator_vio.xdc.
Open Vivado text editor, copy your constraints code in it or write directly in it and save the constraints le in
your working directory.
The complete sozius_xz_modulator_vio.xdc source le you can nd in the further text.
Note : If you want to read and learn more about XDC constraints les, please refer to the "Basic FPGA
Tutorial", sub-chapter 9.1 "Creating XDC File" where you will nd all the necessary information about
the types of constraints, how to create them depending on the target board type and use them in your design.
sozius_xz_modulator_vio.xdc:
- In the Add Sources dialog box select Add or create constraints option to add the sozius_xz_modulator_vio.xdc
constraints le into our project and click Next.
32 2023/01/10 www.so-logic.net
CHAPTER 2. CREATING THE HARDWARE PLATFORM
- In the Add or Create Constraints dialog box, click + icon and select Add Files... option.
Add Constraint Files dialog box, browse to the project working directory and select the soz-
- In the
ius_xz_modulator_vio.xdc constraints le.
OK and the sozius_xz_modulator_vio.xdc constraints le should appear in the Add or Create
- Click
Constraints dialog box.
- Click Finish and your constraints le should appear under the Constraints in the Sources view.
Finally, we must congure the Zynq PS part to work on Sozius development board.
This includes a number of conguration steps, one of them being the proper conguration of the PS GPIO module to
connect to the LEDs and switches that are present on the Sozius board.
Also, we must enable the Triple Timer Counter 0 (TTC0) module within Zynq PS, that will be used in the "modulator"
design. All these PS conguration steps can be done using the Vivado GUI, by creating a block design.
However, since this task includes a lot of manual settings of the Zynq PS, a better approach would be to do this manual
conguration only once and then to create a Tcl script le that can be used in all future congurations of the Zynq PS
part.
The Tcl script that should be used to correctly congure Zynq PS to work on Sozius board is sozius_xz_lab_ps_bd.tcl.
This Tcl script le is too long to be shown in the tutorial, so ask your instructor for details.
www.so-logic.net 2023/01/10 33
CHAPTER 2. CREATING THE HARDWARE PLATFORM
- Next step is to execute the presented Tcl le in the Vivado IDE. Go to the Tcl console window and type the
following and press enter:
source <path>/sozius_xz_lab_ps_bd.tcl
<path> stands for the full path to the folder where the sozius_xz_lab_ps_bd.tcl Tcl le is stored.
After Vivado has nished with the Tcl script execution, a created block diagram containing Zynq PS will be
visible in the Vivado IDE, as shown on the Figure 2.20.
There are two possibilities for netlist and bitstream le generation. One is to generate these les after a
hardware platform is specied and the second one is to generate them after a software application development
is completed.
If you would like to generate netlist and bitstream le after hardware platform specication:
- In the Vivado Flow Navigator, click Run Synthesis command, and wait for task to be completed.
34 2023/01/10 www.so-logic.net
CHAPTER 2. CREATING THE HARDWARE PLATFORM
- When the synthesis process is completed, click Run Implementation command, and wait for task to be completed.
- At the end, when the implementation process is completed, click Generate Bitstream command. After this step,
bitstream le will be generated.
Figure 2.20: Run Synthesis, Run Implementation and Generate Bitstream commands from the Vivado Flow
Navigator
- The last step in hardware platform creation will be to download generated bitstream le to the target FPGA device.
To download generated bitstream le to the target FPGA device use Flow Navigator's Open Hardware Manager
command.
Note : If you would like to read detailed steps how to program your target FPGA device, please open Basic FPGA
Tutotial, sub-chapter 9.4 "Program Device".
If you would you like to generate netlist and bitstream les after a software application development is completed
(in case of using MicroBlaze-based processor systems), please follow the next chapter where will be explained
in detail the necessary steps.
www.so-logic.net 2023/01/10 35
CHAPTER 2. CREATING THE HARDWARE PLATFORM
36 2023/01/10 www.so-logic.net
Chapter 3
In the previous chapter we have designed the hardware component of our embedded system.
To complete the design process we must now create a software component for our embedded system.
This application specic software will be executed on the ARM processor that is a part of our hardware
platform.
When using Xilinx development tools, software design process is done using the Vitis Core Development Kit
tool. Our software application will be developed for the hardware platform built in IP Integrator tool.
The rst step in software creation is to export the hardware design into the Vitis IDE.
The Vitis IDE is part of the Vitis software platform. The Vitis IDE is designed to be used for the development
of embedded software applications targeted towards Xilinx embedded processors. The Vitis IDE works with
37
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
The hardware design will be exported to an XSA le that will be used by the Vitis IDE to create a software
application for our hardware.
To create a software platform for your embedded design, use the following steps:
Export Hardware
- When the Sozius board is programmed, select File -> Export -> Export Hardware... option from the
main Vivado IDE menu.
Export Hardware Platform wizard will guide you through the process of exporting hardware platform for
use in the Vitis tools.
To export a Hardware Platform, you will need to provide a name and location for the exported le and specify
the platform properties.
- In the Export Hardware Platform diloag box select Fixed as Platform type and click Next.
- In the Output dialog box select Include bitstream option and click Next.
38 2023/01/10 www.so-logic.net
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
- In the File dialog box enter the name of your XSA le in the XSA le name led and check the directory
where the XSA le will be stored. Click Next.
- In the Exporting Hardware Platform dialog box to export the hardware platform just click Finish.
www.so-logic.net 2023/01/10 39
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
In order to get the internal FPGA clock running, we must run some application on the processing system. In
order to do this, following steps must be performed:
Select Start -> All Programs -> Xilinx Design Tools -> Vitis 2022.2 -> Xilinx Vitis IDE 2022.2
and the Vivado Vitis Eclipse Launcher dialog box will appear.
- In the Eclipse Launcher dialog box select a directory as workspace in the Workspace eld and click Next.
40 2023/01/10 www.so-logic.net
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
File -> New -> Platform Project... option, or just click the
- On the Vitis IDE getting started page, select
link Create Platform Project and the New Platform Project wizard opens.
- In the Create new platform project dialog box enter a name for your platform project in the Project
name eld, in our case it will be modulator_sozius, enable Use default location option and click Next.
www.so-logic.net 2023/01/10 41
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
- In the Platform dialog box select Browse beside the Hardware Specication to provide your XSA le.
42 2023/01/10 www.so-logic.net
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
- In the Create Platform from XSA dialog box browse and select the XSA le that you exported from the
Vivado Design Suite and click Open.
- In the Platform dialog box provide the hardware and software specication for the new platform project.
In the XSA le eld, browse and select the XSA le that you exported from the Vivado Design Suite
Use the dropdown menus to select standalone as the operating system and ps7_cortexa9_0 as the
processor
www.so-logic.net 2023/01/10 43
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
- Click Finish and Vitis IDE will create your platform project.
Build Project
44 2023/01/10 www.so-logic.net
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
Each software project must have a corresponding Board Support Package (BSP).
A Board Support Package is a collection of libraries and drivers that form the lowest level of your software
application stack.
Before you can create and use software applications in Vitis IDE, BSP will be created. There are several kinds
of BSPs. The standalone BSP is the most commonly used domain.
- In the Explorer window, double-click on the platform.spr le to open the platform tab for viewing and
modication.
- In the modulator_sozius window, select the appropriate domain/Board Support Package and the overview
page opens.
www.so-logic.net 2023/01/10 45
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
Using the Overview page, you can select the OS Version and which of the Supported Libraries are to be enabled
in this domain/BSP.
- In the Board Support Package Settings dialog box leave all default options as they are set in all four tabs
OK.
(Overview, standalone, drivers and ps7_cortexa9.0) and click
The Board Support Package settings page enables you to congure parameters of the OS and its constituent
libraries.
46 2023/01/10 www.so-logic.net
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
Note : Options for only the libraries that you enabled in the Overview page will be visible. Options for the
OS/standalone supported peripherals, that are present in the hardware platform, are also shown on the page.
The Drivers page lists all the device drivers assigned for each peripheral in your system. You can select each
peripheral and change its default device driver assignment and its version. If you want to remove a driver for a
peripheral, assign the driver to none.
Some device drivers export parameters that you can congure. If a device in the driver list has parameters, it
is listed in navigation pane on the left and you can access them by clicking on the device name.
The Driver Conguration page lists all of the congurable driver parameters for the device selected under the
drivers entry on the left. To change a parameter, click on the corresponding Value eld and type the new
setting.
When you nish with all the settings you want to make, click OK. The Vitis software platform regenerates the
domain/BSP sources.
If the Build All option is selected in the Project menu, Vitis software platform automatically rebuilds your
target platform with your new settings applied.
After installing the Vitis software platform, the next step is to create a software application project. Software
application projects are the nal application containers. The project directory that is created contains (or links
to) your C/C++ source les, executable output le, and associated utility les, such as the Makeles used to
build the project.
The Vitis software platform automatically creates a system project for you. A system project is a top-level
container project that holds all of the applications that can run in a system at the same time. This is useful
if you have many processors in your system, especially if they communicate with one another, because you can
debug, launch, and prole applications as a set instead of as individual items.
- In the Vitis IDE select File -> New -> Application Project... option.
- In the Create a New Application Project dialog box just click Next to skip the welcome page instructions.
www.so-logic.net 2023/01/10 47
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
- In the Platform dialog box Select a platform from repository tab opens. You should choose a platform
for your project. You can either use a pre-supplied platform (from Xilinx or another vendor), a previously
created custom platform, or you can create one automatically from an exported Vivado hardware project.
48 2023/01/10 www.so-logic.net
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
- In the Application project Details dialog box, specify the application project name (modulator_sozius_no_intc)
and its system project properties and click Next.
www.so-logic.net 2023/01/10 49
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
Note: In our design we will create two application projects. One will be without interrupt controller
(modulator_sozius_no_intc), and the second one will be with interrupt controller (modulator_sozius_intc).
- In the Domain dialog box, provide the domain and other software details for your project. In our case leave
all default parameters and clickNext.
- In the Templates dialog box select empty Application application and click Finish.
50 2023/01/10 www.so-logic.net
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
- Repeat the same procedure to create the other application project, named modulator_socius_intc.
After this step you should have both application projects in the Vitis IDE Project Explorer window.
3.4 Creating a C/C++ Source Files for Sozius Board Based Hard-
ware Platform
Creating a C/C++ Source Files for Sozius Board Based Hardware Platform
Now it's time to start writing the software for this project.
With the Vivado tool we have advantage to develop software independently from the hardware, using Vitis tool.
To create source les necessary for our embedded system that will be running on the Sozius board, we must
create a software component.
This application specic software will be executed on the ARM processor that is already part of our hardware
platform.
As we already said in the previous sub-chapter, we will develop two application projects. One will be without
interrupt controller (modulator_sozius_no_intc), and the second one will be with interrupt controller
(modulator_sozius_intc). The idea was to illustrate how the same problem can be solved in a number of
dierent ways.
In the ARM-based processor system we will also use UART Controller, that is integral part of the Zynq7
processing system, to transmit debug and system status information during application execution to the attached
PC. This will be achieved using xil_printf function.
www.so-logic.net 2023/01/10 51
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
Source les that will be created for modulator_sozius_no_intc application project are:
modulator_sozius_no_intc.c
modulator.h
init_sin.c
To create source les necessary for modulator_sozius_no_intc application project, please do the following:
- Expand modulator_sozius_no_intc application project in Project Explorer and src folder should
appear.
In the src folder you should nd your source code after creation.
- Right-click on the src folder and select New -> File option.
- Click Finish and your modulator_sozius_no_intc.c source le should appear in the src folder, as we
already said.
- Double-click on the modulator_sozius_no_intc.c source le in the Project Explorer and it will be imme-
diately opened.
- When you nished with all modications, click Save and Vitis IDE will automatically build your application.
- Repeat the same procedure to also create modulator.h and init_sin.c source les.
52 2023/01/10 www.so-logic.net
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
Note : The complete source les for modulator_sozius_no_intc.c, modulator.h and init_sin.c you can
nd in the text below.
modulator_sozius_no_intc.c:
#include "xparameters.h"
#include "xgpiops.h"
#include "xstatus.h"
#include "xttcps.h"
#include "modulator.h"
#include "xil_printf.h"
// Definitions of actual pin locations for LED and push-button on the socius board
#define ps_sw2_a 10
#define ps_sw2_b 11
#define ps_sw3_a 12
// New type definition that will hold all relevant configuration parameters for the TTC module
typedef struct {
u32 OutputHz; /* Output frequency */
u16 Interval; /* Interval value */
u8 Prescaler; /* Prescaler value */
u16 Options; /* Option settings */
} TmrCntrSetup;
// Instance of a TmrCntrSetup type, holding TTC0 configuration parameters that will be used in modulator example
static TmrCntrSetup SettingsTable[1] = {
{10, 65000, 2, 0}, /* Ticker timer counter initial setup, only output freq */
};
int main(void)
{
/*********************** Variable Definitions ********************/
XGpioPs GpioLeds; // XGPIO instance that will be used to work with LED
XGpioPs_Config *GPIOConfigPtr;
unsigned int end_time; // Will be used to represent the END_TIME_0 or END_TIME_1 value
unsigned int threshold; // Will be used to represent the current value of the sine signal
u16 current_time; // Represents the current timer value
// Sine amplitude values that will be used to generate the PWM signal
static unsigned int sine_ampl[COUNT_DEPTH_END];
xil_printf("Initializing peripherals!\r\n");
xil_printf("Initializing LEDs!\r\n");
// LEDs initialization
GPIOConfigPtr = XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID);
XGpioPs_CfgInitialize(&GpioLeds, GPIOConfigPtr, GPIOConfigPtr ->BaseAddr);
// On the socius board we must properly control both ends of a LED, hence we must use two GPIO ports
XGpioPs_SetDirectionPin(&GpioLeds, ps_sw2_a, 1);
XGpioPs_SetOutputEnablePin(&GpioLeds, ps_sw2_a, 1);
XGpioPs_SetDirectionPin(&GpioLeds, ps_sw2_b, 1);
XGpioPs_SetOutputEnablePin(&GpioLeds, ps_sw2_b, 1);
// Set the value of one end of LED to always be equal to zero, by changing the other end we will turn it on and off
XGpioPs_WritePin(&GpioLeds, ps_sw2_b, 0x0);
xil_printf("Initializing SWITCHes!\r\n");
// SWITCHes initialization
// Set the direction of the GPIO port connected to the push button to INPUT
XGpioPs_SetDirectionPin(&GpioLeds, ps_sw3_a, 0);
xil_printf("Initializing TIMER!\r\n");
// TIMER initialization
TimerSetup = &SettingsTable[TTC_DEVICE_ID];
// Store the current configuration of the timer in a TimerInst object, using a pointer to access it
Timer = &TimerInst;
Timer->Config = *Config;
www.so-logic.net 2023/01/10 53
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
XTtcPs_Stop(Timer);
do // Pause
{
current_time = XTtcPs_GetCounterValue(Timer);
}
while(current_time<threshold);
do // Pause
{
current_time = XTtcPs_GetCounterValue(Timer);
}
while(current_time<end_time);
count_depth ++;
if (count_depth == COUNT_DEPTH_END)
count_depth = 0;
return 0;
}
modulator.h:
54 2023/01/10 www.so-logic.net
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
#ifndef MODULATOR_H_
#define MODULATOR_H_
#include "math.h"
#define DEPTH 8 // the number of samples in one period of the signal (2^8=256)
#define WIDTH 12 // the number of bits used to represent amplitude value (2^12=4096)
#define COUNT_DEPTH_END 1 << DEPTH // final threshold value for the depth counter (2^8=256)
#endif /* MODULATOR_H_ */
init_sin.c:
#include "modulator.h"
Additional Steps
- Select modulator_sozius_no_intc application project, right-click on it and choose C/C++ Build Set-
tings option.
- In the Properties for modulator_sozius_no_intc dialog box choose C/C++ Build -> Settings
option.
- In the Settings dialog box, select Tool Settings tab and under the ARM v7 gcc linker select Libraries
option.
www.so-logic.net 2023/01/10 55
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
- In the Enter Value dialog box, type m and click OK to add math library in the Libraries list.
- In the Properties for modulator_sozius_no_intc diloag box, click Apply and Close.
As you can see from the source code above, we have used a lot of dierent functions.
56 2023/01/10 www.so-logic.net
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
XTtcPs_Stop (InstancePtr)
XTtcPs_Start (InstancePtr)
XTtcPs_ResetCounterValue (InstancePtr)
All of these functions and it's denitions and explanations, you can nd in the Xilinx directory:
There, you can nd a plenty of dierent functions that you can use in your software design. In this tutorial we
have represent just those functions that we have used in our software design. Here are some of them:
#include <xgpiops.h>
This function looks for the device conguration based on the unique device ID.
The table XGpioPs_CongTable[] contains the conguration information for each device in the system.
Parameters:
Returns: A pointer to the conguration table entry corresponding to the given device ID, or NULL if no
match is found.
Note: None.
#include <xgpiops.c>
Initialize the XGpioPs instance provided by the caller based on the given conguration data.
Parameters:
InstancePtr - is a pointer to an XGpioPs instance. The memory the pointer references must be
pre-allocated by the caller. Further calls to manipulate the driver through the XGpioPs API must
be made with this pointer.
Cong - is a reference to a structure containing information about a specic GPIO device. This
function initializes an InstancePtr object for a specic device specied by the contents of Cong.
This function can initialize multiple instance objects with the use of multiple calls giving dierent
Cong information on each call.
www.so-logic.net 2023/01/10 57
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
EectiveAddr - is the device base address in the virtual memory address space. The caller is responsi-
ble for keeping the address mapping from EectiveAddr to the device physical base address unchanged
once this function is invoked. Unexpected errors may occur if the address mapping changes after this
function is called. If address translation is not used, use Cong->BaseAddress for this parameters,
passing the physical address instead.
Note: None.
Referenced by XGpioPs_Initialize().
#include <xgpiops.c>
Parameters:
InstancePtr - is a pointer to the XGpioPs instance. Further calls to manipulate the driver through
the XGpioPs API must be made with this pointer.
Pin - is the pin number to which the Data is to be written. Valid values are 0-117 in Zynq and 0-173
in Zynq Ultrascale+ MP.
Direction - is the direction to be set for the specied pin. Valid values are 0 for Input Direction, 1
for Output Direction.
Returns: None.
Note: None.
References: XGpioPs::IsReady.
#include <xgpiops.c>
Parameters:
InstancePtr - is a pointer to the XGpioPs instance. Further calls to manipulate the driver through
the XGpioPs API must be made with this pointer.
Pin - is the pin number to which the Data is to be written. Valid values are 0-117 in Zynq and 0-173
in Zynq Ultrascale+ MP.
OpEnable - species whether the Output Enable for the specied pin should be enabled. Valid values
are 0 for Disabling Output Enable, 1 for Enabling Output Enable.
Returns: None.
Note: None.
References: XGpioPs::IsReady.
58 2023/01/10 www.so-logic.net
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
#include <xgpiops.c>
Parameters:
InstancePtr - is a pointer to the XGpioPs instance. Further calls to manipulate the driver through
the XGpioPs API must be made with this pointer.
Pin - is the pin number to which the Data is to be written. Valid values are 0-117 in Zynq and 0-173
in Zynq Ultrascale+ MP.
Returns: None.
Note: This function does a masked write to the specied pin of the specied GPIO bank. The pre-
vious state of other pins is maintained.
References: XGpioPs::IsReady.
#include <xgpiops.c>
Parameters:
InstancePtr - is a pointer to the XGpioPs instance. Further calls to manipulate the driver through
the XGpioPs API must be made with this pointer.
Pin - is the pin number for which the data has to be read. Valid values are 0-117 in Zynq and 0-173
in Zynq Ultrascale+ MP. See xgpiops.h for the mapping of the pin numbers in the banks.
Note: This function is used for reading the state of the specied GPIO pin.
References: XGpioPs::IsReady.
#include <xttcps.h>
A table contains the conguration info for each device in the system.
Parameters:
Returns: A pointer to the conguration found or NULL if the specied device ID was not found. See
XTtcPs_Cong.
xttcps.h for the denition of
Note: None.
www.so-logic.net 2023/01/10 59
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
#include <xttcps.c>
Initializes a specic XTtcPs instance such that the driver is ready to use.
This function initializes a single timer counter in the triple timer counter function block.
Overow Mode
Counter disabled
Parameters:
EectiveAddr - is the device base address in the virtual memory address space. The caller is re-
sponsible for keeping the address mapping from EectiveAddr to the device physical base address
unchanged once this function is invoked. Unexpected errors may occur if the address mapping changes
after this function is called. If address translation is not used, then use CongPtr->BaseAddress for
this parameter, passing the physical address instead.
Returns:
Note: Device has to be stopped rst to call this function to initialize it.
#include <xttcps.h>
Parameters:
Options - contains the specied options to be set. This is a bit mask where a 1 means to turn the
option on, and a 0 means to turn the option o. One or more bit values may be contained in the
mask. See the bit denitions named XTTCPS_*_OPTION in the le xttcps.h.
Returns:
60 2023/01/10 www.so-logic.net
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
Note: None.
#include <xttcps.h>
Parameters:
Returns: None.
#include <xttcps.c>
This function sets the prescaler enable bit and if needed sets the prescaler bits in the control regis-
ter.
Parameters:
PrescalerValue - is a number from 0-16 that sets the prescaler to use. If the parameter is
0 - 15, use a prescaler on the clock of 2( P rescalerV alue + 1), or 2-65536. If the parameter
is XTTCPS_CLK_CNTRL_PS_DISABLE, do not use a prescaler.
Returns: None.
Note: None.
XTtcPs_Stop (InstancePtr)
#include <xttcps.h>
This macro may be called at any time to stop the counter. The counter holds the last value until it
is reset, restarted or enabled.
www.so-logic.net 2023/01/10 61
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
Parameters:
Returns: None.
Referenced by XTtcPs_CfgInitialize().
XTtcPs_Start (InstancePtr)
#include <xttcps.h>
This function starts the counter/timer without resetting the counter value.
Parameters:
Returns: None.
#include <xttcps.h>
Parameters:
Returns: zynq:16 bit counter value. zynq ultrascale+mpsoc:32 bit counter value.
XTtcPs_ResetCounterValue (InstancePtr)
#include <xttcps.h>
It may be called at any time. The counter is reset to either 0 or 0xFFFF, or the interval value, depending
62 2023/01/10 www.so-logic.net
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
on the increment/decrement mode. The state of the counter, as started or stopped, is not aected by
calling reset.
Parameters:
Returns: None.
Referenced by XTtcPs_CfgInitialize().
Source les that will be created for modulator_sozius_intc application project are:
modulator_sozius_intc.c
modulator.h
init_sin.c
modulator.h and init_sin.c source les are the same, only modulator sozius_intc.c source le is dierent.
To create source les necessary for modulator_sozius_intc application project, please do the following:
- Expand modulator_sozius_intc application project in Project Explorer and src folder should appear.
In the src folder you should nd your source code after creation.
Note: In the step 3 use modulator_sozius_intc.c as the le name for the C source code le.
modulator.h - this le is identical with the modulator.h le used in the design without interrupt controller.
init_sin.c - this le is identical with the init_sin.c le used in the design without interrupt controller.
- Final step requires adding a math library to the ARM v7 gcc linker library settings. To include math library,
please repeat steps from the end of the previous "Modulator design without interrupt controller" section, from
the slides 42, 43, 44.
modulator_sozius_intc.c:
#include "xparameters.h"
#include "xparameters_ps.h"
#include "xgpiops.h"
#include "xstatus.h"
#include "xttcps.h"
#include "xscugic.h"
#include "xil_exception.h"
#include "modulator.h"
#include "xil_printf.h"
// Definitions of actual pin locations for LED and push-button on the socius board
www.so-logic.net 2023/01/10 63
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
#define ps_sw2_a 10
#define ps_sw2_b 11
#define ps_sw3_a 12
// New type definition that will hold all relevant configuration parameters for the TTC module
typedef struct {
u32 OutputHz; /* Output frequency */
u16 Interval; /* Interval value */
u8 Prescaler; /* Prescaler value */
u16 Options; /* Option settings */
} TmrCntrSetup;
// Instance of a TmrCntrSetup type, holding TTC0 configuration parameters that will be used in modulator example
static TmrCntrSetup SettingsTable[1] = {
{10, 65000, 2, 0}, /* Ticker timer counter initial setup, only output freq */
};
static int interrupt_occurred = 0; // variable which will signal when that interrupt has occurred
interrupt_occurred = 1;
// stop timer
XTtcPs_Stop((XTtcPs *)CallBackRef);
StatusEvent = XTtcPs_GetInterruptStatus((XTtcPs *)CallBackRef);
XTtcPs_ClearInterruptStatus((XTtcPs *)CallBackRef, StatusEvent);
}
return XST_SUCCESS;
}
int main(void)
{
/*********************** Variable Definitions ********************/
XGpioPs GpioLeds; // XGPIO instance that will be used to work with LED
XGpioPs_Config *GPIOConfigPtr;
unsigned int end_time; // Will be used to represent the END_TIME_0 or END_TIME_1 value
unsigned int threshold; // Will be used to represent the current value of the sine signal
unsigned int sine_ampl[COUNT_DEPTH_END]; // Sine amplitude values that will be used to generate the PWM signal
64 2023/01/10 www.so-logic.net
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
xil_printf("Initializing LEDs!\r\n");
// LEDs initialization
GPIOConfigPtr = XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID);
XGpioPs_CfgInitialize(&GpioLeds, GPIOConfigPtr, GPIOConfigPtr ->BaseAddr);
// On the socius board we must properly control both ends of a LED, hence we must use two GPIO ports
XGpioPs_SetDirectionPin(&GpioLeds, ps_sw2_a, 1);
XGpioPs_SetOutputEnablePin(&GpioLeds, ps_sw2_a, 1);
XGpioPs_SetDirectionPin(&GpioLeds, ps_sw2_b, 1);
XGpioPs_SetOutputEnablePin(&GpioLeds, ps_sw2_b, 1);
// Set the value of one end of LED to always be equal to zero, by changing the other end we will turn it on and off
XGpioPs_WritePin(&GpioLeds, ps_sw2_b, 0x0);
xil_printf("Initializing SWITCHes!\r\n");
// SWITCHes initialization
// Set the direction of the GPIO port connected to the push button to INPUT
XGpioPs_SetDirectionPin(&GpioLeds, ps_sw3_a, 0);
xil_printf("Initializing TIMER!\r\n");
// TIMER initialization
TimerSetup = &SettingsTable[TTC_DEVICE_ID];
// Store the current configuration of the timer in a TimerInst object, using a pointer to access it
Timer = &TimerInst;
Timer->Config = *Config;
www.so-logic.net 2023/01/10 65
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
// Send the current system status information to the terminal using UART
if (sw0 != prev_sw0)
{
if ((sw0 & SWITCH_POS) == 0)
xil_printf("User selected PWM signal generation with 1 Hz frequency.\r\n");
else
xil_printf("User selected PWM signal generation with 3.5 Hz frequency.\r\n");
}
prev_sw0 = sw0;
if (interrupt_occurred == 1)
{
interrupt_occurred = 0;
if (led_state == 1)
{
// Write the starting counter value, reset_value_0 = end_time - threshold
XTtcPs_SetInterval(Timer, reset_value_0);
led_state = 0;
count_depth ++;
if (count_depth == COUNT_DEPTH_END)
count_depth = 0;
threshold = scaling_factor * sine_ampl[count_depth];
}
else
{
// Write the starting counter value, reset_value_1 = threshold
XTtcPs_SetInterval(Timer, reset_value_1);
led_state = 1;
}
}
}
return 0;
}
In the modulator_sozius_intc.c source code with the interrupt controller, we have used almost the same
66 2023/01/10 www.so-logic.net
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
functions as in the modulator_sozius_no_intc.c source code without using an interrupt controller. Here
are the functions that we have used in our design. Some of them are already explained below the
modulator_sozius_no_intc.c source le.
As we already said, all of these functions and it's denitions and explanations, you can nd in the Xilinx
directory:
#include <xttcps.h>
Returns: None.
www.so-logic.net 2023/01/10 67
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
#include <xttcps.h>
Parameters:
InterruptMask - denes which interrupt should be cleared. Constants are dened in xttcps_hw.h
as XTTCPS_IXR_*. This is a bit mask, all set bits will be cleared, cleared bits will not be cleared.
Returns: None.
#include <xttcps.h>
Parameters:
InterruptMask - denes which interrupt should be enabled. Constants are dened in xttcps_hw.h
as XTTCPS_IXR_*. This is a bit mask, all set bits will be enabled, cleared bits will not be disabled.
Returns: None.
If you dive deeper into the modulator_socius_intc.c source code, you can nottice that additional code has
been included before the main. The function IntcInitFunction (u16 DeviceId, XtmrCtr *TmrInstan-
cePtr) is necessary and contains additional code to:
connect the interrupt controller interrupt handler to the hardware interrupt handling logic in the ARM
processor
connect a timer device driver handler that will be called when an interrupt for the timer occurs. This
device driver handler performs the specic interrupt processing for the device.
As you can see from the code above, in the IntcInitFunction (u16 DeviceId, XtmrCtr *TmrInstancePtr)
denition we have used some new functions:
68 2023/01/10 www.so-logic.net
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
#include <xscugic.h>
A table contains the conguration info for each device in the system.
Parameters:
Returns: A pointer to the XScuGic conguration structure for the specied device, or NULL if the
device was not found.
Note: None.
Referenced by ScuGicExample().
#include <xscugic.c>
Parameters:
Note: None.
Referenced by ScuGicExample().
#include <xscugic.c>
Makes the connection between the Int_Id of the interrupt source and the associated handler that is
to run when the interrupt is recognized.
The argument provided in this call as the Callbackref is used as the argument for the handler when
it is called.
Parameters:
www.so-logic.net 2023/01/10 69
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
Int_Id - contains the ID of the interrupt source and should be in the range of 0 to
XSCUGIC_MAX_NUM_INTR_INPUTS - 1.
CallBackRef - is the callback reference, usually the instance pointer of the connecting driver.
Note: WARNING: The handler provided as an argument will overwrite any handler that was previ-
ously connected.
Referenced by ScuGicExample().
#include <xscugic.c>
Any pending interrupt condition for the specied Int_Id will occur after this function is called.
Parameters:
Int_Id - contains the ID of the interrupt source and should be in the range of 0 to
XSCUGIC_MAX_NUM_INTR_INPUTS - 1
Returns: None.
Note: None.
A linker is a program that takes one or more object les (.o) generated by a compiler and combines them into
a single executable (.elf ) le.
70 2023/01/10 www.so-logic.net
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
Linker program combines all les from the application project into the executable .elf le. This process is
controlled by the Linker Script le.
Elf le is organized by logical section from each object le. Each section is located in a physical memory space
as dened by the linker script. Relocatable symbols are resolved to their physical addresses. Other symbols,
such as those for debugging are also added to the .elf le.
When the linker executes, it rst combines all of the object sections. Then it resolves addresses and writes LDL
les, see Figure 3.23.
Linker Script
Linker Script controls the linking process. It maps the code and data to a specied memory space, sets the
entry point to the executable, reserve space for the heap and stack, dene the layout and start address of each
section.
www.so-logic.net 2023/01/10 71
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
Linker script is required if the design contains a discontinuous memory space. It has it's own language and can
be dicult to write. Because of this, Xilinx provides a Linker Script Generator.
Linker Script will be automatically generated when you create an Xilinx C Project within Xilinx Vitis tool. In
our case it will be in the moment when we have created modulator_socius_no_intc C project.
If you want to view or make some modications to existing linker script le, please do the following:
- In the Vitis IDE ProjectExplorer tab, expand the modulator_sozius_no_intc application project,
select modulator_sozius_no_intc.prj le and select Xilinx -> Generate Linker Script command and
the Linker Scrip dialog box will appear, see Figure 3.24.
The left side of the dialog box is read-only, except for the Output Script name and project build settings in
the Modify project build settings as follows eld. This region shows all the available memory areas for
the design. You have two choices of how to allocate memory: using the Basic tab or the Advanced tab. Both
perform the same tasks; however, the Basic tab is less granular and treats all types of data as data and all
types of instructions as code. This is often sucient to accomplish most tasks. Use the Advanced tab for
precise allocation of software blocks into various types of memory.
- If you want to modify the default settings for the Linker Script le, make the required modications, click
Generate button and the new linker script le will be created.
To build an executable le for this application, Vivado IDE performs the following actions, see Figure 3.25.
72 2023/01/10 www.so-logic.net
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
First, Vitis IDE builds the Board Support Package (BSP) using LibGen tool. In our case it is called
software platform.
Then, Vitis IDE compiles the application software using platform- specic gcc/g++ compiler.
At the end, the object les from the application and the BSP are linked together to form the nal
executable le (.elf le). This step is performed by a linker which takes as input a set of object les and
a linker script that species where object les should be placed in memory.
Vitis IDE builds BSP ones, after it's creation. For every source le modication, Vitis IDE will automatically
generate a new .elf le, compiling all source les that are out of date and linking them with the BSP.
Makeles
Compilation of source les into object les is controlled using Makeles. With Vitis IDE, there are two possible
options for Makeles:
1. Managed Make : For Managed Make projects, Vitis IDE automatically creates Makeles. Makeles created
by Vitis IDE typically compile the sources into object les, and nally link the dierent object les into
an executable. In most cases, managed make simply eliminates the job of writing Makeles. This is the
suggested option.
2. Standard Make : If you want ultimate control over the compilation process, use standard make projects.
In this case, you must manually write a Makele with steps to compile and link an application. Using the
standard Make ow hides a number of dependencies from Vitis IDE, so you must follow manual steps for
www.so-logic.net 2023/01/10 73
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
other tasks such as debugging or running the application from within Vitis IDE. Therefore, the Standard
Make ow is not recommended for general use.
Build Congurations
Software developers typically build dierent versions of executables, with dierent settings used to build those
executables. For example, an application that is built for debugging uses a certain set of options (such as
compiler ags and macro denitions), while the same application is built with a dierent set of options for
eventual release to customers. Vitis IDE makes it easier to maintain these dierent proles using the concept
of build congurations.
Linker Settings
Build Report
When the program nishes building selected conguration, build report will be visible in the Vitis Console
window, including generated code size information.
For example, in case of building modulator_sozius_no_intc conguration ARM microprocessor, code size
report is shown on the following gure.
Figure 3.31: Console window with code size information for the modulator_socius_no_intc build conguration
for ARM-based system
As a part of building process, information on the size of your application will normally be displayed at the end
of the build log of the Console view, as it is shown on the Figure 3.18. Here is the explanation for each column
separately:
text - shows the size of the code and read-only (constant) data in your application (in decimal).
data - shows the size of the initialised data in your application (in decimal). Data counted in the "data"
section is not constant, so it will end up in RAM memory. But, because this data is initialised and the
initial value is constant, it will be stored in the FLASH memory. The linker allocates the space for initial
data values in FLASH which are then copied to RAM in the startup code.
bss - shows the size of uninitialized data in your application (in decimal). bss counts for the uninitialized
data in the RAM which will be initialized to zero value in the startup code.
Typically,
74 2023/01/10 www.so-logic.net
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
Remember that the RAM consumption provided by this is only that of global data. It will not include any
memory consumed by application stack and heap when application is actually executing.
By comparing the sizes of the generated executable les for MicroBlaze and ARM processors, they dier.
This is typical if we compile the same source code targeting dierent processors.
The reason for this is that dierent tool-chains (compilers, linkers, assemblers) are used with dierent processors.
All this inevitably leads to generation of dierent sizes of executable les when targeting dierent processors, even if
completely identical source code is used.
When planing to migrate an existing software application to a new processor, careful considerations need to be made
regarding the expected executable le size, which can either increase or decrease compared to existing solution.
The same holds for the execution time of the software application when targeting dierent processor platforms.
Additional aspect that can also inuence the size and speed of the generated software platform is the choice of the
programming language used to specify the software part of an embedded system.
Currently C language dominates embedded software development, but C++ language is increasingly starting to be used
also.
The reason for this is that the C++ is one of the dominant programming languages used in desktop applications, servers
and networks to which an embedded system will typically interface.
Furthermore, there are many additional pros for using C++ language in embedded system development: better support
for multicore programming, use of object oriented programming style, function overloaded, use of templates, etc.
However, when compiled, C++ programs tend to be bigger and slower then equivalent C programs.
In the past this was a major concern, but nowadays, with the availability of matured C++ compilers targeting
embedded systems and with careful usage of advanced C++ language features this no longer needs to be or is
the case. Having said this, this doesn't imply that C language will stop being one of the major programming
languages used for embedded software development in the foreseeable future, especially in the resource- and
time-critical embedded applications.
You can run your software application on your hardware platform using Vitis tool. The program will run to
termination.
www.so-logic.net 2023/01/10 75
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
The run workow is described in the previous diagram, see Figure 3.27.
Executable ELF File: To debug your application, you must use a compiled Executable and Linkable
Format (ELF) le.
Run Conguration: To launch the run session, you must create a run conguration in Vitis IDE. This
conguration captures options required to start a run session, including the executable name, processor
target to run and other information.
JTAG Settings: In most cases, Vitis IDE can automatically detect the JTAG settings and doesn't
require special settings.
Run Console: This XMD console view enables you to stop the program execution or terminate the run
session.
You can repeat the cycle of modifying the code, building the executable, and running the program in Vitis IDE.
The program can be run on all supported debug targets.
Before you can run your application, you must generate netlist and bitstream le (if you didn't generate them
after the hardware platform is specied) and download the FPGA's bitstream le to the board.
To generate netlist and bitstream le, go back to the Vivado IDE main window and follow the same steps as it is
explained at the of the Chapter 2.3.1 Create ARM-based hardware platform for Sozius development
board.
To download your ARM-based bitstream le to the target board using Vitis IDE tool, connect the additional
USB cable that will be used to provide UART interface, that will be used during system debug, and do the
following steps:
- Select Xilinx -> Program Device from the Vitis main window.
76 2023/01/10 www.so-logic.net
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
- The Program FPGA diloag box will appear. The bitstream eld should already be populated with the
Program.
correct bitstream le. Click
- Select target application project and rst start Build Project process by pressing Build Project button.
After building project process is completed press Run project button to run the target application project.
Debug button to open a Debug section. In the bottom of the Vitis IDE Debug section,
- In the Vitis IDE select
open Vitis Serial Terminal tab and click the green "+" button to connect with the serial port.
- In the Connect to serial port dialog box, in the Port eld choose COM4 serial port to connect with and
leave all other parameters unchanged. Click OK.
www.so-logic.net 2023/01/10 77
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
After connecting the terminal with the serial port, in the SDK Terminal window you should see notication
about successfully connection.
- When the ZynqPL is successfully congured with the bitstream le, we can now launch our software application
on the Zynq PS:
In the Project Explorer select your application project, right-click on it and select Run As -> Launch on
Hardware (System Debugger) option.
- Open the SDK Terminal window and you should see all the messages sent by software application.
As you can see, before changing switch position, one PWM signal generation frequency is selected.
In case of using Sozius development board, press and hold the push button 4 and the terminal will detect
that the second PWM signal generation frequency is selected as we predicted in the software application.
When you release the push button 4, the PWM signal generation frequency will return to the value that was
present before pressing the button.
78 2023/01/10 www.so-logic.net
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
Figure 3.39: Terminal window with messages sent by software application after changing the switch position on
the development board
Hardware debugging via logic probe, logic analyzer, on- circuit emulator, or background debugger
GDB tools
GDB (GNU Debugger) is a powerful, exible tool that provides a unied graphical interface for debugging
and verifying MicroBlaze processor systems during various development phases. With GDB debugger you
can debug programs written in C and C++.
GNU tools
Communicate with the hardware through XMD
The actual debugger is XMD. GDB is the user interface, or GUI, that talks to XMD through a TCP/IP port
via Tcl commands, see Illustration 3.34.
The main purpose of XMD is to attach to the debug hardware interface of the embedded processor, the MicroB-
laze Debug Module (MDM). This is done via an internal JTAG chain facilitated by the BSCAN component on
www.so-logic.net 2023/01/10 79
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
the FPGA. The MDM also oers a JTAG uart feature that will show up as a AXI bus uart peripheral for the
MicroBlaze. XMD provides many services, including download cable connection and control. One of the main
functions of XMD is the debug engine. This engine provides the command interface to the processor debug
hardware via a Tcl script and/or simple command line interface. You could directly debug a program from the
XMD command line console, but this would be a painful process. The GDB debugger provides an easy-to-use
graphical interface that interfaces Tcl with XMD.
Executable ELF File: To debug your application, you must use an Executable and Linkable Format
(ELF) le compiled for debugging. The debug ELF le contains additional debug information for the
debugger to make direct associations between the source code and the binaries generated from that original
source.
Debug Conguration: In order to launch the debug session, you must create a debug conguration in
Vitis IDE. This conguration captures options required to start a debug session, including the executable
name, processor target to debug, and other information.
JTAG Settings: When debugging the program on a hardware target, Vitis IDE uses XMD for commu-
nication to the processor using a JTAG interface on the board. The JTAG settings for the debug session
can be specied in the JTAG Settings dialog box. In most cases, the debugger can automatically detect
the JTAG settings and do not need to provide special settings.
Vitis IDE Debug Perspective: Using the Debug perspective, you can manage the debugging or running
of a program in the Workbench. You can control the execution of your program by setting breakpoints,
suspending launched programs, stepping through your code, and examining the contents of variables.
80 2023/01/10 www.so-logic.net
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
You can repeat the cycle of modifying the code, building the executable, and debugging the program in the
Vitis IDE.
Note : If you edit the source code after compiling, the line numbering will be out of step because the debug
information is tied directly to the source. Similarly, debugging optimized binaries can also cause unexpected
jumps in the execution trace.
Vitis IDE supports debugging of a program on processor running on a FPGA. All processor architectures are
supported. Vitis IDE communicates to the processor on the FPGA over the JTAG interface using the Xilinx
JTAG cable. Before you debug the processor on the FPGA, you should congure the FPGA with the appropriate
system bitstream.
The debug logic for each processor enables program debugging by controlling the processor execution. The
debug logic on hard ARM processor cores is built in and always available for debugging. However, the debug
logic on soft MicroBlaze processor cores is congurable and can be enabled or disabled by the hardware designer
when building the embedded hardware.
Enabling the debug logic on MicroBlaze processors provides advanced debugging capabilities such as hard-
ware breakpoints, read/write memory watchpoints, safe-mode debugging, and more visibility into MicroBlaze
processors. This is the recommended method of debugging MicroBlaze software.
If the debug logic is disabled on the hardware, you can debug programs using XMDStub (a ROM monitor).
XMDStub is a small debug stub that runs on MicroBlaze processors and can perform basic debug operations
such as reading and writing memory and register values and controlling the program execution. It should be
initialized to the processor local memory at the reset location, so when the processor resets, the XMDStub is
run and ready for debugging. It communicates to XMD over a Universal Asynchronous Receiver-Transmitter
(UART), which could be JTAG-based or RS232-based. This method is not supported in Vitis IDE and you
should use the XMD command-line tool for debugging.
The another way to open Debug Congurations dialog box is to select modulator_sozius_no_intc project
in the Project Explorer window, right-click on it and choose Debug As -> Debug Congurations... option.
Debug Congurations dialog box, you can see that the Vitis IDE tool has automatically created
- In the
Debugger_modulator_sozius_no_intc-Default debug conguration for us, see Figure 3.37.
www.so-logic.net 2023/01/10 81
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
- Click Debug.
The ELF le will be downloaded to the FPGA into the bootloop placeholder space in the betstream.
- If the Conrm Perspective Switch dialog box appears, click Yes to switch to the Debug perspective.
You can also switch to this perspective by clicking on the Window -> Open perspective... command, see
Figure 3.39.
82 2023/01/10 www.so-logic.net
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
When the Open Pespective dialog box appears, choose Debug option and click OK, see Figure 3.40.
The Debug perspective lets you manage the debugging or running of a program in the Workbench. You can
control the execution of your program by setting breakpoints, suspending launched programs, stepping through
your code, and examining the content of variables.
The stack frame for the suspended program that you are debugging
The Debug perspective also drives the C/C++ Editor. As you step through your program, the C/C++ Editor
highlights the location of the execution pointer, see Figure 3.41.
- The Debug perspective will open, showing the modulator_sozius_no_intc.c source le in the source view,
various variables dened in the le in the Variables view, Outline view showing the objects which are in the
current scope, thread created and the program suspended in the Debug view. Note that the program operation
is suspended at the rst executable statement (at the main() entry point), see Figure 3.41.
www.so-logic.net 2023/01/10 83
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
In the process of debugging the most important task is adding breakpoints to halt program execution at the user
Step Into, Step Over, Step Return
specied points. Once program execution is suspended, user can use
and Resume commands to control program execution from the encountered breakpoint. These commands,
together with variables and memory views, enable user to have total control and overview of the program
execution process during debugging.
Following steps will illustrate how these commands can be used in debug process.
Step Into, Step Over and Step Return commands. As already noted,
- First we will illustrate the usage of
main() function. We can
after debugging process is started, program operation will suspend at the start of the
use Step Into button to enter into the Xilinx provided function for the text printing, xil_printf, in order to
overview the function execution in more details. After you press the Step Into button, debugger will reach the
xil_printf function call within the main() function. After pressing the Step Into button once more, debugger
will automatically jump to the rst executable statement of the xil_printf function as shown on the Figure
3.42.
Figure 3.47: Result of the execution of the Step Into command on the xil_printf function
- While the debugger is working within a function call, you can use Step Return button to execute all
remaining statements within a function in order to quickly return to the point where a function has been called.
84 2023/01/10 www.so-logic.net
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
In our case if we press Step Return button once, debugger will execute all remaining statements within the
xil_printf function and return to the main() function, because xil_printf function has been called from the
main() function, and suspend program execution at the next executable statement, which in our case is yet
another function call, this time to the another xil_printf function, as shown on the Figure 3.43.
Figure 3.48: Result of Step Return command execution within xil_printf function
- Although Step Into command can be very useful in the process of program debugging, quite often we are
not interested into details of every function execution. If we would like to skip over known working functions,
because we have debugged them previously, we can use Step Over button that will execute the complete
function in one step, treating C function calls as a single C statement. Please notice that our program that
we are currently debugging has suspended execution at the second xil_printf function call as shown on the
previous gure. If you are not interested in the details of the execution of this function, you can execute it at
once by clicking on the Step Over button. Debugger will now execute all the statements within the xil_printf
function and only then suspend the program execution once more, after reaching the rst executable statement
located after the xil_printf function, in our case this would be another XGpioPs_LookupCong function
call, as shown on the Figure 3.44.
Figure 3.49: Result of the execution of the Step Over command on the second xil_printf function
Breakpoints
Next we will illustrate how to use breakpoints to suspend program execution at the user-selected line of program
code. A breakpoint suspends the execution of a program at the location where the breakpoint is set. By default,
Vitis sets breakpoints at main() and exit() functions. When you start a debug session, the processor stops at
the start of the main() function of the program. There are two types of breakpoints used by the debugger:
Software Breakpoint - To set a software breakpoint, the debugger modies the program instruction
at the breakpoint address. The debugger does not require any hardware resources for setting a software
breakpoint, so you can essentially set any number of software breakpoints in your debug session. The de-
bugger requires access to read and write to the breakpoint address location. This is the default breakpoint
used by the debugger.
www.so-logic.net 2023/01/10 85
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
Hardware Breakpoint - To set a hardware breakpoint, the debugger does not require modication of
the program instruction at the breakpoint address. Each processor provides a limited set of hardware
breakpoints. In the case of MicroBlaze processors, this is congurable and set by the hardware developer
and should be used wisely. You should use hardware breakpoints when the debugger cannot read or write
to the program memory, such as when using Flash memory.
- We will place the rst breakpoint withininit_sin_f function, at the line 36, where for loop is located.
init_sin_f function is dened in the init_sin.c source le. First we must select init_sin.c source le by
clicking on the init_sin.c tab. Next, point the mouse to the line 36 in the init_sin.c source le and right-click
on the blue stripe located on the left border of the Sources window. A drop-down menu will appear from which
Add Breakpoint... option should be selected, see Figure 3.45.
- When you select Add Breakpoint... option a Properties for C/C++ Line Breakpoint dialog box will
appear allowing you to specify the properties of the new breakpoint as shown on the Figure 3.46. Since we want
to add a simple breakpoint at this moment, we don't have to change anything, so simply click OK.
86 2023/01/10 www.so-logic.net
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
- After you have added a new breakpoint, its location in the program code will be made visible by the blue circle
marker located on the blue stripe just left of the program code line for which the breakpoint was specied, see
Figure 3.47.
- Please remember that our program execution is currently suspended at line 86 in the modulator_sozius_no_intc.c
source le and that there are several executable statements located between this line and line 126 where call
to the init_sin_f function is located, which contains the breakpoint. These statements need to be executed
before reaching the breakpoint. These executable statements can be executed by pressing the Step Over
button appropriate number of times until we reach line 126. However, this would be a very inecient way of
program debugging. Instead we can use Resume button to quickly execute all executable statements between
our current position and the breakpoint position.
- After we have pressed Resume button, debugger will execute all necessary statements until it reaches a
breakpoint set at line 36 within the init_sin.c source le and then suspend program execution, as show on the
Figure 3.48.
- Next we will illustrate how the Memory tab can be used to monitor the content of the array variables. Please
notice that program execution is suspended at line 36. We will use the Monitor tab to overlook this initialization
process. First thing that must done is to determine the base address at which the sine_ampl array is stored
in the memory. To do so, look in the Variables tab for variable with sine_ampl name. Inspect the content of
the Value eld located in the same row. This is the starting address of the sine_ampl array.
www.so-logic.net 2023/01/10 87
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
- To open Memory tab, select Window -> Show View -> Memory option from the main menu
- In the Memory tab, click on the Add Memory Monitor button. A new dialog box will appear where we
should specify the address or expression to monitor. In our case we will specify the starting address of the
sine_ampl array, 0x0010c024, see Figure 3.50. After you do so, click OK.
- In the Memory tab, please notice that a new Memory monitor has been added, monitoring the memory
content starting from the address 0x0010c024 as shown on the Figure 3.51. Currently the content of all memory
locations staring from the address 0x0010c024 is 0x00000000. This is ne, since we still have not initialized the
sine_ampl array.
Figure 3.56: Content of the sine_ampl array in Memory window before array initialization
- Let us initialize the rst member of the sine_ampl array, with index value 0. Please press Step Over button
once. After the rst step over command, debugger will execute the for statement. Since this is the rst time
i to 0, as specied by the for statement.
this statement is executed it will set the value of the iterator variable
This change of the variable i value is also indicated in the Variables tab, where line holding the variable i is
coloured yellow and holds the new value for the variable i, see Figure 3.52.
88 2023/01/10 www.so-logic.net
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
- Press Step Over button three times more. This time debugger will execute the line of code that initialises
sine_ampl array member with index value i=0. After debugger nishes executing this line of code it will
suspend program execution and update the Memory tab as shown on the Figure 3.53. If you inspect the value
stored at the memory location 0x0010c024, you can see that it has changed from 0x00000000 to 0x000007FF
which is the correct initial value for the sine_ampl[0] array member.
Figure 3.58: Indication of the change of the sine_ampl value in Memory window
- Finally, we will create a conditional breakpoint in order to stop the sine_ampl initialization process after a
specied number of array element have been initialized. Right-click on the blue stripe just left of the line 38
and select Add Breakpoint... option once more. Properties for C/C++ Line Breakpoint dialog box
will appear as before. Since now we would like to place a conditional breakpoint we must specify breakpoint
condition using the Condition eld. In this example we would like to break a program execution when loop
iterator i reaches the value 5. This would mean that debugger should stop sine_ampl array initialization process
after sine_ampl members 0-4 have been initialized. To specify this condition type i==5 in the Condition
eld as show on the Figure 3.54 press OK button to complete the conditional breakpoint setup.
www.so-logic.net 2023/01/10 89
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
Figure 3.59: Properties for C/C++ Line Breakpoint dialog box - condition breakpoint setup i==5
- You can verify that a new conditional breakpoint has been placed at the line 38 which is designated by the
blue circle located on the blue stripe just left to the line 38. Since this breakpoint is conditional breakpoint,
next to the blue circle a question marker is also visible as shown on the Figure 3.55.
- Remove the existing breakpoint at line 36 by right-clicking on the blue stripe just left of the line 36 and
selecting Toggle Breakpoint option.
- Press Resume button to continue program execution. Debugger will continue initialising sine_ampl array
until it reaches the condition specied in the conditional breakpoint located at line 38. This will happen when
loop iterator i reaches the value of 5. After this condition is met, debugger will suspend program execution and
display the current content of sine_ampl array in the memory tab as show on the Figure 3.56. Please notice
that sine_ampl members with index values 0-4 have already been initialised to appropriate values, because
memory locations with addresses from 0x0010c024 to 0x0010c034 have values that are dierent from 0.
Since sine_ampl array is an array of unsigned integers, each array member occupies one double word in the
memory. This means that sine_ampl members with index values 0-4 should occupied memory block starting
from 0x0010c024 to 0x0010c034, which is exactly the memory block that has values dierent from 0 as show in
the Memory tab.
90 2023/01/10 www.so-logic.net
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
www.so-logic.net 2023/01/10 91
CHAPTER 3. CREATING THE SOFTWARE PLATFORM USING VITIS
92 2023/01/10 www.so-logic.net
Chapter 4
First we will show how to create a hardware platform that includes a custom IP, in our case it will be PWM
Modulator IP core (modulator_axi_ip_v1.0 ) with AXI-Lite interface.
Next, we will show how to develop a basic driver for PWM Modulator IP core, how to integrate it in the
Xilinx Vitis tool chain, and how to develop an application that will use this driver to communicate with PWM
Modulator IP core.
PWM Modulator IP core (modulator_axi_ip_v1.0 ) was developed and packaged in the sub-chapter 11.2 "Cre-
ating Modulator IP Core with AXI4 Interface" of the Vivado "Basic FPGA Tutorial".
Users not familiar with the details of the PWM Modulator IP core and the process of packaging it to the Vivado
compliant IP core, please refer to the mentioned sub-chapter 11.2 for more details.
Block diagram of the hardware platform that we will create is shown on the Figure 4.1.
93
CHAPTER 4. USING CUSTOM IPS IN EMBEDDED SYSTEM DESIGN
Figure 4.1: Structure of microprocessor-based embedded system, using a custom IP to generate pwm signal
Comparing the Figure 4.1 with the Figure 1.3 it can be seen that the structure of new hardware platform is
simpler. In the new hardware platform only two IP cores are needed: standard Xilinx one-channel GPIO IP core
and an instance of PWM Modulator (modulator_axi_ip_v1.0) custom IP core. Since all the functionality
needed to generate pwm signal is packaged inside PWM Modulator custom IP core, there is no need for timer
and interrupt controller IP cores that are present on Figure 1.3.
PWM Modulator (modulator_axi_ip_v1.0 ) custom IP core is designed to be fully self-contained pwm generator
module.
It can generate pwm output signal, modulated by the sine signal, with two dierent, user-dened frequencies.
It uses an AXI-Lite interface to connect to the AXI4 internal system bus of any AXI enabled microprocessor.
Operation of the PWM Modulator custom IP core is controlled through the set of three internal 32-bit cong-
uration registers, which are accessed through the AXI-Lite interface:
the rst register, sel REGISTER, will be used to replace the sel switch from the board
the second register, inc_freqhigh REGISTER, will be used for storing inc_freqhigh increment values
the third register, (inc_freqlow REGISTER, will be used for storing inc_freqlow increment values
Table 4.1 shows the internal 32-bit conguration registers address map.
94 2023/01/10 www.so-logic.net
CHAPTER 4. USING CUSTOM IPS IN EMBEDDED SYSTEM DESIGN
This information is important for the software developer in order to correctly access, congure and control
PWM Modulator IP core.
Next, we will show how to create a hardware platform, based on the ARM processor, using Vivado IP integrator
tool.
- Create a new modulator_sozius_axi project using Vivado IDE wizard. To create a new project, please
repeat steps 1-7 from the Sub-chapter 2.1 Create a New Project.
When we have created a new project, we have to add packaged IP to the IP Catalog. The following steps will
show you how to add packaged IP to the IP Catalog:
- Create a new folder, ip_repository, in the same directory where modulator_sozius_axi project is created.
This new folder will be place where we will copy the packaged modulator_axi_ip IP core.
- Copy the packaged modulator_axi_ip IP core to the ip_repository folder and extract it inside the same
folder.
- Then, in the Flow Navigator, under the Project Manager, click on the Settings command.
Settings dialog box, under the Project Settings commands from the left pane, expand IP and select
- In the
Repository option.
Repository Manager lets you add or remove user repositories and establish precedence between repositories.
www.so-logic.net 2023/01/10 95
CHAPTER 4. USING CUSTOM IPS IN EMBEDDED SYSTEM DESIGN
- In the Repository manager window, click + icon to add the desired repository.
- In the IP Repositories window, choose ip_repository folder with packaged modulator_axi_ip IP core
and click Select.
- In the Add Repository dialog box, click OK to add the selected repository (ip_repository with 1 IP) to the
project.
- In the Repository manager window, when ip_repository is added to the IP Repositories section, click
OK.
96 2023/01/10 www.so-logic.net
CHAPTER 4. USING CUSTOM IPS IN EMBEDDED SYSTEM DESIGN
- In the Flow Navigator, under the Project Manager, click IP Catalog command to verify the presence
of the previously added IP in the IP Catalog.
In the Search eld type the name of your IP core (modulator_axi_ip) and you should nd it under AXI
Peripheral IPs.
Now, when we have all the necessary IPs for our design, we will create block design for our project.
The following steps describe how to use the IP Integrator within your project:
www.so-logic.net 2023/01/10 97
CHAPTER 4. USING CUSTOM IPS IN EMBEDDED SYSTEM DESIGN
After Vivado has nished with the Tcl script execution, a created block diagram containing Zynq PS will be
visible in the Vivado IDE.
- The next step will be to add the rest of the necessary IPs into the design canvas. These IPs are located in the
Vivado IP Catalog and IP Integrator gave you a possibility to add them on three ways:
In the design canvas, right-click and choose Add IP... option, see Figure 4.7, or
Use the Add IP link in the IP Integrator canvas, see Figure 4.8, or
98 2023/01/10 www.so-logic.net
CHAPTER 4. USING CUSTOM IPS IN EMBEDDED SYSTEM DESIGN
on the Add IP button in the IP Integrator sidebar menu, see Figure 4.9.
- When you nd it, press enter on the keyboard or simply double-click on the modulator_axi_ip_v1.0 core
in the IP Catalog and the selected core will be automatically instantiated into the IP Integrator design canvas.
www.so-logic.net 2023/01/10 99
CHAPTER 4. USING CUSTOM IPS IN EMBEDDED SYSTEM DESIGN
Figure 4.11: Automatically instantiated modulator_axi_ip_v1.0 core in the IP Integrator design canvas
- Click OK.
As we already said, in our design we will program PL part of the Zynq FPGA. Since existing LEDs and switches
on the Sozius development board are connected to the PS part of the Zynq FPGA, we have to instantiate
Integrated Logic Analyzer (ILA) and Virtual Input/Output (VIO) cores into our design. All the
detailed information about ILA and VIO cores you can nd in the Chapter 10 "Debugging Design" of the
"Basic FPGA Tutorial" tutorial.
- The next IPs necessary for our design are Binary Counter (c_counter_binary_0 ), ILA (ila_0 ) and VIO
vio_0 IPs. Add all three IPs into the "modulator_sozius_axi" block design as it is shown on the Figure 4.13
and make the following IP customizations.
Add Binary Counter, ILA and VIO IP Core into the Design
Figure 4.13: IP Integrator design canvas with instantiated Counter, ILA and VIO IPs
- Double-click on the Binary Counter (c_counter_binary_0 ) IP and in the Binary Counter (12.0) Re-
customization IP dialog box set the following parameters:
Figure 4.14: Binary Counter (12.0) re-customization IP dialog box - Basic tab
enable Clock Enable (CE) and Synchronous Clear (SCLR) options, see Figure 4.15 and click
OK.
Figure 4.15: Binary Counter (12.0) re-customization IP dialog box - Control tab
- Because of the structure of the binary counter that we need, we also had to include one invertor into our IP
Integrator design.
- Add Utility Vector Logic (util_vector_logic_0 ) IP into design canvas, double-click on it and re-customize
it.
In the Utility Vector Logic (2.0) dialog box, make the following changes:
click OK.
- Double-click on the ILA IP and in the ILA (Integrated Logic Analzyer (6.2)) dialog box, in the General
Options, set the following parameters:
enable Capture Control option in the Trigger And Storage Settings section, as it is shown on the
Figure 4.17.
Figure 4.17: ILA (Integrated Logic Analyzer (6.2)) Re-customize IP dialog box - General Options
set 32 bits as Probe Width[1..4096] value of PROBE0 probe, as it is shown on the Figure 4.18, and
click OK.
Figure 4.18: ILA (Integrated Logic Analyzer (6.2)) Re-customize IP dialog box - Probe Ports(0..7)
- In case of VIO core, double-click on theVIO IP and in the VIO (Virtual Input/Output (3.0)) dialog
box, in the General Options tab, set the Output Probe Count to be 0, see Figure 4.19. In case of VIO
core we will need only one input probe, to connect it with the modulator_axi_ip_0 pwm_o port.
Figure 4.19: VIO (Virtual Input/Output (3.0)) Re-customize IP dialog box - General Options
- In the PROBE_IN Ports(0..0) tab, leave PROBE_IN0 port to be 1-bit width, because pwm_o port is
OK, see Figure 4.20.
also 1-bit wide and click
Figure 4.20: VIO (Virtual Input/Output (3.0)) Re-customize IP dialog box - PROBE_IN Ports(0..0)
- Add the AXI Protocol Converter (axi_protocol_converter_0 ) IP core into the block design.
- In the AXI Protocol Converter (2.1) dialog box, make the following changes:
At this stage of the design process, the IP Integrator design canvas should look like as it is shown on the Figure
4.22.
Figure 4.22: IP Integrator design canvas with added all the necessary IPs
ZYNQ7 Processing System (5.5) re-customize IP dialog box, select PS-PL Conguration option
- In the
PS-PL Conguration section, expand AXI Non Secure Enablement, then
from the left menu. In the
expand GP Master AXI Interface and enable M AXI GP0 interface option, see Figure 4.23, and click
OK.
We need this extra GPIO port to connect it with AXI Protocol Converter that will be connected with the
modulator_axi_ip IP core.
After we re-customized ZYNQ7 Processing System, the new M_AXI_GP0 port will appear in the ZYNQ IP
block, see Figure 4.24.
After we added all the necessary IPs into our design and after all the necessary IP customizations, the IP
Integrator design canvas should look as it is shown on the Figure 4.24.
Figure 4.24: IP Integrator design canvas with all necessary IPs and re-customizations
Place the cursor on top of the desired pin and you can notice that the cursor changes into a pencil indicating
that a connection can be made from that pin. Clicking the left mouse button a connection starts. Click and
drag the cursor from one pin to another. You must press and hold down the left mouse button while dragging
the connection from one pin to another. As you drag the connection wire, a green checkmark appears on the
port indicating that a valid connection can be made between these points. The Vivado IP Integrator highlights
all possible connections points in the subsystem design as you interactively wire the pins and ports. Release the
left mouse button and Vivado IP integrator makes connection between desired ports. Repeat this procedure
until all the pins become associated.
Note : Connect all the IPs on the same way as it is shown on the Figure 4.25. As you can see we connected all
the IPs, except clock ports and reset ports.
- In the IP Integrator window, click the Run Connection Automation link and the list of the ports/interfaces
that can use the Connection Automation feature will show up.
Run Connection Automation link is IP Integrator feature that assist you in putting together a basic
microprocessor system, making internal connections between dierent blocks and making connections to external
interfaces.
- In the Run Connection Automation dialog box enable All Automation (6 out of 6 selected) and click
OK.
After running the connection automation, the connections will be made and highlighted in the IP Integrator
design canvas.
- Click on the Address Editor tab, beside Diagram tab, to open the Address Editor window.
- In the Address Editor window, expand sozius_xz_lab_ps, then expand Data, after that expand Un-
mapped Slaves and right-click on the S00_AXI and choose Assign option to assign some memory space for
S00_AXI of the modulator_axi_ip_0 IP core.
Validate Design
- From the toolbar menu of the design canvas, run the IP subsystem design rule checks by clicking the Validate
Design button.
Figure 4.29: Validate Design button from the main toolbar menu
Alternatively, you can do the same by selecting Tools -> Validate Design from the main menu
- In the Validate Design dialog box, click OK, see Figure 4.31.
Use the File -> Save Block Design command from the main menu to save the design.
Synthesize and Implement Design, Generate Bitstream File and Program Target Device
- Synthesize your design using Run Synthesis command from the Flow Navigator / Synthesis section.
- Implement your design using Run Implementation command from the Flow Navigator / Implementa-
tion section.
- Generate bitstream le using Generate Bitstream command from the Flow Navigator / Program and
Debug section.
- Program FPGA device using Open Hardware Manager command from the Flow Navigator / Program
and Debug section.
To complete the design, we must now create a software component for our embedded system. This application
specic software will be executed on the ARM processor that is already part of our hardware platform.
- The rst step in software creation is to export the hardware design into the Vitis tool. To export your hardware
platform into the Vitis tool and to launch Vitis IDE, please repeat steps 1 - 7 from the Chapter 3 "Creating
the Software Platform using Vitis".
modulator_sozius platform project, repeat steps from the Sub-chapter 3.1 "Create a Plat-
- To create
form Project".
modulator_sozius_axi application project, repeat steps from the Sub-chapter 3.3 "Create a
- To create
Application Project".
The complete modulator_sozius_axi.c and modulator_sozius_axi.h source les you can nd in the text
below.
modulator_sozius_axi.c
#include "xparameters.h"
#include "xgpiops.h"
#include "xstatus.h"
#include "math.h"
#include "modulator_sozius_axi.h"
int main(void)
{
/*********************** Variable Definitions ********************/
XGpioPs GpioSwitches; // XGPIO instance that will be used to work with SWITCHes
XGpioPs_Config *GPIOConfigPtr;
int temp;
// SWITCHes initialization
GPIOConfigPtr = XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID);
XGpioPs_CfgInitialize(&GpioSwitches, GPIOConfigPtr, GPIOConfigPtr ->BaseAddr);
XGpioPs_SetDirectionPin(&GpioSwitches, SWITCH_CHANNEL, 0);
modulator_axi4_lite_interface = INC_FREQHIGH_REGISTER_ADDRESS;
*modulator_axi4_lite_interface = INC_FREQHIGH;
modulator_axi4_lite_interface = INC_FREQLOW_REGISTER_ADDRESS;
*modulator_axi4_lite_interface = INC_FREQLOW;
// readback
modulator_axi4_lite_interface = SEL_REGISTER_ADDRESS;
temp = *modulator_axi4_lite_interface;
modulator_axi4_lite_interface = INC_FREQHIGH_REGISTER_ADDRESS;
temp = *modulator_axi4_lite_interface;
modulator_axi4_lite_interface = INC_FREQLOW_REGISTER_ADDRESS;
temp = *modulator_axi4_lite_interface;
sel = sel & SWITCH_POS; // masking (we want to check the status of sel only)
if (sel != current_sel)
{
current_sel = sel;
modulator_axi4_lite_interface = SEL_REGISTER_ADDRESS;
*modulator_axi4_lite_interface = sel;
}
}
return 0;
}
modulator_sozius_axi.h
#ifndef MODULATOR_H_
#define MODULATOR_H_
#include "math.h"
#include "xparameters.h"
// increment value for higher frequency, when sel = 1 (F_HIGH = 3.5 Hz)
// INC_FREQHIGH = C * F_HIGH CLOCK_RATE = 150
#define INC_FREQHIGH roundf(C * F_HIGH / CLOCK_RATE)
#endif /* MODULATOR_H_ */
- Select the target application project (modulator_sozius_axi), then build project and at the end run your
application project.
Importnat Note: If an Makele error occurs after starting the build project process (as it is shown in the
Figure 4.32), it is necessary to do the following:
vitis_axi/modulator_sozius/zynq_fsbl/zynq_fsbl_bsp/ps7_cortexa9_0/libsrc
go to the folder
src folder copy the Makele and rewrite it over it the Makele
and open any of the oered folders. In the
in the vitis_axi/modulator_sozius/zynq_fsbl/zynq_fsbl_bsp/ps7_cortexa9_0/libsrc/
modulator_axi_ip_v1_0/src folder.
- Turn back to the Vivado IDE and in the Hardware window of the Hardware Manager right-click on the
FPGA device (xc7z020_1) and select Refresh Device option.
When the debug cores are detected upon refreshing a hardware device, the default dashboard for each debug
core is automatically opened.
Vivado Logic Analyzer is an integrated logic analyzer in the Vivado Design Suite.
In this chapter you will learn how to debug your ARM-based system using the Vivado logic analyzer and you
will take advantage of it's functions to debug and discover some potential root causes of your design.
In-system debugging allows you to debug your design in real-time on your target hardware.
IP Integrator provides ways to instrument your design for debugging, which will be explained in this chapter.
After programming the FPGA device with the .bit le that contains the ILA and VIO cores, the Hardware
window now shows the ILA and VIO cores that were detected after scanning the device, see Figure 4.40.
Ones you have the debug cores in your design, you can use the run time logic analyzer features to debug the
design in hardware. The Vivado logic analyzer feature is used to interact with new ILA, VIO, and JTAG-to-AXI
Master debug cores that are in your design.
The next step in our design process is to set up the ILA core.
When the debug cores are detected upon refreshing a hardware device, the default dashboard for each debug
core is automatically opened.
Every default dashboard contains windows relevant to the debug core the dashboard is created for. The default
dashboard created for the ILA debug core contains ve windows, as can be seen on the previous illustration:
Settings window
Status window
Waveform window
- Open the VIO dashboard by clicking the hw_vios tab and press blue + button in the middle of the VIO
dashboard to add the probes.
- In the Add Probes window select the only oered pwm_o probe and click OK.
- In the VIO Probes window you will see one 1-bit probe, pwm_o.
pwm_o probe is actually connected to the pwm_o output port of the Modulator AXI module. In the VIO
pwm_o signal.
Probes window, you can observe the rate of change of the
- Turn back to the ILA dashboard by clicking the h_ila_1 tab and in the Trigger Setup window press blue
+ button in the middle to add the probes.
- In the Add Probes window select only pwm_o_1 probe and click OK.
The another way to add debug probes to the Trigger Setup window is to drag and drop the probes from the
Debug Probes window to the Trigger Setup window.
Important : Only probes that are in the Trigger Setup or Capture Setup window participate in the trigger
condition. Any probes that are not in the window are set to "don't care" values and are not used as part of the
trigger condition.
The Debug Probes window contains information about the nets that you probed in your design using the ILA
and/or VIO cores. This debug probe information is extracted from your design and stored in a data le that
typically has an .ltxle extension. Normally, the ILA probe le is automatically created during implementation
process. This le is automatically associated with the FPGA hardware device if the probes le is called debug
nets.ltx and is found in the same directory as the bitstream le that is associated with the device.
Now, when the ILA debug probe pwm_o_1 is in the Trigger Setup window, we can create trigger conditions
and debug probe compare values.
- In the Trigger Setup window, leave == (equal) value in the Operator cell, [H] (Hexadecimal) value in
the Radix cell and set the Value parameter to be 0 (logical zero).
Figure 4.38: Changing the Compare Values in the Trigger Setup window
As you can see from the illustration above, the Trigger Setup window contains three elds that you can
congure:
Operator : This is the comparison operator that you can set to the following values:
== (equal)
!= (not equal)
Radix : This is the radix or base of the Value that you can set to the following values:
[B] Binary
[H] Hexadecimal
[O] Octal
[A] ASCII
[U] Unsigned Decimal
[S] Signed Decimal
Value : This is the comparison value that will be compared (using the Operator) with the real-time on
the nets(s) in the design that are connected to the probe input of the ILA debug core. Depending on the
radix settings, the Value string is as follows:
Binary
* 0 : logical zero
* 1 : logical one
* X : don't care
* R : rising or low-to-high transition
* F : falling or high-to-low transition
* B : either low- to-high or high-to-low transitions
* N : no transition (current sample value is the same as the previousvalue)
Hexadecimal
* X : All bits corresponding to the value string character are "don't care" values
Unsigned Decimal
* Any non-negative integer value
Signed Decimal
* Any integer value
You can use the ILA Dashboard to interact with the ILA core in several ways:
Use BASIC and ADVANCED trigger modes to trigger on various events in hardware
Use ALLWAYS and BASIC capture modes to control ltering of the data to be captured
Set the trigger position to any sample within the capture window
Monitor the trigger and capture status of the ILA debug core
Capture mode - selects what condition is evaluated before each sample is captured:
ALWAYS: store a data sample during a given clock cycle regardless of any capture conditions
BASIC: store a data sample during a given clock cycle only if the capture condition evaluates "true"
Data Depth - sets the data depth of the ILA core captured data buer. You can set the data depth to any
power of two from 1 to the maximum data depth.
Trigger Position - sets the position of the trigger mark in the captured data buer. You can set the trigger
position to any sample number in the captured data buer. For instance, in the case of a captured data buer
that is 1024 sample deep:
sample number 0 corresponds to the rst (left- most) sample in the captured data buer
sample number 1023 corresponds to the last (right-most) sample in the captured data buer
sample numbers 511 and 512 correspond to the two "center" samples in the captured data buer
- In the ILA Settings window, change the Capture mode to be BASIC in the Capture Mode Settings
section.
- In the Capture Setup window press blue + button in the middle to add the probes.
- In the Add Probes window select only pwm_o_1 probe and click OK.
Capture Setup window, leave == (equal) value in the Operator cell, [B] (Binary) value in the
- In the
Radix cell and set the Value parameter to be F (1-to-0 transition).
Figure 4.40: Changing the Compare Values in the Capture Setup window
- After we set all the ILA core parameters, we can run or arming the ILA core trigger.
We can run or arm the ILA core trigger in two dierent modes:
Run Trigger mode - arms the ILA core to detect the trigger event that is dened by the ILA core trigger
condition and probe compare values.
To run this mode, click the Run Trigger button in the Hardware or Debug Probes window.
Run Trigger Immediate mode arms the ILA core to trigger immediately regardless of the settings of the ILA
core trigger condition and probe compare values. This command is useful for capturing any values present at the
probe inputs of the ILA core.
To run this mode, click the Run Trigger Immediate button in the Hardware or Debug Probes window.
You can also arm the trigger by selecting and right-clicking on the ILA core (hw_ila_1) in the
Hardware
window and selecting Run Trigger or Run Trigger Immediate option from the popup menu, see Figure
4.47.
Once the ILA core captured data has been uploaded to the Vivado IDE, it is displayed in the Waveform
Viewer.
After triggering the ILA core, in the waveform viewer change the c_counter_binary_0_Q[31:0] Waveform
Style from Digital to Analog, and your captured waveform should look like as the waveform on the following
gure.
- To change the switch position from 0 to 1, press and hold the push button 4 on the Sozius development
board and while holding the push button 4 run trigger for the ILA core. Do not release the button until the
trigger is complete.
- Now when you have changed the switch position, run the trigger for the ILA core one more. After triggering
the ILA core your captured waveform should look like as the waveform on the following gure.
By comparing the waveforms shown on Figures 4.48 and 4.49 we can observe that they dier in the amplitude
value. This is expected since the waveforms actually represent the width of the PWM pulse generated by the
modulator module. Since the frequencies of two generated PWM signals der (one has a frequency of 1 Hz
and the other of 3.5 Hz) and the PWM pulse width measurement module always uses the same frequency for
measuring the duration of the PWM pulse, when the PWM frequency increases the duration of the PWM pulse
will decrease, therefore decreasing the amplitude of the output signal of the PWM pulse width measurement
module.
The ILA core can capture data samples when the core status is Pre-Trigger, Waiting for Trigger or Port-Trigger.
As we already said, Capture mode selects what condition is evaluated before each sample is captured. Capture
mode stores a data sample during a given clock cycle only if the capture condition evaluates "true". We used
pwm_o signal to do the signal capturing.
Capture condition is a Boolean combination of events that is detected by match unit comparators that are
attached to the trigger ports of the core. Only when this combination is detected, data will be stored in the
ILA's buer.
To be able to capture at least one period of the sine signal and to store it in the ILA buer, we have to use
capture condition feature. After triggering the ILA core, in the waveform viewer change the Waveform Style
from Digital to Analog and your captured waveform should look like as the waveform on the Figure 4.48 or
Figure 4.49.
In all previous software applications communication with the modulator_axi_ip IP core was done by directly accessing
its internal registers.
This requires intimate knowledge of modulator_axi_ip IP core's internal organization, as well as exact address values
for every internal register.
This approach signicantly complicates software application development by a typical software developer, who is not
aware and even doesn't want to be aware of all these details about the hardware device he is using.
A better approach includes an additional software layer that provides a software interface to the hardware device, enabling
user application to access hardware functions without needing to know precise details of the hardware being used.
A device driver typically communicates with the hardware device through the system bus or some other communication
subsystem to which the hardware device is connected.
When a user application program invokes a routine in the driver, the driver issues low level commands to the hardware
device.
Once the hardware device sends data back to the device driver,the driver may invoke routines in the original calling
program.
Device drivers simplify programming by acting as translator between a hardware device and the applications or operating
system that use it.
Programmers can write the higher level application code independently of what ever specic hardware the end-user is
using.
In this sub-chapter it will be shown how to develop a device driver for the modulator_axi_ip IP core and
how to integrate it and use it within the Xilinx Vitis tool.
Writing your drivers with a hierarchical structure is considered to be good design practice, and will save you a
lot of time when you need to debug the design.
Once you have veried that you have some measurable results by writing to one register, you can then move on
to testing some of the others.
It is advisable to create a function that can be re-used in your code, because this will form the basis of your
driver hierarchy.
The great way to start writing your own driver is to use the supplied Xilinx IO driver functions.
The xil_io driver provides some useful IO functions which can read and write data values to registers in the
hardware IP core, and these are perfect when the user wishes to design a driver for custom IP.
xil_io is easy to use and can be included in any project using a simple #include statement in the C code.
The most commonly used functions calls are Xil_Out32() and Xil_In32(), but similar functions exist for 16 bit
and 8 bit data transfers.
For example, following two functions can be used to write and read a value from the selected internal register
of the IP core. modulator_axi_ip_Set_Register function writes a user specied value (supplied through
the value input argument) to the selected custom IP internal register (selected by specifying base address value,
via baseaddr input argument and oset value, via oset input argument) using Xil_Out32 function. Similarly,
modulator_axi_ip_Get_Register function reads the current value from the selected custom IP internal
register and returns it to the caller, using Xil_In32 function.
In these functions, we have implemented the use of osets from the base address, rather then using hard-coded
addresses each time. Another good coding practice would be to use #dene statements to specify names for
commonly used hex values, enabling the user to quickly identify which register is being addressed without having
to constantly cross-reference everything to the address map of the IP.
Presented two functions enable us to communicate with the custom IP core's internal registers to exercise the
various features of the custom IP core.
In the case of the modulator_axi_ip IP core, following additional driver functions can be dened:
When called, this function sets the selected PWM frequency value to the user specied value in the selected
instance of the modulator_axi_ip IP core. Instance of the modulator_axi_ip IP core for which one of the
PWM frequency values should be changes is specied through the Modulator_AXI *InstancePtr input argu-
ment. Which one of the two PWM frequency values should be changed is specied through the int freq_sel
input argument. Finally, new PWM frequency value that should be used is specied through oat freq_val
input argument. Function calculates the necessary value of the increment factor (variable inc_factor) based on
the desired PWM frequency value and specied characteristics of the selected instance of the modulator_axi_ip
IP core (InstancePtr->NCO_Width and InstancePtr->ClockRate ). Calculated increment factor is then written
to the appropriate internal register of the selected instance of the modulator_axi_ip IP core, using modula-
tor_axi_ip_Set_Register function.
return temp;
}
When called, this function returns the current value of selected PWM frequency from the selected instance of the
modulator_axi_ip IP core. Instance of the modulator_axi_ip IP core from which one of the PWM frequency
values will be read is specied through the Modulator_AXI *InstancePtr input argument. Which one of the
two PWM frequency values should be read is specied through the int freq_sel input argument. Function rst
reads the current value of the increment factor from the selected instance of the modulator_axi_ip IP core,
using modulator_axi_ip_Get_Register function. This increment factor value is then converted to frequency
value and returned to the calling function.
When called, this function sets the value of the PWM frequency selector in the selected instance of the mod-
ulator_axi_ip IP core to the user specied value. Instance of the modulator_axi_ip IP core for which the
PWM frequency selector value will be set is specied through the Modulator_AXI InstancePtr input argument.
Which one of the two PWM frequency values should be used is specied through the int freq_sel input argu-
ment. Function simply writes the freq_sel input argument's value to the appropriate internal register of the
selected instance of the modulator_axi_ip IP core by calling modulator_axi_ip_Set_Register function.
When called, this function simply returns the current PWM frequency selector value from the selected instance
of the modulator_axi_ip IP core. Instance of the modulator_axi_ip IP core from which one of the PWM
frequency values will be read is specied through the Modulator_AXI InstancePtr input argument. Current
PWM frequency selector value is read from the selected instance of the modulator_axi_ip IP core, using
modulator_axi_ip_Get_Register function.
To facilitate the possibility of having more then one instance of the modulator_axi_ip IP core in the system,
driver functions presented above were written using a parametrized approach, via Modulator_AXI *Instan-
cePtr input argument.
By modifying this input argument value, calling function can communicate with dierent instances of the
modulator_axi_ip IP core that may be present in the system.
However, this approach requires that the user initialize every instance of the modulator_axi_ip IP core that is
present in the system, and keep track of this via a separate Modulator_AXI object.
// Assert arguments
Xil_AssertNonvoid(InstancePtr != NULL);
modulator_axi_ip_Initialize function initializes the selected Modulator_AXI object (specied by the Mod-
ulator_AXI InstancePtr input argument) with the conguration information related to the modulator_axi_ip
IP core whose device ID value is specied via the u16 DeviceId input argument. First, the function searches
for the conguration information of the selected modulator_axi_ip IP core by searching through a modula-
tor_axi_ip_CongTable array. modulator_axi_ip_CongTable array holds the conguration information of
every modulator_axi_ip IP core instance that is present in the system. Please notice that this conguration
information array is created automatically by the Xilinx Vitis tool, during the building of the Board Support
Package, using the instructions in the specic user-dened TCL le, that will be discussed later in this chapter.
If a device ID match is found, conguration of the selected modulator_axi_ip IP core is copied to the CongPtr
object. Using this conguration information, supplied Modulator_AXI is initialized and returned to the calling
function, along with the status information about the completed initialization process.
Finally, to complete our device driver for the modulator_axi_ip IP core, one more driver function is needed,
modulator_axi_ip_Set_Input_Clock_Frequency. This driver function is called for each instance
of the modulator_axi_ip IP core during the initialization process, to correctly set the system clock fre-
quency value that is used in every instance of the modulator_axi_ip IP core. This information is needed
to correctly calculate increment factors in the modulator_axi_ip_Set_PWM_Frequency and modula-
tor_axi_ip_Get_PWM_Frequency driver functions.
The driver functions shown above should illustrate that drivers can quickly be built to exercise the various
features of the custom IP.
It should also be very obvious that we are starting to generate signicant amounts of code, and we already have
many functions in our source le which are beginning to be untidy.
To continue the development of the driver functions, we can now move to code into a separate modula-
tor_axi_ip.c source le, and the custom prototypes into a modulator_axi_ip.h header le.
To maintain visibility of the functions across the dierent les, be sure to add the line #include "modula-
tor_axi_ip.h" at the top of the main application source le, and also to the modulator_axi_ip.c source le.
With this le structure in place, it is possible to quickly and easily continue the development of the custom
IP drivers, while keeping the source code in the main test application tidy and manageable. For very complex
driver functions, additional source les can be added to the le set to facilitate hierarchy in the source code.
Below, the complete device driver source code for modulator_axi_ip IP core, organized into two separate les,
modulator_axi_ip.c and modulator_axi_ip.h, is presented.
modulator_axi_ip.c:
#include "xil_io.h"
#include "xstatus.h"
#include "xparameters.h"
#include "modulator_axi_ip.h"
/****************************************************************************/
/**
* Initialize the Modulator_AXI instance provided by the caller based on the
* given DeviceID.
*
* Nothing is done except to initialize the InstancePtr.
*
* @param InstancePtr is a pointer to an Modulator_AXI instance. The memory the
* pointer references must be pre-allocated by the caller. Further
* calls to manipulate the instance/driver through the Modulator_AXI API
* must be made with this pointer.
* @param DeviceId is the unique id of the device controlled by this Modulator_AXI
* instance. Passing in a device id associates the generic Modulator_AXI
* instance to a specific device, as chosen by the caller or
* application developer.
*
* @return
* - XST_SUCCESS if the initialization was successful.
* - XST_DEVICE_NOT_FOUND if the device configuration data was not
* found for a device with the supplied device ID.
*
* @note None.
*
*****************************************************************************/
int modulator_axi_ip_Initialize(Modulator_AXI *InstancePtr, u16 DeviceId)
{
modulator_axi_ip_Config *ConfigPtr = NULL;
int Index;
// Assert arguments
Xil_AssertNonvoid(InstancePtr != NULL);
{
if (modulator_axi_ip_ConfigTable[Index].DeviceId == DeviceId)
{
ConfigPtr = &modulator_axi_ip_ConfigTable[Index];
break;
}
}
return temp;
}
modulator_axi_ip.h:
#include "xil_types.h"
/**
* This typedef contains configuration information for the device.
*/
typedef struct {
u16 DeviceId; /* Unique ID of device */
u32 BaseAddress; /* Device base address */
u32 LUT_Depth; /* Number of samples in one period of the signal */
u32 LUT_Width; /* Number of bits used to represent amplitude value */
u32 NCO_Width; /* Number of bits used for numerically controlled oscillator */
} modulator_axi_ip_Config;
/**
* The Modulator_AXI driver instance data. The user is required to allocate a
* variable of this type for every Modulator_AXI device in the system. A pointer
* to a variable of this type is then passed to the driver API functions.
*/
typedef struct {
u32 BaseAddress; /* Device base address */
u32 IsReady; /* Device is initialized and ready */
u32 ClockRate; /* Input clock frequency, specified in Hz */
u32 LUT_Depth; /* Number of samples in one period of the signal */
u32 LUT_Width; /* Number of bits used to represent amplitude value */
u32 NCO_Width; /* Number of bits used for numerically controlled oscillator */
} Modulator_AXI;
When the driver source les have been written, the next step is to put the driver les into a directory tree
structure that the Xilinx Vitis will understand.
This is a very important step because the Xilinx tools will expect to nd les and folders with reserved names.
- Create the folder structure in the same way as it is shown on the previous gure, wherever you like on the
disk.
The top level folder can be called anything that the user chooses, and be placed anywhere that they choose,
but must contain a sub-folder called "drivers".
Below that, there may be one or more folders which represent each driver for the custom IP.
Driver Sub-folders
Under the structure described above, three further folders are expected:
The "src" folder contains the .c and .h source les for the driver, in addition to a Makele which can be
written to describe the build process required to compile the sources in the correct order / dependency.
The "example" folder contains examples of software application code, showing how your driver must be
used in a nal application.
The "data" folder contains following control les which are specic to the operation of the Xilinx Vitis
tools, and which detail how various device driver source les should be used:
the TCL le - used by the Xilinx BSP creation tools to automatically generate some parameters
which can be used later by the software developer
Save the source les, modulator_axi_ip.c and modulator_axi_ip.h, created in the previous sub-chapter
in the "src" folder, because the "src" folder should contain the .c and .h source les for the driver.
The rst control le in the "data" folder is the Microprocessor Driver Denition (MDD) le.
The le name is required to have a sux of "_v2_1_0.mdd", and in the case of our example has the full name
of "modulator_axi_v2_1_0.mdd".
The sux relates to the version of the syntax that is used within the MDD le, in this case v2.10.
The content of the MDD le is relatively simple, and for most MDD les the text is identical with the exception
of one or two lines.
In our case, the content of the "modulator_axi_v2_1_0.mdd" MDD le is the following:
modulator_axi_v2_1_0.mdd:
END DRIVER
The "OPTION supported_peripherals" line should be updated to list the peripherals that are served by
the custom driver. In the example shown here, there is just one peripheral which will be supported by the
driver, but multiple peripherals can be listed, separated by a space. The names of supported peripherals must
match the names of the IPs that have been created using the IP Packager. In our case driver supports only one
IP, modulator_axi_ip.
The "BEGIN driver" line should be edited to create a name for the driver that is being developed. There
are no specic rules for the naming convention of this parameter, but it is wise to create a name that will be
clearly identiable to the end user. In our case, a name for the driver will be modulator_axi. Identical name
should be used in the "OPTION NAME" line of the MDD le.
The "OPTION driver_state" line allows the user to maintain a recognised lifespan of their custom driver,
and list the driver as either ACTIVE, DEPRECATED, or OBSOLETE as and when the developer choose to
supersede or retire it from the service. The eect of changing this option away from the "active" state, generates
either warnings or errors in the driver compile process when it is invoked by the end user. Driver that we are
developing is in the active state, so this option is set to ACTIVE.
The "OPTION copyles" line tells the Xilinx Vitis which source les in the custom driver's "src" directory
should be copied when the Vitis generates the BSP. In most cases this will be left set to "all".
The "VERSION" line allows the user to specify a version number for their driver. This should match the
directory name used previously.
- Create modulator_axi_v2_1_0.mdd control le, following instructions from the text above, and save it
into the "data" folder.
The second control le in the "data" folder is the TCL le, which is used by the Xilinx BSP creation tools to
automatically generate some parameters which can be used later by the software developers.
modulator_axi_v2_1_0.tcl :
- Create modulator_axi_v2_1_0.tcl control le, following instructions from the text above, and save it
into the "data" folder.
The TCL le shown above, that is part of a device driver we are developing, is actually only 4 lines in length,
including the nal curly bracket on its own line, but lines 2, 3 & 4 are extremely long and therefore dicult to
re-produce clearly in this document.
The lines 2 & 4 should be edited by the developer (beginning with "dene_include_le" and "dene_canonical_xpars"),
to list a number of parameters that will be generated automatically by the BSP generation tools before being
placed into a le called "xparameters.h" within the automatically generated board support package.
The editable section of these lines begins with the parameter after "xparameters.h". In this example, the rst two
editable parameters are "modulator_axi_ip" and "NUM_INSTANCES". These two parameters are used to cre-
ate a #dene statement in the "xparameters.h" le called "XPAR_MODULATOR_AXI_IP_NUM_INSTANCES"
and assign a numerical value to it. The Xilinx BSP generation tools have the ability to automatically count
the number of instances of each type of peripheral that are added into the user's embedded processor design.
This information can be of great use when the same driver is used to control multiple instances of the same
peripheral, and provides the ability for a loop to be created in software that will automatically update when
the number of instances of a given peripheral is increased and decreased in the design. In our example there is
only one instance of the modulator_axi_ip peripheral, so we need not worry about such a feature, but the line
"#dene XPAR_MODULATOR_AXI_IP_NUM_INSTANCES 1" will be automatically added to
the "xparameters.h" le when the BSP is generated.
xparameters.h File
The list of parameters in the TCL le on the rest of lines 2 and 4 represent additional #dene statements that will be
generated in the "xparameters.h" le.
Each of the #dene statements will have a prex related to the instance name of the IP, and a sux copied from the
text string in quotes on lines 2 and 4 of the TCL le.
Each of the text strings matches the name of a Generic in the top level VHDL entity for the custom IP, and the value
of each #dene is populated using either the Generic's default value in the VHDL code, or the value that the user has
set in the Vivado block diagram tool to override that default.
In our case, eight #dene statements will be created in the "xparameters.h" le during BSP generation, and they will
be populated according to the user's chosen settings and the number of instances of the custom IP that were added to
the user's design.
In our case, a section of "xparameters.h" le related to the modulator_axi_ip peripheral, that will be automat-
ically generated, is shown below:
Tcl command in line 3 is used to automatically generate modulator_axi_ip conguration table array object,
that was discussed in the modulator_axi_ip_Initialize function. This time, the editable section of this line
begins with the parameter after $drv_handle. First parameter species the lename of the C source code le
("modulator_axi_driver_g.c", in our case) that will be automatically generated by the Xilinx Vitis tool during
the BSP generation process. This C le will hold a denition of the conguration table related to the IP, whose
name is specied by the second parameter ("modulator_axi_ip" ). Remaining parameters in the line dene the
elds that each conguration table array member should contain (in our case each member should consist from
four elds, "DEVICE_ID" "C_S00_AXI_BASEADDR" "lut_depth_g" "lut_width_g" "nco_width_g").
As before, each of the text strings must match the name of a Generic in the top level VHDL entity for the
custom IP, and the values of these elds will be populated automatically using either the Generic's default value
in the VHDL code, or the value that the user has set in the Vivado block diagram tool to override that default.
The ability to generate a software BSP which will automatically update itself based upon changes made to the
hardware design is an incredibly powerful feature, and can save the software engineering team a lot of manual
development eort and time. The end user's software application can then make use of these parameters,
allowing the hardware and software teams to work completely independently, yet vastly reduce the possibility
for software errors and bugs to occur due to any changes made in the hardware design that were not manually
communicated to the software engineering team.
With the driver control les written and placed in the correct folder structure, the nal step is to congure the
Xilinx Vitis tools so that they have visibility of the new driver in the list of available driver repositories.
- In the Vitis IDE, open the Preferences dialog by choosing Window -> Preferences from the menu bar.
- In the Preferences dialog box, select the Xilinx -> Software Repositories pane.
- In the Preferences dialog box, click the New... button next to the Local Repositories list to add a new
repository.
- In the Select Folder dialog box, point to the folder that you created for your custom drivers and click OK.
The folder you choose here should be the level of the le system above the drivers folder. In our case it is
pwm_modulator_driver folder.
- Click the Rescan Repositories button, followed by Apply and Close button.
This setting will provide visibility of your custom driver to the Vitis tool, and will enable your driver to be
selected in the BSP settings.
There is an addition list shown on this screen called Global Repositories, see Figure 4.44. This performs
an identical function to that of adding the drivers to the "Local Repositories" list, with the exception that the
drivers will be visible to any Vitis workspace that is created or opened in the future. This is a powerful feature if
your goal is to create a single repository of custom drivers for multiple custom IPs, and still have them routinely
available and visible to all of your Vitis workspaces.
The task of creating and conguring your custom IP and custom driver is now complete, and the Vivado and
Vitis tools will now have visibility of them. The nal stage is to select your custom driver and allocate it to be
automatically compiled as part of the BSP for your user application.
- Select the modulator_sozius platform project and then select Modify BSP Settings....
- In the Board Support Package Settings dialog box, choose the drivers pane from the choices shown on
the left of the window.
- In the Drivers conguration table, identify the instance of your custom IP (modulator_axi). It will now
be possible to select your custom driver from the drop down menu in the Driver column of the table.
Figure 4.47: Board Support Package Settings dialog box with selected modulator_axi custom driver
Note : If you had created multiple folders representing dierent versions of the same driver, you will also be able
to choose the version of the driver in the Driver Version column.
If you now examine the "include" and "libsrc" folders in the standalone_bsp_0 BSP's source tree, you
will nd that your header le (modulator_axi_ip.h ) and C source le (modulator_axi_ip.c ) have been au-
tomatically copied and compiled into the BSP. Furthermore, one additional C source le is automatically
modulator_axi_driver_g.c. Remember that this is the source le we have specied in the
generated,
modulator_axi_v2_1_0.tcl TCL le. This source le contains the conguration table array object, hold-
ing all relevant conguration information for every modulator_axi_ip peripheral that is present in the embedded
system. In our case, we have only one instance of the modulator_axi_ip peripheral, so the generated congura-
tion table array object has only one entry, as can be seen by inspecting the generated modulator_axi_driver_g.c
source le. There is no longer any requirement for custom driver functions to be added to the list of sources
for each application, because they are now included in the BSP alongside the drivers provided by Xilinx for
supplied IP.
Creating New Application Project that will use Developed Device Driver
Creating New Application Project that will use Developed Device Driver
The last step in our design process will be to create a new application project that will use developed modu-
lator_axi device driver.
Note : For example, these two les can be stored in the "examples" folder from the device driver folder
structure.
modulator_axi_using_driver.c:
#include "xparameters.h"
#include "xgpiops.h"
#include "xstatus.h"
#include "modulator_axi_using_driver.h"
#include "modulator_axi_ip.h"
int main(void)
{
/*********************** Variable Definitions ********************/
XGpioPs GpioSwitches; // XGPIO instance that will be used to work with SWITCHEs
XGpioPs_Config *GPIOConfigPtr;
// SWITCHes initialization
GPIOConfigPtr = XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID);
XGpioPs_CfgInitialize(&GpioSwitches, GPIOConfigPtr, GPIOConfigPtr ->BaseAddr);
XGpioPs_SetDirectionPin(&GpioSwitches, SWITCH_CHANNEL, 0);
// Set the input clock frequency for the Modulator_AXI instance to appropriate value
modulator_axi_ip_Set_Input_Clock_Frequency (&pwm_modulator, CLOCK_RATE);
// Set the values for two possible frequencies for the PWM signal
modulator_axi_ip_Set_PWM_Frequency (&pwm_modulator, 0, F_HIGH);
temp = modulator_axi_ip_Get_PWM_Frequency (&pwm_modulator, 0);
// Select the initial frequency that will be used to generate PWM signal
modulator_axi_ip_Select_PWM_Frequency (&pwm_modulator, sel);
current_sel = modulator_axi_ip_Get_Selected_PWM_Frequency (&pwm_modulator);
sel = sel & SWITCH_POS; // masking (we want to check the status of sel only)
if (sel != current_sel)
{
// Write the new switch position to the pwm_modulator core
modulator_axi_ip_Select_PWM_Frequency (&pwm_modulator, sel);
current_sel = modulator_axi_ip_Get_Selected_PWM_Frequency (&pwm_modulator);
}
return 0;
}
modulator_axi_using_driver.h:
#ifndef MODULATOR_H_
#define MODULATOR_H_
#include "math.h"
#include "xparameters.h"
#endif /* MODULATOR_H_ */
- In the Vitis IDE, select File -> New -> Application Project... option from the main Vitis menu.
- In the Create a New Application Project dialog box just click Next.
- In the Platform dialog box select modulator_sozius [custom] and click Next.
- In the Application Project Details dialog box type modulator_axi_using_driver as a new application
project name and click Next.
- In the Domain dialog box leave all parameters unchanged and click Next.
- In the Templates dialog box select Empty Application and click Finish.
- Expand modulator_axi_using_driver application project in the Project Explorer window and in the
src folder add previously created modulator_axi_using_driver.c and modulator_axi_using_driver.h
les.
Sub-chapter
- Run your application on the Sozius development board, on the same way as it is explained in the
3.7. "Running Application Project" and you should get the same results like in the Sub-chapter 4.2
"Debug a Design using Integrated Vivado Logic Analyzer".
CONCLUSION
In this tutorial a range of dierent implementations of the PWM Modulator system has been presented:
2. A software solution that uses an external timer and interrupt controller, PWM_SW_intc
3. A hardware/software co-design solution, that uses a custom PWM Modulator IP core, PWM_HW/SW
Furthermore, in the "Basic FPGA Tutorial" a pure hardware solution for the PWM Modulator system has also
been presented, PWM_HW.
All these solutions represent valid and functionally correct implementations of the PWM Modulator system,
but they dier in a way how this functionality is actually implemented and therefore have dierent performance
results and resource requirements.
The performance of the PWM Modulator system can be represented by three attributes:
Maximum frequency of generated PWM signal is probably the most important performance attribute of any
PWM Modulator system. Typical PWM signal frequency values range from 0.05 Hz, in case of an electric stove,
120 Hz, in case of a lamp dimmer, from few KHz to tens of KHz for a motor drive, and well into the tens or
hundreds of kHz in audio ampliers and computer power supplies.
Four proposed PWM Modulator systems have dierent maximum PWM frequency values.
PWM_HW and PWM_HW/SW solutions can generate PWM signals with the highest frequency values, be-
cause the PWM signal generation is done completely in hardware. In the proposed hardware solution, maximum
PWM frequency is limited by two factors:
maximum operating frequency of the Frequency Trigger module (see Chapter 2 "Frequency Trigger" from
the "Basic FPGA Tutorial" ), Fmax, and
the number of bits used to represent amplitude value of the PWM modulating signal ("Width G" eld
from the modulator_axi_ip customization window, shown on the Figure 5.12, or width eld in the de-
sign_setting_g generic from the modulator top-level entity, see Chapter 8 "Modulator" from the "Basic
FPGA Tutorial" ), width.
The maximum PWM frequency that can be generated by the PWM_HW and PWM_HW/SW solutions can
be calculated by the following formula:
Fmax
P W MF max = 2width
139
CHAPTER 5. CONCLUSION
For example, maximum PWM signal frequency that can be generated with the conguration of the PWM
Modulator IP core that was used in this tutorial is equal to:
100M Hz
P W MF max = 212 = 24414Hz
This value is sucient for the majority of PWM applications, described earlier.
Estimating the maximum PWM signal frequencies for the software solutions (PWM_SW_no_intc and PWM_SW_intc )
is not so straightforward. In these implementations maximum PWM frequency is also constrained by the
amount of time required to complete one iteration of the while loop located in the main procedures from
themodulator_no_intc_mb.c , modulator_intc_mb.c, modulator_no_intc_axi.c and modula-
tor_intc_axi.c source codes. The estimations of exact maximum PWM signal frequency values would require
a measurement of this while loop time. Furthermore, this time would also depend on the type of the processor
that executes the PWM Modulator software and the C/C++ compiler settings that were used to build the
executable version.
Next, we will comment on the pros and cons of each of the proposed implementation of the PWM Modulator
system.
PWM_SW_no_intc implementation
Pros:
Easy to modify the used PWM generator algorithm, for instance easy to change the waveform of the
modulating signal, all we need to do is to recompile the modied PWM Modulator source code.
Cons:
Maximum frequency of generated PWM signal is the lowest from all proposed implementations, because
the duration of one iteration of the while loop from the modulator_no_intc_mb.c and modula-
tor_no_intc_axi.c source codes is the longest. This duration becomes even longer if the processor
needs to carry out some additional tasks other then PWM generation, because all these tasks would have
to be included in this while loop.
Jitter of generated PWM signal is signicant, and potentially can be very high, but it can be relatively
accurately estimated (since we know the amount and ordering of additional commands that are located
in the while loop) and compensated.
PWM_SW_intc implementation
Pros:
More dicult to implement than the PWM_SW_no_intc implementation, because it requires the con-
guration of the interrupt controller and writing the interrupt routine.
In case a processor has to perform some other actions apart from the PWM signal generation, this is the
only acceptable solution (from "pure" software solutions).
Easy to modify the used PWM generator algorithm, for instance easy to change the waveform of the
modulating signal, all we need to do is to recompile the modied PWM Modulator source code.
Cons:
Maximum frequency of generated PWM signal should be a little bit higher then the maximum PWM signal
frequency generated by the PWM_SW_no_intc implementation, because the duration of one iteration
of the while loop in this case is shorter.
Jitter of generated PWM signal is signicant, and potentially can be very high, but this time it can not be
predicted in advance, because we can not estimate the actual point in time when an PWM timer interrupt
will be generated. This is even more unpredictable if there are other interrupt sources with equal or higher
interrupt priority present in the system.
PWM_HW/SW implementation
Pros:
Oers the generation of PWM signal with the highest possible frequency, identical to the PWM_HW
implementation, because in this implementation, as well as PWM_HW implementation, PWM signal
generation is done completely in hardware.
Great exibility (for example, easy adjusting of frequencies of generated PWM signals, easy integration
into more complex systems like multiple PWM generators, etc.), especially when compared with the
PWM_HW implementation.
Cons:
If PWM Modulator IP core is not readily available, then the development time is signicant, however if
this IP is available then the development time is comparable with the "pure" software solutions.
PWM_HW implementation
Pros:
Oers the generation of PWM signal with the highest possible frequency, identical to the PWM_HW
implementation, because in this implementation, as well as PWM_HW implementation, PWM signal
generation is done completely in hardware.
Cons:
Limited exibility, because every modication of the design requires a modication of hardware, leading
to the necessity to reimplement the complete system (redo the hardware synthesis, place and route the
design), which in large systems can take several hours.
Long development time, because we need to design and verify a complete hardware system, which is much
more time consuming that software design.
EXERCISES
This section holds a set of exercises that can be used to verify the knowledge acquired by reading this tutorial.
Exercise 1
Modify the init_sin.c source code in order to be able to generate PWM signal modulated by the following
signals:
Exercise 2
Make the necessary modications to the ARM-based embedded system and modulator_no_intc.c application
to enable the user to specify which type of modulating signal (sine, triangle or sawtooth) should be used to
generate PWM signal on-the-y, during the operation of the system.
Exercise 3
Using the ARM-based embedded system and modulator_no_intc.c application as a starting point, make the
necessary modications to both the hardware and software parts of the ARM-based embedded system in order
to be able to generate two completely independent PWM generators.
Exercise 4
Using the ARM-based embedded system and modulator_intc.c application as a starting point, make the nec-
essary modications to both the hardware and software parts of the ARM-based embedded system in order to
be able to generate two completely independent PWM generators.
Exercise 5
Using the ARM-based embedded system based on the PWM modulator custom IP core and modulator_axi.c
application as a starting point, make the necessary modications to both the hardware and software parts of the
ARM-based embedded system in order to be able to generate two completely independent PWM generators.
Exercise 6
Using the ARM-based embedded system based on the PWM modulator custom IP core, modulator_axi driver
and modulator_axi_using_driver.c application as a starting point, make the necessary modications to both
the hardware and software parts of the ARM-based embedded system in order to be able to generate two
completely independent PWM generators.
143