Merge pull request #1926 from enjoy-digital/hyperbus_variable_latency
HyperRAM: Add variable latency and configuration support.
This commit is contained in:
commit
576ab24b6c
|
@ -1,7 +1,7 @@
|
||||||
#
|
#
|
||||||
# This file is part of LiteHyperBus
|
# This file is part of LiteX.
|
||||||
#
|
#
|
||||||
# Copyright (c) 2019-2022 Florent Kermarrec <florent@enjoy-digital.fr>
|
# Copyright (c) 2019-2024 Florent Kermarrec <florent@enjoy-digital.fr>
|
||||||
# Copyright (c) 2019 Antti Lukats <antti.lukats@gmail.com>
|
# Copyright (c) 2019 Antti Lukats <antti.lukats@gmail.com>
|
||||||
# Copyright (c) 2021 Franck Jullien <franck.jullien@collshade.fr>
|
# Copyright (c) 2021 Franck Jullien <franck.jullien@collshade.fr>
|
||||||
# SPDX-License-Identifier: BSD-2-Clause
|
# SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
@ -11,6 +11,9 @@ from migen import *
|
||||||
from litex.gen import *
|
from litex.gen import *
|
||||||
from litex.gen.genlib.misc import WaitTimer
|
from litex.gen.genlib.misc import WaitTimer
|
||||||
|
|
||||||
|
from litex.soc.interconnect.csr import *
|
||||||
|
from litex.soc.interconnect import stream
|
||||||
|
|
||||||
from litex.build.io import DifferentialOutput
|
from litex.build.io import DifferentialOutput
|
||||||
|
|
||||||
from litex.soc.interconnect import wishbone
|
from litex.soc.interconnect import wishbone
|
||||||
|
@ -21,25 +24,56 @@ class HyperRAM(LiteXModule):
|
||||||
tCSM = 4e-6
|
tCSM = 4e-6
|
||||||
"""HyperRAM
|
"""HyperRAM
|
||||||
|
|
||||||
Provides a very simple/minimal HyperRAM core that should work with all FPGA/HyperRam chips:
|
Provides a very simple/minimal HyperRAM core with a Wishbone Interface that can work with all
|
||||||
- FPGA vendor agnostic.
|
FPGA/HyperRam chips:
|
||||||
- no setup/chip configuration (use default latency).
|
- Vendor agnostic.
|
||||||
|
- Fixed/Variable latency.
|
||||||
|
- Latency/Registers (re-)configuration.
|
||||||
|
|
||||||
This core favors portability and ease of use over performance.
|
Parameters:
|
||||||
"""
|
pads (Record) : Interface to the HyperRAM connection pads.
|
||||||
def __init__(self, pads, latency=6, sys_clk_freq=None):
|
latency (int, optional) : Initial latency setting, defaults to 6.
|
||||||
|
latency_mode (str, optional) : Specifies the latency mode ('fixed' or 'variable'), defaults to 'fixed'.
|
||||||
|
sys_clk_freq (float, optional) : System clock frequency in Hz.
|
||||||
|
with_csr (bool, optional) : Enables CSR interface for Latency/Registers configuration, defaults to True.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
pads (Record) : Platform pads of HyperRAM.
|
||||||
|
bus (wishbone.Interface) : Wishbone Interface.
|
||||||
|
"""
|
||||||
|
def __init__(self, pads, latency=6, latency_mode="fixed", sys_clk_freq=None, with_csr=True):
|
||||||
self.pads = pads
|
self.pads = pads
|
||||||
self.bus = bus = wishbone.Interface(data_width=32, address_width=32, addressing="word")
|
self.bus = bus = wishbone.Interface(data_width=32, address_width=32, addressing="word")
|
||||||
|
|
||||||
|
# Config/Reg Interface.
|
||||||
|
# ---------------------
|
||||||
|
self.conf_rst = Signal()
|
||||||
|
self.conf_latency = Signal(8, reset=latency)
|
||||||
|
self.reg_write = Signal()
|
||||||
|
self.reg_read = Signal()
|
||||||
|
self.reg_addr = Signal(2)
|
||||||
|
self.reg_write_done = Signal()
|
||||||
|
self.reg_read_done = Signal()
|
||||||
|
self.reg_write_data = Signal(16)
|
||||||
|
self.reg_read_data = Signal(16)
|
||||||
|
if with_csr:
|
||||||
|
self.add_csr(default_latency=latency)
|
||||||
|
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
|
# Parameters.
|
||||||
|
# -----------
|
||||||
|
assert latency_mode in ["fixed", "variable"]
|
||||||
|
|
||||||
|
# Internal Signals.
|
||||||
|
# -----------------
|
||||||
clk = Signal()
|
clk = Signal()
|
||||||
clk_phase = Signal(2)
|
clk_phase = Signal(2)
|
||||||
cs = Signal()
|
cs = Signal()
|
||||||
ca = Signal(48)
|
ca = Signal(48)
|
||||||
ca_active = Signal()
|
ca_active = Signal()
|
||||||
sr = Signal(48)
|
sr = Signal(48)
|
||||||
sr_new = Signal(48)
|
sr_next = Signal(48)
|
||||||
dq = self.add_tristate(pads.dq) if not hasattr(pads.dq, "oe") else pads.dq
|
dq = self.add_tristate(pads.dq) if not hasattr(pads.dq, "oe") else pads.dq
|
||||||
rwds = self.add_tristate(pads.rwds) if not hasattr(pads.rwds, "oe") else pads.rwds
|
rwds = self.add_tristate(pads.rwds) if not hasattr(pads.rwds, "oe") else pads.rwds
|
||||||
dw = len(pads.dq) if not hasattr(pads.dq, "oe") else len(pads.dq.o)
|
dw = len(pads.dq) if not hasattr(pads.dq, "oe") else len(pads.dq.o)
|
||||||
|
@ -50,7 +84,7 @@ class HyperRAM(LiteXModule):
|
||||||
|
|
||||||
# Rst.
|
# Rst.
|
||||||
if hasattr(pads, "rst_n"):
|
if hasattr(pads, "rst_n"):
|
||||||
self.comb += pads.rst_n.eq(1)
|
self.comb += pads.rst_n.eq(1 & ~self.conf_rst)
|
||||||
|
|
||||||
# CSn.
|
# CSn.
|
||||||
self.comb += pads.cs_n[0].eq(~cs)
|
self.comb += pads.cs_n[0].eq(~cs)
|
||||||
|
@ -80,16 +114,16 @@ class HyperRAM(LiteXModule):
|
||||||
dqi = Signal(dw)
|
dqi = Signal(dw)
|
||||||
self.sync += dqi.eq(dq.i) # Sample on 90° and 270°
|
self.sync += dqi.eq(dq.i) # Sample on 90° and 270°
|
||||||
self.comb += [
|
self.comb += [
|
||||||
sr_new.eq(Cat(dqi, sr[:-dw])),
|
sr_next.eq(Cat(dqi, sr[:-dw])),
|
||||||
If(ca_active,
|
If(ca_active,
|
||||||
sr_new.eq(Cat(dqi[:8], sr[:-8])) # Only 8-bit during Command/Address.
|
sr_next.eq(Cat(dqi[:8], sr[:-8])) # Only 8-bit during Command/Address.
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
self.sync += If(clk_phase[0] == 0, sr.eq(sr_new)) # Shift on 0° and 180°
|
self.sync += If(clk_phase[0] == 0, sr.eq(sr_next)) # Shift on 0° and 180°
|
||||||
|
|
||||||
# Data Shift-Out Register ------------------------------------------------------------------
|
# Data Shift-Out Register ------------------------------------------------------------------
|
||||||
self.comb += [
|
self.comb += [
|
||||||
bus.dat_r.eq(sr_new),
|
bus.dat_r.eq(sr_next),
|
||||||
If(dq.oe,
|
If(dq.oe,
|
||||||
dq.o.eq(sr[-dw:]),
|
dq.o.eq(sr[-dw:]),
|
||||||
If(ca_active,
|
If(ca_active,
|
||||||
|
@ -98,41 +132,73 @@ class HyperRAM(LiteXModule):
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Register Access/Buffer -------------------------------------------------------------------
|
||||||
|
reg_write_req = Signal()
|
||||||
|
reg_read_req = Signal()
|
||||||
|
self.reg_buf = reg_buf = stream.SyncFIFO(
|
||||||
|
layout = [("write", 1), ("read", 1), ("addr", 4), ("data", 16)],
|
||||||
|
depth = 4,
|
||||||
|
)
|
||||||
|
reg_ep = reg_buf.source
|
||||||
|
self.comb += [
|
||||||
|
reg_buf.sink.valid.eq(self.reg_write | self.reg_read),
|
||||||
|
reg_buf.sink.write.eq(self.reg_write),
|
||||||
|
reg_buf.sink.read.eq(self.reg_read),
|
||||||
|
reg_buf.sink.addr.eq(self.reg_addr),
|
||||||
|
reg_buf.sink.data.eq(self.reg_write_data),
|
||||||
|
reg_write_req.eq(reg_ep.valid & reg_ep.write),
|
||||||
|
reg_read_req.eq( reg_ep.valid & reg_ep.read),
|
||||||
|
]
|
||||||
|
self.sync += If(reg_buf.sink.valid,
|
||||||
|
self.reg_write_done.eq(0),
|
||||||
|
self.reg_read_done.eq(0),
|
||||||
|
)
|
||||||
|
|
||||||
# Command generation -----------------------------------------------------------------------
|
# Command generation -----------------------------------------------------------------------
|
||||||
ashift = {8:1, 16:0}[dw]
|
ashift = {8:1, 16:0}[dw]
|
||||||
self.comb += [
|
self.comb += [
|
||||||
ca[47].eq(~bus.we), # R/W#
|
# Register Command Generation.
|
||||||
ca[45].eq(1), # Burst Type (Linear)
|
If(reg_write_req | reg_read_req,
|
||||||
ca[16:45].eq(bus.adr[3-ashift:]), # Row & Upper Column Address
|
ca[47].eq(reg_ep.read), # R/W#
|
||||||
ca[ashift:3].eq(bus.adr), # Lower Column Address
|
ca[46].eq(1), # Register Space.
|
||||||
|
ca[45].eq(1), # Burst Type (Linear)
|
||||||
|
Case(reg_ep.addr, {
|
||||||
|
0 : ca[0:40].eq(0x00_00_00_00_00), # Identification Register 0 (Read Only).
|
||||||
|
1 : ca[0:40].eq(0x00_00_00_00_01), # Identification Register 1 (Read Only).
|
||||||
|
2 : ca[0:40].eq(0x00_01_00_00_00), # Configuration Register 0.
|
||||||
|
3 : ca[0:40].eq(0x00_01_00_00_01), # Configuration Register 1.
|
||||||
|
}),
|
||||||
|
# Wishbone Command Generation.
|
||||||
|
).Else(
|
||||||
|
ca[47].eq(~bus.we), # R/W#
|
||||||
|
ca[46].eq(0), # Memory Space.
|
||||||
|
ca[45].eq(1), # Burst Type (Linear)
|
||||||
|
ca[16:45].eq(bus.adr[3-ashift:]), # Row & Upper Column Address
|
||||||
|
ca[ashift:3].eq(bus.adr), # Lower Column Address
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
# Latency count starts from the middle of the command (thus the -4). In fixed latency mode
|
|
||||||
# (default), latency is 2 x Latency count. We have 4 x sys_clk per RAM clock:
|
|
||||||
latency_cycles = (latency * 2 * 4) - 4
|
|
||||||
|
|
||||||
# Bus Latch --------------------------------------------------------------------------------
|
# Bus Latch --------------------------------------------------------------------------------
|
||||||
bus_adr = Signal(32)
|
bus_adr = Signal(32)
|
||||||
bus_we = Signal()
|
bus_we = Signal()
|
||||||
bus_sel = Signal(4)
|
bus_sel = Signal(4)
|
||||||
bus_latch = Signal()
|
bus_latch = Signal()
|
||||||
self.sync += If(bus_latch,
|
self.sync += If(bus_latch,
|
||||||
If(bus.we,
|
If(bus.we, sr.eq(Cat(Signal(16), bus.dat_w))),
|
||||||
sr.eq(Cat(Signal(16), bus.dat_w)),
|
|
||||||
),
|
|
||||||
bus_we.eq(bus.we),
|
bus_we.eq(bus.we),
|
||||||
bus_sel.eq(bus.sel),
|
bus_sel.eq(bus.sel),
|
||||||
bus_adr.eq(bus.adr)
|
bus_adr.eq(bus.adr)
|
||||||
)
|
)
|
||||||
|
|
||||||
# FSM (Sequencer) --------------------------------------------------------------------------
|
# FSM (Sequencer) --------------------------------------------------------------------------
|
||||||
cycles = Signal(8)
|
cycles = Signal(8)
|
||||||
first = Signal()
|
first = Signal()
|
||||||
|
refresh = Signal()
|
||||||
self.fsm = fsm = FSM(reset_state="IDLE")
|
self.fsm = fsm = FSM(reset_state="IDLE")
|
||||||
fsm.act("IDLE",
|
fsm.act("IDLE",
|
||||||
NextValue(first, 1),
|
NextValue(first, 1),
|
||||||
If(bus.cyc & bus.stb,
|
If(clk_phase == 0,
|
||||||
If(clk_phase == 0,
|
If((bus.cyc & bus.stb) | reg_write_req | reg_read_req,
|
||||||
NextValue(sr, ca),
|
NextValue(sr, ca),
|
||||||
NextState("SEND-COMMAND-ADDRESS")
|
NextState("SEND-COMMAND-ADDRESS")
|
||||||
)
|
)
|
||||||
|
@ -146,18 +212,53 @@ class HyperRAM(LiteXModule):
|
||||||
dq.oe.eq(1),
|
dq.oe.eq(1),
|
||||||
# Wait for 6*2 cycles...
|
# Wait for 6*2 cycles...
|
||||||
If(cycles == (6*2 - 1),
|
If(cycles == (6*2 - 1),
|
||||||
NextState("WAIT-LATENCY")
|
If(reg_write_req,
|
||||||
|
NextValue(sr, Cat(Signal(40), self.reg_write_data[8:])),
|
||||||
|
NextState("REG-WRITE-0")
|
||||||
|
).Else(
|
||||||
|
# Sample RWDS to know if 1X/2X Latency should be used (Refresh).
|
||||||
|
NextValue(refresh, rwds.i | (latency_mode in ["fixed"])),
|
||||||
|
NextState("WAIT-LATENCY")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
fsm.act("REG-WRITE-0",
|
||||||
|
# Set CSn.
|
||||||
|
cs.eq(1),
|
||||||
|
# Send Reg on DQ.
|
||||||
|
ca_active.eq(1),
|
||||||
|
dq.oe.eq(1),
|
||||||
|
# Wait for 2 cycles...
|
||||||
|
If(cycles == (2 - 1),
|
||||||
|
NextValue(sr, Cat(Signal(40), self.reg_write_data[:8])),
|
||||||
|
NextState("REG-WRITE-1")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
fsm.act("REG-WRITE-1",
|
||||||
|
# Set CSn.
|
||||||
|
cs.eq(1),
|
||||||
|
# Send Reg on DQ.
|
||||||
|
ca_active.eq(1),
|
||||||
|
dq.oe.eq(1),
|
||||||
|
# Wait for 2 cycles...
|
||||||
|
If(cycles == (2 - 1),
|
||||||
|
reg_ep.ready.eq(1),
|
||||||
|
NextValue(self.reg_write_done, 1),
|
||||||
|
NextState("IDLE")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
fsm.act("WAIT-LATENCY",
|
fsm.act("WAIT-LATENCY",
|
||||||
# Set CSn.
|
# Set CSn.
|
||||||
cs.eq(1),
|
cs.eq(1),
|
||||||
# Wait for Latency cycles...
|
# Wait for 1X or 2X Latency cycles... (-4 since count start in the middle of the command).
|
||||||
If(cycles == (latency_cycles - 1),
|
If(((cycles == 2*(self.conf_latency * 4) - 4 - 1) & refresh) | # 2X Latency (No DRAM refresh required).
|
||||||
|
((cycles == 1*(self.conf_latency * 4) - 4 - 1) & ~refresh) , # 1X Latency ( DRAM refresh required).
|
||||||
# Latch Bus.
|
# Latch Bus.
|
||||||
bus_latch.eq(1),
|
bus_latch.eq(1),
|
||||||
# Early Write Ack (to allow bursting).
|
# Early Write Ack (to allow bursting).
|
||||||
bus.ack.eq(bus.we),
|
If(~reg_read_req,
|
||||||
|
bus.ack.eq(bus.we),
|
||||||
|
),
|
||||||
NextState("READ-WRITE-DATA0")
|
NextState("READ-WRITE-DATA0")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -168,6 +269,7 @@ class HyperRAM(LiteXModule):
|
||||||
burst_timer.wait.eq(1),
|
burst_timer.wait.eq(1),
|
||||||
# Set CSn.
|
# Set CSn.
|
||||||
cs.eq(1),
|
cs.eq(1),
|
||||||
|
ca_active.eq(reg_read_req),
|
||||||
# Send Data on DQ/RWDS (for write).
|
# Send Data on DQ/RWDS (for write).
|
||||||
If(bus_we,
|
If(bus_we,
|
||||||
dq.oe.eq(1),
|
dq.oe.eq(1),
|
||||||
|
@ -182,7 +284,7 @@ class HyperRAM(LiteXModule):
|
||||||
If(n == (states - 1),
|
If(n == (states - 1),
|
||||||
NextValue(first, 0),
|
NextValue(first, 0),
|
||||||
# Continue burst when a consecutive access is ready.
|
# Continue burst when a consecutive access is ready.
|
||||||
If(bus.stb & bus.cyc & (bus.we == bus_we) & (bus.adr == (bus_adr + 1)) & (~burst_timer.done),
|
If(~reg_read_req & bus.stb & bus.cyc & (bus.we == bus_we) & (bus.adr == (bus_adr + 1)) & (~burst_timer.done),
|
||||||
# Latch Bus.
|
# Latch Bus.
|
||||||
bus_latch.eq(1),
|
bus_latch.eq(1),
|
||||||
# Early Write Ack (to allow bursting).
|
# Early Write Ack (to allow bursting).
|
||||||
|
@ -194,7 +296,14 @@ class HyperRAM(LiteXModule):
|
||||||
),
|
),
|
||||||
# Read Ack (when dat_r ready).
|
# Read Ack (when dat_r ready).
|
||||||
If((n == 0) & ~first,
|
If((n == 0) & ~first,
|
||||||
bus.ack.eq(~bus_we),
|
If(reg_read_req,
|
||||||
|
reg_ep.ready.eq(1),
|
||||||
|
NextValue(self.reg_read_done, 1),
|
||||||
|
NextValue(self.reg_read_data, bus.dat_r),
|
||||||
|
NextState("IDLE"),
|
||||||
|
).Else(
|
||||||
|
bus.ack.eq(~bus_we),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -206,3 +315,49 @@ class HyperRAM(LiteXModule):
|
||||||
t = TSTriple(len(pad))
|
t = TSTriple(len(pad))
|
||||||
self.specials += t.get_tristate(pad)
|
self.specials += t.get_tristate(pad)
|
||||||
return t
|
return t
|
||||||
|
|
||||||
|
def add_csr(self, default_latency=6):
|
||||||
|
# Config Interface.
|
||||||
|
# -----------------
|
||||||
|
self.config = CSRStorage(fields=[
|
||||||
|
CSRField("rst", offset=0, size=1, pulse=True, description="HyperRAM Rst."),
|
||||||
|
CSRField("latency", offset=8, size=8, description="HyperRAM Latency (X1).", reset=default_latency),
|
||||||
|
])
|
||||||
|
self.comb += [
|
||||||
|
self.conf_rst.eq( self.config.fields.rst),
|
||||||
|
self.conf_latency.eq(self.config.fields.latency),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Reg Interface.
|
||||||
|
# --------------
|
||||||
|
self.reg_control = CSRStorage(fields=[
|
||||||
|
CSRField("write", offset=0, size=1, pulse=True, description="Issue Register Write."),
|
||||||
|
CSRField("read", offset=1, size=1, pulse=True, description="Issue Register Read."),
|
||||||
|
CSRField("addr", offset=8, size=4, values=[
|
||||||
|
("``0b00``", "Identification Register 0 (Read Only)."),
|
||||||
|
("``0b01``", "Identification Register 1 (Read Only)."),
|
||||||
|
("``0b10``", "Configuration Register 0."),
|
||||||
|
("``0b11``", "Configuration Register 1."),
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
self.reg_status = CSRStatus(fields=[
|
||||||
|
CSRField("write_done", offset=0, size=1, description="Register Write Done."),
|
||||||
|
CSRField("read_done", offset=1, size=1, description="Register Read Done."),
|
||||||
|
])
|
||||||
|
self.reg_wdata = CSRStorage(16, description="Register Write Data.")
|
||||||
|
self.reg_rdata = CSRStatus( 16, description="Register Read Data.")
|
||||||
|
|
||||||
|
self.comb += [
|
||||||
|
# Control.
|
||||||
|
self.reg_write.eq(self.reg_control.fields.write),
|
||||||
|
self.reg_read.eq( self.reg_control.fields.read),
|
||||||
|
self.reg_addr.eq( self.reg_control.fields.addr),
|
||||||
|
|
||||||
|
# Status.
|
||||||
|
self.reg_status.fields.write_done.eq(self.reg_write_done),
|
||||||
|
self.reg_status.fields.read_done.eq( self.reg_read_done),
|
||||||
|
|
||||||
|
# Data.
|
||||||
|
self.reg_write_data.eq(self.reg_wdata.storage),
|
||||||
|
self.reg_rdata.status.eq(self.reg_read_data),
|
||||||
|
]
|
||||||
|
|
|
@ -175,6 +175,87 @@ __attribute__((__used__)) int main(int i, char **c)
|
||||||
|
|
||||||
sdr_ok = 1;
|
sdr_ok = 1;
|
||||||
|
|
||||||
|
#ifdef CSR_HYPERRAM_BASE /* FIXME: Move to libbase/hyperram.h/c? */
|
||||||
|
/* Helper Functions */
|
||||||
|
|
||||||
|
printf("HyperRAM init...\n");
|
||||||
|
void hyperram_write_reg(uint16_t reg_addr, uint16_t data) {
|
||||||
|
/* Write data to the register */
|
||||||
|
hyperram_reg_wdata_write(data);
|
||||||
|
hyperram_reg_control_write(
|
||||||
|
1 << CSR_HYPERRAM_REG_CONTROL_WRITE_OFFSET |
|
||||||
|
0 << CSR_HYPERRAM_REG_CONTROL_READ_OFFSET |
|
||||||
|
reg_addr << CSR_HYPERRAM_REG_CONTROL_ADDR_OFFSET
|
||||||
|
);
|
||||||
|
/* Wait for write to complete */
|
||||||
|
while ((hyperram_reg_status_read() & (1 << CSR_HYPERRAM_REG_STATUS_WRITE_DONE_OFFSET)) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t hyperram_read_reg(uint16_t reg_addr) {
|
||||||
|
/* Read data from the register */
|
||||||
|
hyperram_reg_control_write(
|
||||||
|
0 << CSR_HYPERRAM_REG_CONTROL_WRITE_OFFSET |
|
||||||
|
1 << CSR_HYPERRAM_REG_CONTROL_READ_OFFSET |
|
||||||
|
reg_addr << CSR_HYPERRAM_REG_CONTROL_ADDR_OFFSET
|
||||||
|
);
|
||||||
|
/* Wait for read to complete */
|
||||||
|
while ((hyperram_reg_status_read() & (1 << CSR_HYPERRAM_REG_STATUS_READ_DONE_OFFSET)) == 0);
|
||||||
|
return hyperram_reg_rdata_read();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Configuration and Utility Functions */
|
||||||
|
|
||||||
|
uint16_t hyperram_get_core_latency_setting(uint32_t clk_freq) {
|
||||||
|
/* Raw clock latency settings for the HyperRAM core */
|
||||||
|
if (clk_freq <= 85000000) return 3; /* 3 Clock Latency */
|
||||||
|
if (clk_freq <= 104000000) return 4; /* 4 Clock Latency */
|
||||||
|
if (clk_freq <= 133000000) return 5; /* 5 Clock Latency */
|
||||||
|
if (clk_freq <= 166000000) return 6; /* 6 Clock Latency */
|
||||||
|
if (clk_freq <= 250000000) return 7; /* 7 Clock Latency */
|
||||||
|
return 7; /* Default to highest latency for safety */
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t hyperram_get_chip_latency_setting(uint32_t clk_freq) {
|
||||||
|
/* LUT/Translated settings for the HyperRAM chip */
|
||||||
|
if (clk_freq <= 85000000) return 0b1110; /* 3 Clock Latency */
|
||||||
|
if (clk_freq <= 104000000) return 0b1111; /* 4 Clock Latency */
|
||||||
|
if (clk_freq <= 133000000) return 0b0000; /* 5 Clock Latency */
|
||||||
|
if (clk_freq <= 166000000) return 0b0001; /* 6 Clock Latency */
|
||||||
|
if (clk_freq <= 250000000) return 0b0010; /* 7 Clock Latency */
|
||||||
|
return 0b0010; /* Default to highest latency for safety */
|
||||||
|
}
|
||||||
|
|
||||||
|
void hyperram_configure_latency(void) {
|
||||||
|
uint16_t config_reg_0 = 0x8f2f;
|
||||||
|
uint16_t core_latency_setting;
|
||||||
|
uint16_t chip_latency_setting;
|
||||||
|
|
||||||
|
/* Compute Latency settings */
|
||||||
|
core_latency_setting = hyperram_get_core_latency_setting(CONFIG_CLOCK_FREQUENCY/4);
|
||||||
|
chip_latency_setting = hyperram_get_chip_latency_setting(CONFIG_CLOCK_FREQUENCY/4);
|
||||||
|
|
||||||
|
/* Write Latency to HyperRAM Core */
|
||||||
|
printf("HyperRAM Core Latency: %d CK (X1).\n", core_latency_setting);
|
||||||
|
hyperram_config_write(core_latency_setting << CSR_HYPERRAM_CONFIG_LATENCY_OFFSET);
|
||||||
|
|
||||||
|
/* Enable Variable Latency on HyperRAM Chip */
|
||||||
|
config_reg_0 &= ~(0b1 << 3); /* Enable Variable Latency */
|
||||||
|
|
||||||
|
/* Update Latency on HyperRAM Chip */
|
||||||
|
config_reg_0 &= ~(0b1111 << 4);
|
||||||
|
config_reg_0 |= chip_latency_setting << 4;
|
||||||
|
|
||||||
|
/* Write Configuration Register 0 to HyperRAM Chip */
|
||||||
|
hyperram_write_reg(2, config_reg_0);
|
||||||
|
|
||||||
|
/* Read current configuration */
|
||||||
|
config_reg_0 = hyperram_read_reg(2);
|
||||||
|
printf("HyperRAM Configuration Register 0: %08x\n", config_reg_0);
|
||||||
|
}
|
||||||
|
hyperram_configure_latency();
|
||||||
|
printf("\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(CSR_ETHMAC_BASE) || defined(MAIN_RAM_BASE) || defined(CSR_SPIFLASH_CORE_BASE)
|
#if defined(CSR_ETHMAC_BASE) || defined(MAIN_RAM_BASE) || defined(CSR_SPIFLASH_CORE_BASE)
|
||||||
printf("--========== \e[1mInitialization\e[0m ============--\n");
|
printf("--========== \e[1mInitialization\e[0m ============--\n");
|
||||||
#ifdef CSR_ETHMAC_BASE
|
#ifdef CSR_ETHMAC_BASE
|
||||||
|
|
|
@ -33,7 +33,33 @@ class TestHyperBus(unittest.TestCase):
|
||||||
pads = Record([("clk_p", 1), ("clk_n", 1), ("cs_n", 1), ("dq", 8), ("rwds", 1)])
|
pads = Record([("clk_p", 1), ("clk_n", 1), ("cs_n", 1), ("dq", 8), ("rwds", 1)])
|
||||||
hyperram = HyperRAM(pads)
|
hyperram = HyperRAM(pads)
|
||||||
|
|
||||||
def test_hyperram_write(self):
|
def test_hyperram_write_latency_5_2x(self):
|
||||||
|
def fpga_gen(dut):
|
||||||
|
yield from dut.bus.write(0x1234, 0xdeadbeef, sel=0b1001)
|
||||||
|
yield
|
||||||
|
|
||||||
|
def hyperram_gen(dut):
|
||||||
|
clk = "___--__--__--__--__--__--__--__--__--__--__--__--__--__--_______"
|
||||||
|
cs_n = "--________________________________________________________------"
|
||||||
|
dq_oe = "__------------____________________________________--------______"
|
||||||
|
dq_o = "002000048d0000000000000000000000000000000000000000deadbeef000000"
|
||||||
|
rwds_oe = "__________________________________________________--------______"
|
||||||
|
rwds_o = "____________________________________________________----________"
|
||||||
|
for i in range(3):
|
||||||
|
yield
|
||||||
|
for i in range(len(clk)):
|
||||||
|
self.assertEqual(c2bool(clk[i]), (yield dut.pads.clk))
|
||||||
|
self.assertEqual(c2bool(cs_n[i]), (yield dut.pads.cs_n))
|
||||||
|
self.assertEqual(c2bool(dq_oe[i]), (yield dut.pads.dq.oe))
|
||||||
|
self.assertEqual(int(dq_o[2*(i//2):2*(i//2)+2], 16), (yield dut.pads.dq.o))
|
||||||
|
self.assertEqual(c2bool(rwds_oe[i]), (yield dut.pads.rwds.oe))
|
||||||
|
self.assertEqual(c2bool(rwds_o[i]), (yield dut.pads.rwds.o))
|
||||||
|
yield
|
||||||
|
|
||||||
|
dut = HyperRAM(HyperRamPads(), latency=5)
|
||||||
|
run_simulation(dut, [fpga_gen(dut), hyperram_gen(dut)], vcd_name="sim.vcd")
|
||||||
|
|
||||||
|
def test_hyperram_write_latency_6_2x(self):
|
||||||
def fpga_gen(dut):
|
def fpga_gen(dut):
|
||||||
yield from dut.bus.write(0x1234, 0xdeadbeef, sel=0b1001)
|
yield from dut.bus.write(0x1234, 0xdeadbeef, sel=0b1001)
|
||||||
yield
|
yield
|
||||||
|
@ -56,10 +82,90 @@ class TestHyperBus(unittest.TestCase):
|
||||||
self.assertEqual(c2bool(rwds_o[i]), (yield dut.pads.rwds.o))
|
self.assertEqual(c2bool(rwds_o[i]), (yield dut.pads.rwds.o))
|
||||||
yield
|
yield
|
||||||
|
|
||||||
dut = HyperRAM(HyperRamPads())
|
dut = HyperRAM(HyperRamPads(), latency=6)
|
||||||
run_simulation(dut, [fpga_gen(dut), hyperram_gen(dut)], vcd_name="sim.vcd")
|
run_simulation(dut, [fpga_gen(dut), hyperram_gen(dut)], vcd_name="sim.vcd")
|
||||||
|
|
||||||
def test_hyperram_read(self):
|
def test_hyperram_write_latency_7_2x(self):
|
||||||
|
def fpga_gen(dut):
|
||||||
|
yield from dut.bus.write(0x1234, 0xdeadbeef, sel=0b1001)
|
||||||
|
yield
|
||||||
|
|
||||||
|
def hyperram_gen(dut):
|
||||||
|
clk = "___--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--_______"
|
||||||
|
cs_n = "--________________________________________________________________________------"
|
||||||
|
dq_oe = "__------------____________________________________________________--------______"
|
||||||
|
dq_o = "002000048d00000000000000000000000000000000000000000000000000000000deadbeef000000"
|
||||||
|
rwds_oe = "__________________________________________________________________--------______"
|
||||||
|
rwds_o = "____________________________________________________________________----________"
|
||||||
|
for i in range(3):
|
||||||
|
yield
|
||||||
|
for i in range(len(clk)):
|
||||||
|
self.assertEqual(c2bool(clk[i]), (yield dut.pads.clk))
|
||||||
|
self.assertEqual(c2bool(cs_n[i]), (yield dut.pads.cs_n))
|
||||||
|
self.assertEqual(c2bool(dq_oe[i]), (yield dut.pads.dq.oe))
|
||||||
|
self.assertEqual(int(dq_o[2*(i//2):2*(i//2)+2], 16), (yield dut.pads.dq.o))
|
||||||
|
self.assertEqual(c2bool(rwds_oe[i]), (yield dut.pads.rwds.oe))
|
||||||
|
self.assertEqual(c2bool(rwds_o[i]), (yield dut.pads.rwds.o))
|
||||||
|
yield
|
||||||
|
|
||||||
|
dut = HyperRAM(HyperRamPads(), latency=7)
|
||||||
|
run_simulation(dut, [fpga_gen(dut), hyperram_gen(dut)], vcd_name="sim.vcd")
|
||||||
|
|
||||||
|
def test_hyperram_write_latency_7_1x(self):
|
||||||
|
def fpga_gen(dut):
|
||||||
|
yield from dut.bus.write(0x1234, 0xdeadbeef, sel=0b1001)
|
||||||
|
yield
|
||||||
|
|
||||||
|
def hyperram_gen(dut):
|
||||||
|
clk = "___--__--__--__--__--__--__--__--__--__--__--_______"
|
||||||
|
cs_n = "--____________________________________________------"
|
||||||
|
dq_oe = "__------------________________________--------______"
|
||||||
|
dq_o = "002000048d0000000000000000000000000000deadbeef000000"
|
||||||
|
rwds_oe = "______________________________________--------______"
|
||||||
|
rwds_o = "________________________________________----________"
|
||||||
|
for i in range(3):
|
||||||
|
yield
|
||||||
|
for i in range(len(clk)):
|
||||||
|
self.assertEqual(c2bool(clk[i]), (yield dut.pads.clk))
|
||||||
|
self.assertEqual(c2bool(cs_n[i]), (yield dut.pads.cs_n))
|
||||||
|
self.assertEqual(c2bool(dq_oe[i]), (yield dut.pads.dq.oe))
|
||||||
|
self.assertEqual(int(dq_o[2*(i//2):2*(i//2)+2], 16), (yield dut.pads.dq.o))
|
||||||
|
self.assertEqual(c2bool(rwds_oe[i]), (yield dut.pads.rwds.oe))
|
||||||
|
self.assertEqual(c2bool(rwds_o[i]), (yield dut.pads.rwds.o))
|
||||||
|
yield
|
||||||
|
|
||||||
|
dut = HyperRAM(HyperRamPads(), latency=7, latency_mode="variable")
|
||||||
|
run_simulation(dut, [fpga_gen(dut), hyperram_gen(dut)], vcd_name="sim.vcd")
|
||||||
|
|
||||||
|
def test_hyperram_read_latency_5_2x(self):
|
||||||
|
def fpga_gen(dut):
|
||||||
|
dat = yield from dut.bus.read(0x1234)
|
||||||
|
self.assertEqual(dat, 0xdeadbeef)
|
||||||
|
dat = yield from dut.bus.read(0x1235)
|
||||||
|
self.assertEqual(dat, 0xcafefade)
|
||||||
|
|
||||||
|
def hyperram_gen(dut):
|
||||||
|
clk = "___--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--_"
|
||||||
|
cs_n = "--________________________________________________________________________"
|
||||||
|
dq_oe = "__------------____________________________________________________________"
|
||||||
|
dq_o = "00a000048d0000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
dq_i = "00000000000000000000000000000000000000000000000000deadbeefcafefade00000000"
|
||||||
|
rwds_oe = "__________________________________________________________________________"
|
||||||
|
for i in range(3):
|
||||||
|
yield
|
||||||
|
for i in range(len(clk)):
|
||||||
|
yield dut.pads.dq.i.eq(int(dq_i[2*(i//2):2*(i//2)+2], 16))
|
||||||
|
self.assertEqual(c2bool(clk[i]), (yield dut.pads.clk))
|
||||||
|
self.assertEqual(c2bool(cs_n[i]), (yield dut.pads.cs_n))
|
||||||
|
self.assertEqual(c2bool(dq_oe[i]), (yield dut.pads.dq.oe))
|
||||||
|
self.assertEqual(int(dq_o[2*(i//2):2*(i//2)+2], 16), (yield dut.pads.dq.o))
|
||||||
|
self.assertEqual(c2bool(rwds_oe[i]), (yield dut.pads.rwds.oe))
|
||||||
|
yield
|
||||||
|
|
||||||
|
dut = HyperRAM(HyperRamPads(), latency=5)
|
||||||
|
run_simulation(dut, [fpga_gen(dut), hyperram_gen(dut)], vcd_name="sim.vcd")
|
||||||
|
|
||||||
|
def test_hyperram_read_latency_6_2x(self):
|
||||||
def fpga_gen(dut):
|
def fpga_gen(dut):
|
||||||
dat = yield from dut.bus.read(0x1234)
|
dat = yield from dut.bus.read(0x1234)
|
||||||
self.assertEqual(dat, 0xdeadbeef)
|
self.assertEqual(dat, 0xdeadbeef)
|
||||||
|
@ -84,5 +190,93 @@ class TestHyperBus(unittest.TestCase):
|
||||||
self.assertEqual(c2bool(rwds_oe[i]), (yield dut.pads.rwds.oe))
|
self.assertEqual(c2bool(rwds_oe[i]), (yield dut.pads.rwds.oe))
|
||||||
yield
|
yield
|
||||||
|
|
||||||
dut = HyperRAM(HyperRamPads())
|
dut = HyperRAM(HyperRamPads(), latency=6)
|
||||||
run_simulation(dut, [fpga_gen(dut), hyperram_gen(dut)], vcd_name="sim.vcd")
|
run_simulation(dut, [fpga_gen(dut), hyperram_gen(dut)], vcd_name="sim.vcd")
|
||||||
|
|
||||||
|
def test_hyperram_read_latency_7_2x(self):
|
||||||
|
def fpga_gen(dut):
|
||||||
|
dat = yield from dut.bus.read(0x1234)
|
||||||
|
self.assertEqual(dat, 0xdeadbeef)
|
||||||
|
dat = yield from dut.bus.read(0x1235)
|
||||||
|
self.assertEqual(dat, 0xcafefade)
|
||||||
|
|
||||||
|
def hyperram_gen(dut):
|
||||||
|
clk = "___--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--_"
|
||||||
|
cs_n = "--________________________________________________________________________________________"
|
||||||
|
dq_oe = "__------------____________________________________________________________________________"
|
||||||
|
dq_o = "00a000048d00000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
dq_i = "000000000000000000000000000000000000000000000000000000000000000000deadbeefcafefade00000000"
|
||||||
|
rwds_oe = "__________________________________________________________________________________________"
|
||||||
|
for i in range(3):
|
||||||
|
yield
|
||||||
|
for i in range(len(clk)):
|
||||||
|
yield dut.pads.dq.i.eq(int(dq_i[2*(i//2):2*(i//2)+2], 16))
|
||||||
|
self.assertEqual(c2bool(clk[i]), (yield dut.pads.clk))
|
||||||
|
self.assertEqual(c2bool(cs_n[i]), (yield dut.pads.cs_n))
|
||||||
|
self.assertEqual(c2bool(dq_oe[i]), (yield dut.pads.dq.oe))
|
||||||
|
self.assertEqual(int(dq_o[2*(i//2):2*(i//2)+2], 16), (yield dut.pads.dq.o))
|
||||||
|
self.assertEqual(c2bool(rwds_oe[i]), (yield dut.pads.rwds.oe))
|
||||||
|
yield
|
||||||
|
|
||||||
|
dut = HyperRAM(HyperRamPads(), latency=7)
|
||||||
|
run_simulation(dut, [fpga_gen(dut), hyperram_gen(dut)], vcd_name="sim.vcd")
|
||||||
|
|
||||||
|
def test_hyperram_read_latency_7_1x(self):
|
||||||
|
def fpga_gen(dut):
|
||||||
|
dat = yield from dut.bus.read(0x1234)
|
||||||
|
self.assertEqual(dat, 0xdeadbeef)
|
||||||
|
dat = yield from dut.bus.read(0x1235)
|
||||||
|
self.assertEqual(dat, 0xcafefade)
|
||||||
|
|
||||||
|
def hyperram_gen(dut):
|
||||||
|
clk = "___--__--__--__--__--__--__--__--__--__--__--__--__--__--__--_"
|
||||||
|
cs_n = "--____________________________________________________________"
|
||||||
|
dq_oe = "__------------________________________________________________"
|
||||||
|
dq_o = "00a000048d0000000000000000000000000000000000000000000000000000"
|
||||||
|
dq_i = "00000000000000000000000000000000000000deadbeefcafefade00000000"
|
||||||
|
rwds_oe = "______________________________________________________________"
|
||||||
|
for i in range(3):
|
||||||
|
yield
|
||||||
|
for i in range(len(clk)):
|
||||||
|
yield dut.pads.dq.i.eq(int(dq_i[2*(i//2):2*(i//2)+2], 16))
|
||||||
|
self.assertEqual(c2bool(clk[i]), (yield dut.pads.clk))
|
||||||
|
self.assertEqual(c2bool(cs_n[i]), (yield dut.pads.cs_n))
|
||||||
|
self.assertEqual(c2bool(dq_oe[i]), (yield dut.pads.dq.oe))
|
||||||
|
self.assertEqual(int(dq_o[2*(i//2):2*(i//2)+2], 16), (yield dut.pads.dq.o))
|
||||||
|
self.assertEqual(c2bool(rwds_oe[i]), (yield dut.pads.rwds.oe))
|
||||||
|
yield
|
||||||
|
|
||||||
|
dut = HyperRAM(HyperRamPads(), latency=7, latency_mode="variable")
|
||||||
|
run_simulation(dut, [fpga_gen(dut), hyperram_gen(dut)], vcd_name="sim.vcd")
|
||||||
|
|
||||||
|
def test_hyperram_reg_write(self):
|
||||||
|
def fpga_gen(dut):
|
||||||
|
yield dut.reg_addr.eq(2)
|
||||||
|
yield dut.reg_write_data.eq(0x1234)
|
||||||
|
yield
|
||||||
|
yield dut.reg_write.eq(1)
|
||||||
|
yield
|
||||||
|
yield dut.reg_write.eq(0)
|
||||||
|
for i in range(128):
|
||||||
|
yield
|
||||||
|
|
||||||
|
def hyperram_gen(dut):
|
||||||
|
clk = "___--__--__--__--___________"
|
||||||
|
cs_n = "--________________----------"
|
||||||
|
dq_oe = "__----------------__________"
|
||||||
|
dq_o = "0060000100000012340000000000"
|
||||||
|
rwds_oe = "____________________________"
|
||||||
|
rwds_o = "____________________________"
|
||||||
|
for i in range(3):
|
||||||
|
yield
|
||||||
|
for i in range(len(clk)):
|
||||||
|
self.assertEqual(c2bool(clk[i]), (yield dut.pads.clk))
|
||||||
|
self.assertEqual(c2bool(cs_n[i]), (yield dut.pads.cs_n))
|
||||||
|
self.assertEqual(c2bool(dq_oe[i]), (yield dut.pads.dq.oe))
|
||||||
|
self.assertEqual(int(dq_o[2*(i//2):2*(i//2)+2], 16), (yield dut.pads.dq.o))
|
||||||
|
self.assertEqual(c2bool(rwds_oe[i]), (yield dut.pads.rwds.oe))
|
||||||
|
self.assertEqual(c2bool(rwds_o[i]), (yield dut.pads.rwds.o))
|
||||||
|
yield
|
||||||
|
|
||||||
|
dut = HyperRAM(HyperRamPads(), with_csr=False)
|
||||||
|
run_simulation(dut, [fpga_gen(dut), hyperram_gen(dut)], vcd_name="sim.vcd")
|
Loading…
Reference in New Issue