COMPAS’25 Tutorial - 4. SD Card & UART

Nicolas Derumigny

Adding UART

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.

Block Design

In the block design view, add a AXI UART 16550 controller and configure it in 16550 mode. Then, connect the following pins:

Inputs

Outputs

Addresses

In the Address Editor tab, set the /axi_uart16550_0/S_AXI Master Base Address to 0x1000_0000 and range to 64K.

Constraints:

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
set_property -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

Device Tree

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
    };
    ...
};

Usage

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.

Adding SD Card

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.

Block Design:

In the block design, add a AXI Quad SPI block, and configure it the following way:

AXI Quad SPI CVA6 SD Configuration

Then, connect the following interfaces:

Inputs

Outputs

Addresses

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.

Constraints:

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
set_property -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

Device Tree:

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;
      };
    };
    ...
  };
Previous page
Summary
Next page