cores/spi: move CSR control/status to add_control method, add loopback capability...
authorFlorent Kermarrec <florent@enjoy-digital.fr>
Sat, 13 Jul 2019 10:54:24 +0000 (12:54 +0200)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Sat, 13 Jul 2019 10:55:19 +0000 (12:55 +0200)
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
test/test_spi.py

index 0ead8062e13c33f1a600fa081ccdeac64f53469c..75d8ac12fc0c0b21da558afcd01831f88ae2ed42 100755 (executable)
@@ -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
-
-        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.irq = Signal()
+        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(len(pads.cs_n), reset=1)
+        self.loopback = 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),
+        ]
index f2b61fc3dab7efcf6301a39487d14981334f4b87..9914dbd2dd348a007e8817cf1859059656de9912 100644 (file)
@@ -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))