diff --git a/litex/soc/cores/spi_flash.py b/litex/soc/cores/spi_flash.py index 97d805f39..65e32cd42 100644 --- a/litex/soc/cores/spi_flash.py +++ b/litex/soc/cores/spi_flash.py @@ -36,7 +36,7 @@ def _format_cmd(cmd, spi_width): class SpiFlashDualQuad(Module, AutoCSR): - def __init__(self, pads, dummy=15, div=2, endianness="big"): + 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 @@ -46,6 +46,11 @@ class SpiFlashDualQuad(Module, AutoCSR): spi_width = len(pads.dq) assert spi_width >= 2 + if with_bitbang: + self.bitbang = CSRStorage(4) + self.miso = CSRStatus() + self.bitbang_en = CSRStorage() + # # # cs_n = Signal(reset=1) @@ -71,13 +76,46 @@ class SpiFlashDualQuad(Module, AutoCSR): else: self.comb += bus.dat_r.eq(reverse_bytes(sr)) - self.comb += [ + 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: @@ -125,13 +163,18 @@ class SpiFlashDualQuad(Module, AutoCSR): class SpiFlashSingle(Module, AutoCSR): - def __init__(self, pads, dummy=15, div=2, endianness="big"): + def __init__(self, pads, dummy=15, div=2, with_bitbang=True, endianness="big"): """ Simple SPI flash. Supports 1-bit reads. Only supports mode0 (cpol=0, cpha=0). """ self.bus = bus = wishbone.Interface() + if with_bitbang: + self.bitbang = CSRStorage(4) + self.miso = CSRStatus() + self.bitbang_en = CSRStorage() + # # # if hasattr(pads, "wp"): @@ -154,12 +197,33 @@ class SpiFlashSingle(Module, AutoCSR): else: self.comb += bus.dat_r.eq(reverse_bytes(sr)) - self.comb += [ + 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: