soc: cores: add up5kspram module

The ICE40UP5K has 128 kB of SPRAM that's designed to be used
as memory for a softcore.  This memory is actually 4 16-bit
chunks that we can gang together to give us either 64 kB or
128 kB.

Add a module that will allow us to use this memory in an ICE40.

Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
William D. Jones 2019-06-20 11:12:46 -07:00 committed by Sean Cross
parent 73dbffe8f2
commit 7656f54d0a
1 changed files with 110 additions and 0 deletions

View File

@ -0,0 +1,110 @@
from migen import *
from litex.soc.interconnect import wishbone
# ICE40 UltraPlus family-specific Wishbone interface to the Single Port RAM
# (SPRAM) primitives. Because SPRAM is much more coarse grained than Block
# RAM resources, this RAM is only minimally configurable at present (64kB or
# 128kB). Because it is single port, this module is meant to be used as the
# CPU's RAM region, leaving block RAM free for other use.
class Up5kSPRAM(Module):
def __init__(self, width=32, size=64*1024):
# Right now, LiteX only supports 32-bit CPUs. To get a 32-bit data bus,
# we must width-cascade 2 16-bit SPRAMs. We've already used 2 out of 4
# SPRAMs for this, so the only other valid config is using all 4 SPRAMs
# by depth-cascading.
if width != 32:
raise ValueError("Width of Up5kSPRAM must be 32 bits")
if size != 64*1024 and size != 128*1024:
raise ValueError("Size of Up5kSPRAM must be 64kB or 128kB.")
self.bus = wishbone.Interface(width)
bytesels = []
for i in range(0, 2):
datain = Signal(16)
dataout = Signal(16)
maskwren = Signal(4)
wren = Signal(1)
# 64k vs 128k-specific routing signals.
datain0 = Signal(16)
dataout0 = Signal(16)
maskwren0 = Signal(4)
if size == 128 * 1024:
datain1 = Signal(16)
dataout1 = Signal(16)
maskwren1 = Signal(4)
# Generic routing common to all depths.
for j in range(16):
self.comb += [self.bus.dat_r[16*i + j].eq(dataout[j])]
self.comb += [
datain.eq(self.bus.dat_w[16*i:16*i+16]),
# MASKWREN is nibble-based, interestingly enough.
maskwren.eq(
Cat(
Replicate(self.bus.sel[2*i], 2),
Replicate(self.bus.sel[2*i + 1], 2),
)
),
wren.eq(self.bus.we & self.bus.stb & self.bus.cyc)
]
# Signals which must be routed differently based on depth.
# 64kB
if size == 64*1024:
self.comb += [
datain0.eq(datain),
dataout.eq(dataout0),
maskwren0.eq(maskwren)
]
# 128kB
else:
self.comb += [
If(self.bus.adr[14],
datain1.eq(datain),
dataout.eq(dataout1),
maskwren1.eq(maskwren)
).Else(
datain0.eq(datain),
dataout.eq(dataout0),
maskwren0.eq(maskwren)
)
]
self.specials.spram = Instance("SB_SPRAM256KA",
i_ADDRESS=self.bus.adr[0:14], i_DATAIN=datain0,
i_MASKWREN=maskwren0,
i_WREN=wren,
i_CHIPSELECT=C(1,1),
i_CLOCK=ClockSignal("sys"),
i_STANDBY=C(0,1),
i_SLEEP=C(0,1),
i_POWEROFF=C(1,1),
o_DATAOUT=dataout0
)
# We need to depth cascade if using 128kB.
if size == 128*1024:
self.specials.spram = Instance("SB_SPRAM256KA",
i_ADDRESS=self.bus.adr[0:14], i_DATAIN=datain1,
i_MASKWREN=maskwren1,
i_WREN=wren,
i_CHIPSELECT=C(1,1),
i_CLOCK=ClockSignal("sys"),
i_STANDBY=C(0,1),
i_SLEEP=C(0,1),
i_POWEROFF=C(1,1),
o_DATAOUT=dataout1
)
self.sync += [
self.bus.ack.eq(0),
If(self.bus.stb & self.bus.cyc & ~self.bus.ack,
self.bus.ack.eq(1)
)
]