From 330f5837d493e625d2de112bfe7cdeee7f7a4df1 Mon Sep 17 00:00:00 2001 From: Rutherther Date: Tue, 14 Jan 2025 22:37:40 +0100 Subject: [PATCH] docs: add readme --- README.md | 155 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..846a9ba --- /dev/null +++ b/README.md @@ -0,0 +1,155 @@ +# SPI peripheral + +The spi perihperal made here is mainly meant to be used as a peripheral for +a microcontroller. But it can be used as standalone in fpga, with driving +logic. Its interface of reception and transmission should be quite a standard +way - there are valid and ready signals. + +Currently master is supported only. The maximum speed to drive the peripheral +at is `f_clk / 2`, where clk is the input clock frequency. The clk is used +as the clock throughout the design, no clock domain crossing is introduced. + +## Design structure + +The blocks in the design are quite simple. + +There is `spi_peripheral` that wraps `spi_masterslave`. +The `spi_masterslave` is meant to hold both slave and master +controllers. Currently only master controller is implemented. + +Apart from that, there is `spi_clkgen` that generates the clock, +dividing by selected divider. `spi_clkmon` for monitoring the clock, +as a slave. Then `shift_register` for transmitting and receiving the data. +Lastly, there is `reg`, as a register for transmitting the data from shift +register at correct shifting edge. + +## Inputs/Outputs + +### SPI peripheral + +For each spi signal (miso, mosi, sck, csn) there are two outputs and +one input. One output, \_t, is for making the pin high impedance, +\_o is the current output of the pin, valid only if \_t is high. +The \_i is the pin value. + +For writing `waddress_i` specifies the address, `wdata_i` is the data to write, +`write_i` says when to write the data into the address. + +For reading, `raddress_i` specifies the address to read from, +`rdata_i` says the data read on the address. `read_i` says that +read from an address is performed. The data appear immediately, +but `read_i` should be asserted, to say the data are read, as +there are side effects even on reading, specifically in the DATA +register. A read will empty the rx buffer. + +The peripheral also has an interrupt, that is an or from the +status register, for unmasked status fields. + +### SPI masterslave + +The master slave also has i/o for spi, same as the peripheral. +Other inputs and outputs map to what is described in the registers +below. + +## Verification + +For verification, cocotb has been used. Cocotb is a Python framework +for co-simulation, supporting various simulators like ModelSim, +Vivado simulator or open-source GHDL. + +There are numerous tests for the spi_masterslave as well as +spi_peripheral that uses the registers for transmitting and receiving data. + +## Memory mapped peripheral + +The peripheral has 4 registers, + +: Peripheral registers + ++----------+---------+---------------------------------------------------+ +| Register | Address | Description | ++==========+=========+===================================================+ +| CTRL | 0 | Control register | ++----------+---------+---------------------------------------------------+ +| INTMASK | 1 | Interrupt masks | ++----------+---------+---------------------------------------------------+ +| STATUS | 2 | Status of the peripheral | ++----------+---------+---------------------------------------------------+ +| DATA | 2 | Received data on read, transmit data on write | ++----------+---------+---------------------------------------------------+ + + +: CTRL register fields + ++----------------------+-----+-----+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Field | Off | Len | Description | ++======================+=====+=====+==================================================================================================================================================================================+ +| EN | 0 | 1 | Enable the peripheral. If disabled, the peripheral won't function and the outputs will be high impedance | ++----------------------+-----+-----+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| MASTER | 1 | 1 | Whether to turn on master mode (currently only master supported) | ++----------------------+-----+-----+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| TX EN | 2 | 1 | Enable transmission. If disabled, mosi is high impedance. To transmit, you still write to data register | ++----------------------+-----+-----+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| RX EN | 3 | 1 | Enable reception. If disabled, no data are filled to rx buffer. | ++----------------------+-----+-----+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| CLOCK POL | 4 | 1 | Clock polarity of the clock when not transmitting. | ++----------------------+-----+-----+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| CLOCK PHA | 5 | 1 | Phase of the clock, if 0, first edge is sampling, second is shifting. The edge type depends on clock polarity. For clock polarity 0, clock phase 0 means sampling on rising edge | ++----------------------+-----+-----+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| LSBFRST | 6 | 1 | What bit to send first, lsb if high | ++----------------------+-----+-----+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| SIZE | 10 | 1 | The size of data to select, selected through generic, default is 0 for 8, 1 for 16 | ++----------------------+-----+-----+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| DIV | 20 | 3 | The divisor of spi clock. Selected through generic. Default is `clk frequency / 2**(DIV_SEL + 1)` | ++----------------------+-----+-----+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +If a field in INTMASK register is set to '1', when the corresponding +field in STATUS register goes high, an interrupt will be fired. + +: INTMASK register fields + ++-----------------+--------+--------+--------------------------+ +| Field | Offset | Length | Description | ++=================+========+========+==========================+ +| RX BUFFER FULL | 0 | 1 | Mask for rx buffer full | ++-----------------+--------+--------+--------------------------+ +| TX BUFFER EMPTY | 1 | 1 | Mask for tx buffer empty | ++-----------------+--------+--------+--------------------------+ + +: STATUS register fields + ++-----------------+--------+--------+--------------------------------------------------------------------------+ +| Field | Offset | Length | Description | ++=================+========+========+==========================================================================+ +| RX BUFFER FULL | 0 | 1 | The receive buffer is full, data can be received via DATA register | ++-----------------+--------+--------+--------------------------------------------------------------------------+ +| TX BUFFER EMPTY | 1 | 1 | The transmit buffer is empty, data can be transmitted by writing to DATA | ++-----------------+--------+--------+--------------------------------------------------------------------------+ +| BUSY | 3 | 1 | The peripheral is currently busy transmitting or receiving | ++-----------------+--------+--------+--------------------------------------------------------------------------+ +| LOST RX DATA | 4 | 1 | Data has been lost, since they weren't read in time | ++-----------------+--------+--------+--------------------------------------------------------------------------+ + +DATA register fields: +The data register size is based on the format specified in CTRL +register. If you write to the register, the data will be transmitted, +if you read, the received data are read. Data can be transmitted +only if TX_BUFFER\_EMPTY. Data can be received only if +RX\_BUFFER\_FULL. + +## Demonstration + +To test the peripheral, Zynq has been used. This repository contains +both Vivado and Vitis projects. As a demonstration, the Zynq is used +as an spi master. It has a counter and sends numbers to the slave, +incrementing by one every second. On STM Nucleo board, a slave has +been made. This slave receives the number and shows it on a seven +segment display on Funduino board. + +## Project structure + +- `hdl_spi/src` - VHDL code describing the spi peripheral itself +- `hdl_spi/tests` - tests in Python, utilizing cocotb for co-simulation +- `vivado` - Vivado project, with AXI peripheral wrapper for spi peripheral, for Zynq Z20 +- `vitis` - Vitis projects, utilizing the project from vivado as a platform +- `stm_spi_funduino` - spi slave on Nucleo, using Funduino board for displaying the numbers -- 2.48.1