From 769d15d433a79cbdac68afa0a14481543d2000f2 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Sat, 13 Jul 2019 12:54:24 +0200 Subject: [PATCH] cores/spi: move CSR control/status to add_control method, add loopback capability and simple xfer loopback test Moving control/status registers to add_control method allow using SPIMaster directly with exposed signals. Add loopback capability (mostly for simulation, but can be useful on hardware too). --- litex/soc/cores/spi.py | 78 +++++++++++++++++++++++++----------------- test/test_spi.py | 17 +++++++++ 2 files changed, 64 insertions(+), 31 deletions(-) diff --git a/litex/soc/cores/spi.py b/litex/soc/cores/spi.py index 0ead8062e..75d8ac12f 100755 --- a/litex/soc/cores/spi.py +++ b/litex/soc/cores/spi.py @@ -21,45 +21,39 @@ class SPIMaster(Module, AutoCSR): configurable data_width and frequency. """ pads_layout = [("clk", 1), ("cs_n", 1), ("mosi", 1), ("miso", 1)] - def __init__(self, pads, data_width, sys_clk_freq, spi_clk_freq): + def __init__(self, pads, data_width, sys_clk_freq, spi_clk_freq, with_control=True): if pads is None: pads = Record(self.pads_layout) - self.pads = pads + if not hasattr(pads, "cs_n"): + pads.cs_n = Signal() + self.pads = pads + self.data_width = data_width - self._control = CSRStorage(16) - self._status = CSRStatus(1) - self._mosi = CSRStorage(data_width) - self._miso = CSRStatus(data_width) - if hasattr(pads, "cs_n"): - self._cs = CSRStorage(len(pads.cs_n), reset=1) + 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), reset=1) + self.loopback = Signal() - self.irq = Signal() + if with_control: + self.add_control() # # # bits = Signal(8) - cs = Signal() + xfer = Signal() shift = Signal() - # Control/Status --------------------------------------------------------------------------- - start = Signal() - length = Signal(8) - done = Signal() - - # XFER start: initialize SPI XFER on SPI_CONTROL_START write and latch length - self.comb += start.eq(self._control.re & self._control.storage[SPI_CONTROL_START]) - self.sync += If(self._control.re, length.eq(self._control.storage[SPI_CONTROL_LENGTH:])) - - # XFER done - self.comb += self._status.status[SPI_STATUS_DONE].eq(done) - # Clock generation ------------------------------------------------------------------------- clk_divide = math.ceil(sys_clk_freq/spi_clk_freq) clk_divider = Signal(max=clk_divide) clk_rise = Signal() clk_fall = Signal() self.sync += [ - If(clk_rise, pads.clk.eq(cs)), + If(clk_rise, pads.clk.eq(xfer)), If(clk_fall, pads.clk.eq(0)), If(clk_fall, clk_divider.eq(0) @@ -73,8 +67,8 @@ class SPIMaster(Module, AutoCSR): # Control FSM ------------------------------------------------------------------------------ self.submodules.fsm = fsm = FSM(reset_state="IDLE") fsm.act("IDLE", - done.eq(1), - If(start, + self.done.eq(1), + If(self.start, NextValue(bits, 0), NextState("WAIT-CLK-FALL") ) @@ -85,12 +79,12 @@ class SPIMaster(Module, AutoCSR): ) ) fsm.act("XFER", - If(bits == length, + If(bits == self.length, NextState("END") ).Elif(clk_fall, NextValue(bits, bits + 1) ), - cs.eq(1), + xfer.eq(1), shift.eq(1) ) fsm.act("END", @@ -104,13 +98,13 @@ class SPIMaster(Module, AutoCSR): # Chip Select generation ------------------------------------------------------------------- if hasattr(pads, "cs_n"): for i in range(len(pads.cs_n)): - self.comb += pads.cs_n[i].eq(~self._cs.storage[i] | ~cs) + self.comb += pads.cs_n[i].eq(~self.cs[i] | ~xfer) # Master Out Slave In (MOSI) generation (generated on spi_clk falling edge) --------------- mosi_data = Signal(data_width) self.sync += \ - If(start, - mosi_data.eq(self._mosi.storage) + If(self.start, + mosi_data.eq(self.mosi) ).Elif(clk_rise & shift, mosi_data.eq(Cat(Signal(), mosi_data[:-1])) ).Elif(clk_fall, @@ -119,7 +113,7 @@ class SPIMaster(Module, AutoCSR): # Master In Slave Out (MISO) capture (captured on spi_clk rising edge) -------------------- miso = Signal() - miso_data = self._miso.status + miso_data = self.miso self.sync += \ If(shift, If(clk_rise, @@ -128,3 +122,25 @@ class SPIMaster(Module, AutoCSR): miso_data.eq(Cat(miso, miso_data[:-1])) ) ) + + # Loopback --------------------------------------------------------------------------------- + self.comb += If(self.loopback, pads.miso.eq(pads.mosi)) + + def add_control(self): + self._control = CSRStorage(16) + self._status = CSRStatus() + self._mosi = CSRStorage(self.data_width) + self._miso = CSRStatus(self.data_width) + self._cs = CSRStorage(len(self.cs), reset=1) + self._loopback = CSRStorage() + + self.comb += [ + self.start.eq(self._control.re & self._control.storage[SPI_CONTROL_START]), + self.length.eq(self._control.storage[SPI_CONTROL_LENGTH:]), + self.mosi.eq(self._mosi.storage), + self.cs.eq(self._cs.storage), + self.loopback.eq(self._loopback.storage), + + self._status.status[SPI_STATUS_DONE].eq(self.done), + self._miso.status.eq(self.miso), + ] diff --git a/test/test_spi.py b/test/test_spi.py index f2b61fc3d..9914dbd2d 100644 --- a/test/test_spi.py +++ b/test/test_spi.py @@ -7,7 +7,24 @@ from migen import * from litex.soc.cores.spi import SPIMaster + class TestSPI(unittest.TestCase): def test_spi_master_syntax(self): 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 generator(dut): + yield dut.loopback.eq(1) + yield dut.mosi.eq(0xdeadbeef) + yield dut.length.eq(32) + yield dut.start.eq(1) + yield + yield dut.start.eq(0) + yield + while (yield dut.done) == 0: + yield + self.assertEqual((yield dut.miso), 0xdeadbeef) + + dut = SPIMaster(pads=None, data_width=32, sys_clk_freq=100e6, spi_clk_freq=5e6, with_control=False) + run_simulation(dut, generator(dut))