spiflash: add read-only variable data width spi flash
authorRobert Jordens <jordens@gmail.com>
Fri, 8 Nov 2013 07:56:17 +0000 (00:56 -0700)
committerSebastien Bourdeauducq <sebastien@milkymist.org>
Mon, 25 Nov 2013 13:23:55 +0000 (14:23 +0100)
Signed-off-by: Robert Jordens <jordens@gmail.com>
misoclib/spiflash/__init__.py [new file with mode: 0644]

diff --git a/misoclib/spiflash/__init__.py b/misoclib/spiflash/__init__.py
new file mode 100644 (file)
index 0000000..6dcfefb
--- /dev/null
@@ -0,0 +1,131 @@
+from migen.fhdl.std import *
+from migen.bus.transactions import *
+from migen.bus import wishbone
+from migen.genlib.misc import timeline
+from migen.genlib.record import Record
+
+
+class SpiFlash(Module):
+       def __init__(self, pads, cmd=0xfffefeff, cmd_width=32, addr_width=24,
+                       dummy=15, div=2):
+               """
+               Simple read-only SPI flash, e.g. N25Q128 on the LX9 Microboard.
+
+               Supports multi-bit pseudo-parallel reads (aka Dual or Quad I/O Fast
+               Read). Only supports mode0 (cpol=0, cpha=0).
+
+               `cmd` is the read instruction. Since everything is transmitted on all
+               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
+               """
+               self.bus = bus = wishbone.Interface()
+
+               ##
+
+               wbone_width = flen(bus.dat_r)
+               spi_width = flen(pads.dq)
+
+               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[:wbone_width]),
+                       dq.o.eq(sr[-spi_width:]),
+               ]
+
+               if div == 1:
+                       i = 0
+                       self.comb += pads.clk.eq(~ClockSignal())
+                       self.sync += sr.eq(Cat(dq.i, sr[:-spi_width]))
+               else:
+                       i = Signal(max=div)
+                       dqi = Signal(spi_width)
+                       self.sync += [
+                               If(i == div//2 - 1,
+                                       pads.clk.eq(1),
+                                       dqi.eq(dq.i),
+                               ),
+                               If(i == div - 1,
+                                       i.eq(0),
+                                       pads.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), pads.cs_n.eq(0), sr[-cmd_width:].eq(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), pads.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 SpiFlashTB(Module):
+       def __init__(self):
+               self.submodules.master = wishbone.Initiator(self.gen_reads())
+               self.pads = Record([("cs_n", 1), ("clk", 1), ("dq", 4)])
+               self.submodules.slave = SpiFlash(self.pads)
+               self.submodules.tap = wishbone.Tap(self.slave.bus)
+               self.submodules.intercon = wishbone.InterconnectPointToPoint(
+                               self.master.bus, self.slave.bus)
+               self.cycle = 0
+
+       def gen_reads(self):
+               for a in range(10):
+                       t = TRead(a)
+                       yield t
+                       print("read {} in {} cycles(s)".format(t.data, t.latency))
+
+       def do_simulation(self, s):
+               if s.rd(self.pads.cs_n):
+                       self.cycle = 0
+               else:
+                       self.cycle += 1
+                       if not s.rd(self.slave.dq.oe):
+                               s.wr(self.slave.dq.i, self.cycle & 0xf)
+               s.interrupt = self.master.done
+
+
+def _main():
+       from migen.sim.generic import Simulator, TopLevel
+       from migen.fhdl import verilog
+
+       pads = Record([("cs_n", 1), ("clk", 1), ("dq", 4)])
+       s = SpiFlash(pads)
+       print(verilog.convert(s, ios={pads.clk, pads.cs_n, pads.dq, s.bus.adr,
+               s.bus.dat_r, s.bus.cyc, s.bus.ack, s.bus.stb}))
+
+       tb = SpiFlashTB()
+       sim = Simulator(tb, TopLevel("spiflash.vcd"))
+       sim.run()
+
+
+if __name__ == "__main__":
+       _main()