From fac80c3a516ca2d096c3ded2d42de60cae7d26e3 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Mon, 26 Aug 2024 11:24:16 +0200 Subject: [PATCH] soc/cores/hyperbus: Full rewrite of HyperRAM core. Rewriting the HyperRAM core to improve its design and functionality. The old core grew complex over time without a clear structure. This new version offers: - IO registers on all signals for better performance. - Flexible clocking options. - Simplified architecture. - Easier to extend with new features. This rewrite provides a base for future development. --- litex/soc/cores/hyperbus.py | 991 ++++++++++++++++---------- litex/soc/software/libbase/hyperram.c | 76 +- litex/soc/software/libbase/hyperram.h | 51 ++ test/test_hyperbus.py | 189 ++--- 4 files changed, 797 insertions(+), 510 deletions(-) diff --git a/litex/soc/cores/hyperbus.py b/litex/soc/cores/hyperbus.py index 3725c33f8..4db0e88df 100644 --- a/litex/soc/cores/hyperbus.py +++ b/litex/soc/cores/hyperbus.py @@ -2,398 +2,600 @@ # This file is part of LiteX. # # Copyright (c) 2019-2024 Florent Kermarrec -# Copyright (c) 2019 Antti Lukats -# Copyright (c) 2021 Franck Jullien +# Copyright (c) 2024 MoTeC # SPDX-License-Identifier: BSD-2-Clause -from migen import * +from migen.genlib.cdc import MultiReg from migen.fhdl.specials import Tristate -from litex.build.io import SDRTristate - from litex.gen import * -from litex.gen.genlib.misc import WaitTimer + +from litex.build.io import SDROutput, SDRInput from litex.soc.interconnect.csr import * -from litex.soc.interconnect import stream +from litex.soc.interconnect import stream +from litex.soc.interconnect import wishbone -from litex.build.io import DifferentialOutput +""" +HyperRAM Core. -from litex.soc.interconnect import wishbone +Provides a HyperRAM Core with PHY, Core logic, and optional CSR interface for LiteX-based systems. +Supports variable latency, configurable clocking (4:1, 2:1), and burst operations. + +Features: +- Variable latency: "fixed" or "variable". +- Configurable clock ratios: 4:1 or 2:1. +- Burst read/write support. +- Wishbone bus interface. +- Optional CSR interface for configuration. +""" + +# HyperRAM Layout ---------------------------------------------------------------------------------- + +# IOs. +# ---- +def hyperam_ios_layout(data_width=8): + """IO layout for HyperRAM PHY.""" + return [ + ("rst_n", 1), + ("clk", 1), + ("cs_n", 1), + ("dq_o", data_width), + ("dq_oe", 1), + ("dq_i", data_width), + ("rwds_o", data_width//8), + ("rwds_oe", 1), + ("rwds_i", data_width//8), + ] + +# PHY. +# ---- +def hyperram_phy_tx_layout(data_width=8): + """Transmit layout for HyperRAM PHY.""" + return [ + ("cmd", 1), + ("dat_w", 1), + ("dat_r", 1), + ("dq", data_width), + ("dq_oe", 1), + ("rwds", data_width//8), + ("rwds_oe", 1), + ] + +def hyperram_phy_rx_layout(data_width=8): + """Receive layout for HyperRAM PHY.""" + return [ + ("dq", data_width), + ] + +# Core. +# ----- +def hyperram_core_tx_layout(data_width=8): + """Transmit layout for HyperRAM Core.""" + return [ + ("dq", data_width), + ("rwds", data_width//8), + ] + +def hyperram_core_rx_layout(data_width=8): + """"Receive layout for HyperRAM Core.""" + return [ + ("dq", data_width), + ] + +# HyperRAM Clk Gen --------------------------------------------------------------------------------- + +class HyperRAMClkGen(LiteXModule): + """ + HyperRAM Clock Generator Module. + + This module generates the necessary clock signals for the HyperRAM at configurable ratios + (4:1, 2:1). It handles phase management and output clock signal generation to synchronize + HyperRAM operations. + """ + def __init__(self): + self.phase = Signal(2) + self.rise = Signal() + self.fall = Signal() + self.cd_hyperram = ClockDomain() + + # # # + + # Clk Phase Generation from 4X Sys Clk. + self.sync += self.phase.eq(self.phase + 1) + self.comb += [ + self.rise.eq(self.phase == 0b11), + self.fall.eq(self.phase == 0b01), + ] + + # HyperRAM Clk Generation. + self.comb += Case(self.phase, { + 0 : self.cd_hyperram.clk.eq(0), + 1 : self.cd_hyperram.clk.eq(1), + 2 : self.cd_hyperram.clk.eq(1), + 3 : self.cd_hyperram.clk.eq(0), + }) + +# HyperRAM SDR PHY --------------------------------------------------------------------------------- + +class HyperRAMSDRPHY(LiteXModule): + """ + HyperRAM Single Data Rate (SDR) PHY Module. + + This module provides a physical interface layer for HyperRAM using a Single Data Rate + (SDR) approach. It manages data transmission and reception, IO connections, and clock + generation for the HyperRAM interface. + + Parameters: + - pads : External pads to connect the PHY signals. + - dq_i_cd : Clock domain for data input signals. + """ + def __init__(self, pads, dq_i_cd="sys"): + self.data_width = data_width = self.get_data_width(pads) + self.sink = sink = stream.Endpoint(hyperram_phy_tx_layout(data_width)) # TX. + self.source = source = stream.Endpoint(hyperram_phy_rx_layout(data_width)) # RX. + self.ios = ios = Record(hyperam_ios_layout(data_width)) # IOs. + + # # # + + # Parameters. + # ----------- + assert data_width in [8, 16] + + # Clk Gen. + # -------- + self.clk_gen = clk_gen = HyperRAMClkGen() + + # Clk/CS/DQ/RWDS Output. + # ---------------------- + self.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + ios.cs_n.eq(1), + If(sink.valid & clk_gen.rise, + NextState("RUN") + ) + ) + fsm.act("RUN", + If(sink.valid, + ios.clk.eq(1), + ios.dq_o.eq( sink.dq), + ios.dq_oe.eq( sink.dq_oe), + ios.rwds_o.eq( sink.rwds), + ios.rwds_oe.eq(sink.rwds_oe), + If(clk_gen.rise | clk_gen.fall, + sink.ready.eq(1) + ), + ).Else( + NextState("END") + ) + ) + fsm.act("END", + source.valid.eq(1), + source.last.eq(1), + If(source.ready, + NextState("IDLE") + ) + ) + + # DQ Input. + # --------- + rwds_i = ios.rwds_i[0] + rwds_i_d = Signal() + dq_i = ios.dq_i + _sync = getattr(self.sync, dq_i_cd) + _sync += rwds_i_d.eq(rwds_i) + self.comb += [ + # When waiting a DQ read... + If(sink.valid & sink.dat_r, + # Sample DQ on RWDS edge. + If(rwds_i ^ rwds_i_d, + source.valid.eq(1), + source.dq.eq(dq_i), + ) + ) + ] + + # Connect IOs to Pads. + # -------------------- + self.connect_to_pads(pads, dq_i_cd) + + def get_data_width(self, pads): + """Returns data width based on pads.""" + if not hasattr(pads.dq, "oe"): + return len(pads.dq) + else: + return len(pads.dq.o) + + def connect_to_pads(self, pads, dq_i_cd): + """Connects PHY signals to external pads.""" + with_tristate = not hasattr(pads, "dq_oe") and not hasattr(pads, "rwds_oe") + + # CS. + # --- + self.specials += MultiReg(i=self.ios.cs_n, o=pads.cs_n, n=1) + + # Rst Output. + # ----------- + self.specials += MultiReg(i=self.ios.rst_n, o=pads.rst_n, n=1) + + # Clk Output. + # ----------- + # Single Ended Clk. + if hasattr(pads, "clk"): + self.specials += MultiReg(i=self.ios.clk & ClockSignal("hyperram"), o=pads.clk, n=3) + # Differential Clk. + elif hasattr(pads, "clk_p"): + self.specials += MultiReg(i= self.ios.clk & ClockSignal("hyperram"), o=pads.clk_p, n=3) + self.specials += MultiReg(i=~(self.ios.clk & ClockSignal("hyperram")), o=pads.clk_n, n=3) + else: + raise ValueError + + # DQ Output/Input. + # ---------------- + if with_tristate: + dq_o = Signal(self.data_width) + dq_oe = Signal() + dq_i = Signal(self.data_width) + self.specials += Tristate(pads.dq, + o = dq_o, + oe = dq_oe, + i = dq_i, + ) + else: + dq_o = pads.dq_o + dq_oe = pads.dq_oe + dq_i = pads.dq_i + self.specials += MultiReg(i=self.ios.dq_oe, o=dq_oe, n=3) + for n in range(self.data_width): + self.specials += [ + MultiReg(i=self.ios.dq_o[n], o=dq_o[n], n=3), + MultiReg(o=self.ios.dq_i[n], i=dq_i[n], n=1, odomain=dq_i_cd), + ] + + # RDWS Output/Input. + # ------------------ + if with_tristate: + rwds_o = Signal(self.data_width//8) + rwds_oe = Signal() + rwds_i = Signal(self.data_width//8) + self.specials += Tristate(pads.rwds, + o = rwds_o, + oe = rwds_oe, + i = rwds_i, + ) + else: + rwds_o = pads.rwds_o + rwds_oe = pads.rwds_oe + rwds_i = pads.rwds_i + self.specials += MultiReg(i=self.ios.rwds_oe, o=rwds_oe, n=3) + for n in range(self.data_width//8): + self.specials += [ + MultiReg(i=self.ios.rwds_o[n], o=rwds_o[n], n=3), + MultiReg(o=self.ios.rwds_i[n], i=rwds_i[n], n=1, odomain=dq_i_cd), + ] + +# HyperRAM Core ------------------------------------------------------------------------------------ + +class HyperRAMCore(LiteXModule): + """ + HyperRAM Core Logic Module + + This module implements the main logic for HyperRAM memory operations, supporting variable + latency, configurable clocking, and a Wishbone interface for data transfer. It manages read and + write operations and interacts with the PHY layer for memory access. + + Parameters: + - phy : SDR PHY interface for data transmission and reception. + - latency : Default latency setting. + - latency_mode : Latency mode "fixed" or "variable". + - clk_ratio : Clock ratio "4:1" or "2:1". + - with_bursting : Enable or disable burst mode. + """ + def __init__(self, phy, latency=7, latency_mode="fixed", clk_ratio="4:1", with_bursting=True): + self.bus = bus = wishbone.Interface(data_width=32, address_width=32, addressing="word") + self.reg = reg = wishbone.Interface(data_width=16, address_width=4, addressing="word") + self.source = source = stream.Endpoint(hyperram_phy_tx_layout(phy.data_width)) # TX. + self.sink = sink = stream.Endpoint(hyperram_phy_rx_layout(phy.data_width)) # RX. + + # # # + + # Config/Reg Interface. + # --------------------- + self.rst = Signal(reset=0) + self.latency = Signal(8, reset=latency) + self.latency_mode = Signal(reset={"fixed": 0b0, "variable": 0b1}[latency_mode]) + + # Signals. + # -------- + self.cmd = cmd = Signal(48) + self.cycles = cycles = Signal(8) + self.latency_x2 = latency_x2 = Signal() + self.bus_latch = bus_latch = Signal() + + # PHY. + # ---- + self.comb += phy.ios.rst_n.eq(~self.rst) + + # Converters. + # ----------- + self.cmd_tx_conv = cmd_tx_conv = stream.Converter(48, 8, reverse=True) + self.reg_tx_conv = reg_tx_conv = stream.StrideConverter( + description_from = hyperram_core_tx_layout(16), + description_to = hyperram_core_tx_layout(8), + reverse = True + ) + self.reg_rx_conv = reg_rx_conv = stream.StrideConverter( + description_from = hyperram_core_rx_layout(8), + description_to = hyperram_core_rx_layout(16), + reverse = True + ) + self.dat_tx_conv = dat_tx_conv = stream.StrideConverter( + description_from = hyperram_core_tx_layout(32), + description_to = hyperram_core_tx_layout(phy.data_width), + reverse = True + ) + self.dat_rx_conv = dat_rx_conv = stream.StrideConverter( + description_from = hyperram_core_rx_layout(phy.data_width), + description_to = hyperram_core_rx_layout(32), + reverse = True + ) + self.comb += [ + If(reg.stb & ~reg.we, + sink.connect(reg_rx_conv.sink), + ).Else( + sink.connect(dat_rx_conv.sink), + ), + dat_rx_conv.source.ready.eq(1), # Always ready. + reg_rx_conv.source.ready.eq(1), # Always ready. + ] + + # Command/Address Gen. + # -------------------- + ashift = {8:1, 16:0}[phy.data_width] + self.comb += [ + # Register Command Gen. + If(reg.stb, + cmd[47].eq(~reg.we), # R/W#. + cmd[46].eq(1), # Register Space. + cmd[45].eq(1), # Burst Type (Linear). + Case(reg.adr, { + 0 : cmd[0:40].eq(0x00_00_00_00_00), # Identification Register 0 (Read Only). + 1 : cmd[0:40].eq(0x00_00_00_00_01), # Identification Register 1 (Read Only). + 2 : cmd[0:40].eq(0x00_01_00_00_00), # Configuration Register 0. + 3 : cmd[0:40].eq(0x00_01_00_00_01), # Configuration Register 1. + }), + # Data Command Gen. + ).Else( + cmd[47].eq(~bus.we), # R/W#. + cmd[46].eq(0), # Memory Space. + cmd[45].eq(1), # Burst Type (Linear). + cmd[ 16:45].eq(bus.adr[3-ashift:]), # Row & Upper Column Address. + cmd[ashift: 3].eq(bus.adr), # Lower Column Address. + ) + ] + + # FSM. + # ---- + self.fsm = fsm = FSM(reset_state="IDLE") + + # IDLE State. + fsm.act("IDLE", + If((bus.cyc & bus.stb) | reg.stb, + NextState("CMD-ADDRESS") + ) + ) + + # Cmd/Address State. + fsm.act("CMD-ADDRESS", + cmd_tx_conv.sink.valid.eq(1), + cmd_tx_conv.sink.data.eq(cmd), + cmd_tx_conv.source.connect(source, keep={"valid", "ready"}), + source.cmd.eq(1), + source.dq.eq(cmd_tx_conv.source.data), + source.dq_oe.eq(1), + If(cmd_tx_conv.sink.ready, + If(reg.stb & reg.we, + NextState("REG-WRITE") + ).Else( + NextState("LATENCY-WAIT") + ) + ) + ) + + # Latency Wait State. + fsm.act("LATENCY-WAIT", + # Sample rwds_i here (FSM is ahead) to determine X1 or X2 latency. + If(cycles == 0, + NextValue(latency_x2, phy.ios.rwds_i[0] | (latency_mode == "fixed")) + ), + source.valid.eq(1), + If(source.ready, + NextValue(cycles, cycles + 1), + # Wait for 1X/2X Latency... + # Latency Count starts 1 HyperRAM Clk before the end of the Cmd. + If(cycles == (2*((self.latency_x2 + 1)*self.latency - 1) - 1), + If(reg.stb & ~reg.we, + NextState("REG-READ") + ).Else( + If(bus.we, + bus_latch.eq(1), + NextState("DAT-WRITE") + ).Else( + NextState("DAT-READ") + ) + ) + ) + ) + ) + + # Register Write State. + fsm.act("REG-WRITE", + reg_tx_conv.sink.valid.eq(1), + reg_tx_conv.sink.dq.eq(reg.dat_w), + reg_tx_conv.source.connect(source), + source.dat_w.eq(1), + source.dq_oe.eq(1), + If(reg_tx_conv.sink.ready, + reg.ack.eq(1), + NextState("END") + ) + ) + + # Register Read State. + fsm.act("REG-READ", + source.valid.eq(1), + source.dat_r.eq(1), + If(reg_rx_conv.source.valid, + reg.ack.eq(1), + reg.dat_r.eq(reg_rx_conv.source.dq), + NextState("END"), + ) + ) + + # Data Write State. + self.sync += [ + If(bus_latch, + dat_tx_conv.sink.dq.eq(bus.dat_w), + dat_tx_conv.sink.rwds.eq(~bus.sel), + ) + ] + self.comb += If(bus_latch, bus.ack.eq(1)) + fsm.act("DAT-WRITE", + dat_tx_conv.sink.valid.eq(1), + dat_tx_conv.source.connect(source), + source.dq_oe.eq(1), + source.rwds_oe.eq(1), + source.dat_w.eq(1), + If(dat_tx_conv.sink.ready, + # Stay in DAT-WRITE while incrementing burst ongoing... + If(with_bursting & bus.cyc & bus.stb & ((bus.cti == 0b10) | (bus.cti == 0b11)), + bus_latch.eq(1), + NextState("DAT-WRITE") + # ..else exit. + ).Else( + NextState("END") + ) + ) + ) + + # Data Read State. + fsm.act("DAT-READ", + source.valid.eq(bus.cyc & bus.stb), + source.dat_r.eq(1), + If(dat_rx_conv.source.valid, + bus.ack.eq(1), + bus.dat_r.eq(dat_rx_conv.source.dq), + # Stay in DAT-READ while incrementing burst ongoing... + If(with_bursting & (bus.cti == 0b10), + NextState("DAT-READ") + # ..else exit. + ).Else( + NextState("END") + ) + ) + ) + fsm.act("END", + NextValue(cycles, cycles + 1), + If(cycles == 8, # FIXME. + NextState("IDLE") + ) + ) + fsm.finalize() + self.sync += If(fsm.next_state != fsm.state, cycles.eq(0)) # HyperRAM ----------------------------------------------------------------------------------------- class HyperRAM(LiteXModule): - tCSM = 4e-6 - """HyperRAM + """ + HyperRAM Top-Level Module. - Provides a very simple/minimal HyperRAM core with a Wishbone Interface that can work with all - FPGA/HyperRam chips: - - Vendor agnostic. - - Fixed/Variable latency. - - Latency/Registers (re-)configuration. + This module integrates the PHY and Core modules to provide a complete interface for HyperRAM + communication in LiteX-based systems. It supports configurable latency, clock ratio, and an + optional CSR interface for advanced configuration and status monitoring. Parameters: - pads (Record) : Interface to the HyperRAM connection pads. - latency (int, optional) : Initial latency setting, defaults to 6. - latency_mode (str, optional) : Specifies the latency mode ('fixed' or 'variable'), defaults to 'variable'. - 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="variable", sys_clk_freq=10e6, clk_ratio="4:1", with_csr=True): - self.pads = pads + - pads : External pads for the HyperRAM interface. + - latency : Default latency setting. + - latency_mode : Latency mode "fixed" or "variable". + - sys_clk_freq : System clock frequency. + - clk_ratio : Clock ratio "4:1" or "2:1". + - with_csr : Include CSR support. + - dq_i_cd : Clock domain for data input. + """ + def __init__(self, pads, latency=7, latency_mode="fixed", sys_clk_freq=100e6, clk_ratio="4:1", with_bursting=True, with_csr=True, dq_i_cd=None): self.bus = bus = wishbone.Interface(data_width=32, address_width=32, addressing="word") # # # # Parameters. # ----------- - dw = len(pads.dq) if not hasattr(pads.dq, "oe") else len(pads.dq.o) - assert dw in [8, 16] + self.pads = pads + self.clk_ratio = clk_ratio assert latency_mode in ["fixed", "variable"] - assert clk_ratio in [ - "4:1", # HyperRAM Clk = Sys Clk/4. - "2:1", # HyperRAM Clk = Sys Clk/2. - ] - self.cd_io = cd_io = { + assert clk_ratio in ["4:1", "2:1"] + + # PHY. + # ---- + phy_cd = { "4:1": "sys", - "2:1": "sys2x" + "2:1": "sys2x", }[clk_ratio] - self.sync_io = sync_io = getattr(self.sync, cd_io) + if dq_i_cd is None: + dq_i_cd = phy_cd + self.phy = phy = ClockDomainsRenamer(phy_cd)(HyperRAMSDRPHY(pads=pads, dq_i_cd=dq_i_cd)) - # Config/Reg Interface. - # --------------------- - self.conf_rst = Signal() - self.conf_latency = Signal(8, reset=latency) - self.stat_latency_mode = Signal(reset={"fixed": 0, "variable": 1}[latency_mode]) - 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) + # FIFOs. + # ------ + self.tx_fifo = tx_fifo = ClockDomainsRenamer(phy_cd)(stream.SyncFIFO(hyperram_phy_tx_layout(phy.data_width), 4)) + self.rx_fifo = rx_fifo = ClockDomainsRenamer(phy_cd)(stream.SyncFIFO(hyperram_phy_rx_layout(phy.data_width), 4)) + + # CDCs. + # ----- + self.tx_cdc = tx_cdc = stream.ClockDomainCrossing( + layout = hyperram_phy_tx_layout(phy.data_width), + cd_from = "sys", + cd_to = phy_cd, + depth = 4, + ) + self.rx_cdc = rx_cdc = stream.ClockDomainCrossing( + layout = hyperram_phy_rx_layout(phy.data_width), + cd_from = dq_i_cd, + cd_to = "sys", + depth = 4, + ) + + # Core. + # ----- + self.core = core = HyperRAMCore( + phy = phy, + latency = latency, + latency_mode = latency_mode, + clk_ratio = clk_ratio, + with_bursting = with_bursting, + ) + self.comb += bus.connect(core.bus) + + # Pipelines. + # --------- + self.tx_pipeline = stream.Pipeline( + core, + tx_cdc, + tx_fifo, + phy, + ) + self.rx_pipeline = stream.Pipeline( + phy, + rx_fifo, + rx_cdc, + core, + ) + + # CSRs. + # ----- if with_csr: - self.add_csr(default_latency=latency) + self.add_csr(default_latency=latency, latency_mode=latency_mode) - # Internal Signals. - # ----------------- - clk = Signal() - clk_phase = Signal(2) - cs = Signal() - ca = Signal(48) - ca_oe = Signal() - sr = Signal(48) - sr_next = Signal(48) - dq_o = Signal(dw) - dq_oe = Signal() - dq_i = Signal(dw) - rwds_o = Signal(dw//8) - rwds_oe = Signal() - rwds_i = Signal(dw//8) - - # 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 += [ # FIXME: Try to move to sync to allow switching to SDRTristate. - # DQ. - dq.o.eq( dq_o), - dq.oe.eq(dq_oe), - - # RWDS. - rwds.o.eq( rwds_o), - rwds.oe.eq(rwds_oe), - ] - self.sync_io += [ - # DQ. - dq_i.eq(dq.i), - - # RWDS. - rwds_i.eq(rwds.i) - ] - - # Drive Control Signals -------------------------------------------------------------------- - - # Rst. - if hasattr(pads, "rst_n"): - self.sync_io += pads.rst_n.eq(1 & ~self.conf_rst) - - # CSn. - pads.cs_n.reset = 2**len(pads.cs_n) - 1 - self.sync_io += pads.cs_n[0].eq(~cs) # Only supporting 1 CS. - - # Clk. - pads_clk = Signal() - self.sync_io += pads_clk.eq(clk) - if hasattr(pads, "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: - raise ValueError - - # Burst Timer ------------------------------------------------------------------------------ - self.burst_timer = burst_timer = WaitTimer(sys_clk_freq * self.tCSM) - - # Clk Generation --------------------------------------------------------------------------- - self.sync_io += [ - clk_phase.eq(0b00), - If(cs, - clk_phase.eq(clk_phase + 1) - ) - ] - 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. - } - if clk_ratio in ["4:1"]: - self.comb += Case(clk_phase, cases) - if clk_ratio in ["2:1"]: - self.sync_io += 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), - ) - ] - if clk_ratio in ["4:1"]: - self.sync += If(clk_phase[0] == 0, sr.eq(sr_next)) - if clk_ratio in ["2:1"]: - self.sync += sr.eq(sr_next) - - # Data Shift-Out Register ------------------------------------------------------------------ - self.comb += bus.dat_r.eq(sr_next) - self.comb += [ - # Command/Address: 8-bit. - If(ca_oe, - dq_o.eq(sr[-8:]) - # Data: dw-bit. - ).Else( - dq_o.eq(sr[-dw:]) - ) - ] - - # 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 ----------------------------------------------------------------------- - ashift = {8:1, 16:0}[dw] - self.comb += [ - # Register Command Generation. - If(reg_write_req | reg_read_req, - ca[47].eq(reg_ep.read), # R/W# - 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 - ) - ] - - # Bus Latch -------------------------------------------------------------------------------- - bus_adr = Signal(32) - bus_we = Signal() - bus_sel = Signal(4) - bus_latch = Signal() - self.sync += If(bus_latch, - If(bus.we, sr.eq(Cat(Signal(16), bus.dat_w))), - bus_we.eq(bus.we), - bus_sel.eq(bus.sel), - bus_adr.eq(bus.adr) - ) - - # FSM (Sequencer) -------------------------------------------------------------------------- - cycles = Signal(8) - first = Signal() - refresh = Signal() - self.fsm = fsm = FSM(reset_state="IDLE") - fsm.act("IDLE", - NextValue(first, 1), - If((bus.cyc & bus.stb) | reg_write_req | reg_read_req, - NextValue(sr, ca), - NextState("SEND-COMMAND-ADDRESS") - ) - ) - fsm.act("SEND-COMMAND-ADDRESS", - # Send Command on DQ. - ca_oe.eq(1), - dq_oe.eq(1), - # Wait for 6*2 cycles. - If(cycles == (6*2 - 1), - 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", - # Send Reg on DQ. - ca_oe.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", - # Send Reg on DQ. - ca_oe.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", - # 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). - ((cycles == 1*(self.conf_latency * 4) - 4 - 1) & ~refresh) , # 1X Latency ( DRAM refresh required). - # Latch Bus. - bus_latch.eq(1), - # Early Write Ack (to allow bursting). - If(~reg_read_req, - bus.ack.eq(bus.we), - ), - NextState("READ-WRITE-DATA0") - ) - ) - states = {8:4, 16:2}[dw] - for n in range(states): - fsm.act(f"READ-WRITE-DATA{n}", - # Enable Burst Timer. - burst_timer.wait.eq(1), - ca_oe.eq(reg_read_req), - # Send Data on DQ/RWDS (for write). - If(bus_we, - dq_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)], - ), - # Wait for 2 cycles. - If(cycles == (2 - 1), - # Set next default state (with rollover for bursts). - NextState(f"READ-WRITE-DATA{(n + 1)%states}"), - # On last state, see if we can continue the burst or if we should end it. - If(n == (states - 1), - NextValue(first, 0), - # Continue burst when a consecutive access is ready. - If(~reg_read_req & bus.stb & bus.cyc & (bus.we == bus_we) & (bus.adr == (bus_adr + 1)) & (~burst_timer.done), - # Latch Bus. - bus_latch.eq(1), - # Early Write Ack (to allow bursting). - bus.ack.eq(bus.we) - # Else end the burst. - ).Elif(bus_we | (~first) | burst_timer.done, - NextState("IDLE") - ) - ), - # Read Ack (when dat_r ready). - If((n == 0) & ~first, - 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), - ) - ) - ) - ) - - # 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() - cycles_rst = { - "4:1" : 0, - "2:1" : 1, - }[clk_ratio] - cycles_inc = { - "4:1" : 1, - "2:1" : 2, - }[clk_ratio] - self.sync += cycles.eq(cycles + cycles_inc) - self.sync += If(fsm.next_state != fsm.state, cycles.eq(cycles_rst)) - - def add_tristate(self, pad, register=False): - class TristatePads: - 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(cd_io), - ) - else: - self.specials += Tristate(pad, - o = t.o, - oe = t.oe, - i = t.i, - ) - return t - - def add_csr(self, default_latency=6): + def add_csr(self, default_latency=7, latency_mode="fixed"): # Config/Status Interface. # ------------------------ self.config = CSRStorage(fields=[ @@ -401,33 +603,26 @@ class HyperRAM(LiteXModule): 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), + self.core.rst.eq( self.config.fields.rst), + self.core.latency.eq(self.config.fields.latency), ] self.status = CSRStatus(fields=[ CSRField("latency_mode", offset=0, size=1, values=[ ("``0b0``", "Fixed Latency."), ("``0b1``", "Variable Latency."), - ]), + ], reset={"fixed": 0b0, "variable": 0b1}[latency_mode]), CSRField("clk_ratio", offset=1, size=4, values=[ ("``4``", "HyperRAM Clk = Sys Clk/4."), ("``2``", "HyperRAM Clk = Sys Clk/2."), - ]), + ], reset={"4:1": 4, "2:1": 2}[self.clk_ratio]), ]) - self.comb += [ - self.status.fields.latency_mode.eq(self.stat_latency_mode), - self.status.fields.clk_ratio.eq({ - "sys" : 4, - "sys2x": 2, - }[self.cd_io]), - ] # 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=[ + CSRField("addr", offset=8, size=2, values=[ ("``0b00``", "Identification Register 0 (Read Only)."), ("``0b01``", "Identification Register 1 (Read Only)."), ("``0b10``", "Configuration Register 0."), @@ -435,23 +630,35 @@ class HyperRAM(LiteXModule): ]), ]) 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."), + CSRField("done", offset=0, size=1, description="Register Access 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), - ] + self.reg_fsm = reg_fsm = FSM(reset_state="IDLE") + reg_fsm.act("IDLE", + self.reg_status.fields.done.eq(1), + If(self.reg_control.fields.write, + NextState("WRITE"), + ).Elif(self.reg_control.fields.read, + NextState("READ"), + ) + ) + reg_fsm.act("WRITE", + self.core.reg.stb.eq(1), + self.core.reg.we.eq(1), + self.core.reg.adr.eq(self.reg_control.fields.addr), + self.core.reg.dat_w.eq(self.reg_wdata.storage), + If(self.core.reg.ack, + NextState("IDLE"), + ) + ) + reg_fsm.act("READ", + self.core.reg.stb.eq(1), + self.core.reg.we.eq(0), + self.core.reg.adr.eq(self.reg_control.fields.addr), + If(self.core.reg.ack, + NextValue(self.reg_rdata.status, self.core.reg.dat_r), + NextState("IDLE"), + ) + ) diff --git a/litex/soc/software/libbase/hyperram.c b/litex/soc/software/libbase/hyperram.c index 9dccd581c..6835f69f8 100644 --- a/litex/soc/software/libbase/hyperram.c +++ b/litex/soc/software/libbase/hyperram.c @@ -18,7 +18,7 @@ static void hyperram_write_reg(uint16_t reg_addr, uint16_t data) { 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); + while ((hyperram_reg_status_read() & (1 << CSR_HYPERRAM_REG_STATUS_DONE_OFFSET)) == 0); } static uint16_t hyperram_read_reg(uint16_t reg_addr) { @@ -29,7 +29,7 @@ static uint16_t hyperram_read_reg(uint16_t reg_addr) { 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); + while ((hyperram_reg_status_read() & (1 << CSR_HYPERRAM_REG_STATUS_DONE_OFFSET)) == 0); return hyperram_reg_rdata_read(); } @@ -55,42 +55,60 @@ static uint16_t hyperram_get_chip_latency_setting(uint32_t clk_freq) { return 0b0010; /* Default to highest latency for safety */ } -static void hyperram_configure_latency(void) { - uint16_t config_reg_0 = 0x8f2f; +void hyperram_init(void) { + uint16_t config_reg_0; uint8_t core_clk_ratio; + uint8_t core_latency_mode; uint16_t core_latency_setting; uint16_t chip_latency_setting; - /* Compute Latency settings */ - core_clk_ratio = (hyperram_status_read() >> CSR_HYPERRAM_STATUS_CLK_RATIO_OFFSET & 0xf); - printf("HyperRAM Clk Ratio %d:1.\n", core_clk_ratio); - core_latency_setting = hyperram_get_core_latency_setting(CONFIG_CLOCK_FREQUENCY/core_clk_ratio); - chip_latency_setting = hyperram_get_chip_latency_setting(CONFIG_CLOCK_FREQUENCY/core_clk_ratio); + printf("HyperRAM init...\n"); - /* Write Latency to HyperRAM Core */ - printf("HyperRAM Core Latency: %d CK (X1).\n", core_latency_setting); + /* Compute Latency settings */ + core_clk_ratio = (hyperram_status_read() >> CSR_HYPERRAM_STATUS_CLK_RATIO_OFFSET) & 0xf; + printf("HyperRAM Clk Ratio %d:1\n", core_clk_ratio); + core_latency_setting = hyperram_get_core_latency_setting(CONFIG_CLOCK_FREQUENCY / core_clk_ratio); + chip_latency_setting = hyperram_get_chip_latency_setting(CONFIG_CLOCK_FREQUENCY / core_clk_ratio); + + /* Configure Latency on HyperRAM Core */ + core_latency_mode = (hyperram_status_read() >> CSR_HYPERRAM_STATUS_LATENCY_MODE_OFFSET) & 0b1; + printf("HyperRAM %s Latency: %d CK (X1)\n", (core_latency_mode == 0) ? "Fixed" : "Variable", core_latency_setting); hyperram_config_write(core_latency_setting << CSR_HYPERRAM_CONFIG_LATENCY_OFFSET); + /* Configure HyperRAM Chip */ + config_reg_0 = ( + /* Burst Length */ + (HYPERRAM_CONFIG_0_REG_BL_32_BYTES << HYPERRAM_CONFIG_0_REG_BL_OFFSET) | + + /* Hybrid Burst Enable */ + (HYPERRAM_CONFIG_0_REG_HBE_LEGACY << HYPERRAM_CONFIG_0_REG_HBE_OFFSET) | + + /* Initial Latency */ + (chip_latency_setting << HYPERRAM_CONFIG_0_REG_IL_OFFSET) | + + /* Fixed Latency Enable */ + (HYPERRAM_CONFIG_0_REG_FLE_ENABLED << HYPERRAM_CONFIG_0_REG_FLE_OFFSET) | + + /* Reserved Bits (Set to 1 for future compatibility) */ + (0b1111 << HYPERRAM_CONFIG_0_REG_RSD_OFFSET) | + + /* Drive Strength */ + (HYPERRAM_CONFIG_0_REG_DS_19_OHM << HYPERRAM_CONFIG_0_REG_DS_OFFSET) | + + /* Deep Power Down: Normal operation */ + (HYPERRAM_CONFIG_0_REG_DPD_DISABLED << HYPERRAM_CONFIG_0_REG_DPD_OFFSET) + ); /* Enable Variable Latency on HyperRAM Chip */ - if (hyperram_status_read() & 0x1) - config_reg_0 &= ~(0b1 << 3); /* Enable Variable Latency */ + if (hyperram_status_read() & 0x1) { + config_reg_0 &= ~(1 << HYPERRAM_CONFIG_0_REG_FLE_OFFSET); + config_reg_0 |= (HYPERRAM_CONFIG_0_REG_FLE_DISABLED << HYPERRAM_CONFIG_0_REG_FLE_OFFSET); + } + hyperram_write_reg(HYPERRAM_CONFIG_0_REG, config_reg_0); - /* 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"); + /* Read current configuration to verify changes */ + config_reg_0 = hyperram_read_reg(HYPERRAM_CONFIG_0_REG); + printf("HyperRAM Configuration Register 0: %04x\n", config_reg_0); + printf("\n"); } #endif \ No newline at end of file diff --git a/litex/soc/software/libbase/hyperram.h b/litex/soc/software/libbase/hyperram.h index 1c8a7d1e7..a20a9433b 100644 --- a/litex/soc/software/libbase/hyperram.h +++ b/litex/soc/software/libbase/hyperram.h @@ -8,6 +8,57 @@ extern "C" { #endif +/* HyperRAM Registers */ +#define HYPERRAM_ID_0_REG 0x0 /* Identification Register 0 */ +#define HYPERRAM_ID_1_REG 0x1 /* Identification Register 1 */ +#define HYPERRAM_CONFIG_0_REG 0x2 /* Configuration Register 0 */ +#define HYPERRAM_CONFIG_1_REG 0x3 /* Configuration Register 1 */ + +/* Configuration Register 0 Field Offsets */ +#define HYPERRAM_CONFIG_0_REG_BL_OFFSET 0 /* Burst Length */ +#define HYPERRAM_CONFIG_0_REG_HBE_OFFSET 2 /* Hybrid Burst Enable */ +#define HYPERRAM_CONFIG_0_REG_FLE_OFFSET 3 /* Fixed Latency Enable */ +#define HYPERRAM_CONFIG_0_REG_IL_OFFSET 4 /* Initial Latency */ +#define HYPERRAM_CONFIG_0_REG_RSD_OFFSET 8 /* Reserved bits */ +#define HYPERRAM_CONFIG_0_REG_DS_OFFSET 12 /* Drive Strength */ +#define HYPERRAM_CONFIG_0_REG_DPD_OFFSET 15 /* Deep Power Down */ + +/* Configuration Register 0 Field Values */ + +/* Burst Length */ +#define HYPERRAM_CONFIG_0_REG_BL_128_BYTES 0b00 +#define HYPERRAM_CONFIG_0_REG_BL_64_BYTES 0b01 +#define HYPERRAM_CONFIG_0_REG_BL_16_BYTES 0b10 +#define HYPERRAM_CONFIG_0_REG_BL_32_BYTES 0b11 + +/* Hybrid Burst Enable */ +#define HYPERRAM_CONFIG_0_REG_HBE_WRAPPED 0b0 +#define HYPERRAM_CONFIG_0_REG_HBE_LEGACY 0b1 + +/* Fixed Latency Enable */ +#define HYPERRAM_CONFIG_0_REG_FLE_DISABLED 0b0 +#define HYPERRAM_CONFIG_0_REG_FLE_ENABLED 0b1 + +/* Initial Latency */ +#define HYPERRAM_CONFIG_0_REG_IL_3_CLOCKS 0b1110 +#define HYPERRAM_CONFIG_0_REG_IL_4_CLOCKS 0b1111 +#define HYPERRAM_CONFIG_0_REG_IL_5_CLOCKS 0b0000 +#define HYPERRAM_CONFIG_0_REG_IL_6_CLOCKS 0b0001 +#define HYPERRAM_CONFIG_0_REG_IL_7_CLOCKS 0b0010 + +/* Drive Strength */ +#define HYPERRAM_CONFIG_0_REG_DS_34_OHM 0b000 +#define HYPERRAM_CONFIG_0_REG_DS_115_OHM 0b001 +#define HYPERRAM_CONFIG_0_REG_DS_67_OHM 0b010 +#define HYPERRAM_CONFIG_0_REG_DS_46_OHM 0b011 +#define HYPERRAM_CONFIG_0_REG_DS_27_OHM 0b101 +#define HYPERRAM_CONFIG_0_REG_DS_22_OHM 0b110 +#define HYPERRAM_CONFIG_0_REG_DS_19_OHM 0b111 + +/* Deep Power Down */ +#define HYPERRAM_CONFIG_0_REG_DPD_DISABLED 0b1 +#define HYPERRAM_CONFIG_0_REG_DPD_ENABLED 0b0 + void hyperram_init(void); #ifdef __cplusplus diff --git a/test/test_hyperbus.py b/test/test_hyperbus.py index 6f0d655ed..ac28f2153 100644 --- a/test/test_hyperbus.py +++ b/test/test_hyperbus.py @@ -1,7 +1,8 @@ # -# This file is part of LiteHyperBus +# This file is part of LiteX # -# Copyright (c) 2019-2022 Florent Kermarrec +# Copyright (c) 2019-2024 Florent Kermarrec +# Copyright (c) 2024 MoTeC # SPDX-License-Identifier: BSD-2-Clause import unittest @@ -16,21 +17,20 @@ def c2bool(c): class Pads: pass - class HyperRamPads: def __init__(self, dw=8): - self.clk = Signal() - self.cs_n = Signal() - self.dq = Record([("oe", 1), ("o", dw), ("i", dw)]) - self.rwds = Record([("oe", 1), ("o", dw//8), ("i", dw//8)]) + self.clk = Signal() + self.rst_n = Signal() + self.cs_n = Signal() + self.dq = Record([("oe", 1), ("o", dw), ("i", dw)]) + self.rwds = Record([("oe", 1), ("o", dw//8), ("i", dw//8)]) - -class TestHyperBus(unittest.TestCase): +class TestHyperRAM(unittest.TestCase): def test_hyperram_syntax(self): - pads = Record([("clk", 1), ("cs_n", 1), ("dq", 8), ("rwds", 1)]) + pads = Record([("clk", 1), ("rst_n", 1), ("cs_n", 1), ("dq", 8), ("rwds", 1)]) hyperram = HyperRAM(pads) - pads = Record([("clk_p", 1), ("clk_n", 1), ("cs_n", 1), ("dq", 8), ("rwds", 1)]) + pads = Record([("clk_p", 1), ("clk_n", 1), ("rst_n", 1), ("cs_n", 1), ("dq", 8), ("rwds", 1)]) hyperram = HyperRAM(pads) def test_hyperram_write_latency_5_2x(self): @@ -39,15 +39,16 @@ class TestHyperBus(unittest.TestCase): yield def hyperram_gen(dut): - clk = "___--__--__--__--__--__--__--__--__--__--__--__--__--__--_______" - cs_n = "--________________________________________________________------" - dq_oe = "__------------____________________________________--------______" - dq_o = "002000048d0000000000000000000000000000000000000000deadbeef000000" - rwds_oe = "__________________________________________________--------______" - rwds_o = "____________________________________________________----________" + clk = "_______--__--__--__--__--__--__--__--__--__--__--__--__--__--_______" + cs_n = "----__________________________________________________________------" + dq_oe = "______------------____________________________________--------______" + dq_o = "0000002000048d0000000000000000000000000000000000000000deadbeef000000" + rwds_oe = "______________________________________________________--------______" + rwds_o = "________________________________________________________----________" + 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(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)) if (yield dut.pads.dq.oe): self.assertEqual(int(dq_o[2*(i//2):2*(i//2)+2], 16), (yield dut.pads.dq.o)) @@ -55,7 +56,7 @@ class TestHyperBus(unittest.TestCase): self.assertEqual(c2bool(rwds_o[i]), (yield dut.pads.rwds.o)) yield - dut = HyperRAM(HyperRamPads(), latency=5, latency_mode="fixed") + dut = HyperRAM(HyperRamPads(dw=8), latency=5, latency_mode="fixed") run_simulation(dut, [fpga_gen(dut), hyperram_gen(dut)], vcd_name="sim.vcd") def test_hyperram_write_latency_5_2x_sys2x(self): @@ -64,12 +65,12 @@ class TestHyperBus(unittest.TestCase): yield def hyperram_gen(dut): - clk = "____--__--__--__--__--__--__--__--__--__--__--__--__--__--_______" - cs_n = "--________________________________________________________-------" - dq_oe = "___------------____________________________________--------______" - dq_o = "0002000048d0000000000000000000000000000000000000000deadbeef000000" - rwds_oe = "___________________________________________________--------______" - rwds_o = "_____________________________________________________----________" + clk = "________________--__--__--__--__--__--__--__--__--__--__--__--__--__--_______" + cs_n = "_------------__________________________________________________________------" + dq_oe = "_______________------------____________________________________--------______" + dq_o = "0000000000000002000048d0000000000000000000000000000000000000000deadbeef000000" + rwds_oe = "_______________________________________________________________--------______" + rwds_o = "_________________________________________________________________----________" 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)) @@ -79,6 +80,8 @@ class TestHyperBus(unittest.TestCase): self.assertEqual(c2bool(rwds_oe[i]), (yield dut.pads.rwds.oe)) self.assertEqual(c2bool(rwds_o[i]), (yield dut.pads.rwds.o)) yield + for i in range(128): + yield dut = HyperRAM(HyperRamPads(), latency=5, latency_mode="fixed", clk_ratio="2:1") generators = { @@ -97,12 +100,13 @@ class TestHyperBus(unittest.TestCase): yield def hyperram_gen(dut): - clk = "___--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--_______" - cs_n = "--________________________________________________________________------" - dq_oe = "__------------____________________________________________--------______" - dq_o = "002000048d000000000000000000000000000000000000000000000000deadbeef000000" - rwds_oe = "__________________________________________________________--------______" - rwds_o = "____________________________________________________________----________" + clk = "_______--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--_______" + cs_n = "----__________________________________________________________________------" + dq_oe = "______------------____________________________________________--------______" + dq_o = "0000002000048d000000000000000000000000000000000000000000000000deadbeef000000" + rwds_oe = "______________________________________________________________--------______" + rwds_o = "________________________________________________________________----________" + 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)) @@ -122,12 +126,13 @@ class TestHyperBus(unittest.TestCase): yield def hyperram_gen(dut): - clk = "___--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--_______" - cs_n = "--________________________________________________________________________------" - dq_oe = "__------------____________________________________________________--------______" - dq_o = "002000048d00000000000000000000000000000000000000000000000000000000deadbeef000000" - rwds_oe = "__________________________________________________________________--------______" - rwds_o = "____________________________________________________________________----________" + clk = "_______--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--_______" + cs_n = "----__________________________________________________________________________------" + dq_oe = "______------------____________________________________________________--------______" + dq_o = "0000002000048d00000000000000000000000000000000000000000000000000000000deadbeef000000" + rwds_oe = "______________________________________________________________________--------______" + rwds_o = "________________________________________________________________________----________" + 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)) @@ -147,12 +152,13 @@ class TestHyperBus(unittest.TestCase): yield def hyperram_gen(dut): - clk = "___--__--__--__--__--__--__--__--__--__--__--_______" - cs_n = "--____________________________________________------" - dq_oe = "__------------________________________--------______" - dq_o = "002000048d0000000000000000000000000000deadbeef000000" - rwds_oe = "______________________________________--------______" - rwds_o = "________________________________________----________" + clk = "_______--__--__--__--__--__--__--__--__--__--__--_______" + cs_n = "----______________________________________________------" + dq_oe = "______------------________________________--------______" + dq_o = "0000002000048d0000000000000000000000000000deadbeef000000" + rwds_oe = "__________________________________________--------______" + rwds_o = "____________________________________________----________" + 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)) @@ -170,18 +176,19 @@ class TestHyperBus(unittest.TestCase): 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 = "__________________________________________________________________________" + clk = "_______--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--_____" + cs_n = "----______________________________________________________________________----" + dq_oe = "______------------____________________________________________________________" + dq_o = "000000a000048d0000000000000000000000000000000000000000000000000000000000000000" + dq_i = "000000000000000000000000000000000000000000000000000000deadbeefcafefade00000000" + rwds_oe = "______________________________________________________________________________" + rwds_i = "______________________________________________________--__--__--__--__________" + yield 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.rwds.i.eq(c2bool(rwds_i[i])) 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)) @@ -197,18 +204,19 @@ class TestHyperBus(unittest.TestCase): 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 = "00a000048d000000000000000000000000000000000000000000000000000000000000000000000000" - dq_i = "0000000000000000000000000000000000000000000000000000000000deadbeefcafefade00000000" - rwds_oe = "__________________________________________________________________________________" + clk = "_______--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--_____" + cs_n = "----______________________________________________________________________________----" + dq_oe = "______------------____________________________________________________________________" + dq_o = "000000a000048d000000000000000000000000000000000000000000000000000000000000000000000000" + dq_i = "00000000000000000000000000000000000000000000000000000000000000deadbeefcafefade00000000" + rwds_oe = "______________________________________________________________________________________" + rwds_i = "______________________________________________________________--__--__--__--__________" + yield 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.rwds.i.eq(c2bool(rwds_i[i])) 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)) @@ -224,18 +232,19 @@ class TestHyperBus(unittest.TestCase): 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 = "__________________________________________________________________________________________" + clk = "_______--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--_____" + cs_n = "----______________________________________________________________________________________----" + dq_oe = "______------------____________________________________________________________________________" + dq_o = "000000a000048d00000000000000000000000000000000000000000000000000000000000000000000000000000000" + dq_i = "0000000000000000000000000000000000000000000000000000000000000000000000deadbeefcafefade00000000" + rwds_oe = "______________________________________________________________________________________________" + rwds_i = "______________________________________________________________________--__--__--__--__________" + yield 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.rwds.i.eq(c2bool(rwds_i[i])) 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)) @@ -251,18 +260,19 @@ class TestHyperBus(unittest.TestCase): 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 = "______________________________________________________________" + clk = "_______--__--__--__--__--__--__--__--__--__--__--__--__--__-______" + cs_n = "----________________________________________________________------" + dq_oe = "______------------________________________________________________" + dq_o = "000000a000048d0000000000000000000000000000000000000000000000000000" + dq_i = "0000000000000000000000000000000000000000deadbeefcafefade0000000000" + rwds_oe = "__________________________________________________________________" + rwds_i = "________________________________________--__--__--__--____________" + yield 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.rwds.i.eq(c2bool(rwds_i[i])) 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)) @@ -276,22 +286,23 @@ class TestHyperBus(unittest.TestCase): def test_hyperram_reg_write(self): def fpga_gen(dut): - yield dut.reg_addr.eq(2) - yield dut.reg_write_data.eq(0x1234) + yield dut.core.reg.adr.eq(2) + yield dut.core.reg.dat_w.eq(0x1234) yield - yield dut.reg_write.eq(1) - yield - yield dut.reg_write.eq(0) - for i in range(128): + yield dut.core.reg.stb.eq(1) + yield dut.core.reg.we.eq(1) + while (yield dut.core.reg.ack) == 0: yield + yield dut.core.reg.stb.eq(0) def hyperram_gen(dut): - clk = "_____--__--__--__--___________" - cs_n = "----________________----------" - dq_oe = "____----------------__________" - dq_o = "000060000100000012340000000000" - rwds_oe = "______________________________" - rwds_o = "______________________________" + clk = "___________--__--__--__--___________" + cs_n = "--------__________________----------" + dq_oe = "__________----------------__________" + dq_o = "000000000060000100000012340000000000" + rwds_oe = "____________________________________" + rwds_o = "____________________________________" + 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)) @@ -303,4 +314,4 @@ class TestHyperBus(unittest.TestCase): yield dut = HyperRAM(HyperRamPads(), with_csr=False) - run_simulation(dut, [fpga_gen(dut), hyperram_gen(dut)], vcd_name="sim.vcd") \ No newline at end of file + run_simulation(dut, [fpga_gen(dut), hyperram_gen(dut)], vcd_name="sim.vcd")# \ No newline at end of file