diff --git a/litex/soc/cores/hyperbus.py b/litex/soc/cores/hyperbus.py index da8c03d2b..9587b747f 100644 --- a/litex/soc/cores/hyperbus.py +++ b/litex/soc/cores/hyperbus.py @@ -1,7 +1,7 @@ # -# 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) 2019 Antti Lukats # Copyright (c) 2021 Franck Jullien # SPDX-License-Identifier: BSD-2-Clause @@ -11,6 +11,9 @@ from migen import * from litex.gen import * 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.soc.interconnect import wishbone @@ -21,25 +24,56 @@ class HyperRAM(LiteXModule): tCSM = 4e-6 """HyperRAM - Provides a very simple/minimal HyperRAM core that should work with all FPGA/HyperRam chips: - - FPGA vendor agnostic. - - no setup/chip configuration (use default latency). + 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 core favors portability and ease of use over performance. - """ - def __init__(self, pads, latency=6, sys_clk_freq=None): + 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 '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.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_phase = Signal(2) cs = Signal() ca = Signal(48) ca_active = Signal() 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 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) @@ -50,7 +84,7 @@ class HyperRAM(LiteXModule): # Rst. if hasattr(pads, "rst_n"): - self.comb += pads.rst_n.eq(1) + self.comb += pads.rst_n.eq(1 & ~self.conf_rst) # CSn. self.comb += pads.cs_n[0].eq(~cs) @@ -80,16 +114,16 @@ class HyperRAM(LiteXModule): dqi = Signal(dw) self.sync += dqi.eq(dq.i) # Sample on 90° and 270° self.comb += [ - sr_new.eq(Cat(dqi, sr[:-dw])), + sr_next.eq(Cat(dqi, sr[:-dw])), 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 ------------------------------------------------------------------ self.comb += [ - bus.dat_r.eq(sr_new), + bus.dat_r.eq(sr_next), If(dq.oe, dq.o.eq(sr[-dw:]), 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 ----------------------------------------------------------------------- ashift = {8:1, 16:0}[dw] self.comb += [ - ca[47].eq(~bus.we), # R/W# - 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 + # 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 + ) ] - # 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_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)), - ), + 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() + 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, - If(clk_phase == 0, + If(clk_phase == 0, + If((bus.cyc & bus.stb) | reg_write_req | reg_read_req, NextValue(sr, ca), NextState("SEND-COMMAND-ADDRESS") ) @@ -146,18 +212,53 @@ class HyperRAM(LiteXModule): dq.oe.eq(1), # Wait for 6*2 cycles... 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", # Set CSn. cs.eq(1), - # Wait for Latency cycles... - If(cycles == (latency_cycles - 1), + # 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). - bus.ack.eq(bus.we), + If(~reg_read_req, + bus.ack.eq(bus.we), + ), NextState("READ-WRITE-DATA0") ) ) @@ -168,6 +269,7 @@ class HyperRAM(LiteXModule): burst_timer.wait.eq(1), # Set CSn. cs.eq(1), + ca_active.eq(reg_read_req), # Send Data on DQ/RWDS (for write). If(bus_we, dq.oe.eq(1), @@ -182,7 +284,7 @@ class HyperRAM(LiteXModule): If(n == (states - 1), NextValue(first, 0), # 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. bus_latch.eq(1), # Early Write Ack (to allow bursting). @@ -194,7 +296,14 @@ class HyperRAM(LiteXModule): ), # Read Ack (when dat_r ready). 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)) self.specials += t.get_tristate(pad) 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), + ] diff --git a/litex/soc/software/bios/main.c b/litex/soc/software/bios/main.c index 67ca3d590..87b4219df 100644 --- a/litex/soc/software/bios/main.c +++ b/litex/soc/software/bios/main.c @@ -175,6 +175,87 @@ __attribute__((__used__)) int main(int i, char **c) 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) printf("--========== \e[1mInitialization\e[0m ============--\n"); #ifdef CSR_ETHMAC_BASE diff --git a/test/test_hyperbus.py b/test/test_hyperbus.py index 8d3a59c9a..9a4bf5cfc 100644 --- a/test/test_hyperbus.py +++ b/test/test_hyperbus.py @@ -33,7 +33,33 @@ class TestHyperBus(unittest.TestCase): pads = Record([("clk_p", 1), ("clk_n", 1), ("cs_n", 1), ("dq", 8), ("rwds", 1)]) 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): yield from dut.bus.write(0x1234, 0xdeadbeef, sel=0b1001) yield @@ -56,10 +82,90 @@ class TestHyperBus(unittest.TestCase): self.assertEqual(c2bool(rwds_o[i]), (yield dut.pads.rwds.o)) yield - dut = HyperRAM(HyperRamPads()) + dut = HyperRAM(HyperRamPads(), latency=6) 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): dat = yield from dut.bus.read(0x1234) self.assertEqual(dat, 0xdeadbeef) @@ -84,5 +190,93 @@ class TestHyperBus(unittest.TestCase): self.assertEqual(c2bool(rwds_oe[i]), (yield dut.pads.rwds.oe)) yield - dut = HyperRAM(HyperRamPads()) + dut = HyperRAM(HyperRamPads(), latency=6) 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") \ No newline at end of file