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()
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:
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)