soc/cores/spi: add optional aligned mode.
authorFlorent Kermarrec <florent@enjoy-digital.fr>
Wed, 22 Apr 2020 11:15:07 +0000 (13:15 +0200)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Wed, 22 Apr 2020 11:15:51 +0000 (13:15 +0200)
In aligned mode, MOSI and MISO bits are located on the LSBs and first transmitted MOSI bit is length - 1 bit.

litex/soc/cores/spi.py
test/test_spi.py

index fdf263e75a29ee1b9c8976870859bce5141429c1..fe09f5e9e34b0f5552dc8b09e1aa31a0284125fb 100644 (file)
@@ -17,7 +17,8 @@ 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, with_csr=True):
+    def __init__(self, pads, data_width, sys_clk_freq, spi_clk_freq, with_csr=True, mode="raw"):
+        assert mode in ["raw", "aligned"]
         if pads is None:
             pads = Record(self.pads_layout)
         if not hasattr(pads, "cs_n"):
@@ -96,15 +97,17 @@ class SPIMaster(Module, AutoCSR):
             for i in range(len(pads.cs_n)):
                 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)
+        # Master Out Slave In (MOSI) generation (generated on spi_clk falling edge) ----------------
+        mosi_data = Array(self.mosi[i] for i in range(data_width))
+        mosi_bit  = Signal(max=data_width)
         self.sync += [
             If(self.start,
-                mosi_data.eq(self.mosi)
+                mosi_bit.eq(self.length - 1 if mode == "aligned" else data_width - 1),
             ).Elif(clk_rise & shift,
-                mosi_data.eq(Cat(Signal(), mosi_data))
-            ).Elif(clk_fall,
-                pads.mosi.eq(mosi_data[-1])
+                mosi_bit.eq(mosi_bit - 1)
+            ),
+            If(clk_fall,
+                pads.mosi.eq(mosi_data[mosi_bit])
             )
         ]
 
index 1cfffd57e53606e3a81f51939862a7f9162bda7e..92d1ad827b56060d94883ae8e039ad1221d0201c 100644 (file)
@@ -13,7 +13,7 @@ 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_master_xfer_loopback(self):
+    def test_spi_master_xfer_loopback_32b_32b(self):
         def generator(dut):
             yield dut.loopback.eq(1)
             yield dut.mosi.eq(0xdeadbeef)
@@ -29,6 +29,22 @@ class TestSPI(unittest.TestCase):
         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_master_xfer_loopback_32b_16b(self):
+        def generator(dut):
+            yield dut.loopback.eq(1)
+            yield dut.mosi.eq(0xbeef)
+            yield dut.length.eq(16)
+            yield dut.start.eq(1)
+            yield
+            yield dut.start.eq(0)
+            yield
+            while (yield dut.done) == 0:
+                yield
+            self.assertEqual((yield dut.miso), 0xbeef)
+
+        dut = SPIMaster(pads=None, data_width=32, sys_clk_freq=100e6, spi_clk_freq=5e6, with_csr=False, mode="aligned")
+        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)