From 5953f901c883fa3c485e8a22629b604b4beda956 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Fri, 8 Nov 2013 00:56:17 -0700 Subject: [PATCH] spiflash: add read-only variable data width spi flash Signed-off-by: Robert Jordens --- misoclib/spiflash/__init__.py | 131 ++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 misoclib/spiflash/__init__.py diff --git a/misoclib/spiflash/__init__.py b/misoclib/spiflash/__init__.py new file mode 100644 index 000000000..6dcfefbf3 --- /dev/null +++ b/misoclib/spiflash/__init__.py @@ -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()