Skip to content

Commit

Permalink
gateware: integrate spi_flash with bitbang reverted bitbang support.
Browse files Browse the repository at this point in the history
  • Loading branch information
enjoy-digital committed Sep 26, 2017
1 parent 040fe0e commit 8f52555
Show file tree
Hide file tree
Showing 6 changed files with 265 additions and 5 deletions.
260 changes: 260 additions & 0 deletions gateware/spi_flash.py
@@ -0,0 +1,260 @@
from litex.gen import *
from litex.gen.genlib.misc import timeline

from litex.soc.interconnect import wishbone
from litex.soc.interconnect.csr import AutoCSR, CSRStorage, CSRStatus


_FAST_READ = 0x0b
_DIOFR = 0xbb
_QIOFR = 0xeb


def _format_cmd(cmd, spi_width):
"""
`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
"""
c = 2**(8*spi_width)-1
for b in range(8):
if not (cmd>>b)%2:
c &= ~(1<<(b*spi_width))
return c


class SpiFlashDualQuad(Module, AutoCSR):
def __init__(self, pads, dummy=15, div=2, with_bitbang=True):
"""
Simple SPI flash.
Supports multi-bit pseudo-parallel reads (aka Dual or Quad I/O Fast
Read). Only supports mode0 (cpol=0, cpha=0).
"""
self.bus = bus = wishbone.Interface()
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)
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),
}
read_cmd, cmd_width = read_cmd_params[spi_width]
addr_width = 24

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 = [
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(Module, AutoCSR):
def __init__(self, pads, dummy=15, div=2, with_bitbang=True):
"""
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"):
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)

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, **kw):
if hasattr(pads, "mosi"):
return SpiFlashSingle(pads, *args, **kw)
else:
return SpiFlashDualQuad(pads, *args, **kw)
2 changes: 1 addition & 1 deletion targets/atlys/base.py
Expand Up @@ -5,7 +5,6 @@
from litex.gen.genlib.resetsync import AsyncResetSynchronizer
from litex.gen.genlib.misc import WaitTimer

from litex.soc.cores import spi_flash
from litex.soc.integration.soc_sdram import *
from litex.soc.integration.builder import *

Expand All @@ -15,6 +14,7 @@

#from gateware import cas
from gateware import info
from gateware import spi_flash

from targets.utils import csr_map_update

Expand Down
2 changes: 1 addition & 1 deletion targets/mimasv2/base.py
Expand Up @@ -7,7 +7,6 @@
from litex.gen import *
from litex.gen.genlib.resetsync import AsyncResetSynchronizer

from litex.soc.cores import spi_flash
from litex.soc.integration.soc_sdram import *
from litex.soc.integration.builder import *

Expand All @@ -17,6 +16,7 @@

from gateware import info
from gateware import cas
from gateware import spi_flash

from targets.utils import csr_map_update

Expand Down
2 changes: 1 addition & 1 deletion targets/minispartan6/base.py
Expand Up @@ -5,7 +5,6 @@
from litex.gen.genlib.io import CRG
from litex.gen.genlib.resetsync import AsyncResetSynchronizer

from litex.soc.cores import spi_flash
from litex.soc.integration.soc_sdram import *
from litex.soc.integration.builder import *

Expand All @@ -15,6 +14,7 @@

from gateware import info
from gateware import cas
from gateware import spi_flash

from targets.utils import csr_map_update

Expand Down
2 changes: 1 addition & 1 deletion targets/opsis/base.py
Expand Up @@ -5,7 +5,6 @@
from litex.gen.genlib.resetsync import AsyncResetSynchronizer
from litex.gen.genlib.misc import WaitTimer

from litex.soc.cores import spi_flash
from litex.soc.integration.soc_sdram import *
from litex.soc.integration.builder import *
from litex.soc.cores.gpio import GPIOIn, GPIOOut
Expand All @@ -20,6 +19,7 @@
from gateware import opsis_i2c
from gateware import shared_uart
from gateware import tofe
from gateware import spi_flash

from targets.utils import csr_map_update

Expand Down
2 changes: 1 addition & 1 deletion targets/pipistrello/base.py
Expand Up @@ -4,7 +4,6 @@
from litex.gen import *
from litex.gen.genlib.resetsync import AsyncResetSynchronizer

from litex.soc.cores import spi_flash
from litex.soc.integration.soc_sdram import *
from litex.soc.integration.builder import *

Expand All @@ -14,6 +13,7 @@

from gateware import info
#from gateware import i2c_hack
from gateware import spi_flash

from targets.utils import csr_map_update

Expand Down

0 comments on commit 8f52555

Please sign in to comment.