Merge pull request #2045 from enjoy-digital/hyperbus_io_regs
Improve HyperRAM core to allow IO Reg inference.
This commit is contained in:
commit
298a004f08
|
@ -7,6 +7,9 @@
|
||||||
# SPDX-License-Identifier: BSD-2-Clause
|
# SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
|
||||||
from migen import *
|
from migen import *
|
||||||
|
from migen.fhdl.specials import Tristate
|
||||||
|
|
||||||
|
from litex.build.io import SDRTristate
|
||||||
|
|
||||||
from litex.gen import *
|
from litex.gen import *
|
||||||
from litex.gen.genlib.misc import WaitTimer
|
from litex.gen.genlib.misc import WaitTimer
|
||||||
|
@ -41,7 +44,7 @@ class HyperRAM(LiteXModule):
|
||||||
pads (Record) : Platform pads of HyperRAM.
|
pads (Record) : Platform pads of HyperRAM.
|
||||||
bus (wishbone.Interface) : Wishbone Interface.
|
bus (wishbone.Interface) : Wishbone Interface.
|
||||||
"""
|
"""
|
||||||
def __init__(self, pads, latency=6, latency_mode="variable", sys_clk_freq=None, with_csr=True):
|
def __init__(self, pads, latency=6, latency_mode="variable", sys_clk_freq=10e6, 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")
|
||||||
|
|
||||||
|
@ -64,6 +67,8 @@ class HyperRAM(LiteXModule):
|
||||||
|
|
||||||
# Parameters.
|
# Parameters.
|
||||||
# -----------
|
# -----------
|
||||||
|
dw = len(pads.dq) if not hasattr(pads.dq, "oe") else len(pads.dq.o)
|
||||||
|
assert dw in [8, 16]
|
||||||
assert latency_mode in ["fixed", "variable"]
|
assert latency_mode in ["fixed", "variable"]
|
||||||
|
|
||||||
# Internal Signals.
|
# Internal Signals.
|
||||||
|
@ -72,63 +77,104 @@ class HyperRAM(LiteXModule):
|
||||||
clk_phase = Signal(2)
|
clk_phase = Signal(2)
|
||||||
cs = Signal()
|
cs = Signal()
|
||||||
ca = Signal(48)
|
ca = Signal(48)
|
||||||
ca_active = Signal()
|
ca_oe = Signal()
|
||||||
sr = Signal(48)
|
sr = Signal(48)
|
||||||
sr_next = Signal(48)
|
sr_next = Signal(48)
|
||||||
dq = self.add_tristate(pads.dq) if not hasattr(pads.dq, "oe") else pads.dq
|
dq_o = Signal(dw)
|
||||||
rwds = self.add_tristate(pads.rwds) if not hasattr(pads.rwds, "oe") else pads.rwds
|
dq_oe = Signal()
|
||||||
dw = len(pads.dq) if not hasattr(pads.dq, "oe") else len(pads.dq.o)
|
dq_i = Signal(dw)
|
||||||
|
rwds_o = Signal(dw//8)
|
||||||
|
rwds_oe = Signal()
|
||||||
|
rwds_i = Signal(dw//8)
|
||||||
|
|
||||||
assert dw in [8, 16]
|
# Tristates.
|
||||||
|
# ----------
|
||||||
|
dq = self.add_tristate(pads.dq, register=False) if not hasattr(pads.dq, "oe") else pads.dq
|
||||||
|
rwds = self.add_tristate(pads.rwds, register=False) if not hasattr(pads.rwds, "oe") else pads.rwds
|
||||||
|
self.comb += [
|
||||||
|
# DQ O/OE.
|
||||||
|
dq.o.eq( dq_o),
|
||||||
|
dq.oe.eq(dq_oe),
|
||||||
|
|
||||||
|
# RWDS O/OE.
|
||||||
|
rwds.o.eq( rwds_o),
|
||||||
|
rwds.oe.eq(rwds_oe),
|
||||||
|
]
|
||||||
|
self.sync += [
|
||||||
|
# DQ I.
|
||||||
|
dq_i.eq(dq.i),
|
||||||
|
|
||||||
|
# RWDS I.
|
||||||
|
rwds_i.eq(rwds.i)
|
||||||
|
]
|
||||||
|
|
||||||
# Drive Control Signals --------------------------------------------------------------------
|
# Drive Control Signals --------------------------------------------------------------------
|
||||||
|
|
||||||
# Rst.
|
# Rst.
|
||||||
if hasattr(pads, "rst_n"):
|
if hasattr(pads, "rst_n"):
|
||||||
self.comb += pads.rst_n.eq(1 & ~self.conf_rst)
|
self.sync += pads.rst_n.eq(1 & ~self.conf_rst)
|
||||||
|
|
||||||
# CSn.
|
# CSn.
|
||||||
self.comb += pads.cs_n[0].eq(~cs)
|
pads.cs_n.reset = 2**len(pads.cs_n) - 1
|
||||||
assert len(pads.cs_n) <= 2
|
self.sync += pads.cs_n[0].eq(~cs) # Only supporting 1 CS.
|
||||||
if len(pads.cs_n) == 2:
|
|
||||||
self.comb += pads.cs_n[1].eq(1)
|
|
||||||
|
|
||||||
# Clk.
|
# Clk.
|
||||||
|
pads_clk = Signal()
|
||||||
|
self.sync += pads_clk.eq(clk)
|
||||||
if hasattr(pads, "clk"):
|
if hasattr(pads, "clk"):
|
||||||
self.comb += pads.clk.eq(clk)
|
# Single Ended Clk.
|
||||||
|
self.comb += pads.clk.eq(pads_clk)
|
||||||
|
elif hasattr(pads, "clk_p"):
|
||||||
|
# Differential Clk.
|
||||||
|
self.specials += DifferentialOutput(pads_clk, pads.clk_p, pads.clk_n)
|
||||||
else:
|
else:
|
||||||
self.specials += DifferentialOutput(clk, pads.clk_p, pads.clk_n)
|
raise ValueError
|
||||||
|
|
||||||
# Burst Timer ------------------------------------------------------------------------------
|
# Burst Timer ------------------------------------------------------------------------------
|
||||||
sys_clk_freq = 10e6 if sys_clk_freq is None else sys_clk_freq
|
self.burst_timer = burst_timer = WaitTimer(sys_clk_freq * self.tCSM)
|
||||||
burst_timer = WaitTimer(sys_clk_freq*self.tCSM)
|
|
||||||
self.burst_timer = burst_timer
|
|
||||||
|
|
||||||
# Clock Generation (sys_clk/4) -------------------------------------------------------------
|
# Clock Generation (sys_clk/4) -------------------------------------------------------------
|
||||||
self.sync += clk_phase.eq(clk_phase + 1)
|
self.sync += [
|
||||||
cases = {}
|
If(cs,
|
||||||
cases[1] = clk.eq(cs) # Set pads clk on 90° (if cs is set)
|
# Increment Clk Phase on CS.
|
||||||
cases[3] = clk.eq(0) # Clear pads clk on 270°
|
clk_phase.eq(clk_phase + 1)
|
||||||
self.sync += Case(clk_phase, cases)
|
).Else(
|
||||||
|
# Else set Clk Phase to default value.
|
||||||
# Data Shift-In Register -------------------------------------------------------------------
|
clk_phase.eq(0b00)
|
||||||
dqi = Signal(dw)
|
|
||||||
self.sync += dqi.eq(dq.i) # Sample on 90° and 270°
|
|
||||||
self.comb += [
|
|
||||||
sr_next.eq(Cat(dqi, sr[:-dw])),
|
|
||||||
If(ca_active,
|
|
||||||
sr_next.eq(Cat(dqi[:8], sr[:-8])) # Only 8-bit during Command/Address.
|
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
self.sync += If(clk_phase[0] == 0, sr.eq(sr_next)) # Shift on 0° and 180°
|
cases = {
|
||||||
|
0b00 : clk.eq(0), # 0°
|
||||||
|
0b01 : clk.eq(cs), # 90° / Set Clk.
|
||||||
|
0b10 : clk.eq(cs), # 180°
|
||||||
|
0b11 : clk.eq(0), # 270° / Clr Clk.
|
||||||
|
}
|
||||||
|
self.comb += Case(clk_phase, cases)
|
||||||
|
|
||||||
|
# Data Shift-In Register -------------------------------------------------------------------
|
||||||
|
self.comb += [
|
||||||
|
# Command/Address: On 8-bit, so 8-bit shift and no input.
|
||||||
|
If(ca_oe,
|
||||||
|
sr_next[8:].eq(sr),
|
||||||
|
# Data: On dw-bit, so dw-bit shift.
|
||||||
|
).Else(
|
||||||
|
sr_next[:dw].eq(dq_i),
|
||||||
|
sr_next[dw:].eq(sr),
|
||||||
|
|
||||||
|
)
|
||||||
|
]
|
||||||
|
self.sync += If(clk_phase[0] == 0, sr.eq(sr_next)) # Shift on 0°/180° (and sampled on 90°/270°).
|
||||||
|
|
||||||
# Data Shift-Out Register ------------------------------------------------------------------
|
# Data Shift-Out Register ------------------------------------------------------------------
|
||||||
self.comb += [
|
self.comb += [
|
||||||
bus.dat_r.eq(sr_next),
|
bus.dat_r.eq(sr_next),
|
||||||
If(dq.oe,
|
If(dq_oe,
|
||||||
dq.o.eq(sr[-dw:]),
|
# Command/Address: 8-bit.
|
||||||
If(ca_active,
|
If(ca_oe,
|
||||||
dq.o.eq(sr[-8:]) # Only 8-bit during Command/Address.
|
dq_o.eq(sr[-8:]),
|
||||||
|
# Data: dw-bit.
|
||||||
|
).Else(
|
||||||
|
dq_o.eq(sr[-dw:]),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
@ -198,19 +244,15 @@ class HyperRAM(LiteXModule):
|
||||||
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(clk_phase == 0,
|
If((bus.cyc & bus.stb) | reg_write_req | reg_read_req,
|
||||||
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")
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
fsm.act("SEND-COMMAND-ADDRESS",
|
fsm.act("SEND-COMMAND-ADDRESS",
|
||||||
# Set CSn.
|
|
||||||
cs.eq(1),
|
|
||||||
# Send Command on DQ.
|
# Send Command on DQ.
|
||||||
ca_active.eq(1),
|
ca_oe.eq(1),
|
||||||
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),
|
||||||
If(reg_write_req,
|
If(reg_write_req,
|
||||||
|
@ -218,17 +260,15 @@ class HyperRAM(LiteXModule):
|
||||||
NextState("REG-WRITE-0")
|
NextState("REG-WRITE-0")
|
||||||
).Else(
|
).Else(
|
||||||
# Sample RWDS to know if 1X/2X Latency should be used (Refresh).
|
# Sample RWDS to know if 1X/2X Latency should be used (Refresh).
|
||||||
NextValue(refresh, rwds.i | (latency_mode in ["fixed"])),
|
NextValue(refresh, rwds_i | (latency_mode in ["fixed"])),
|
||||||
NextState("WAIT-LATENCY")
|
NextState("WAIT-LATENCY")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
fsm.act("REG-WRITE-0",
|
fsm.act("REG-WRITE-0",
|
||||||
# Set CSn.
|
|
||||||
cs.eq(1),
|
|
||||||
# Send Reg on DQ.
|
# Send Reg on DQ.
|
||||||
ca_active.eq(1),
|
ca_oe.eq(1),
|
||||||
dq.oe.eq(1),
|
dq_oe.eq(1),
|
||||||
# Wait for 2 cycles...
|
# Wait for 2 cycles...
|
||||||
If(cycles == (2 - 1),
|
If(cycles == (2 - 1),
|
||||||
NextValue(sr, Cat(Signal(40), self.reg_write_data[:8])),
|
NextValue(sr, Cat(Signal(40), self.reg_write_data[:8])),
|
||||||
|
@ -236,11 +276,9 @@ class HyperRAM(LiteXModule):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
fsm.act("REG-WRITE-1",
|
fsm.act("REG-WRITE-1",
|
||||||
# Set CSn.
|
|
||||||
cs.eq(1),
|
|
||||||
# Send Reg on DQ.
|
# Send Reg on DQ.
|
||||||
ca_active.eq(1),
|
ca_oe.eq(1),
|
||||||
dq.oe.eq(1),
|
dq_oe.eq(1),
|
||||||
# Wait for 2 cycles...
|
# Wait for 2 cycles...
|
||||||
If(cycles == (2 - 1),
|
If(cycles == (2 - 1),
|
||||||
reg_ep.ready.eq(1),
|
reg_ep.ready.eq(1),
|
||||||
|
@ -249,8 +287,6 @@ class HyperRAM(LiteXModule):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
fsm.act("WAIT-LATENCY",
|
fsm.act("WAIT-LATENCY",
|
||||||
# Set CSn.
|
|
||||||
cs.eq(1),
|
|
||||||
# Wait for 1X or 2X Latency cycles... (-4 since count start in the middle of the command).
|
# Wait for 1X or 2X Latency cycles... (-4 since count start in the middle of the command).
|
||||||
If(((cycles == 2*(self.conf_latency * 4) - 4 - 1) & refresh) | # 2X Latency (No DRAM refresh required).
|
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).
|
((cycles == 1*(self.conf_latency * 4) - 4 - 1) & ~refresh) , # 1X Latency ( DRAM refresh required).
|
||||||
|
@ -268,14 +304,12 @@ class HyperRAM(LiteXModule):
|
||||||
fsm.act(f"READ-WRITE-DATA{n}",
|
fsm.act(f"READ-WRITE-DATA{n}",
|
||||||
# Enable Burst Timer.
|
# Enable Burst Timer.
|
||||||
burst_timer.wait.eq(1),
|
burst_timer.wait.eq(1),
|
||||||
# Set CSn.
|
ca_oe.eq(reg_read_req),
|
||||||
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),
|
||||||
rwds.oe.eq(1),
|
rwds_oe.eq(1),
|
||||||
*[rwds.o[dw//8-1-i].eq(~bus_sel[4-1-n*dw//8-i]) for i in range(dw//8)],
|
*[rwds_o[dw//8-1-i].eq(~bus_sel[4-1-n*dw//8-i]) for i in range(dw//8)],
|
||||||
),
|
),
|
||||||
# Wait for 2 cycles (since HyperRAM's Clk = sys_clk/4).
|
# Wait for 2 cycles (since HyperRAM's Clk = sys_clk/4).
|
||||||
If(cycles == (2 - 1),
|
If(cycles == (2 - 1),
|
||||||
|
@ -308,13 +342,38 @@ class HyperRAM(LiteXModule):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# CS --------------------------------------------------------------------------------------
|
||||||
|
self.comb += If(~fsm.ongoing("IDLE"), cs.eq(1)) # CS when not in IDLE state.
|
||||||
|
self.comb += If(fsm.before_leaving("IDLE"), cs.eq(1)) # Early Set.
|
||||||
|
self.comb += If(fsm.before_entering("IDLE"), cs.eq(0)) # Early Clr.
|
||||||
|
|
||||||
|
# FSM Cycles -------------------------------------------------------------------------------
|
||||||
fsm.finalize()
|
fsm.finalize()
|
||||||
self.sync += cycles.eq(cycles + 1)
|
self.sync += cycles.eq(cycles + 1)
|
||||||
self.sync += If(fsm.next_state != fsm.state, cycles.eq(0))
|
self.sync += If(fsm.next_state != fsm.state, cycles.eq(0))
|
||||||
|
|
||||||
def add_tristate(self, pad):
|
def add_tristate(self, pad, register=False):
|
||||||
t = TSTriple(len(pad))
|
class TristatePads:
|
||||||
self.specials += t.get_tristate(pad)
|
def __init__(self, width):
|
||||||
|
self.o = Signal(len(pad))
|
||||||
|
self.oe = Signal()
|
||||||
|
self.i = Signal(len(pad))
|
||||||
|
t = TristatePads(len(pad))
|
||||||
|
if register:
|
||||||
|
for n in range(len(pad)):
|
||||||
|
self.specials += SDRTristate(pad[n],
|
||||||
|
o = t.o[n],
|
||||||
|
oe = t.oe,
|
||||||
|
i = t.i[n],
|
||||||
|
clk = ClockSignal("sys"),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.specials += Tristate(pad,
|
||||||
|
o = t.o,
|
||||||
|
oe = t.oe,
|
||||||
|
i = t.i,
|
||||||
|
)
|
||||||
return t
|
return t
|
||||||
|
|
||||||
def add_csr(self, default_latency=6):
|
def add_csr(self, default_latency=6):
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
#include <libbase/spiflash.h>
|
#include <libbase/spiflash.h>
|
||||||
#include <libbase/uart.h>
|
#include <libbase/uart.h>
|
||||||
#include <libbase/i2c.h>
|
#include <libbase/i2c.h>
|
||||||
|
#include <libbase/hyperram.h>
|
||||||
|
|
||||||
#include <liblitedram/sdram.h>
|
#include <liblitedram/sdram.h>
|
||||||
#include <liblitedram/utils.h>
|
#include <liblitedram/utils.h>
|
||||||
|
@ -173,88 +174,10 @@ __attribute__((__used__)) int main(int i, char **c)
|
||||||
printf("\n");
|
printf("\n");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
sdr_ok = 1;
|
sdr_ok = 1;
|
||||||
|
|
||||||
#ifdef CSR_HYPERRAM_BASE /* FIXME: Move to libbase/hyperram.h/c? */
|
#ifdef CSR_HYPERRAM_BASE
|
||||||
/* Helper Functions */
|
hyperram_init();
|
||||||
|
|
||||||
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 */
|
|
||||||
if (hyperram_status_read() & 0x1)
|
|
||||||
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
|
#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)
|
||||||
|
|
|
@ -11,7 +11,8 @@ OBJECTS = \
|
||||||
uart.o \
|
uart.o \
|
||||||
spiflash.o \
|
spiflash.o \
|
||||||
i2c.o \
|
i2c.o \
|
||||||
isr.o
|
isr.o \
|
||||||
|
hyperram.o
|
||||||
|
|
||||||
all: libbase.a
|
all: libbase.a
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
// This file is Copyright (c) 2024 Florent Kermarrec <florent@enjoy-digital.fr>
|
||||||
|
// License: BSD
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <libbase/hyperram.h>
|
||||||
|
|
||||||
|
#include <generated/csr.h>
|
||||||
|
|
||||||
|
#ifdef CSR_HYPERRAM_BASE
|
||||||
|
|
||||||
|
static 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
static 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 */
|
||||||
|
|
||||||
|
static 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 */
|
||||||
|
}
|
||||||
|
|
||||||
|
static 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 */
|
||||||
|
}
|
||||||
|
|
||||||
|
static 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 */
|
||||||
|
if (hyperram_status_read() & 0x1)
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hyperram_init(void) {
|
||||||
|
printf("HyperRAM init...\n");
|
||||||
|
hyperram_configure_latency();
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,17 @@
|
||||||
|
// This file is Copyright (c) 2024 Florent Kermarrec <florent@enjoy-digital.fr>
|
||||||
|
// License: BSD
|
||||||
|
|
||||||
|
#ifndef __HYPERRAM_H
|
||||||
|
#define __HYPERRAM_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void hyperram_init(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __HYPERRAM_H */
|
|
@ -45,8 +45,6 @@ class TestHyperBus(unittest.TestCase):
|
||||||
dq_o = "002000048d0000000000000000000000000000000000000000deadbeef000000"
|
dq_o = "002000048d0000000000000000000000000000000000000000deadbeef000000"
|
||||||
rwds_oe = "__________________________________________________--------______"
|
rwds_oe = "__________________________________________________--------______"
|
||||||
rwds_o = "____________________________________________________----________"
|
rwds_o = "____________________________________________________----________"
|
||||||
for i in range(3):
|
|
||||||
yield
|
|
||||||
for i in range(len(clk)):
|
for i in range(len(clk)):
|
||||||
self.assertEqual(c2bool(clk[i]), (yield dut.pads.clk))
|
self.assertEqual(c2bool(clk[i]), (yield dut.pads.clk))
|
||||||
self.assertEqual(c2bool(cs_n[i]), (yield dut.pads.cs_n))
|
self.assertEqual(c2bool(cs_n[i]), (yield dut.pads.cs_n))
|
||||||
|
@ -71,8 +69,6 @@ class TestHyperBus(unittest.TestCase):
|
||||||
dq_o = "002000048d000000000000000000000000000000000000000000000000deadbeef000000"
|
dq_o = "002000048d000000000000000000000000000000000000000000000000deadbeef000000"
|
||||||
rwds_oe = "__________________________________________________________--------______"
|
rwds_oe = "__________________________________________________________--------______"
|
||||||
rwds_o = "____________________________________________________________----________"
|
rwds_o = "____________________________________________________________----________"
|
||||||
for i in range(3):
|
|
||||||
yield
|
|
||||||
for i in range(len(clk)):
|
for i in range(len(clk)):
|
||||||
self.assertEqual(c2bool(clk[i]), (yield dut.pads.clk))
|
self.assertEqual(c2bool(clk[i]), (yield dut.pads.clk))
|
||||||
self.assertEqual(c2bool(cs_n[i]), (yield dut.pads.cs_n))
|
self.assertEqual(c2bool(cs_n[i]), (yield dut.pads.cs_n))
|
||||||
|
@ -97,8 +93,6 @@ class TestHyperBus(unittest.TestCase):
|
||||||
dq_o = "002000048d00000000000000000000000000000000000000000000000000000000deadbeef000000"
|
dq_o = "002000048d00000000000000000000000000000000000000000000000000000000deadbeef000000"
|
||||||
rwds_oe = "__________________________________________________________________--------______"
|
rwds_oe = "__________________________________________________________________--------______"
|
||||||
rwds_o = "____________________________________________________________________----________"
|
rwds_o = "____________________________________________________________________----________"
|
||||||
for i in range(3):
|
|
||||||
yield
|
|
||||||
for i in range(len(clk)):
|
for i in range(len(clk)):
|
||||||
self.assertEqual(c2bool(clk[i]), (yield dut.pads.clk))
|
self.assertEqual(c2bool(clk[i]), (yield dut.pads.clk))
|
||||||
self.assertEqual(c2bool(cs_n[i]), (yield dut.pads.cs_n))
|
self.assertEqual(c2bool(cs_n[i]), (yield dut.pads.cs_n))
|
||||||
|
@ -123,8 +117,6 @@ class TestHyperBus(unittest.TestCase):
|
||||||
dq_o = "002000048d0000000000000000000000000000deadbeef000000"
|
dq_o = "002000048d0000000000000000000000000000deadbeef000000"
|
||||||
rwds_oe = "______________________________________--------______"
|
rwds_oe = "______________________________________--------______"
|
||||||
rwds_o = "________________________________________----________"
|
rwds_o = "________________________________________----________"
|
||||||
for i in range(3):
|
|
||||||
yield
|
|
||||||
for i in range(len(clk)):
|
for i in range(len(clk)):
|
||||||
self.assertEqual(c2bool(clk[i]), (yield dut.pads.clk))
|
self.assertEqual(c2bool(clk[i]), (yield dut.pads.clk))
|
||||||
self.assertEqual(c2bool(cs_n[i]), (yield dut.pads.cs_n))
|
self.assertEqual(c2bool(cs_n[i]), (yield dut.pads.cs_n))
|
||||||
|
@ -151,8 +143,6 @@ class TestHyperBus(unittest.TestCase):
|
||||||
dq_o = "00a000048d0000000000000000000000000000000000000000000000000000000000000000"
|
dq_o = "00a000048d0000000000000000000000000000000000000000000000000000000000000000"
|
||||||
dq_i = "00000000000000000000000000000000000000000000000000deadbeefcafefade00000000"
|
dq_i = "00000000000000000000000000000000000000000000000000deadbeefcafefade00000000"
|
||||||
rwds_oe = "__________________________________________________________________________"
|
rwds_oe = "__________________________________________________________________________"
|
||||||
for i in range(3):
|
|
||||||
yield
|
|
||||||
for i in range(len(clk)):
|
for i in range(len(clk)):
|
||||||
yield dut.pads.dq.i.eq(int(dq_i[2*(i//2):2*(i//2)+2], 16))
|
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(clk[i]), (yield dut.pads.clk))
|
||||||
|
@ -179,8 +169,6 @@ class TestHyperBus(unittest.TestCase):
|
||||||
dq_o = "00a000048d000000000000000000000000000000000000000000000000000000000000000000000000"
|
dq_o = "00a000048d000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||||
dq_i = "0000000000000000000000000000000000000000000000000000000000deadbeefcafefade00000000"
|
dq_i = "0000000000000000000000000000000000000000000000000000000000deadbeefcafefade00000000"
|
||||||
rwds_oe = "__________________________________________________________________________________"
|
rwds_oe = "__________________________________________________________________________________"
|
||||||
for i in range(3):
|
|
||||||
yield
|
|
||||||
for i in range(len(clk)):
|
for i in range(len(clk)):
|
||||||
yield dut.pads.dq.i.eq(int(dq_i[2*(i//2):2*(i//2)+2], 16))
|
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(clk[i]), (yield dut.pads.clk))
|
||||||
|
@ -207,8 +195,6 @@ class TestHyperBus(unittest.TestCase):
|
||||||
dq_o = "00a000048d00000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
dq_o = "00a000048d00000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||||
dq_i = "000000000000000000000000000000000000000000000000000000000000000000deadbeefcafefade00000000"
|
dq_i = "000000000000000000000000000000000000000000000000000000000000000000deadbeefcafefade00000000"
|
||||||
rwds_oe = "__________________________________________________________________________________________"
|
rwds_oe = "__________________________________________________________________________________________"
|
||||||
for i in range(3):
|
|
||||||
yield
|
|
||||||
for i in range(len(clk)):
|
for i in range(len(clk)):
|
||||||
yield dut.pads.dq.i.eq(int(dq_i[2*(i//2):2*(i//2)+2], 16))
|
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(clk[i]), (yield dut.pads.clk))
|
||||||
|
@ -235,8 +221,6 @@ class TestHyperBus(unittest.TestCase):
|
||||||
dq_o = "00a000048d0000000000000000000000000000000000000000000000000000"
|
dq_o = "00a000048d0000000000000000000000000000000000000000000000000000"
|
||||||
dq_i = "00000000000000000000000000000000000000deadbeefcafefade00000000"
|
dq_i = "00000000000000000000000000000000000000deadbeefcafefade00000000"
|
||||||
rwds_oe = "______________________________________________________________"
|
rwds_oe = "______________________________________________________________"
|
||||||
for i in range(3):
|
|
||||||
yield
|
|
||||||
for i in range(len(clk)):
|
for i in range(len(clk)):
|
||||||
yield dut.pads.dq.i.eq(int(dq_i[2*(i//2):2*(i//2)+2], 16))
|
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(clk[i]), (yield dut.pads.clk))
|
||||||
|
@ -261,14 +245,12 @@ class TestHyperBus(unittest.TestCase):
|
||||||
yield
|
yield
|
||||||
|
|
||||||
def hyperram_gen(dut):
|
def hyperram_gen(dut):
|
||||||
clk = "___--__--__--__--___________"
|
clk = "_____--__--__--__--___________"
|
||||||
cs_n = "--________________----------"
|
cs_n = "----________________----------"
|
||||||
dq_oe = "__----------------__________"
|
dq_oe = "____----------------__________"
|
||||||
dq_o = "0060000100000012340000000000"
|
dq_o = "000060000100000012340000000000"
|
||||||
rwds_oe = "____________________________"
|
rwds_oe = "______________________________"
|
||||||
rwds_o = "____________________________"
|
rwds_o = "______________________________"
|
||||||
for i in range(3):
|
|
||||||
yield
|
|
||||||
for i in range(len(clk)):
|
for i in range(len(clk)):
|
||||||
self.assertEqual(c2bool(clk[i]), (yield dut.pads.clk))
|
self.assertEqual(c2bool(clk[i]), (yield dut.pads.clk))
|
||||||
self.assertEqual(c2bool(cs_n[i]), (yield dut.pads.cs_n))
|
self.assertEqual(c2bool(cs_n[i]), (yield dut.pads.cs_n))
|
||||||
|
|
Loading…
Reference in New Issue