A couple years ago, AMD launched their new line of Kria system-on-module devices that feature a custom-built AMD Zynq™ UltraScale+™ MPSoC on a credit cardsized PCB intended for users to integrate into their production deployment after using one of their carrier boards to stand up embedded vision designs for security smart cameras, retail analytics, smart city, or machine vision-type applications.
The first Kria starter kit was the KV260 Vision AI Starter Kit with a carrier board that boasted peripherals ready for out-of-the-box Vision AI development for the K26 SOM. Then AMD released the KR260 Robotics Starter Kit that had a carrier board with peripherals geared towards out-of-the-box robotics and industrial application development also for the K26 SOM. Now AMD has released a third Kria platform with the Kria KD240 Drives Starter Kit with the new K24 SOM. The KD240 carrier board has peripherals for driving a 3-phase motor along with a series of ADC chips to read and report back the voltage and current data of the motor to the K24 SOM. This allows for the K24 SOM to exercise precise torque and speed control of the motor.
The K24 SOM is like the K26 SOM in that it also has a custom-built Zynq UltraScale+ MPSoC optimized specifically for it with LPDDR4 memory, nonvolatile storage devices, and a security module. The specs of the custom-built Zynq UltraScale+ MPSoC on the K24 have been specifically tuned for motor control and industrial communication applications. Consequentially, the K24 SOM is also a strong tool for digital signal processing (DSP) as the main concepts of 3-phase motor control are ultimately DSP functions.
Digital Signal Processing IPsBefore getting into the complexities of driving a 3-phase brushless DC motor using the feedback sinusoids from the 3-phase inverter circuit along with a quadrature encoder with index, like the one that can be purchased in the KD240 Drives Starter Kit, an exercise in DSP and filtering basics on the KD240 is a great exercise.
There are two IP blocks in the AMD Vivado™ IP library that I use in most of my DSP based designs: the DDS Compiler IP and the FIR Compiler IP. These two IPs in the Vivado catalog are super handy because it's straightforward and quick to simply drop them into a new design and configure them as required.
The AMD DDS Compiler IP is a configurable Direct Digital Synthesizer (DDS). A DDS contains a lookup table for the data values of a sinusoid which takes in a given input phase value and outputs the appropriate data/magnitude value for a sinusoid. This input value determines the frequency of the output waveform in that the smaller the value is, the slower the DDS steps through the sinusoid lookup table and the output waveform is lower in frequency. In contrast, the higher the input value, the faster the DDS steps through the lookup table and the higher frequency the output waveform is. This input value is commonly referred to as the tuning word, but in the AMD DDS Compiler IP, it is referred to as the phase increment.
The DDS Compiler IP is an easy way to generate a sinusoid within a design for a multitude of reasons. In this exercise, I'll be using the DDS compiler to generate a chirp signal to validate and verify the functionality of the FIR filters I've implemented. Since brushless DC motors can be damaged somewhat easily with sinusoids of improper amplitude and/or phase, it's important to verify a filter's characteristics ahead of time.
By simply streaming a series of phase increment values to a DDS Complier IP, a chirp signal spanning the frequency range of a target FIR filter can be created to feed into it. For example, I've tested low pass filters I've designed by inputting a chirp signal into it that starts in its passband range and goes up through its stopband so I can see its attenuation all the way through its transition band.
The AMD FIR Compiler IP is configurable FIR filter that allows a user to simply specify the filter coefficients and hardware parameters such as coefficient width and sampling frequency. The configuration window of the FIR Compiler also shows a frequency response plot of the filter with the given coefficient set and configuration parameters for extra verification.
While I've written my own custom HDL for FIR filters, the FIR Compiler is much faster and easier to throw into a new design.
So in this project, I'll be testing three FIR filters with different frequency responses (low pass, bandpass, and high pass) using the same chirp signal to show how a quickly this DSP design cycle can be implemented in behavioral simulation and validated on hardware in the K24 on the KD240.
Create Vivado ProjectCreate a new Vivado project targeting the Kria KD240 with the Drives Starter Kit carrier specified for both Connector 1 of the K24 SOM and Connector 2 of the K24 SOM. The board files for the K24 and KD240 Drive Starter Kit are only available in Vivado software versions 2023.1 or later. Since 2023.1 is the latest version at the time of writing, that is what I've using here. See my previous post here for how to install 2023.1 on Ubuntu 22.04.
Source the Vivado tools to the environment then launch Vivado and select Create Project from the start menu. The project creation wizard will appear to allow you to set the project name, set the location, and specify if the project will be used for hardware acceleration later in Vitis (which I'm not for now so I left the option Project is an extensible Vitis platform unchecked).
Switch to the Boards tab when asked to select the default part for the project, and type "Kria" in the search bar. Single-click on the row for Kria KD240 Drives Starter Kit SOM to select it.
Then click on the Connections hyperlink to bring up the menu for selecting how the K24 SOM is connected to the KD240 carrier board. Be sure to match Connector 1 on K24 SOM (SOM240_1) to DrivesStarter Kit Carrier(SOM240_1) and Connector 2 on K24 SOM (SOM40_2) to DrivesStarter Kit Carrier(SOM40_2) between the K24 SOM and the Drives Starter Kit:
Click Finish and wait for Vivado to generate the new project.
Vivado Block DesignVivado facilitates either a graphical design flow or a purely RTL design flow for instantiating AMD IP or user IP. This tutorial follows the graphical design flow with the use of the block design.
Create a new block design by selecting Create Block Design from the Flow Navigator window, and giving it the desired name.
Once Vivado opens into a new blank block design, the first step is to instantiate the IP to interface/configure the ARM-core processors in the Zynq UltraScale+ MPSoC and access the peripherals connected via its MIO pins (ie- DDR, ethernet, etc.). Click the + button at the top of the Diagram window and search for "zynq" in the window that appears. Double-click the option for Zynq UltraScale+ MPSoC to add the IP block to the design.
A green banner will appear at the top of the Diagram window with the option to Run Block Automation. This option tells Vivado to apply the configuration to the Zynq UltraScale+ MPSoC IP that are specific to the Kria KD240 based on the board preset files targeted by selecting the KD240 as the default part for the project.
Click the hyperlink to Run Block Automation then click OK in the window that pops up, ensuring that the box to Apply Board Preset is checked:
After the Block Automation is complete, add a Processor System Reset to control the timing of the reset signal to the logic that will be running the the programmable logic (PL) of the K24.
Connect the slowest_sync_clk
input of the Processor System Reset to pl_clk0
from the Zynq UltraScale+ MPSoC. Then connect ext_reset_in
of the Processor System Reset to pl_reset0
from the Zynq UltraScale+ MPSoC.
Finally, connect pl_clk0
to maxihpm0_fpd_aclk
and pl_clk1
to maxihpm1_fpd_aclk
on the Zynq UltraScale+ MPSoC.
With this bare bones design in place, a top level HDL wrapper to instantiate the block design in the project can be created. Right-click on the block design file in the Sources window and select Create HDL Wrapper...
Select the option to allow Vivado to auto-manage the wrapper and click OK.
Vivado will then generate the HDL top level wrapper file and set it as the top file in the project.
Be sure to create this HDL wrapper and set it as the top file because HDL files can't be added to the block design as a module later on if they are currently set of the project's top file which Vivado will try to do automatically if this HDL wrapper doesn't exist first.
Chirp Signal with DDS CompilerNext, add the DDS Compilers to the design. As a rule of thumb, when creating digital signals like this, you not only have to adhere to the Nyquist rule that the sample frequency needs to be at least twice that of the target signal's frequency, but the clock frequency being used to generate a sine wave like this should be at least 10 times that of target signal's frequency.
Therefore, since I'm using pl_clk0
that is set to 100MHz, the max frequency of any sinusoid I generate from the DDS Compiler will be 10MHz. So my chirp signal will go from 1MHz to 10 MHz in 1 MHz intervals. The exact math of how to calculate the phase increment inputs to get the desired output frequencies for the DDS Compiler is covered in a previous post of mine here. Even though that tutorial was done with version 2019.2, the DDS Compiler IP configuration/functionality is the same. You'll probably also notice how the chirp signal in that tutorial starts to look distorted after the 10MHz mark (the chirp in that tutorial goes from 1MHz - 25MHz also with a 100MHz pl_clk0
) to demonstrate the 10 times rule of thumb I mentioned above.
Click the + button at the top of the Diagram window and search for "dds" in the window that appears and double-click DDS Compiler in the list to add it to the block design.
Double-click on the DDS Compiler IP to open its configuration window after it appears in the block design.
I personally like to use hardware parameters for Parameter Selection in the Configuration tab to set the input phase width to 32 and the output data width to 16 as it's the most universal, but the is ultimately design dependent based on specific use cases.
In the Implementation tab, set the Phase Increment Programmability to Streaming and leave the Phase Offset Programmability to None. Set the Output Selection to either Sine or Cosine, just not Sine and Cosine.
Under the Detailed Implementation select Packet Framing for DATA has TLAST and check the box to Output TREADY. Also check the option for ARESETn to create a reset port on the DDS Compiler.
Click OK in the DDS Compiler's IP window, to save the configuration settings and close the window.
Since I'm using the same chirp signal to test my three target FIR filters, I simply copy+pasted the DDS Compiler IP I just configured two more times to ultimately get three DDS Compiler IPs configured the same:
In another previous project, I detailed the simple state machine that I wrote to stream the phase increment values to the input of a DDS Compiler. The main functionality of this FSM is to handle the timing of when to increment the phase increment value, and the AXI Stream interface signal handshaking.
The DDS Compiler changes its output frequency pretty much instantaneously with the change of phase increment on its input, so it's up to the user to make sure to hold a certain phase increment on its input for the number of respective output periods desired. For the sake of uniformity, my FSM holds the phase increment value on the DDS Compiler input for the length on the period of the slowest sinusoid. This is the 1MHz signal (so 1us) in this case since the chirp is going from 1MHz to 10MHz.
Since this is custom HDL I want to add to the block design, the source file first needs to be create/added to the Vivado project then added as a Module to the block design.
Select Add Sources from the Flow Navigator window. Then select the option to Add or create design sources and click Next.
I've also attached my FSM verilog file below so it can be downloaded and the option to Add Files can be selected here then pointed to that download. Be sure that the option to Scan and add RTL include files into project as well as Copy sources into project are checked before clicking Finish.
Once the RTL source file has been added to the Vivado project, go to the Diagram window of the block design and right-click anywhere in blank space with the Diagram window. Select the option from the menu to Add Modules...
This will bring up the list of all of the valid HDL modules in the Vivado project that can be added to the block design.
Select the phase increment state machine module and click OK. Repeat two more times to add one for each of the other DDS Compilers.
Connect phase increment output to the DDS compilers input. Connect all clock signals for to pl_clk0
and all reset signals to peripheral_aresetn
of the processor system reset IP.
With the DDS Compilers in place with the phase increment state machines to control them, the next step is to add the FIR Compiler IPs for each of the target FIR filters.
Click the + button at the top of the Diagram window and search for "fir" in the window that appears and double-click FIR Compiler in the list to add it to the block design.
Double-click the FIR Compiler IP block with open its configuration window. Under the Channel Specification tab, I changed the Input Sampling Frequency to 100MHz as I designed my FIRs (shown in next step) to sample at 100MHz for the sake of getting as much data as possible to show at once in the waveform window of the behavioral simulation and ILAs for the sake of a demo.
Also change Clock Frequency to 100MHz since the clock driving the FIR Compiler IP (pl_clk0
) is 100MHz.
In the Implementation tab, I changed Quantization to Quantize Only and Coefficient Fractional Bits to 2. These particular settings are to match the design of my FIR filter which happens to be for 16-bit data, 16-bit coefficients, and 2 bits to represent the fractional part of the coefficients. You should replace these settings to match your FIR's design.
Click OK to save and close the FIR's configuration window, then copy+paste the FIR Compiler IP two more times as well.
Connect the M_AXIS_DATA
output of each DDS Compiler to the S_AXIS_DATA
input of each respective FIR Compiler. Also connect each input clock of the FIR Compilers to pl_clk0
.
While filter design tool software can get pretty expensive, there are a few hidden gems out there for free on the great World Wide Web. My favorite site for quickly generating filter coefficients based on the desired pass and stop bands is http://t-filter.engineerjs.com/
Basic rules of thumb to keep in mind:
- Total range of FIR is 0 Hz to one half of the sample frequency (50MHz in my case since I'm using a sample frequency of 100MHz).
- Some amount of transition band is needed between each pass and stop band
- Keep the transition band on either side of your passband symmetrical
So I created the following three FIRs to filter my chirp:
LPF FIR with a passband from 0 Hz to 5MHz:
BPF FIR with a passband from 5MHz to 8MHz:
FIR Compiler with BPF coefficients that have a passband from 8MHz to 10MHz (which is doubling as my HPF for my target signal since my chirp is from 1MHz to 10MHz).
Notice that I set the fixed point precision bits in the filter tool to 16 to match the settings in my FIR Compiler IP. Then I set the coefficients to be displayed in plain text in int data type so I can simply copy+paste them into a COE file to point to from each FIR Compiler IP.
For each of the COE files, the radix needs to be defined (which is 10 in my case) and the coefficients be defined with the CoefData
variable and comma delimited:
Note: I got some feedback that my coefficient files I attached below are downloading with the.txt extension appended to it. The coefficient files must use the.coe extension to work with the FIR Compiler so be sure to remove that.txt extension if downloading my files from this project post.
Then go back to each FIR Compiler to point to each respective COE file in the Filter Options tab. Set Select Source to COE File then point to the COE file for Coefficient File:
Once the COE file loads with the filter coefficients, the Freq. Response tab will show the frequency response plot so it can be initially validated before spending the time of launching a simulation or generating a bitstream in the event that there is some sort of data type/width mismatch.
With the design complete at this point, I also chose to add some integrated logic analyzers (ILAs) to be able to view the data flow going in and out of the FIR filter when the bitstream is running on the programmable logic (PL) of the K24.
Right-click on the signal line you want to add the ILA to and select Debug.
Since the chirp signal is the same from each DDS Compiler, I only selected the input of the first FIR to add to the ILA. I also manually added an ILA from the IP catalog for the outputs from the FIR compilers.
Once all of the desired signals have been selected for Debug (ie - add to the ILA) select the option from the green banner to Run Connection Automation.
Check the box to enable each signal and enable the option for AXI-Stream Protocol Checker since it is an AXI interface between each IP block.
Click OK to run the connection automation for Vivado to connect the ILA.
Validate the block design (check box icon at the top of the Diagram window) to verify there are no errors or critical warnings, then save the block design.
As an initial functionality check that takes less time than generating a bitstream and running it on hardware, a behavioral simulation will show the FIRs in action. This will allow for a shorter revision cycle in that all you have to do is modify the FIR compiler settings, validate and save the block design then relaunch the simulation.
To run a simulation, a test bench needs to be created. Select Add Sources > Add or create simulation sources then follow the prompts to create a new Verilog file for the test bench (or VHDL if that's the language set in the project settings).
Don't worry about defining any top level ports for the test bench as they aren't needed there. Just click OK.
The test bench file will appear in the Sources window under Simulation Sources. Right-click on it and select the option to Set as Top for the simulation sources.
Since the design runs independently, the test bench is really easy to write: all that is needed is the instantiation of the block design (copy + paste from the HDL wrapper just created), and a clock and reset signal. So it ended up looking like:
Once the test bench is created, select Run Simulation > Run Behavioral Simulation from Flow Navigator window.
Once the simulation has launched, go through the Scope tab and add each desired signal to the Waveform window (I chose the same signals as I added to the ILA in the block design).
Then at the top of the Simulation window, specify a time to run the design for - 100us will capture a sufficient about of data in this design, and click the run to time button (play button icon with (T) subscript).
Change the radix of the target signals from the DDS and FIR to Signed Decimal and the waveform style to Analog.
Which makes the results a easier to evaluate visually:
Zooming in to two cycles of the chirp:
The output of the DDS is the top waveform, with the LPF FIR output below it, then the BPF FIR, and HPF FIR.
Generate BitstreamWith the FIR filters validated from a behavioral perspective, it's ready to run on hardware. Run synthesis and implementation, then generate a bitstream. To save a few button clicks, you can select Generate Bitstream from the Flow Navigator window and Vivado will then detect and ask if to go ahead and run synthesis and implementation.
Once a bitstream has been successfully generated, export the hardware, including the bitstream, in the AMD compressed.XSA format. The output location of the generate.XSA file can be pretty much in any desired location (within the limits of your OS's constraints - ie don't save it somewhere like the /dev
directory in Linux).
Select File > Export > Export Hardware... and go through the screens of the export wizard through to the Finish option:
Even though this particular design is purely RTL, the Zynq UltraScale+ MPSoC processing system must be properly initialized and booted with an FSBL and U-Boot before the programmable logic (PL) will be able to access any clock to be able to run (this is a security feature of the Zynq UltraScale+ MPSoC).
Vitis can be launched from Vivado by selecting Tools > Launch Vitis IDE, then specify the desired location and directory to create the Vitis project workspace. I personally like to create a directory titled "vitis_workspace" in the top level directory of the Vivado project that the.XSA hardware file was exported from that I'll be using to create the Platform in Vitis with.
Once Vitis launches into the new, blank workspace select the option to create a new Platform Project. Select the.XSA exported from Vivado as the hardware reference. Then select the operating system type that will be running on the Arm®-core processing system of the Zynq UltraScale+ MPSoC (ie - standalone or Linux).
I chose standalone since my only goal is to get the Zynq UltraScale+ MPSoC booted up so the PL can have access to a clock to run the design from the bitstream.
Once the Platform Project has generated, run a quick build to generate all of the files needed for the application to build on.
Then create a new Application Project by selecting New > Application Project...
Point it to the platform just created by the Platform Project, specify the OS type (must match selection made in Platform Project), give the application the desired name, and select a template to use.
In my case, I just selected the simple Hello World application template so I don't have to write any code. Click Finish and wait for Vitis to generate the application project.
One final thing before building the project is to update the BSP settings for which of the two UART interfaces of the Zynq UltraScale+ MPSoC chip to use for functions such as print statements. The platform defaults to UART 0, but the USB UART connector on the KD240 is connected to UART 1 of the K24 SOM.
Open the platform.xpr
file from the Explorer window (it should still be open unless it was manually closed). Both the application BSP and Zynq FSBL BSP needs to be updated. So first in the platform, navigate to psu_cortexa53_0 > zynqmp_fsbl > Board Support Package, and click the Modify BSP Settings... button.
A new window will pop up for the Board Support Package settings. Navigate to Overview > standalone and change the option for stdin and stdout from psu_uart_0 to psu_uart_1 (click on the name and it will show a dropdown). Then click OK and wait for the platform to detect the change.
Then navigate to psu_cortexa53_0 > standalone on psu_cortexa53_0 > Board Support Package, and click the Modify BSP Settings... button.
Again, a new window will pop up for the Board Support Package settings. Navigate to Overview > standalone on psu_cortexa53_0 and change the option for stdin and stdout from psu_uart_0 to psu_uart_1. Then click OK and wait for the platform to detect the change.
Finally, build the application project and launch a debug run on hardware (right-click on Application project name in Explorer window then select Debug As > Launch Hardware (Single Application Debug)) - the KD240 needs to be connected to the host PC and powered before launching the debug run.
This satisfies the bare minimum boot necessary for the Zynq UltraScale+ MPSoC as the AMD Vitis™ software to program the PL with the bitstream so the design will be running and the ILA(s) can be accessed from Hardware Manger in Vivado.
View in Hardware ManagerWith the Zynq UltraScale+ MPSoC booted and running, switch back to Vivado and launch Hardware Manager from the Flow Navigator. Since the bitstream was already programmed onto the Zynq UltraScale+ MPSoC in Vitis, it doesn't need to be programmed again.
If you launch Hardware Manager with the Vivado project, the bitstream was generated in open, the ILAs will appear automatically. If you launch Hardware Manager with no Vivado project open, or a different project open, then you'll have to specify the Debug Nets (.ltx) file from the impl_1 directory of the the Vivado project the bitstream was generated in.
Since the design is free running in the PL, simply run the ILA to see the waveforms from the DDS and FIRs:
And now the FIR functionality has been verified in both simulation and on hardware!
AMD sponsored this project. The opinions expressed in this project are those of Whitney Knitter. All opinions are provided by Whitney Knitter and have not been independently verified by AMD. Performance benefits are impacted by a variety of variables. Results herein are specific to Whitney Knitter and may not be typical. AMD, the AMD Arrow logo, Kria, UltraScale+, Vitis, Vivado, Zynq and combinations thereof are trademarks of Advanced Micro Devices, Inc. Other product names used in this project are for identification purposes only and may be trademarks of their respective companies.
Comments