This repo is an implementation of a custom SPI driver that allows an FPGA to interact with RHD2164, including the DDR mode during sampling.
The figure below shows the conceptual overview of the system. Since SPI is widely available as a normal peripheral, this implementation is not flexible enough to be used as a general-purpose SPI. With Xilinx FPGAs, an AXI interface should be used to expose the RHD SPI, located in the Programmable Logic (PL), to the Processing System (PS). Among others, this allows PYNQ to access it as a memory-mapped peripheral.
Make sure the CS Inactive time is long enough, which is adjustable with the CLKS_PER_HALF_BIT parameter in hdl/spi_master_cs.v
. This time is the minimum delay between consecutive start commands. o_done
does not toggle high before this time is elapsed.
The module is interacted with like so:
- Write some data to
i_din
, which will be sent via the MOSI line when the transfer starts - Toggle
i_start
. Setting it to 1 will start the transfer, so you should reset it eventually to not keep transferring stuff - Wait until
o_done
goes high, which indicates that the SPI transfer is finished (both RX and TX) - Read data
- Non-convert command: Read
o_dout_a
, which contains the data received on MISO. - Convert command: Read
o_dout_a
, which contains MISO A data (eg channels 0-31). Reado_dout_b
, which contains MISO B data (eg channels 32-63).
The SPI sampling subsystem is a little bit trickier to implement, since Intan's RHD2164 has a custom DDR pattern. The figure below illustrates the block design. DOUT_B[0]
from spi_master.v
always returns 0, since it is sampled at CS
's rising edge. Thus, it is spi_master_cs.v
's responsibility to sample MISO one last time when toggling CS high.
In Xilinx PS-PL applications, AXI can be used to bridge the PS and the PL by memory-mapping HDL ports. As such, this design provides an AXI Block Design to easily access the SPI module within a PS environment. Refer to Xilinx Development.
Personally, I'd recommend developing the HDL code and testbench in a nice IDE like VS Code. Custom HDL sources are located in hdl/
.
This project uses the cocotb Python library to write the testbench, which is located under tests/
. Refer to cocotb's documentation to know how to set it up and use it. The current Makefile
uses Icarus Verilog as simulator.
The tests can be ran by doing ./run.sh
from the repo's root.
To install Icarus Verilog on Ubuntu, run sudo apt-get update && sudo apt-get install iverilog -y
Passing testbench tests do not ensure the design will work post-synthesis, let alone synthesize.
The original Vivado version used is 2022.1, so the project may very well break if another version is used.
To get started quickly, open Vivado and create a new project with your target board. Then, add the sources located in source/
to the project.
Generate the block design: Tools -> Run tcl script -> <path-to-project>/vivado/axi-rhd-spi.tcl
. The block design should get generated. Make sure the Zynq Processing System is configured correctly for your board. Then, create an HDL Wrapper over the Block Design and set it as Top Level.
You should now be ready to develop (you should still follow the development setup), simulate, generate the bitstream, etc.
You can export build artifacts with the vivado/copy-exported.sh
script, just make sure to set the right paths and project names in it.
If you have a Zybo Z7-20 board and want to skip the Vivado project creation, you may use the precompiled artifacts located under `vivado/zybo-z720.
Vitis is used to create the Processing System-side application, which may or may not communicate with the PL.
To use Vitis, just follow any well-made Vitis tutorial, such as Digilent's.
Some more varied and advanced Vitis tutorials are available on Xilinx's Vitis-Tutorials repo.
A working barebones demo is located at vitis/main.c
. To use it, just create a Hello World
application in Vitis, and copy-paste the contents of main.c
into the newly created main file. The payload starts at 0x0000
and increments by 1 after every packet.
The AXI GPIO driver is compatible with PYNQ. The setup is a bit more complicated, so refer to PYNQ's README.