cores/spi_flash: add back old SpiFlashDualQuad and rename new one as SpiFlashQuadRead...
authorFlorent Kermarrec <florent@enjoy-digital.fr>
Tue, 12 May 2020 14:51:47 +0000 (16:51 +0200)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Tue, 12 May 2020 14:51:47 +0000 (16:51 +0200)
litex/soc/cores/spi_flash.py

index a1848e30b0cfcd837401e688358ce6b8a883a9f8..f69751705f25e6fffbc1b45baafe26b361b81794 100644 (file)
@@ -19,13 +19,16 @@ from litex.soc.cores.spi import SPIMaster
 
 # SpiFlash Quad/Dual/Single (memory-mapped) --------------------------------------------------------
 
-_QIOFR = 0xeb
-_QIOPP = 0x12
+_FAST_READ = 0x0b
+_DIOFR     = 0xbb
+_QIOFR     = 0xeb
+_QIOPP     = 0x12
 
 def _format_cmd(cmd, spi_width):
     """
     `cmd` is the read instruction. Since everything is transmitted on all
-    dq lines (cmd, adr and data), we need to reformat cmd in single spi mode.
+    dq lines (cmd, adr and data), extend/interleave cmd to full pads.dq
+    width even if dq1-dq3 are don't care during the command phase:
     For example, for N25Q128, 0xeb is the quad i/o fast read, and
     extended to 4 bits (dq1,dq2,dq3 high) is: 0xfffefeff
     """
@@ -78,8 +81,272 @@ class SpiFlashCommon(Module):
         if hasattr(self, "clk_primitive_needed"):
             assert self.clk_primitive_registered == True
 
-
 class SpiFlashDualQuad(SpiFlashCommon, AutoCSR):
+    def __init__(self, pads, dummy=15, div=2, with_bitbang=True, endianness="big"):
+        """
+        Simple SPI flash.
+        Supports multi-bit pseudo-parallel reads (aka Dual or Quad I/O Fast
+        Read). Only supports mode3 (cpol=1, cpha=1).
+        """
+        SpiFlashCommon.__init__(self, pads)
+        self.bus = bus = wishbone.Interface()
+        spi_width = len(pads.dq)
+        assert spi_width >= 2
+
+        if with_bitbang:
+            self.bitbang = CSRStorage(4, reset_less=True, fields=[
+                CSRField("mosi", description="Output value for MOSI pin, valid whenever ``dir`` is ``0``."),
+                CSRField("clk", description="Output value for SPI CLK pin."),
+                CSRField("cs_n", description="Output value for SPI CSn pin."),
+                CSRField("dir", description="Sets the direction for *ALL* SPI data pins except CLK and CSn.", values=[
+                    ("0", "OUT", "SPI pins are all output"),
+                    ("1", "IN", "SPI pins are all input"),
+                ])
+            ], description="""
+                Bitbang controls for SPI output.  Only standard 1x SPI is supported, and as
+                a result all four wires are ganged together.  This means that it is only possible
+                to perform half-duplex operations, using this SPI core.
+            """)
+            self.miso = CSRStatus(description="Incoming value of MISO signal.")
+            self.bitbang_en = CSRStorage(description="Write a ``1`` here to disable memory-mapped mode and enable bitbang mode.")
+
+        # # #
+
+        cs_n = Signal(reset=1)
+        clk = Signal()
+        dq_oe = Signal()
+        wbone_width = len(bus.dat_r)
+
+
+        read_cmd_params = {
+            4: (_format_cmd(_QIOFR, 4), 4*8),
+            2: (_format_cmd(_DIOFR, 2), 2*8),
+            1: (_format_cmd(_FAST_READ, 1), 1*8)
+        }
+        read_cmd, cmd_width = read_cmd_params[spi_width]
+        addr_width = 24
+
+        dq = TSTriple(spi_width)
+        # Keep DQ2,DQ3 as outputs during bitbang, this ensures they activate ~WP or ~HOLD functions
+        self.specials.dq0 = Tristate(pads.dq[0], o=dq.o[0], i=dq.i[0], oe=dq.oe)
+        self.specials.dq1 = Tristate(pads.dq[1], o=dq.o[1], i=dq.i[1], oe=dq.oe)
+        self.specials.dq2 = Tristate(pads.dq[2], o=dq.o[2], i=dq.i[2], oe=(dq.oe | self.bitbang_en.storage))
+        self.specials.dq3 = Tristate(pads.dq[3], o=dq.o[3], i=dq.i[3], oe=(dq.oe | self.bitbang_en.storage))
+
+        sr = Signal(max(cmd_width, addr_width, wbone_width))
+        if endianness == "big":
+            self.comb += bus.dat_r.eq(sr)
+        else:
+            self.comb += bus.dat_r.eq(reverse_bytes(sr))
+
+        hw_read_logic = [
+            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]),
+
+                # In Dual/Quad mode, no single data pin is consistently
+                # an input or output thanks to dual/quad reads, so we need a bit
+                # to swap direction of the pins. Aside from this additional bit,
+                # bitbang mode is identical for Single/Dual/Quad; dq[0] is mosi
+                # and dq[1] is miso, meaning remaining data pin values don't
+                # appear in CSR registers.
+                If(self.bitbang.storage[3],
+                    dq.oe.eq(0)
+                ).Else(
+                    dq.oe.eq(1)
+                ),
+                If(self.bitbang.storage[1], # CPOL=0/CPHA=0 or CPOL=1/CPHA=1 only.
+                    self.miso.status.eq(dq.i[1])
+                ),
+                dq.o.eq(Cat(self.bitbang.storage[0], Replicate(1, spi_width-1)))
+            ]
+
+            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:
+            i = Signal(max=div)
+            dqi = Signal(spi_width)
+            self.sync += [
+                If(i == div//2 - 1,
+                    clk.eq(1),
+                    dqi.eq(dq.i),
+                ),
+                If(i == div - 1,
+                    i.eq(0),
+                    clk.eq(0),
+                    sr.eq(Cat(dqi, sr[:-spi_width]))
+                ).Else(
+                    i.eq(i + 1),
+                ),
+            ]
+
+        # spi is byte-addressed, prefix by zeros
+        z = Replicate(0, log2_int(wbone_width//8))
+
+        seq = [
+            (cmd_width//spi_width*div,
+                [dq_oe.eq(1), cs_n.eq(0), sr[-cmd_width:].eq(read_cmd)]),
+            (addr_width//spi_width*div,
+                [sr[-addr_width:].eq(Cat(z, bus.adr))]),
+            ((dummy + wbone_width//spi_width)*div,
+                [dq_oe.eq(0)]),
+            (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)
+
+class SpiFlashSingle(SpiFlashCommon, AutoCSR):
+    def __init__(self, pads, dummy=15, div=2, with_bitbang=True, endianness="big"):
+        """
+        Simple memory-mapped SPI flash.
+        Supports 1-bit reads. Only supports mode3 (cpol=1, cpha=1).
+        """
+        SpiFlashCommon.__init__(self, pads)
+        self.bus = bus = wishbone.Interface()
+
+        if with_bitbang:
+            self.bitbang = CSRStorage(4, reset_less=True, fields=[
+                CSRField("mosi", description="Output value for SPI MOSI pin."),
+                CSRField("clk", description="Output value for SPI CLK pin."),
+                CSRField("cs_n", description="Output value for SPI CSn pin."),
+                CSRField("dir", description="Unused in this design.")
+            ], description="""Bitbang controls for SPI output.""")
+            self.miso = CSRStatus(description="Incoming value of MISO pin.")
+            self.bitbang_en = CSRStorage(description="Write a ``1`` here to disable memory-mapped mode and enable bitbang mode.")
+
+        # # #
+
+        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))
+        if endianness == "big":
+            self.comb += bus.dat_r.eq(sr)
+        else:
+            self.comb += bus.dat_r.eq(reverse_bytes(sr))
+
+        hw_read_logic = [
+            pads.clk.eq(clk),
+            pads.cs_n.eq(cs_n),
+            pads.mosi.eq(sr[-1:])
+        ]
+
+        if with_bitbang:
+            bitbang_logic = [
+                pads.clk.eq(self.bitbang.storage[1]),
+                pads.cs_n.eq(self.bitbang.storage[2]),
+                If(self.bitbang.storage[1], # CPOL=0/CPHA=0 or CPOL=1/CPHA=1 only.
+                    self.miso.status.eq(pads.miso)
+                ),
+                pads.mosi.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:
+            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, **kwargs):
+    if hasattr(pads, "mosi"):
+       return SpiFlashSingle(pads, *args, **kwargs)
+    else:
+        return SpiFlashDualQuad(pads, *args, **kwargs)
+
+# SpiFlash Quad Read/Write (memory-mapped) ---------------------------------------------------------
+
+class SpiFlashQuadReadWrite(SpiFlashCommon, AutoCSR):
     def __init__(self, pads, dummy=15, div=2, with_bitbang=True, endianness="big"):
         """
         Simple SPI flash.
@@ -137,7 +404,7 @@ class SpiFlashDualQuad(SpiFlashCommon, AutoCSR):
         self.specials.dq0 = Tristate(pads.dq[0], o=dq.o[0], i=dq.i[0], oe=dq.oe)
         self.specials.dq1 = Tristate(pads.dq[1], o=dq.o[1], i=dq.i[1], oe=dq.oe)
         if with_bitbang:
-            # Keep DQ2,DQ3 as outputs during bitbang, this ensures they activate ~WP or ~HOLD functions            
+            # Keep DQ2,DQ3 as outputs during bitbang, this ensures they activate ~WP or ~HOLD functions
             self.specials.dq2 = Tristate(pads.dq[2], o=dq.o[2], i=dq.i[2], oe=(dq.oe | self.bitbang_en.storage))
             self.specials.dq3 = Tristate(pads.dq[3], o=dq.o[3], i=dq.i[3], oe=(dq.oe | self.bitbang_en.storage))
         else:
@@ -284,7 +551,7 @@ class SpiFlashDualQuad(SpiFlashCommon, AutoCSR):
 
         # write data to sr
         self.sync += If(queue.status[2] & (i == div - 1) & ~self.en_quad.storage[0],
-                        sr[-max_transfer_size:].eq(self.spi_in.storage), queue.status[2].eq(0), queue.status[3].eq(1), cs_n.eq(0), dq_oe.eq(1))            
+                        sr[-max_transfer_size:].eq(self.spi_in.storage), queue.status[2].eq(0), queue.status[3].eq(1), cs_n.eq(0), dq_oe.eq(1))
 
         # count spi to slave transfer cycles
         self.sync += If(queue.status[3] & (self.in_left > 0) & (i == div - 1), self.in_left.eq(self.in_left - 1), dq_oe.eq(1))
@@ -309,127 +576,6 @@ class SpiFlashDualQuad(SpiFlashCommon, AutoCSR):
         self.sync += timeline(queue.status[0] & ~self.en_quad.storage[0] & (i == div - 1), accumulate_timeline_deltas(read_seq))
         self.sync += timeline(queue.status[1] & ~self.en_quad.storage[0] & (i == div - 1), accumulate_timeline_deltas(write_seq))
 
-
-class SpiFlashSingle(SpiFlashCommon, AutoCSR):
-    def __init__(self, pads, dummy=15, div=2, with_bitbang=True, endianness="big"):
-        """
-        Simple memory-mapped SPI flash.
-        Supports 1-bit reads. Only supports mode3 (cpol=1, cpha=1).
-        """
-        SpiFlashCommon.__init__(self, pads)
-        self.bus = bus = wishbone.Interface()
-
-        if with_bitbang:
-            self.bitbang = CSRStorage(4, reset_less=True, fields=[
-                CSRField("mosi", description="Output value for SPI MOSI pin."),
-                CSRField("clk", description="Output value for SPI CLK pin."),
-                CSRField("cs_n", description="Output value for SPI CSn pin."),
-                CSRField("dir", description="Unused in this design.")
-            ], description="""Bitbang controls for SPI output.""")
-            self.miso = CSRStatus(description="Incoming value of MISO pin.")
-            self.bitbang_en = CSRStorage(description="Write a ``1`` here to disable memory-mapped mode and enable bitbang mode.")
-
-        # # #
-
-        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))
-        if endianness == "big":
-            self.comb += bus.dat_r.eq(sr)
-        else:
-            self.comb += bus.dat_r.eq(reverse_bytes(sr))
-
-        hw_read_logic = [
-            pads.clk.eq(clk),
-            pads.cs_n.eq(cs_n),
-            pads.mosi.eq(sr[-1:])
-        ]
-
-        if with_bitbang:
-            bitbang_logic = [
-                pads.clk.eq(self.bitbang.storage[1]),
-                pads.cs_n.eq(self.bitbang.storage[2]),
-                If(self.bitbang.storage[1], # CPOL=0/CPHA=0 or CPOL=1/CPHA=1 only.
-                    self.miso.status.eq(pads.miso)
-                ),
-                pads.mosi.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:
-            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, **kwargs):
-    if hasattr(pads, "mosi"):
-       return SpiFlashSingle(pads, *args, **kwargs)
-    else:
-        return SpiFlashDualQuad(pads, *args, **kwargs)
-
 # Xilinx 7-Series FPGAs SPI Flash (non-memory-mapped) ----------------------------------------------
 
 class S7SPIFlash(Module, AutoCSR):