add SpiFlashSingle and rename SpiFlash to SpiFlashDualQuad
authorFlorent Kermarrec <florent@enjoy-digital.fr>
Thu, 26 Jan 2017 11:31:26 +0000 (12:31 +0100)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Thu, 26 Jan 2017 12:28:18 +0000 (13:28 +0100)
litex/soc/cores/flash/spi_flash.py

index 5bbec0b39e4c7bd0b0d32466e5440cc6fefa6b88..10190ae8a3053f7f7a5d3a0886f8d98c1f445ad7 100644 (file)
@@ -25,23 +25,19 @@ def _format_cmd(cmd, spi_width):
     return c
 
 
-class SpiFlash(Module, AutoCSR):
-    def __init__(self, pads, dummy=15, div=2, with_bitbang=True):
+class SpiFlashDualQuad(Module, AutoCSR):
+    def __init__(self, pads, dummy=15, div=2):
         """
-        Simple SPI flash, e.g. N25Q128 on the LX9 Microboard.
-
+        Simple SPI flash.
         Supports multi-bit pseudo-parallel reads (aka Dual or Quad I/O Fast
         Read). Only supports mode0 (cpol=0, cpha=0).
         Optionally supports software bitbanging (for write, erase, or other commands).
         """
         self.bus = bus = wishbone.Interface()
         spi_width = len(pads.dq)
-        if with_bitbang:
-            self.bitbang = CSRStorage(4)
-            self.miso = CSRStatus()
-            self.bitbang_en = CSRStorage()
+        assert spi_width >= 2
 
-        ###
+        # # #
 
         cs_n = Signal(reset=1)
         clk = Signal()
@@ -57,52 +53,19 @@ class SpiFlash(Module, AutoCSR):
         read_cmd, cmd_width = read_cmd_params[spi_width]
         addr_width = 24
 
-        pads.cs_n.reset = 1
-
         dq = TSTriple(spi_width)
         self.specials.dq = dq.get_tristate(pads.dq)
 
         sr = Signal(max(cmd_width, addr_width, wbone_width))
         self.comb += bus.dat_r.eq(sr)
 
-        hw_read_logic = [
+        self.comb += [
             pads.clk.eq(clk),
             pads.cs_n.eq(cs_n),
             dq.o.eq(sr[-spi_width:]),
             dq.oe.eq(dq_oe)
         ]
 
-        if with_bitbang:
-            bitbang_logic = [
-                pads.clk.eq(self.bitbang.storage[1]),
-                pads.cs_n.eq(self.bitbang.storage[2]),
-                If(self.bitbang.storage[3],
-                    dq.oe.eq(0)
-                ).Else(
-                    dq.oe.eq(1)
-                ),
-                If(self.bitbang.storage[1],
-                    self.miso.status.eq(dq.i[1])
-                )
-            ]
-            if spi_width > 1:
-                bitbang_logic += [
-                    dq.o.eq(Cat(self.bitbang.storage[0], Replicate(1, spi_width-1)))
-                ]
-            else:
-                bitbang_logic += [
-                    dq.o.eq(self.bitbang.storage[0])
-                ]
-
-            self.comb += \
-                If(self.bitbang_en.storage,
-                    bitbang_logic
-                ).Else(
-                    hw_read_logic
-                )
-        else:
-            self.comb += hw_read_logic
-
         if div < 2:
             raise ValueError("Unsupported value \'{}\' for div parameter for SpiFlash core".format(div))
         else:
@@ -147,3 +110,90 @@ class SpiFlash(Module, AutoCSR):
             t += dt
 
         self.sync += timeline(bus.cyc & bus.stb & (i == div - 1), tseq)
+
+
+class SpiFlashSingle(Module, AutoCSR):
+    def __init__(self, pads, dummy=15, div=2):
+        """
+        Simple SPI flash.
+        Supports 1-bit reads. Only supports mode0 (cpol=0, cpha=0).
+        Optionally supports software bitbanging (for write, erase, or other commands).
+        """
+        self.bus = bus = wishbone.Interface()
+
+        # # #
+
+        if hasattr(pads, "wp"):
+            self.comb += pads.wp.eq(1)
+
+        if hasattr(pads, "hold"):
+            self.comb += pads.hold.eq(1)
+
+        cs_n = Signal(reset=1)
+        clk = Signal()
+        wbone_width = len(bus.dat_r)
+
+        read_cmd = _FAST_READ
+        cmd_width = 8
+        addr_width = 24
+
+        sr = Signal(max(cmd_width, addr_width, wbone_width))
+        self.comb += bus.dat_r.eq(sr)
+
+        self.comb += [
+            pads.clk.eq(clk),
+            pads.cs_n.eq(cs_n),
+            pads.mosi.eq(sr[-1:])
+        ]
+
+        if div < 2:
+            raise ValueError("Unsupported value \'{}\' for div parameter for SpiFlash core".format(div))
+        else:
+            i = Signal(max=div)
+            miso = Signal()
+            self.sync += [
+                If(i == div//2 - 1,
+                    clk.eq(1),
+                    miso.eq(pads.miso),
+                ),
+                If(i == div - 1,
+                    i.eq(0),
+                    clk.eq(0),
+                    sr.eq(Cat(miso, sr[:-1]))
+                ).Else(
+                    i.eq(i + 1),
+                ),
+            ]
+
+        # spi is byte-addressed, prefix by zeros
+        z = Replicate(0, log2_int(wbone_width//8))
+
+        seq = [
+            (cmd_width*div,
+                [cs_n.eq(0), sr[-cmd_width:].eq(read_cmd)]),
+            (addr_width*div,
+                [sr[-addr_width:].eq(Cat(z, bus.adr))]),
+            ((dummy + wbone_width)*div,
+                []),
+            (1,
+                [bus.ack.eq(1), cs_n.eq(1)]),
+            (div, # tSHSL!
+                [bus.ack.eq(0)]),
+            (0,
+                []),
+        ]
+
+        # accumulate timeline deltas
+        t, tseq = 0, []
+        for dt, a in seq:
+            tseq.append((t, a))
+            t += dt
+
+        self.sync += timeline(bus.cyc & bus.stb & (i == div - 1), tseq)
+
+
+def SpiFlash(pads, *args, **kw):
+    if hasattr(pads, "mosi"):
+        return SpiFlashSingle(pads, *args, **kw)
+    else:
+        return SpiFlashDualQuad(pads, *args, **kw)