First, we will add a UART controller to our design, in order to see boot logs. For that, we will add a 16550-compatible controller to the CVA6 SoC, linked to our Pmod UART-USB adapter wired to our host machine. Note that the UART C driver is already present in the boot ROM, so we do not need to write any initialization / driver code.
In the block design view, add a AXI UART 16550
controller and configure it in 16550 mode. Then, connect the following pins:
S_AXI
to the AXI interconnect block inside the northbridge one. To do this, click on “+” on the northbridge block, then double-click on the “AXI Interconnect” and add another master interface. Then, link this new master to the UART’s S_AXI
; and connect the new AXI Interconnect’s clock and reset pins (on the bottom left of the block) to the northbridge aclk
and aresetn
input signals.s_axi_clk
to the clock generator’s clk_25
(25 MHz) pins_axi_aresetn
to the Processor System Reset’s peripherals_aresetn
pinfreeze
to a constant 0
valueUART.sin
(click on the “+” sign to see the inner signals of the UART
interfaces) to external uart_rxd
(input), and UART.sout
to external uart_txd
(output). You need to add manually these pins (using right click -> create port).ip2intc_irpt
to uart_irq_i
input of ariane_peripherals
In the Address Editor
tab, set the /axi_uart16550_0/S_AXI
Master Base Address to 0x1000_0000
and range to 64K
.
In the constraint file, specifies the relationship between the design’s uart_rxd
/uart_txd
external IOs and the physical Pmod interface (i.e. FPGA pin). Use the PYNQ-Z2 documentation (page 24 of the PDF) and the Pmod UART-USB documentation, deduce the FPGA pins corresponding to the chosen Pmod connector.
Then, add to the pynq_z2.xdc
constraint file the following code, replacing <PIN>
with the corresponding FPGA pin identifier.
## UART
-dict {PACKAGE_PIN <PIN> IOSTANDARD LVCMOS33} [get_ports uart_txd ]; # module RXD -> FPGA TXD
set_property -dict {PACKAGE_PIN <PIN> IOSTANDARD LVCMOS33} [get_ports uart_rxd ]; # module TXD -> FPGA RXD set_property
Insert into the device tree files the following entries, that advertise a 16550-compatible UART running at 25 MHz with a baud rate of 115,200 available on physical address 0x10000000
, mapped on interrupt vector 1 (by RISC-V specification). Here is the documentation of the signification of device tree entries for this UART.
L26: soc {
...
uart@10000000 {
compatible = "xlnx,axi-uart16550-1.01.a", "xlnx,xps-uart16550-2.00.a", "ns16550";
reg = <0x0 0x10000000 0x0 0x1000>;
clock-frequency = <25000000>;
current-speed = <115200>;
interrupt-parent = <&PLIC0>;
interrupts = <1>;
reg-shift = <2>; // regs are spaced on 32 bit boundary
reg-io-width = <4>; // only 32-bit access are supported
};
...
};
Once the bitstream generation is finished, plug the USB-UART adapter to the Pmod header you have chosen, and connect its other (microUSB) end to your PC using the microUSB to USB cable. Then, you need to connect to the serial interface before flashing the bitstream using the following command:
screen /dev/ttyUSB0 115200
The begging of the boot sequence should print on the console.
As SD card controllers are often proprietary and/or expansive, we will use standard Xilinx SPI controller to pilot accesses to the SD card. This addition is very similar to the UART one: instantiation of a controller block, definition of the constraints and edition of the device tree.
In the block design, add a AXI Quad SPI
block, and configure it the following way:
Then, connect the following interfaces:
AXI_FULL
to the AXI interconnect block inside the northbridge one, similarly to the UART AXIext_spi_clk
to the clock generator’s clk_50
pin (50 MHz, double of the maximum SPI frequency intended at the SPI interface)ext_axi4_aclk
to the clock generator’s clk_25
pins_axi_aresetn
to the Processor System Reset’s peripherals_aresetn
pinSPI_0.io0_o
to external spi_mosi
(output), SPI_0.io1_i
to external spi_miso
(input), SPI_0.sck_o
to external spi_clk_o
(output), and SPI_0.ss_o
to external spi_ss
(output)ip2intc_irpt
to spi_irq_i
input of ariane_peripherals
Similarly to the UART controller, in the Address Editor
tab, set the /axi_quad_spi_0/axi_mm
Master Base Address to 0x2000_0000
and range to 64K
.
Once again, similarly to the UART controller, add to the pynq_z2.xdc
constraint file the following code, replacing <PIN>
with the corresponding FPGA pin identifier.
## SD card
# SPI mode, wired on PMOD
-dict {PACKAGE_PIN <PIN> IOSTANDARD LVCMOS33} [get_ports spi_clk_o ]; # Serial Clock SD card pin
set_property -dict {PACKAGE_PIN <PIN> IOSTANDARD LVCMOS33} [get_ports spi_miso ] ; # MISO SD card pin
set_property -dict {PACKAGE_PIN <PIN> IOSTANDARD LVCMOS33} [get_ports spi_mosi ] ; # MOSI SD card pin
set_property -dict {PACKAGE_PIN <PIN> IOSTANDARD LVCMOS33} [get_ports spi_ss ] ; # Chip Select SD card pin set_property
Insert into the device tree files the following entries, that advertise an SPI controller running at 25 MHz, available on physical address 0x20000000
, mapped on interrupt vector 2 (by RISC-V specification). Here is the documentation for the device tree SPI controller node, and here for the mmc memory node.
L26: soc {
...
xps-spi@20000000 {
compatible = "xlnx,xps-spi-2.00.b", "xlnx,xps-spi-2.00.a";
#address-cells = <1>;
#size-cells = <0>;
interrupt-parent = <&PLIC0>;
interrupts = < 2 2 >;
reg = < 0x0 0x20000000 0x0 0x1000 >;
xlnx,family = "zynq7";
xlnx,fifo-exist = <0x1>;
xlnx,num-ss-bits = <0x1>;
xlnx,num-transfer-bits = <0x8>;
xlnx,sck-ratio = <0x4>;
mmc@0 {
compatible = "mmc-spi-slot";
reg = <0>;
spi-max-frequency = <25000000>;
voltage-ranges = <3300 3300>;
disable-wp;
};
};
...
};