Microchip Polarfire SoC series - #3 Custom IP
Welcome to the blog post number 3 in the Microchip PolarFire SoC series!
Today, we’re integrating a custom IP into the PolarFire SoC video kit’s base design, addressing a key aspect of practical FPGA development. We’re going to add the system version IP that I have created for the FPGA meets devops video series, but this time the bus interface is APB instead of AXI. A simple testbench written in Python and cocoTB is used to validate the IP. And we’re going to add the system version application to the Linux image with a custom meta layer.
In this blog post, I assume you’ve watched the first two videos of the Microchip Polarfire SoC series where I explained how to install the tools, build the Yocto image, etc.
Full disclaimer: this video is not sponsored by Microchip and the video kit has been bought by Starware Design.
Video
You can find this blog post in video format on YouTube (embedded below).
Custom IP
While I was doing some research for this blog post, I found out that it is not possible to package an IP core and add it to the vault. I hope this feature will be added to Libero soon. This is the article from microchip that confirms that.
What it is possible to do today is to take an HDL file and create a so called HDL+ core.
Checkout the IP core repo in the FPGA folder from the previous video.
git clone https://github.com/starwaredesign/ip_repo_microchip.git
Let’s add the system version verilog file to the project with File->Import->HDL source files. Then click on build hierarchy.
Right click on the system version file and click on “create core from HDL”. We can see it parses the parameters and the module inputs and outputs. Now edit the interface so the APB port is recognised. Drag the core into the smart design and wire it to the rest of the design.
We also add the pin assignment constraints. To simulate the board type and revision, I soldered a bank of dip switches to an FMC board.
# DQ16 - H29 - HPC_LA24_N_B9 -
set_io -port_name {board_type[0]} \
-pin_name E15 \
-fixed true \
-io_std LVCMOS33 \
-RES_PULL UP \
-DIRECTION INPUT
# DQ17 - H31 - HPC_LA28_P_B9 -
set_io -port_name {board_type[1]} \
-pin_name D13 \
-fixed true \
-io_std LVCMOS33 \
-RES_PULL UP \
-DIRECTION INPUT
# DQ18 - H32 - HPC_LA28_N_B9 -
set_io -port_name {board_type[2]} \
-pin_name C13 \
-fixed true \
-io_std LVCMOS33 \
-RES_PULL UP \
-DIRECTION INPUT
# DQ19 - H34 - HPC_LA30_P_B9 -
set_io -port_name {board_type[3]} \
-pin_name F18 \
-fixed true \
-io_std LVCMOS33 \
-RES_PULL UP \
-DIRECTION INPUT
# DQ20 - H25 - HPC_LA30_N_B9 -
set_io -port_name {board_rev[0]} \
-pin_name F17 \
-fixed true \
-io_std LVCMOS33 \
-RES_PULL UP \
-DIRECTION INPUT
# DQ21 - H37 - HPC_LA32_P_B9 -
set_io -port_name {board_rev[1]} \
-pin_name F14 \
-fixed true \
-io_std LVCMOS33 \
-RES_PULL UP \
-DIRECTION INPUT
# DQ22 - H38 - HPC_LA32_N_B9 -
set_io -port_name {board_rev[2]} \
-pin_name E13 \
-fixed true \
-io_std LVCMOS33 \
-RES_PULL UP \
-DIRECTION INPUT
# DQ25 - G9 - HPC_LA03_P_B9 -
set_io -port_name {board_rev[3]} \
-pin_name E25 \
-fixed true \
-io_std LVCMOS33 \
-RES_PULL UP \
-DIRECTION INPUT
We can also add false path constraints on the board version and board type inputs.
# CDC constraints
set_false_path -from [get_ports {*board_type[*]}]
set_max_delay 3 -from [get_pins {systemversion_0/board_type_d1[*]/CLK}] -to [get_pins {systemversion_0/board_type_d2[*]/D}]
set_false_path -from [get_ports {*board_rev[*]}]
set_max_delay 3 -from [get_pins {systemversion_0/board_rev_d1[*]/CLK}] -to [get_pins {systemversion_0/board_rev_d2[*]/D}]
Unfortunately, the toolchain doesn’t support data path only type of constraints nor tags like ASYNC_REG like in AMD Vivado.
Click on build component, build the bitstream and flash it to the board.
Simulation
Before we test the IP on the FPGA, let’s run a simple test bench for the system version IP. If you like more details about cocotb, the test runner, and the test case checkout episode 5 of the FPGA meets devops series.
We’re going to use the modelsim/questa simulator that is part of the standard installation of Libero SoC. We are also going to use an external cocotb library to get an APB driver and monitor.
python -m venv .venv
source .venv/bin/activate
pip install cocotb
pip install cocotbext-apb
pip install pytest
Export the Questa path:
export PATH=/usr/local/microchip/Libero_SoC_v2023.2/QuestaSim/linux_x86_64:$PATH
export LM_LICENSE_FILE=1702@localhost
Run the test bench with the following command line:
pytest -s -v --log-level=INFO
Software
Now that we have the system version IP inside the FPGA fabric, how can we get access to it? We can re-use the system version application from the FPGA meets devops series. Since that application is written in C and interacts with the IP via UIO, we can re-use it as is without any modifications.
Checkout the meta-swd layer:
git clone https://github.com/starwaredesign/meta-swd.git
Add it to bitbake:
. ./meta-polarfire-soc-yocto-bsp/polarfire-soc_yocto_setup.sh
bitbake-layers add-layer ../meta-swd
And add the system version app to the image. Edit conf/local.conf and add
IMAGE_INSTALL:append = " python3-gpiod system-version"
Add the IP to the device tree using the same commands from the previous video:
export MACHINE=mpfs-video-kit
devtool modify mpfs-linux
code workspace/sources/mpfs-linux
Add to the polarfire video kit dtsi (arch/riscv/boot/dts/microchip/mpfs-video-kit-fabric.dtsi) the following:
systemversion: systemversion0@40002000 {
compatible = "generic-uio";
status = "okay";
reg = <0x0 0x40002000 0x0 0x1000>;
};
The memory address is from Libero SoC.
Let’s build the kernel
devtool build mpfs-linux
And build the image
devtool build-image mpfs-dev-cli
And now write the image
umount /media/matteo/boot
umount /media/matteo/root
sudo bmaptool copy tmp-glibc/deploy/images/mpfs-video-kit/mpfs-dev-cli-mpfs-video-kit.rootfs.wic /dev/sdc
Unmount the SD card and put the SD card into the video kit.
Testing
Let’s start the board and connect to the console over the serial port. I have wired the dip switches to this FMC board that has two pin headers with easy access to the I/Os. Use print-systemversion to get the FPGA version, board type, and board revision. You should see something like this
root@mpfs-video-kit:~# print-systemversion
********************************************************************************
Board type 15, board revision 15
********************************************************************************
FPGA version 0.1.0
********************************************************************************
Disclaimer
All trademarks, logos, and brand names shown on this video are the property of their respective owners. The use of these trademarks does not imply any affiliation with, or endorsement by, their owners. This blog post is intended for educational/entertainment/informational purposes only, and no copyright infringement is intended.