import math
from migen import *
+from migen.genlib.cdc import MultiReg
from litex.soc.interconnect.csr import *
self._status.status[SPI_STATUS_DONE].eq(self.done),
self._miso.status.eq(self.miso),
]
+
+# SPI Slave ----------------------------------------------------------------------------------------
+
+class SPISlave(Module):
+ """4-wire SPI Slave
+
+ Provides a simple and minimal hardware SPI Slave with CPOL=0, CPHA=0 and build time configurable
+ data_width.
+ """
+ pads_layout = [("clk", 1), ("cs_n", 1), ("mosi", 1), ("miso", 1)]
+ def __init__(self, pads, data_width):
+ if pads is None:
+ pads = Record(self.pads_layout)
+ if not hasattr(pads, "cs_n"):
+ pads.cs_n = Signal()
+ self.pads = pads
+ self.data_width = data_width
+
+ 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()
+ self.loopback = Signal()
+
+ # # #
+
+ clk = Signal()
+ cs = Signal()
+ mosi = Signal()
+ miso = Signal()
+
+ # IOs <--> Internal (input resynchronization) ----------------------------------------------
+ self.specials += [
+ MultiReg(pads.clk, clk),
+ MultiReg(~pads.cs_n, cs),
+ MultiReg(pads.mosi, mosi),
+ ]
+ self.comb += pads.miso.eq(miso)
+
+ # Clock detection --------------------------------------------------------------------------
+ clk_d = Signal()
+ clk_rise = Signal()
+ clk_fall = Signal()
+ self.sync += clk_d.eq(clk)
+ self.comb += clk_rise.eq(clk & ~clk_d)
+ self.comb += clk_fall.eq(~clk & clk_d)
+
+ # Control FSM ------------------------------------------------------------------------------
+ self.submodules.fsm = fsm = FSM(reset_state="IDLE")
+ fsm.act("IDLE",
+ If(cs,
+ self.start.eq(1),
+ NextValue(self.length, 0),
+ NextState("XFER")
+ ).Else(
+ self.done.eq(1)
+ )
+ )
+ fsm.act("XFER",
+ If(~cs,
+ self.irq.eq(1),
+ NextState("IDLE")
+ ),
+ NextValue(self.length, self.length + clk_rise)
+ )
+
+ # Master In Slave Out (MISO) generation (generated on spi_clk falling edge) ----------------
+ miso_data = Signal(data_width)
+ self.sync += \
+ If(self.start,
+ miso_data.eq(self.miso)
+ ).Elif(cs & clk_fall,
+ miso_data.eq(Cat(Signal(), miso_data[:-1]))
+ )
+ self.comb += \
+ If(self.loopback,
+ miso.eq(mosi)
+ ).Else(
+ miso.eq(miso_data[-1]),
+ )
+
+ # Master Out Slave In (MOSI) capture (captured on spi_clk rising edge) ---------------------
+ self.sync += \
+ If(cs & clk_rise,
+ self.mosi.eq(Cat(mosi, self.mosi[:-1]))
+ )
from migen import *
-from litex.soc.cores.spi import SPIMaster
+from litex.soc.cores.spi import SPIMaster, SPISlave
class TestSPI(unittest.TestCase):
spi_master = SPIMaster(pads=None, data_width=32, sys_clk_freq=100e6, spi_clk_freq=5e6)
self.assertEqual(hasattr(spi_master, "pads"), 1)
- def test_spi_xfer_loopback(self):
+ def test_spi_master_xfer_loopback(self):
def generator(dut):
yield dut.loopback.eq(1)
yield dut.mosi.eq(0xdeadbeef)
dut = SPIMaster(pads=None, data_width=32, sys_clk_freq=100e6, spi_clk_freq=5e6, with_csr=False)
run_simulation(dut, generator(dut))
+
+ def test_spi_slave_syntax(self):
+ spi_slave = SPISlave(pads=None, data_width=32)
+ self.assertEqual(hasattr(spi_slave, "pads"), 1)
+
+ def test_spi_slave_xfer(self):
+ class DUT(Module):
+ def __init__(self):
+ pads = Record([("clk", 1), ("cs_n", 1), ("mosi", 1), ("miso", 1)])
+ self.submodules.master = SPIMaster(pads, data_width=32,
+ sys_clk_freq=100e6, spi_clk_freq=5e6,
+ with_csr=False)
+ self.submodules.slave = SPISlave(pads, data_width=32)
+
+ def master_generator(dut):
+ yield dut.master.mosi.eq(0xdeadbeef)
+ yield dut.master.length.eq(32)
+ yield dut.master.start.eq(1)
+ yield
+ yield dut.master.start.eq(0)
+ yield
+ while (yield dut.master.done) == 0:
+ yield
+ self.assertEqual((yield dut.master.miso), 0x12345678)
+
+ def slave_generator(dut):
+ yield dut.slave.miso.eq(0x12345678)
+ while (yield dut.slave.start) == 0:
+ yield
+ while (yield dut.slave.done) == 0:
+ yield
+ self.assertEqual((yield dut.slave.mosi), 0xdeadbeef)
+ self.assertEqual((yield dut.slave.length), 32)
+
+ dut = DUT()
+ run_simulation(dut, [master_generator(dut), slave_generator(dut)])