Microchip Polarfire SoC series - #2 Basic design
Welcome to the blog post number 2 in the Microchip PolarFire SoC series! Today, we’re creating a basic design for the PolarFire SoC video kit from scratch. While in the previous blog post we’ve seen how to build the reference design, it is important to be able to create a design from scratch. Until you do, you might miss some important details.
We’re going to create a custom MSS configuration and an FPGA design with two GPIO banks connected to the LEDs and dip switches on the video kit.
We’re going to add support for the GPIO banks to the Linux kernel, and write some examples in Python for testing.
I assume you’ve read the first blog post of the Microchip Polarfire SoC series since I did explain about how to install the tools, build the HSS firmware, 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).
MSS configuration
Let’s create a new directory for this project and a new directory for the MSS configuration. Then copy the MSS configuration from the reference design. The MSS configuration has the DDR4 memory configuration and since we’re using the same board, we can use the reference design as a starting point.
mkdir -p basic_design/mss_config
cp video_kit_reference_design/polarfire-soc-video-kit-reference-design/script_support/MSS_VIDEO_KIT/H264/MSS_VIDEO_KIT_H264.cfg basic_design/mss_config/basic_design_mss.cfg
cd basic_design/mss_config/
pfsoc_mss
Open the config file. To understand which peripherals we should keep, open the reference design so it is easier to see how each peripheral is used.
We keep eMMC and SD, USB, Ethernet MAC 0 and 1, UART0 and UART1 (used for the HSS and the Linux console).
We can disable I2C 0 since it is used to configure the cameras. GPIO1 (bank 2) are used for the SD card and for the USB PHY reset, so we keep them.
GPIO2 (Fabric) are used for LEDs and signals related to the camera interface and video pipeline. We can remove them since we’re going to add a bank of GPIOs inside the FPGA fabric.
Regarding the MSS to/from Fabric Interface Controllers, we don’t need FIC1. That is used by the video pipeline to write into the MSS memory.
We don’t need the embedded DLL. It is required if the APB clock frequency is equal or greater than 125MHz.
From a clocking point of view, we keep all the clocks based on the dedicated REFCLK on Bank5 as in the video reference design.
Click on Edit->Edit settings and change the module name to basic_design_mss. Note that this name needs to be different from the top level design entity in the FPGA.
Save the changes and generate the output files. As described in the previous video, there are two files we need. The XML for the FPGA project in Libero and the CFG file for the HART software services.
FPGA design
Create a directory named fpga and start libero
cd ../
mkdir fpga
libero &
Click on “new project” and create a new project called basic design in the FPGA directory. The target device is the MPFS250TS-1FCG1152I.
In device settings keep the default configuration and click on Finish.
Create a new smart design that will be the top level entity for this design with File->New->Smart Design. Name it basic_design.
Now let’s import the MSS configuration with File->Import->MSS component and select the .cxz we have generated with the MSS configuration.
Do a right click on the basic_design_mss and “instantiate in basic_design”. Let’s add 2 blocks of GPIOs to connect to the LEDs. Of course we could use a single GPIO block, but I want to show how to handle multiple IPs on the APB bus.
Since we’re going to use the GPIOs with the Linux device device driver provided in the kernel, you have to configure the GPIO banks not to use a fixed function (input or output). While this would use fewer logic elements, you will not be able to use the GPIOs as input.
When configuring the GPIOs as a fixed function, the configuration register is not present and will always read 0s.
https://github.com/linux4microchip/linux/blob/linux-6.6-mchp/drivers/gpio/gpio-mpfs.c
The GPIO get function checks the configuration register and if the GPIO is configured as output it will use the output register. Otherwise it will use the input register. But if the GPIO bank is configured as a fixed function, the configuration register always reads 0 which means output. So the GPIO get function will never use the input register.
Let’s create a port for the LEDs and one for the dip switches and connect them to the GPIO blocks.
Instantiate a Core APB3 block as the interconnect between the two blocks of GPIOs and the MSS.
We can see two GPIO blocks mapped into the MSS memory space (Memory map button).
Regarding the clock and reset, we just need a clock in the 50 - 100MHz range. There is a 50 MHz single ended oscillator on the board (CLK50MHZ).
Add a reset block and a polar fire init monitor.
Connect the init done between the reset block and the init monitor.
Connect the fabric power on reset from the init monitor to reset block and the MSS reset input.
Connect the MSS reset output to the external reset input of the reset block.
Connect the fabric reset output from the reset block to the two GPIO blocks.
Connect the MSS and the two GPIO blocks to the 50MHz clock.
Also tie high BANK VDDI STATUS and PLL LOCK and tie low SS_BUSY and FF_US_RESTORE.
Click on “promote to top level” for the UART signals to create ports with the same name as the signals on the MSS.
There are a few more signals to tie high/low for the Ethernet PHY: VSC_8662_CMODE3 to 7 (all low, except 6 high), VSC_8662_SRESET (high). These CMODE signals are used to set the default PHY configuration. Also connect the VSC_8662_RESETN to the MSS reset.
Tie low the GPIO inputs and the interrupts.
Click on the design rules check and you shouldn’t get any errors.
Click on generate component.
Let’s copy the constraints for the Ethernet PHY and the two UARTs from the video kit reference design.
cp VIDEO_KIT_MAC.pdc basic_design/constraints/io/
cp VIDEO_KIT_MMUART0.pdc basic_design/constraints/io/
cp VIDEO_KIT_MMUART1.pdc basic_design/constraints/io/
And then create another constraints file for the LEDs, dipswitches, and clocks.
vi fpga/basic_design/constraint/io/user.pdc
And we copy the following:
set_iobank -bank_name Bank0 \
-vcci 1.20 \
-vref 0.60 \
-fixed false \
-update_iostd true
set_io -port_name {leds[0]} \
-pin_name AE27 \
-fixed true \
-io_std LVCMOS12 \
-DIRECTION OUTPUT
set_io -port_name {leds[1]} \
-pin_name AE22 \
-fixed true \
-io_std LVCMOS12 \
-DIRECTION OUTPUT
set_io -port_name {leds[2]} \
-pin_name AP28 \
-fixed true \
-io_std LVCMOS12 \
-DIRECTION OUTPUT
set_io -port_name {leds[3]} \
-pin_name AP29 \
-fixed true \
-io_std LVCMOS12 \
-DIRECTION OUTPUT
set_io -port_name {dipswitches[0]} \
-pin_name AN28 \
-fixed true \
-io_std LVCMOS12 \
-DIRECTION INPUT
set_io -port_name {dipswitches[1]} \
-pin_name AM28 \
-fixed true \
-io_std LVCMOS12 \
-DIRECTION INPUT
set_io -port_name {dipswitches[2]} \
-pin_name AN26 \
-fixed true \
-io_std LVCMOS12 \
-DIRECTION INPUT
set_io -port_name {dipswitches[3]} \
-pin_name AN27 \
-fixed true \
-io_std LVCMOS12 \
-DIRECTION INPUT
set_io -port_name CLK50MHZ \
-pin_name T3 \
-DIRECTION INPUT
Then we create a timing constraints file for the 50MHz clock.
vi basic_design/constraint/user.sdc
And we copy the following:
create_clock -name {CLK50MHZ} -period 20.0 [ get_ports { CLK50MHZ } ]
And now build the bitstream.
Now you might have noticed that we didn’t create pin assignment constraints for all the I/O ports on the block design. I.e. We created pin assignment constraints for the UARTs, but not for the Ethernet PHY. Let’s open the MSS configuration again. If we look at the I/Os some are fixed like for the Ethernet, and some are fabric. Which means that for the fixed signals, even if they appear on the block design ports, they don’t need a pin assignment constraint.
On the block design, you can see the fixed signals have a different colour and the ports were created automatically when we imported the MSS configuration in Libero.
Let’s flash the bitstream on the FPGA.
If you’re switching between projects don’t use open recent project directly, but close the current project otherwise libero doesn’t find the licence for the device.
Now that we have the bitstream on the FPGA, let’s create the HART software services firmware.
Building the HSS
Clone the repository
git clone https://github.com/polarfire-soc/hart-software-services.git
cd hart-software-services/
Configure the environment
export SC_INSTALL_DIR=/usr/local/microchip/SoftConsole-v2022.2-RISC-V-747/
export PATH=$PATH:$SC_INSTALL_DIR/python3/bin:$SC_INSTALL_DIR/riscv-unknown-elf-gcc/bin
export FPGENPROG=/usr/local/microchip/Libero_SoC_v2023.2/Libero/bin64/fpgenprog
Start with the default configuration for the video kit
make BOARD=mpfs-video-kit defconfig
And configure the HSS to use the custom MSS config file
make BOARD=mpfs-video-kit config
Build the HSS and flash it to the board
make BOARD=mpfs-video-kit -j8
make BOARD=mpfs-video-kit program
Building the Yocto image
Clone the repository with the repo manifest:
mkdir yocto
cd yocto
repo init -u
https://github.com/polarfire-soc/polarfire-soc-yocto-manifests.git -b main -m default.xml
Sync the repo:
repo sync
repo rebase
And now initialise the environment
. ./meta-polarfire-soc-yocto-bsp/polarfire-soc_yocto_setup.sh
Export the board we’re going to use. Or you could edit the local config. Note the dot at the beginning of the command line.
Let’s build the development image for the video kit as is.
MACHINE=mpfs-video-kit bitbake mpfs-dev-cli
Add the Python GPIOD package to the image. We’re going to use it to write a few test scripts later in the video.
vi conf/local.conf
And add:
IMAGE_INSTALL:append = " python3-gpiod "
PREFERRED_VERSION_libgpiod:forcevariable = "2.1.%"
ERROR_QA:remove:pn-mpfs-linux = "version-going-backwards"
The last line is to avoid errors when building the image with devtool.
Now we want to modify the DTS for the video kit to add support for the two banks of GPIOs.
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) a clock source and the two GPIO banks. The memory addresses are from Libero SoC:
fabric_clk3: fabric-clk3 {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <50000000>;
};
and inside "fabric"
fabric_gpios_leds: gpio@40001000 {
compatible = "microchip,coregpio-rtl-v3";
reg = <0x0 0x40001000 0x0 0x1000>;
clocks = <&fabric_clk3>;
gpio-controller;
#gpio-cells = <2>;
ngpios = <4>;
gpio-line-names = "LED0", "LED1", "LED2", "LED3";
};
fabric_gpios_dipswitches: gpio@40000000 {
compatible = "microchip,coregpio-rtl-v3";
reg = <0x0 0x40000000 0x0 0x1000>;
clocks = <&fabric_clk3>;
gpio-controller;
#gpio-cells = <2>;
ngpios = <4>;
gpio-line-names = "SWITCH0", "SWITCH1", "SWITCH2", "SWITCH3";
};
Let’s build the kernel
devtool build mpfs-linux
Build the image to test it
devtool build-image mpfs-dev-cli
unmount the SD card. In this case, I have written the SD card previously so I unmount the boot and root partitions
umount /media/matteo/boot
umount /media/matteo/root
And now write the image (change sdx to the correct device)
sudo bmaptool copy tmp-glibc/deploy/images/mpfs-video-kit/mpfs-dev-cli-mpfs-video-kit.rootfs.wic /dev/sdx
Now to test the GPIOs, we can use Python and the GPIOD python bindings. Let’s find the GPIOs controllers to use:
ls /dev/gpio*
The first GPIO controller is the one in the MSS and the other two are the ones in the FPGA fabric.
Let’s write a first example to print the information of the two GPIO banks we have added:
vi example_gpio_info.py
and add
import gpiod
with gpiod.Chip("/dev/gpiochip1") as chip:
info = chip.get_info()
print(f"{info.name} [{info.label}] ({info.num_lines} lines)")
with gpiod.Chip("/dev/gpiochip2") as chip:
info = chip.get_info()
print(f"{info.name} [{info.label}] ({info.num_lines} lines)")
And run it
python3 example_gpio_info.py
Now we can write a second example, blinking one of the LEDs every second.
vi example_gpio_blinky.py
And add:
import time
import gpiod
from gpiod.line import Direction, Value
LINE = "LED0"
with gpiod.request_lines(
"/dev/gpiochip1",
consumer="blink-example",
config={
LINE: gpiod.LineSettings(
direction=Direction.OUTPUT, output_value=Value.ACTIVE
)
},
) as request:
while True:
request.set_value(LINE, Value.ACTIVE)
time.sleep(1)
request.set_value(LINE, Value.INACTIVE)
time.sleep(1)
and run it
python3 example_gpio_blinky.py
And you should see the LED blinking.
Now for the last example, let’s read the status of the input every second.
vi example_gpio_status.py
And add
import time
import gpiod
from gpiod.line import Direction, Value
LINE = "SWITCH0"
with gpiod.request_lines(
"/dev/gpiochip2",
consumer="blink-example",
config={
LINE: gpiod.LineSettings(
direction=Direction.INPUT
)
},
) as request:
while True:
print(request.get_value(LINE))
time.sleep(1)
and run it
python3 example_gpio_status.py
Now commit the changes we’ve done to the Linux kernel so we can create a patch.
pushd /work/projects/swd/polarfire/basic_design/yocto/build/workspace/sources/mpfs-linux
git status
git add arch/riscv/boot/dts/microchip/mpfs-video-kit-fabric.dtsi
git commit -m "Add support for the basic Polarfire example on the video kit"
popd
We’re going to create a new meta layer that we’re going to use for our kernel patch.
bitbake-layers create-layer ../meta-example
And add it to the bitbake build environment
bitbake-layers add-layer ../meta-example
To export your commits as patches and create a bbappend file, use the following command.
devtool finish mpfs-linux ../meta-example
And build the image, flash it to the SD card, and test it again.
bitbake mpfs-dev-cli
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.
Tags: polarfire, microchip, embedded software, fpga