use platform.add_extension to first define the pins:
from nmigen.resources.memory import HyperRAMResources
- hyperram_ios = HyperRAMResources(cs="B1",
+ hyperram_ios = HyperRAMResources(cs="B1", # or cs="C0 C1 C2 C3" for Quad
dq="D0 D1 D2 D3 D4 D7 D6 D7",
rwds="B2", rst_n="B3", ck_p="B4",
attrs=Attrs(IOSTANDARD="LVCMOS33"))
self.platform.add_resources(hyperram_ios)
io = self.platform.request("hyperram")
+and then declare the instance using those pins:
+
+ hyperram = HyperRAM(io=io, phy_kls=HyperRAMPHY,
+ latency=7) # Winbond W956D8MBYA
+ # latency=6 for Cypress S27KL0641DABHI020
+
this trick will work with the 1-IC HyperRAM PMOD by Piotr Esden, sold
by 1bitsquared. however for the *four* IC HyperRAM PMOD, *four* cs_n pins
-are needed (and is not currently supported).
-on the TODO list for this module: interleave multiple HyperRAM
-cs_n's to give striped (like RAID) memory accesses behind one single
-Wishbone interface.
+are needed. These are then used to select, in turn, each IC, sequentially:
+ * Access to 0x00000-0xfffff will activate CS0n,
+ * Access to 0x100000-0x1fffff will activate CS1n,
+ * Access to 0x200000-0x2fffff will activate CS2n,
+ * Access to 0x300000-0x3fffff will activate CS3n
+
+TODO: interleave multiple HyperRAM cs_n's to give striped (like RAID)
+memory accesses behind one single Wishbone interface.
+TODO: investigate whether HyperBUS can do CSn-striping in hardware
+(it should do, but this will require configuration registers to be written)
"""
addr_width=23, # 8 GBytes, per IC
bus=None, features=frozenset()):
super().__init__()
+ self.n_cs = n_cs = len(io.cs_n)
+ self.cs_bits = cs_bits = n_cs.bit_length()-1
self.io = io
self.phy = phy_kls(io)
self.latency = latency
- self.bus = wishbone.Interface(addr_width=21,
+ # per IC, times n_cs
+ addr_width += cs_bits
+ self.bus = wishbone.Interface(addr_width=addr_width-2,
data_width=32, granularity=8,
features=features)
- mmap = MemoryMap(addr_width=23, data_width=8)
- mmap.add_resource(object(), name="hyperram", size=2**23)
+ self.size = 2**addr_width
+ mmap = MemoryMap(addr_width=addr_width, data_width=8)
+ mmap.add_resource(object(), name="hyperram", size=self.size)
self.bus.memory_map = mmap
- self.size = 2**23
# # #
def elaborate(self, platform):
m = Module()
m.submodules.phy = self.phy
bus = self.bus
+ cs_bits = self.cs_bits
comb, sync = m.d.comb, m.d.sync
ck = self.phy.ck
rwds_o = self.phy.rwds_o
rwds_oe = self.phy.rwds_oe
+ # chip&address selection: use the MSBs of the address for chip-select
+ # (bus_adr_hi) by doing "1<<bus_adr_hi". this has to be captured
+ # (cs_latch) and asserted as part of bus_latch. therefore *before*
+ # that happens (SEND-COMMAND-ADDRESS and WAIT-STATE) cs has to be
+ # set to the "unlatched" version.
+ bus_adr_lo = self.bus.adr[:-cs_bits]
+ if cs_bits != 0:
+ bus_adr_hi = self.bus.adr[-cs_bits:]
+ else:
+ bus_adr_hi = 0
+
# Clock Generation (sys_clk/4) -----------------------------------
+ # this is a cheap-and-cheerful way to create phase-offsetted DDR:
+ # simply divide the main clock into 4 phases. it does mean that
+ # the HyperRAM IC is being run at 1/4 rate. sigh.
sync += clk_phase.eq(clk_phase + 1)
with m.Switch(clk_phase):
with m.Case(1):
ashift = {8:1, 16:0}[dw]
la = 3-ashift
comb += [
- ca[47].eq(~self.bus.we), # R/W#
- ca[45].eq(1), # Burst Type (Linear)
- ca[16:45].eq(self.bus.adr[la:]), # Row & Upper Column Address
- ca[ashift:3].eq(bus.adr), # Lower Column Address
+ ca[47].eq(~self.bus.we), # R/W#
+ ca[45].eq(1), # Burst Type (Linear)
+ ca[16:45].eq(bus_adr_lo[la:]), # Row & Upper Column Address
+ ca[ashift:3].eq(bus_adr_lo), # Lower Column Address
]
# Latency count starts from the middle of the command (thus the -4).
sync += sr.eq(Cat(Const(0, 16), bus.dat_w))
sync += [ bus_we.eq(bus.we),
bus_sel.eq(bus.sel),
- bus_adr.eq(bus.adr),
+ bus_adr.eq(bus_adr_lo),
cs_latch.eq(cs)
]
-
-
# Sequencer -------------------------------------------------------
cycles = Signal(8)
first = Signal()
with m.State("SEND-COMMAND-ADDRESS"):
sync += cycles.eq(cycles+1)
- comb += cs.eq(1) # Set CSn.
+ comb += cs.eq(1<<bus_adr_hi) # Set CSn direct (not via latch)
comb += ck_active.eq(1) # Activate clock
comb += ca_active.eq(1) # Send Command on DQ.
comb += dq_oe.eq(1), # Wait for 6*2 cycles...
with m.State("WAIT-LATENCY"):
sync += cycles.eq(cycles+1)
- comb += cs.eq(1) # Set CSn directly (not via latch)
+ comb += cs.eq(1<<bus_adr_hi) # Set CSn directly (not via latch)
comb += ck_active.eq(1) # Activate clock
# Wait for Latency cycles...
with m.If(cycles == (latency_cycles - 1)):
m.next = "READ-WRITE-DATA0"
sync += cycles.eq(0)
+ # for-loop creates multple READ-WRITE-DATA states, to send/get
+ # dw bits at a time.
states = {8:4, 16:2}[dw]
for n in range(states):
with m.State("READ-WRITE-DATA%d" % n):
sync += cycles.eq(cycles+1)
- comb += cs.eq(cs_latch), # Set CSn from Latch
+ comb += cs.eq(cs_latch), # *now* set CSn from Latch
comb += ck_active.eq(1) # Activate clock
# Send Data on DQ/RWDS (for write).
with m.If(bus_we):
# Continue burst when consecutive access ready.
with m.If(bus.stb & bus.cyc &
(bus.we == bus_we) &
- (bus.adr == (bus_adr + 1))):
+ (bus_adr_lo == (bus_adr + 1)) &
+ ((1<<bus_adr_hi) == cs_latch)):
comb += bus_latch.eq(1), # Latch Bus (and cs)
# Early Write Ack (to allow bursting).
comb += bus.ack.eq(bus.we)