cores/spi_flash: add back old SpiFlashDualQuad and rename new one as SpiFlashQuadReadWrite.
This commit is contained in:
parent
2a5a7536b8
commit
3fb99b7d33
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue