From 688dae01128e655cd8173e2fb47fffd77abf4c8e Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Fri, 4 Aug 2023 17:24:51 +0200 Subject: [PATCH] cores/spi: Add new SPIMMAP core allowing doing SPI accesses directly from MMAP. Implements a new SPIMMAP module, allowing accessing multiple SPI peripherals directly from MMAP. It allows configurable SPI transactions: mode, bit order, and data width. Developed and funded through a collaboration with MoTeC. Example of integration: # SPI MMAP --------------------------------------------------------------------------------- spi_pads = Record([("clk", 1), ("cs_n", 8), ("mosi", 1), ("miso", 1)]) spi_mmap_tx_region = SoCRegion(origin=0x8000_0000, size=4096, cached=False) spi_mmap_rx_region = SoCRegion(origin=0x8000_1000, size=4096, cached=False) self.spi_mmap = SPIMMAP( pads = spi_pads, data_width = 32, sys_clk_freq = sys_clk_freq, tx_origin = spi_mmap_tx_region.origin, rx_origin = spi_mmap_rx_region.origin, tx_fifo_depth = 32, rx_fifo_depth = 32, ) self.bus.add_slave(name="spi_tx", slave = self.spi_mmap.tx_mmap.bus, region = spi_mmap_tx_region, ) self.bus.add_slave(name="spi_rx", slave = self.spi_mmap.rx_mmap.bus, region = spi_mmap_rx_region, ) self.irq.add("spi_mmap", use_loc_if_exists=True) Example of use from CPU C firmware: /* SPI TX Offsets */ #define SPI_TX_CTRL_ENABLE (1 << 0) #define SPI_TX_CTRL_THRESHOLD (1 << 16) #define SPI_TX_STAT_ONGOING (1 << 0) #define SPI_TX_STAT_EMPTY (1 << 1) #define SPI_TX_STAT_FULL (1 << 2) #define SPI_TX_STAT_LEVEL (1 << 16) /* SPI RX Offsets */ #define SPI_RX_CTRL_ENABLE (1 << 0) #define SPI_RX_CTRL_THRESHOLD (1 << 16) #define SPI_RX_STAT_ONGOING (1 << 0) #define SPI_RX_STAT_EMPTY (1 << 1) #define SPI_RX_STAT_FULL (1 << 2) #define SPI_RX_STAT_LEVEL (1 << 16) /* SPI TX/RX Engine */ #define SPI_TX_RX_ENGINE_ENABLE (1 << 0) /* SPI SLOT Offsets */ #define SPI_SLOT_ENABLE (1 << 0) #define SPI_SLOT_MODE (1 << 1) #define SPI_SLOT_LENGTH (1 << 3) #define SPI_SLOT_BITORDER (1 << 5) #define SPI_SLOT_LOOPBACK (1 << 6) #define SPI_SLOT_DIVIDER (1 << 16) /* SPI SLOT Values */ #define SPI_SLOT_MODE_0 0b00 #define SPI_SLOT_MODE_3 0b11 #define SPI_SLOT_LENGTH_32B 0b00 #define SPI_SLOT_LENGTH_16B 0b01 #define SPI_SLOT_LENGTH_8B 0b10 #define SPI_SLOT_BITORDER_MSB_FIRST 0b0 #define SPI_SLOT_BITORDER_LSB_FIRST 0b1 #define SPI_SLOT_EV_TX (1 << 0) #define SPI_SLOT_EV_RX (1 << 1) /* Test SPI with various length (BE) */ void test_spi_length_8_16_32(void) { volatile unsigned char *spi_tx_8 = (unsigned char *)SPI_TX_BASE; volatile unsigned short *spi_tx_16 = (unsigned short *)SPI_TX_BASE; volatile unsigned int *spi_tx_32 = (unsigned int *)SPI_TX_BASE; volatile unsigned char *spi_rx_8 = (unsigned char *)SPI_RX_BASE; volatile unsigned short *spi_rx_16 = (unsigned short *)SPI_RX_BASE; volatile unsigned int *spi_rx_32 = (unsigned int *)SPI_RX_BASE; int errors = 0; printf("Test SPI with various length (BE): 8, 16 and 32-bit...\n"); /* Configure Slots */ spi_mmap_ctrl_slot_control0_write( 1 * SPI_SLOT_ENABLE | SPI_SLOT_MODE_0 * SPI_SLOT_MODE | SPI_SLOT_LENGTH_32B * SPI_SLOT_LENGTH | SPI_SLOT_BITORDER_MSB_FIRST * SPI_SLOT_BITORDER | 1 * SPI_SLOT_LOOPBACK | 4 * SPI_SLOT_DIVIDER ); spi_mmap_ctrl_slot_control1_write( 1 * SPI_SLOT_ENABLE | SPI_SLOT_MODE_0 * SPI_SLOT_MODE | SPI_SLOT_LENGTH_32B * SPI_SLOT_LENGTH | SPI_SLOT_BITORDER_MSB_FIRST * SPI_SLOT_BITORDER | 1 * SPI_SLOT_LOOPBACK | 4 * SPI_SLOT_DIVIDER ); spi_mmap_ctrl_slot_control2_write( 1 * SPI_SLOT_ENABLE | SPI_SLOT_MODE_0 * SPI_SLOT_MODE | SPI_SLOT_LENGTH_32B * SPI_SLOT_LENGTH | SPI_SLOT_BITORDER_MSB_FIRST * SPI_SLOT_BITORDER | 1 * SPI_SLOT_LOOPBACK | 4 * SPI_SLOT_DIVIDER ); spi_mmap_ctrl_slot_control3_write( 1 * SPI_SLOT_ENABLE | SPI_SLOT_MODE_0 * SPI_SLOT_MODE | SPI_SLOT_LENGTH_32B * SPI_SLOT_LENGTH | SPI_SLOT_BITORDER_MSB_FIRST * SPI_SLOT_BITORDER | 1 * SPI_SLOT_LOOPBACK | 4 * SPI_SLOT_DIVIDER ); /* Enable SPI Engine */ spi_mmap_tx_rx_engine_control_write(1 * SPI_TX_RX_ENGINE_ENABLE); /* TX 8-bit transfers */ spi_tx_8[0] = 0x5a; spi_tx_8[4] = 0x01; spi_tx_8[8] = 0x5a; spi_tx_8[12] = 0x01; /* TX 16-bit transfers */ spi_tx_16[0] = 0x5aa5; spi_tx_16[2] = 0x0102; spi_tx_16[4] = 0x5aa5; spi_tx_16[6] = 0x0102; /* TX 32-bit transfers */ spi_tx_32[0] = 0x5aa55aa5; spi_tx_32[1] = 0x01020304; spi_tx_32[2] = 0x5aa55aa5; spi_tx_32[3] = 0x01020304; /* Small delay */ busy_wait(1); /* Read RX 8-bit transfers */ if (spi_rx_8[ 0] != 0x5a) errors++; if (spi_rx_8[ 4] != 0x01) errors++; if (spi_rx_8[ 8] != 0x5a) errors++; if (spi_rx_8[12] != 0x01) errors++; /* Read RX 16-bit transfers */ if (spi_rx_16[0] != 0x5aa5) errors++; if (spi_rx_16[2] != 0x0102) errors++; if (spi_rx_16[4] != 0x5aa5) errors++; if (spi_rx_16[6] != 0x0102) errors++; /* Read RX 32-bit tranfers */ if (spi_rx_32[0] != 0x5aa55aa5) errors++; if (spi_rx_32[1] != 0x01020304) errors++; if (spi_rx_32[2] != 0x5aa55aa5) errors++; if (spi_rx_32[3] != 0x01020304) errors++; /* Disable SPI Engine */ spi_mmap_tx_rx_engine_control_write(0 * SPI_TX_RX_ENGINE_ENABLE); /* Result */ printf("errors: %d\n", errors); } /* Test SPI with various clk divider */ void test_spi_clk_divider(void) { volatile unsigned int *spi_tx_32 = (unsigned int *)SPI_TX_BASE; volatile unsigned int *spi_rx_32 = (unsigned int *)SPI_RX_BASE; int errors = 0; printf("Test SPI with various clk divider: 4, 8, 16 and 32...\n"); /* Configure Slots */ spi_mmap_ctrl_slot_control0_write( 1 * SPI_SLOT_ENABLE | SPI_SLOT_MODE_0 * SPI_SLOT_MODE | SPI_SLOT_LENGTH_32B * SPI_SLOT_LENGTH | SPI_SLOT_BITORDER_MSB_FIRST * SPI_SLOT_BITORDER | 1 * SPI_SLOT_LOOPBACK | 4 * SPI_SLOT_DIVIDER ); spi_mmap_ctrl_slot_control1_write( 1 * SPI_SLOT_ENABLE | SPI_SLOT_MODE_0 * SPI_SLOT_MODE | SPI_SLOT_LENGTH_32B * SPI_SLOT_LENGTH | SPI_SLOT_BITORDER_MSB_FIRST * SPI_SLOT_BITORDER | 1 * SPI_SLOT_LOOPBACK | 8 * SPI_SLOT_DIVIDER ); spi_mmap_ctrl_slot_control2_write( 1 * SPI_SLOT_ENABLE | SPI_SLOT_MODE_0 * SPI_SLOT_MODE | SPI_SLOT_LENGTH_32B * SPI_SLOT_LENGTH | SPI_SLOT_BITORDER_MSB_FIRST * SPI_SLOT_BITORDER | 1 * SPI_SLOT_LOOPBACK | 16 * SPI_SLOT_DIVIDER ); spi_mmap_ctrl_slot_control3_write( 1 * SPI_SLOT_ENABLE | SPI_SLOT_MODE_0 * SPI_SLOT_MODE | SPI_SLOT_LENGTH_32B * SPI_SLOT_LENGTH | SPI_SLOT_BITORDER_MSB_FIRST * SPI_SLOT_BITORDER | 1 * SPI_SLOT_LOOPBACK | 32 * SPI_SLOT_DIVIDER ); /* Enable SPI Engine */ spi_mmap_tx_rx_engine_control_write(1 * SPI_TX_RX_ENGINE_ENABLE); /* TX 32-bit transfers */ spi_tx_32[0] = 0x01020304; spi_tx_32[1] = 0x5aa55aa5; spi_tx_32[2] = 0x01020304; spi_tx_32[3] = 0x5aa55aa5; /* Small delay */ busy_wait(1); /* Read RX 32-bit tranfers */ if (spi_rx_32[0] != 0x01020304) errors++; if (spi_rx_32[1] != 0x5aa55aa5) errors++; if (spi_rx_32[2] != 0x01020304) errors++; if (spi_rx_32[3] != 0x5aa55aa5) errors++; /* Disable SPI Engine */ spi_mmap_tx_rx_engine_control_write(0 * SPI_TX_RX_ENGINE_ENABLE); /* Result */ printf("errors: %d\n", errors); } /* Test SPI with various SPI modes */ void test_spi_modes(void) { volatile unsigned int *spi_tx_32 = (unsigned int *)SPI_TX_BASE; volatile unsigned int *spi_rx_32 = (unsigned int *)SPI_RX_BASE; int errors = 0; printf("Test SPI with various SPI modes: 0 and 3...\n"); /* Configure Slots */ spi_mmap_ctrl_slot_control0_write( 1 * SPI_SLOT_ENABLE | SPI_SLOT_MODE_0 * SPI_SLOT_MODE | SPI_SLOT_LENGTH_32B * SPI_SLOT_LENGTH | SPI_SLOT_BITORDER_MSB_FIRST * SPI_SLOT_BITORDER | 1 * SPI_SLOT_LOOPBACK | 4 * SPI_SLOT_DIVIDER ); spi_mmap_ctrl_slot_control1_write( 1 * SPI_SLOT_ENABLE | SPI_SLOT_MODE_3 * SPI_SLOT_MODE | SPI_SLOT_LENGTH_32B * SPI_SLOT_LENGTH | SPI_SLOT_BITORDER_MSB_FIRST * SPI_SLOT_BITORDER | 1 * SPI_SLOT_LOOPBACK | 4 * SPI_SLOT_DIVIDER ); /* Enable SPI Engine */ spi_mmap_tx_rx_engine_control_write(1 * SPI_TX_RX_ENGINE_ENABLE); /* TX 32-bit transfers */ spi_tx_32[0] = 0x5aa55aa5; spi_tx_32[1] = 0x5aa55aa5; /* Small delay */ busy_wait(1); /* Read RX 32-bit tranfers */ if (spi_rx_32[0] != 0x5aa55aa5) errors++; if (spi_rx_32[1] != 0x5aa55aa5) errors++; /* Disable SPI Engine */ spi_mmap_tx_rx_engine_control_write(0 * SPI_TX_RX_ENGINE_ENABLE); /* Result */ printf("errors: %d\n", errors); } /* Test SPI with various bitorders */ void test_spi_bitorders(void) { volatile unsigned int *spi_tx_32 = (unsigned int *)SPI_TX_BASE; volatile unsigned int *spi_rx_32 = (unsigned int *)SPI_RX_BASE; int errors = 0; printf("Test SPI with various bitorders: MSB and LSB first...\n"); /* Configure Slots */ spi_mmap_ctrl_slot_control0_write( 1 * SPI_SLOT_ENABLE | SPI_SLOT_MODE_0 * SPI_SLOT_MODE | SPI_SLOT_LENGTH_32B * SPI_SLOT_LENGTH | SPI_SLOT_BITORDER_MSB_FIRST * SPI_SLOT_BITORDER | 1 * SPI_SLOT_LOOPBACK | 4 * SPI_SLOT_DIVIDER ); spi_mmap_ctrl_slot_control1_write( 1 * SPI_SLOT_ENABLE | SPI_SLOT_MODE_0 * SPI_SLOT_MODE | SPI_SLOT_LENGTH_32B * SPI_SLOT_LENGTH | SPI_SLOT_BITORDER_LSB_FIRST * SPI_SLOT_BITORDER | 1 * SPI_SLOT_LOOPBACK | 4 * SPI_SLOT_DIVIDER ); /* Enable SPI Engine */ spi_mmap_tx_rx_engine_control_write(1 * SPI_TX_RX_ENGINE_ENABLE); /* TX 32-bit transfers */ spi_tx_32[0] = 0xff000000; spi_tx_32[1] = 0xff000000; /* Small delay */ busy_wait(1); /* Read RX 32-bit tranfers */ if (spi_rx_32[0] != 0xff000000) errors++; if (spi_rx_32[1] != 0xff000000) errors++; /* Disable SPI Engine */ spi_mmap_tx_rx_engine_control_write(0 * SPI_TX_RX_ENGINE_ENABLE); /* Result */ printf("errors: %d\n", errors); } /* Test SPI TX/RX levels */ void test_spi_tx_rx_levels(void) { volatile unsigned int *spi_tx_32 = (unsigned int *)SPI_TX_BASE; volatile unsigned int *spi_rx_32 = (unsigned int *)SPI_RX_BASE; int i; int errors = 0; int pattern; printf("Test SPI TX/RX levels...\n"); /* Configure Slots */ spi_mmap_ctrl_slot_control0_write( 1 * SPI_SLOT_ENABLE | SPI_SLOT_MODE_0 * SPI_SLOT_MODE | SPI_SLOT_LENGTH_32B * SPI_SLOT_LENGTH | SPI_SLOT_BITORDER_MSB_FIRST * SPI_SLOT_BITORDER | 1 * SPI_SLOT_LOOPBACK | 128 * SPI_SLOT_DIVIDER ); /* Enable SPI Engine */ spi_mmap_tx_rx_engine_control_write(1 * SPI_TX_RX_ENGINE_ENABLE); /* TX 32-bit transfers */ pattern = 0x00000001; for (i=0; i<16; i++){ if ((spi_mmap_ctrl_tx_status_read() >> 16) != i) errors++; spi_tx_32[0] = pattern; } /* Small delay */ busy_wait(1); /* Read RX 32-bit tranfers */ for (i=0; i<16; i++){ pattern = spi_rx_32[0]; if ((spi_mmap_ctrl_rx_status_read() >> 16) != (16-1-i)) errors++; } if ((spi_mmap_ctrl_tx_status_read() >> 16) != 0) errors++; if ((spi_mmap_ctrl_rx_status_read() >> 16) != 0) errors++; /* Disable SPI Engine */ spi_mmap_tx_rx_engine_control_write(0 * SPI_TX_RX_ENGINE_ENABLE); /* Result */ printf("errors: %d\n", errors); } /* Test SPI TX/RX IRQs */ void test_spi_tx_rx_irqs(void) { volatile unsigned int *spi_tx_32 = (unsigned int *)SPI_TX_BASE; volatile unsigned int *spi_rx_32 = (unsigned int *)SPI_RX_BASE; int errors = 0; int data __attribute__((unused)); printf("Test SPI TX/RX IRQs...\n"); /* Configure Slots */ spi_mmap_ctrl_slot_control0_write( 1 * SPI_SLOT_ENABLE | SPI_SLOT_MODE_0 * SPI_SLOT_MODE | SPI_SLOT_LENGTH_32B * SPI_SLOT_LENGTH | SPI_SLOT_BITORDER_MSB_FIRST * SPI_SLOT_BITORDER | 1 * SPI_SLOT_LOOPBACK | 128 * SPI_SLOT_DIVIDER ); /* Enable TX/RX EventManager */ spi_mmap_ev_enable_write(0); spi_mmap_ev_pending_write(spi_mmap_ev_pending_read()); spi_mmap_ev_enable_write(SPI_SLOT_EV_TX | SPI_SLOT_EV_RX); /* Enable SPI Engine */ spi_mmap_tx_rx_engine_control_write(1 * SPI_TX_RX_ENGINE_ENABLE); /* TX 32-bit transfers */ spi_tx_32[0] = 0x00000001; /* Small delay */ busy_wait(1); /* Verify TX/RX events */ if (spi_mmap_ev_pending_read() != (SPI_SLOT_EV_TX | SPI_SLOT_EV_RX)) errors++; /* Read RX 32-bit tranfers */ data = spi_rx_32[0]; /* Clear events */ spi_mmap_ev_pending_write(spi_mmap_ev_pending_read()); /* Verify TX/RX events */ if (spi_mmap_ev_pending_read() != 0) errors++; /* Disable SPI Engine */ spi_mmap_tx_rx_engine_control_write(0 * SPI_TX_RX_ENGINE_ENABLE); /* Result */ printf("errors: %d\n", errors); } /* Test SPI Back-to-Back */ void test_spi_back_to_back(void) { volatile unsigned int *spi_tx_32 = (unsigned int *)SPI_TX_BASE; volatile unsigned int *spi_rx_32 = (unsigned int *)SPI_RX_BASE; int errors = 0; printf("Test SPI Back-to-Back...\n"); /* Configure Slots */ spi_mmap_ctrl_slot_control0_write( 1 * SPI_SLOT_ENABLE | SPI_SLOT_MODE_0 * SPI_SLOT_MODE | SPI_SLOT_LENGTH_32B * SPI_SLOT_LENGTH | SPI_SLOT_BITORDER_MSB_FIRST * SPI_SLOT_BITORDER | 1 * SPI_SLOT_LOOPBACK | 8 * SPI_SLOT_DIVIDER ); /* Enable SPI Engine */ spi_mmap_tx_rx_engine_control_write(1 * SPI_TX_RX_ENGINE_ENABLE); /* TX 32-bit transfers */ spi_tx_32[0] = 0x00000001; spi_tx_32[0] = 0x00000002; /* Small delay */ busy_wait(1); /* Read RX 32-bit tranfers */ if (spi_rx_32[0] != 0x00000001) errors++; if (spi_rx_32[0] != 0x00000002) errors++; /* Disable SPI Engine */ spi_mmap_tx_rx_engine_control_write(0 * SPI_TX_RX_ENGINE_ENABLE); /* Result */ printf("errors: %d\n", errors); } --- CHANGES.md | 1 + litex/soc/cores/spi/spi_mmap.py | 698 ++++++++++++++++++++++++++++++++ 2 files changed, 699 insertions(+) create mode 100644 litex/soc/cores/spi/spi_mmap.py diff --git a/CHANGES.md b/CHANGES.md index 57cc6ec5..412c1ead 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -55,6 +55,7 @@ - litepcie/endpoint/tlp : Added optional Configuration/PTM TLP support to Packetizer/Depacketizer. - liteth/arp : Added proper multi-entries ARP table. - liteiclink/serdes : Added tx/rx_clk sharing capabilities on Xilinx transceivers. + - soc/cores/spi : Added new SPIMMAP core allowing SPI accesses through MMAP. [> Changed ---------- diff --git a/litex/soc/cores/spi/spi_mmap.py b/litex/soc/cores/spi/spi_mmap.py new file mode 100644 index 00000000..77ced6bc --- /dev/null +++ b/litex/soc/cores/spi/spi_mmap.py @@ -0,0 +1,698 @@ +# +# This file is part of LiteX. +# +# Copyright (c) 2022-2023 MoTeC +# Copyright (c) 2022-2023 Florent Kermarrec +# SPDX-License-Identifier: BSD-2-Clause + +from migen import * + +from litex.gen import * +from litex.gen.genlib.misc import WaitTimer + +from litex.soc.interconnect.csr import * +from litex.soc.interconnect.csr_eventmanager import * +from litex.soc.interconnect import stream +from litex.soc.interconnect import wishbone + +# Constants / Layouts / Helpers -------------------------------------------------------------------- + +_nslots_max = 16 + +# SPI Layout +def spi_layout(data_width=32, be_width=4, cs_width=1): + return [ + ("data", data_width), + ("be", be_width), + ("cs", cs_width) + ] + +# SPI Slot Constants. + +SPI_SLOT_MODE_0 = 0b00 +SPI_SLOT_MODE_3 = 0b11 + +SPI_SLOT_LENGTH_32B = 0b00 +SPI_SLOT_LENGTH_16B = 0b01 +SPI_SLOT_LENGTH_8B = 0b10 + +SPI_SLOT_BITORDER_MSB_FIRST = 0b0 +SPI_SLOT_BITORDER_LSB_FIRST = 0b1 + +# SPI Master --------------------------------------------------------------------------------------- + +class SPIMaster(LiteXModule): + """4-wire SPI Master + + Provides a simple and minimal hardware SPI Master with Mode0 to Mode3 support. + """ + def __init__(self, pads, data_width, sys_clk_freq, clk_settle_time=20e-9): + # Config. + self.loopback = Signal() + self.clk_divider = Signal(16) + self.mode = Signal(2) + + # Interface. + self.start = Signal() + self.length = Signal(8) + self.done = Signal() + self.irq = Signal() + self.mosi = Signal(data_width) + self.miso = Signal(data_width) + self.cs = Signal(len(pads.cs_n)) + + # # # + + # Signals ---------------------------------------------------------------------------------- + # CPHA — SPI Clock Phase Bit + # 1 = Sampling of data at even edges (2,4,6,...,16) of the SCK clock + # 0 = Sampling of data at odd edges (1,3,5,...,15) of the SCK clock + cpha = self.mode[0] + # CPOL — SPI Clock Polarity Bit + # 1 = Active-low. In idle state SCK is high: odd edges are falling + # 0 = Active-high. In idle state SCK is low: odd edges are rising + cpol = self.mode[1] + + clk = Signal() + clk_d = Signal() + clk_enable = Signal() + clk_run = Signal() + clk_count = Signal(16) + clk_odd = Signal() + clk_even = Signal() + + data_count = Signal(8) + + mosi_shift = Signal() + mosi_data = Signal(data_width) + + miso = Signal() + miso_shift = Signal() + miso_data = Signal(data_width) + + # Chip Select generation ------------------------------------------------------------------- + + self.sync += pads.cs_n.eq(~self.cs) + # TODO: need to guarantee cs_n remains asserted for 1/2 SCK after edge 16 + # at the end of a transfer, however next byte for this transfer can start + # in this region + + # Clk Generation --------------------------------------------------------------------------- + + clk_settle = WaitTimer(int(sys_clk_freq*clk_settle_time)) + self.submodules += clk_settle + + clk_fsm = FSM(reset_state="IDLE") + self.submodules += clk_fsm + clk_fsm.act("IDLE", + If(self.start, + NextState("SETTLE") + ) + ) + clk_fsm.act("SETTLE", + clk_settle.wait.eq(1), + If(clk_settle.done, + NextState("RUN") + ) + ) + clk_fsm.act("RUN", + clk_enable.eq(1), + If(self.done, + NextState("IDLE") + ) + ) + self.sync += [ + If(clk_enable, + clk_count.eq(clk_count + 1), + If(clk_count == self.clk_divider[2:], + clk.eq(~clk), + clk_count.eq(0) + ), + If(clk_odd, + clk_run.eq(1)) + ).Else( + clk.eq(0), + clk_count.eq(0), + clk_run.eq(0) + ) + ] + self.comb += pads.clk.eq((clk & ~self.done) ^ cpol) + self.sync += clk_d.eq(clk) + self.comb += [ + If(clk_enable, + clk_odd.eq( clk & ~clk_d), + clk_even.eq(~clk & clk_d), + ) + ] + + # Master FSM ------------------------------------------------------------------------------- + + self.master_fsm = master_fsm = FSM(reset_state="IDLE") + master_fsm.act("IDLE", + self.done.eq(1), + If(self.start, + self.done.eq(0), + NextState("RUN") + ), + NextValue(data_count, 0), + ) + master_fsm.act("RUN", + clk_enable.eq(1), + # regardless of CPHA update data_count on even edge + If(clk_even, + NextValue(data_count, data_count + 1), + If(data_count == (self.length - 1), + self.irq.eq(1), + NextState("IDLE") + ) + ) + ) + + # Master Out Slave In (MOSI) generation ---------------------------------------------------- + # - Shift on clk odd edge (** but not the first one **) for: + # - Mode 1 & 3 (CPHA=1) + # - Shift on clk even edge for: + # - Mode 0 & 2 (CPHA=0) + + self.comb += Case(cpha, { + 0b0 : mosi_shift.eq(clk_even), + 0b1 : mosi_shift.eq(clk_odd & clk_run), + }) + self.sync += [ + If(self.start, + mosi_data.eq(self.mosi) + ).Elif(mosi_shift, + mosi_data.eq(Cat(Signal(), mosi_data)) + ), + ] + self.comb += pads.mosi.eq(mosi_data[-1]) + + # Master In Slave Out (MISO) capture ------------------------------------------------------- + # - Clocked out by slave on odd edge, so captured on even edge for: + # - Mode 1 & 3 (CPHA=1) + # - Clocked out by slave on even edge, so captured on odd edge for: + # - Mode 0 & 2 (CPHA=0) + # NOTE: The data capture should occur on the subsequent clock edge. E.g. for CPHA=1, + # falling clock edge is bit of data clocked out. On the subsequent raising + # edge the MISO data should be captured. + + self.comb += Case(cpha, { + 0b0 : miso_shift.eq(clk_odd), + 0b1 : miso_shift.eq(clk_even), + }) + self.comb += Case(self.loopback, { + 0b0 : miso.eq(pads.miso), + 0b1 : miso.eq(pads.mosi), + }) + self.sync += [ + If(miso_shift, + miso_data.eq(Cat(miso, miso_data)) + ) + ] + self.comb += self.miso.eq(miso_data) + +# SPI FIFO ----------------------------------------------------------------------------------------- + +@ResetInserter() +class SPIFIFO(LiteXModule): + def __init__(self, data_width=32, nslots=1, depth=32): + self.fifo = stream.SyncFIFO(layout=spi_layout( + data_width = data_width, + be_width = data_width//8, + cs_width = nslots + ), depth=depth, buffered=True) + for name in ["level", "sink", "source"]: + setattr(self, name, getattr(self.fifo, name)) + +# SPI Ctrl ----------------------------------------------------------------------------------------- + +class SPICtrl(LiteXModule): + autocsr_exclude = {"ev"} + def __init__(self, nslots=1, + # TX. + default_tx_enable = 0b1, + # RX. + default_rx_enable = 0b1, + # Slots. + default_slot_enable = 0b1, + default_slot_mode = SPI_SLOT_MODE_3, + default_slot_length = SPI_SLOT_LENGTH_32B, + default_slot_bitorder = SPI_SLOT_BITORDER_MSB_FIRST, + default_slot_loopback = 0b1, + default_slot_divider = 2, + ): + self.nslots = nslots + self.slot_controls = [] + self.slot_status = [] + + # Create TX/RX Control/Status registers. + self.tx_control = CSRStorage(fields=[ + CSRField("enable", size=1, offset=0, values=[ + ("``0b0``", "TX Disabled."), + ("``0b1``", "TX Enabled."), + ], reset=default_tx_enable), + CSRField("threshold", size=16, offset=16, description="TX_FIFO IRQ Threshold.", reset=0) + ]) + self.tx_status = CSRStatus(fields=[ + CSRField("ongoing", size=1, offset=0, values=[ + ("``0b0``", "TX Xfer idle."), + ("``0b1``", "TX Xfer ongoing."), + ]), + CSRField("empty", size=1, offset=1, values=[ + ("``0b0``", "TX FIFO Empty."), + ("``0b1``", "TX FIFO Empty."), + ]), + CSRField("full", size=1, offset=2, values=[ + ("``0b0``", "TX FIFO Full."), + ("``0b1``", "TX FIFO Full."), + ]), + CSRField("level", size=16, offset=16, description="TX FIFO Level.") + ]) + self.rx_control = CSRStorage(fields=[ + CSRField("enable", size=1, offset=0, values=[ + ("``0b0``", "RX Disabled."), + ("``0b1``", "RX Enabled."), + ], reset=default_rx_enable), + CSRField("threshold", size=16, offset=16, description="RX_FIFO IRQ Threshold.", reset=0) + ]) + self.rx_status = CSRStatus(fields=[ + CSRField("ongoing", size=1, offset=0, values=[ + ("``0b0``", "RX Xfer idle."), + ("``0b1``", "RX Xfer ongoing."), + ]), + CSRField("empty", size=1, offset=1, values=[ + ("``0b0``", "RX FIFO Empty."), + ("``0b1``", "RX FIFO Empty."), + ]), + CSRField("full", size=1, offset=2, values=[ + ("``0b0``", "RX FIFO Full."), + ("``0b1``", "RX FIFO Full."), + ]), + CSRField("level", size=16, offset=16, description="RX FIFO Level.") + ]) + + # Create IRQ registers. + self.ev = EventManager() + self.ev.tx = EventSourceProcess(edge="rising") + self.ev.rx = EventSourceProcess(edge="rising") + self.ev.finalize() + self.comb += [ + # TX IRQ when FIFO's level <= TX Threshold. + self.ev.tx.trigger.eq(self.tx_status.fields.level <= self.tx_control.fields.threshold), + # RX IRQ when FIFO's level > RX Threshold. + self.ev.rx.trigger.eq(self.rx_status.fields.level > self.rx_control.fields.threshold), + ] + + # Create Slots Control/Status registers. + for slot in range(nslots): + control = CSRStorage(name=f"slot_control{slot}", fields=[ + CSRField("enable", size=1, offset=0, values=[ + ("``0b0``", "Slot Disabled."), + ("``0b1``", "Slot Enabled."), + ], reset=default_slot_enable), + CSRField("mode", size=2, offset=1, values=[ + ("``0b00``", "SPI Mode 0 (CPOL=0, CPHA=0)."), + ("``0b01``", "SPI Mode 1 (CPOL=0, CPHA=1)."), + ("``0b10``", "SPI Mode 2 (CPOL=1, CPHA=0)."), + ("``0b11``", "SPI Mode 3 (CPOL=1, CPHA=1)."), + ], reset=default_slot_mode), + CSRField("length", size=2, offset=3, values=[ + ("``0b00``", "32-bit Max."), + ("``0b01``", "16-bit Max."), + ("``0b10``", " 8-bit Max."), + ("``0b11``", "Reserved."), + ], reset=default_slot_length), + CSRField("bitorder", size=1, offset=5, values=[ + ("``0b0``", "MSB-First."), + ("``0b1``", "LSB-First."), + ], reset=default_slot_bitorder), + CSRField("loopback", size=1, offset=6, values=[ + ("``0b0``", "Loopback Disabled."), + ("``0b1``", "Loopback Enabled."), + ], reset=default_slot_loopback), + CSRField("divider", size=16, offset=16, values=[ + ("``0x0000``", "Reserved."), + ("``0x0001``", "Reserved."), + ("``0x0002``", "SPI-Clk = Sys-Clk/2."), + ("``0x0004``", "SPI-Clk = Sys-Clk/4."), + ("``0xxxxx``", "SPI-Clk = Sys-Clk/xxxxx."), + ], reset=default_slot_divider) + ]) + status = CSRStatus(name=f"slot_status{slot}") # CHECKME: Useful? + setattr(self, f"slot_control{slot}", control) + setattr(self, f"slot_status{slot}", status) + self.slot_controls.append(control) + self.slot_status.append(status) + + def get_ctrl(self, name, slot=None, cs=None): + assert not ((slot is None) and (cs is None)) + if cs is None: + cs = Signal(self.nslots) + self.comb += cs.eq(1<