Getting started with FPGA control development
Table of Contents
This note explains how to get started with the implementation of power converter control algorithms in the FPGA of imperix power electronic controllers. The benefit of offloading all or parts of the computations from the CPU to the FPGA is that it often results in much faster closed-loop control systems.
First, the FPGA control starter template is presented and a tutorial on how to create this template is provided. Then, the reader will learn how to retrieve ADC results from the FPGA as well as to exchange data with the CPU. The page ends with a simple hello-world example illustrating all the keys steps of FPGA control implementation.
This page is the first of a 3-part tutorial explaining step-by-step how to implement the closed-loop control of a buck converter in FPGA without using VHDL or Verilog. The second note explains how to generate a PWM modulator in FPGA using the Simulink blockset Xilinx System Generator or MATLAB HDL Coder. The last note shows how to create the PI-based current control using high-level synthesis with Xilinx Model Composer (Simulink blockset) or Xilinx Vitis HLS (C++).
Presentation of the FPGA control starter template
The FPGA control starter template allows for easy integration of custom FPGA-based control algorithms in the sandbox area of the B-Box RCP or the B-Board PRO. As shown in the image below, it consists of:
- the obfuscated “imperix firmware IP” which contains the FPGA logic required to operate imperix controllers (documented in PN116), and
- the “ix axis interface” module which provides easy-to-use AXI4-Stream interfaces to exchange data with the user logic.
The provided AXI4-Stream interface module connects to the data interfaces and timing signals of the imperix firmware IP, interprets these signals, and converts them into much more user-friendly AXI4-Stream (AXIS) interfaces (ADC
, CPU2FPGA
and FPGA2CPU
). The AXI4-Stream protocol is a widely used standard to interconnect components that exchange data. This means that the provided template can directly be connected to a wide range of Xilinx-provided IPs or to user-made algorithms developed using High-Level Synthesis (HLS) design tools such as Vitis HLS (C++) or Model Composer (Simulink).
This AXI4-Stream interface module is written in VHDL. It is provided as a starting point and will meet the need of most applications. However, if required, it can be edited by the user to add extra input/outputs, rename them, change their data sizes, etc.
Creating the FPGA control starter template
The Vivado Design Suite installation guide explains how to download and install Xilinx Vivado for free.
Downloading the required sources
All the required sources are packed into the FPGA_Sandbox_template archive which can be downloaded from the Download and update imperix IP page.
Since version 3.9, the template file structure is the following
- constraints
- sandbox_pins_*.xdc: top-level ports to a physical package pin assignation
- hdl
- AXIS_interface.vhd: AXI4-Stream interface module, presented on the next chapter
- user_cb_pwm.vhd: simple carrier-based modulator, described in TN141
- ix_repo: Vivado IP Catalog repository. Contains the imperix firmware IP and its interfaces.
- scripts:
- create_project.bat: launch Vivado and call create_project.tcl
- create_project.tcl: contains the TCL commands that create and configure the sandbox Vivado project
- vivado: contains the Vivado projects generated by the create_project script
Starting a new imperix sandbox project
- Download FPGA_Sandbox_template_*.zip
- Unzip it and save the content somewhere on the PC
- Rename the folder to something more explicit
- Open scripts/create_project.bat using a text editor
- Set the vivado_path variable to match the Vivado version installed on the PC
- Double click on scripts/create_project.bat
Windows Defender SmartScreen may display a warning pop-up. Simply click More info then Run anyway. - Enter a project name and click enter
The Vivado sandbox project will be created and configured, its block design is shown below.
If, for some reason, the script is not working properly then the Vivado project can be created manually by following the procedure below:
Using the SBIO_BUS
The SBIO_BUS (SandBox IO bus) is a 16-bit memory-mapped bus allowing the CPU to addressing up to 1024 register in the FPGA.
SBIO modules
Two SBIO modules are provided in the template:
- the AXI4-Stream interface (
AXIS_interface.vhd
) which is described in the next section, - the SBIO registers (
sbio_registers.vhd
) shown below, which provides 16-bit registers that can easily be connected to user logic.
The user can read and write from this bus using the SBO and SBI blocks as shown below. During execution, SBIs are read before each CPU task executions and SBOs written at the end of each CPU task execution.
SBIO interconnect
The SBIO interconnect increases the number of SBIO_BUS interfaces, allowing to connect multiple SBIO modules as illustrated below.
The address mapping of the SBIO interconnect is shown below, it divides the SBIO addressable range in 4 smaller areas.
As an example, to write to SBO_reg_03 of an sbio_registers
block connected to S2_SBIO_BUS, The user has to use an SBO block to register number 512+3=515.
How the AXI4-Stream interface operates
This section focuses on the AXI-Stream interface module (ix_axis_interface). For further information on the imperix firmware IP (IMPERIX_FW) please refer to the imperix firmware IP product guide.
AXIS_interface.vhd
can easily be edited to improve the readability, by renaming the interfaces and removing the unused ones for instance. In this example, the interface is kept as it is.Retrieving analog measurement with the M_AXIS_ADC interfaces
The Master AXI4-Stream interfaces M_AXIS_ADC_00 to M_AXIS_ADC_15 correspond to the 16 analog inputs of the imperix device.
They return the raw 16-bit signed integer result from the ADC each time conversion results are available. Consequently, users should manually perform the data-type conversion and apply correct gains in their FPGA projects, to transform the acquired value in its physical unit. To learn how to compute this gain, please refer to the last section of the ADC page.
Exchanging data using M_AXIS_CPU2FPGA and S_AXIS_FPGA2CPU
The Master AXI4-Stream interfaces M_AXIS_CPU2FPGA and the Slave AXI4-Stream interfaces S_AXIS_FPGA2CPU serve to exchange 32-bit data between the CPU code and the FPGA.
To read/write values on the FPGA2CPU/CPU2FPGA ports, the user can download the Simulink model from the step-by-step hello world section below and re-use the following blocks
The provided template uses the following mapping between the 16-bit SBI/SBO registers and the 32-bit AXI4-Stream interfaces:
If the user chooses to write a single-precision floating-point data on the AXI4-Stream interfaces M_AXIS_CPU2FPGA_00, then he has to:
- use a MATLAB Function block to transform a single value into two uint16 values (see code below)
- and then use the SBO block to send these two uint16 values to
SBO_reg_00
andSBO_reg_01
(CPU2FPGA_00
).
function [y1,y2] = single2sbo(u)
temp = typecast(single(u),'uint16');
y1 = temp(1);
y2 = temp(2);
Code language: Matlab (matlab)
And if he wishes to read a result from the FPGA to the CPU (still in single-precision floating-point format) using S_AXIS_FPGA2_CPU_01, then he has to:
- use the SBI block to retrieve the two uint16 values from
SBO_reg_02
andSBO_reg_03
(FPGA2CPU_01
) - and then use a MATLAB Function block to transform these two uint16 values into a single value (see code below).
function y = sbi2single(u1,u2)
y = single(0); % fix simulink bug: force compiled size of output
y = typecast([uint16(u1) uint16(u2)], 'single');
Code language: Matlab (matlab)
Getting the sample time Ts
The M_AXIS_Ts interface provides the sample period in nanoseconds in a 32-bit unsigned integer format. This signal may be used, for instance, by the integrators of PI controllers. This value is measured by counting the time difference between two adc_done_pulse.
Using reset signals
The AXI4-Stream module also provides two reset signals:
- nReset_sync: this reset signal is activated each time the user code is loaded through Cockpit. It can be used as a standard reset signal.
- nReset_ctrl: this reset is triggered from the CPU through SBO_reg_63 using a core state block. Its intended use is, for instance, to reset the PI controller integrator when the converter is not operating (when the PWM outputs are disabled).
Both signals are active-low and activated for 4 periods of clk_250_mhz
.
Step-by-step “hello world” example
This simple “hello world” example serves to showcase the complete FPGA development workflow on Vivado.
As illustrated on the image below, it does the following:
- From the CPU, a gain value (single-precision) is transferred to the FPGA using the CPU2FPGA_00 interface (SBO_00 and SBO_01).
- In the FPGA, the data coming from the ADC_00 interface is converted into a single value.
- The ADC value is then multiplied by the gain.
- Finally, the multiplication result is sent back to the CPU through FPGA2CPU_00.
- The raw value of the ADC is also sent to the CPU using FPGA2CPU_01.
The FPGA logic will be implemented using exclusively readily available Xilinx Vivado IP, namely:
- Floating-point IP configured for Fixed-to-float operation to convert an int16 to a single-precision floating-point value.
- Floating-point IP configured for the Multiply operation to multiply two single-precision floating-point values.
- AXI4-Stream Broadcast IP to duplicate the output of the int16 to single block.
Other useful Xilinx Vivado IPs are listed in the AXI4-Stream IPs from Xilinx page. The user can also implement his own IP blocks, either using directly VHDL or Verilog, or high-level design tools such as Vitis HLS (C++) or Model Composer (Simulink).
CPU-side implementation
The CPU-side code provided below has been implemented using Simulink and the imperix ACG SDK. To make these variables available during run-time, the gain is set using a tunable parameter and the adc_raw and result are read using probes. These 3 values are encoded as single (32-bit single-precision floating-point). The MATLAB Function blocks single2sbo and sbi2single allow to easily map single values to SBI and SBO blocks.
FPGA-side implementation
As mentioned earlier, only standard Xilinx Vivado IP blocks are used to implement the algorithm in this example. The PN159_vivado_design.pdf file below shows the full Vivado FPGA design. Here are the step-by-step instructions to reproduce it.
- Add a block to convert the int16 data of ADC_00 to a single-precision floating-point
- Right-click somewhere in the block design and choose Add IP…
- Search for the Floating-point IP, drag-and-drop it on the diagram.
- Rename it as int16_to_single.
- Double-click on the int16_to_single block. In the pop-up window, select the Fixed-to-float operation, change Auto to Manual and set the precision type to Custom. Then, set the integer width to 16, and select Single as precision for the result.
- Add the multiplier block
- Add another Floating-point IP and rename it as single_multiplier.
- Double-click on the block.
- Select the Multiply operation and Single input.
- Broadcast one stream to two streams
- Right-click somewhere in the block design and choose Add IP…
- Search for the AXI4-Stream Broadcaster, drag it and drop it on the diagram.
- Keep all options to auto.
- Connect all the blocks as follow:
- M_AXIS_ADC_00 to S_AXIS_A of int16_to_single
- M_AXIS_RESULTof int16_to_single to S_AXIS of axis_boradcaster_0
- M00_AXIS of axis_broadcaster_0 to S_AXIS_FPGA2CPU_00
- M01_AXIS of axis_broadcaster_0 to S_AXIS_B of single_multiplier
- M_AXIS_CPU2FPGA_00 to S_AXIS_A of single_multiplier
- M_AXIS_RESULTof single_multiplier to S_AXIS_FPGA2CPU_01
- clk_250_mhz to all aclk inputs
- nReset_sync to aresetn of axis_broadcaster_0
- Finally, the design can be synthesized and the bitstream generated. Click Generate bitstream. It will launch the synthesis, implementation and bitstream generation.
- Always make sure that your design meets the timing requirements!
This information is available from the Project Summary
To learn more please visit the Xilinx documentation on Timing Closure.
- If the timing requirement are met, click on File → Export → Export Bitstream File… to save the bitstream somewhere on the computer.
Loading the bitstream into the imperix controller
Using imperix Cockpit, the bitstream is loaded into the imperix controller device from the target configuration window.
Experimental validation
Finally, the CPU code is generated from the Simulink model and loaded into the device, as explained in the programming and operating imperix controllers page.
To test the design, a sinusoidal signal is fed to the analog input of the B-Box. Then, using Cockpit’s scope, the result = 0.5*adc_raw is observed:
Going further
Testing M_AXIS_Ts
This section goes a bit further in the demonstration of the provided AXI4-Stream M_AXIS_Ts to help understand the M_AXIS_Ts interface, and show how to transfer an uint32 value from the FPGA to the CPU.
The modification of the FPGA bitstream is quite simple: simply connect M_AXIS_Ts to S_AXIS_FPGA2CPU_02 as shown in orange on the image below. This allows reading the sample time Ts value from the CPU.
On the CPU, to retrieve the Ts value, the value is read from S_AXIS_FPGA2_CPU_02 (SBI_04 and SBI_05). Because the value is a 32-bit unsigned integer, the transformation is different from before as shown below.
Option 1
Option 2
function y = sbi2uint32(u1,u2)
y = uint32(0); % fix compiled size of output
y = typecast([uint16(u1) uint16(u2)], 'uint32');
Code language: Matlab (matlab)
Finally, using Cockpit, the result can be observed. If the control task frequency (CLOCK_0) is set to 50 kHz, then M_AXIS_Ts will return 20’000 ns.
If CLOCK_0 is kept at 50 kHz and the oversampling is activated with an oversampling ratio of 20, then M_AXIS_Ts will return 1000 ns, which corresponds to the actual sampling period.
Using the USR pins
The imperix controllers feature 36 user-configurable 3.3V I/Os (the USR pins) that are directly accessible from the FPGA. Their physical locations are available in the B-Board PRO datasheet and B-Box RCP datasheet.
By default, the USR pins are connect to the imperix IP. Currently they are only used to communicate with the with the motor interface. If the motor interface is not used, then the USR port can safely be deleted.
These pins are then available for other use. The screenshot show an example where the USR pins 0, 1, and 2 are used.
The constraints\sandbox_pins.xdc
must be edited accordingly. As shown below, we recommend commenting (#) the unused pins to avoid generated unnecessary warning the Vivado.For more information on constraints in Xilinx FPGA please refer to the using constraints in Vivado Design Suite user guide.
top_wrapper.vhd
is up-to-date, the recommended procedure is to remove the current one and generate a new one:– Right click on
top_wrapper
-> Remove File from Project…– Check “Also delete the project file from disk” -> OK
– Right click on
top
-> Create HDL WrapperThe FPGA-based SPI communication IP for ADC page shows an example where USR pins are used to drive an external ADC using the SPI protocol.
Additional tutorials
The page custom FPGA PWM modulator explains how to drive PWM outputs or to use the CLOCK interfaces through a simple example. The PWM modulator sources are provided as VHDL, as a Xilinx System Generator model, and as a MATLAB HDL Coder model.
The page high-level synthesis for FPGA developments shows how to integrate HLS-generated IPs in an FPGA control implementation using a PI-based current control of a buck power converter as an example.