From b95b66b55478e1c6106947ac22a9a277703e392e Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 20 Aug 2024 10:10:53 +0200 Subject: [PATCH 01/16] soc/cores/hyperbus: Switch to Tristate instead of TSTriple and prepare for SDRTristate (not enabled for now). --- litex/soc/cores/hyperbus.py | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/litex/soc/cores/hyperbus.py b/litex/soc/cores/hyperbus.py index 2098d0463..cf94c1adc 100644 --- a/litex/soc/cores/hyperbus.py +++ b/litex/soc/cores/hyperbus.py @@ -7,6 +7,9 @@ # SPDX-License-Identifier: BSD-2-Clause from migen import * +from migen.fhdl.specials import Tristate + +from litex.build.io import SDRTristate from litex.gen import * from litex.gen.genlib.misc import WaitTimer @@ -312,9 +315,26 @@ class HyperRAM(LiteXModule): self.sync += cycles.eq(cycles + 1) self.sync += If(fsm.next_state != fsm.state, cycles.eq(0)) - def add_tristate(self, pad): - t = TSTriple(len(pad)) - self.specials += t.get_tristate(pad) + 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, + o = t.o[n], + oe = t.oe, + i = t.i[n], + ) + else: + self.specials += Tristate(pad, + o = t.o, + oe = t.oe, + i = t.i, + ) return t def add_csr(self, default_latency=6): From a960dc33bcebb9a65a8f671ffe951c949b306ec2 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 20 Aug 2024 10:26:34 +0200 Subject: [PATCH 02/16] soc/cores/hyperbus: Minor cleanup changes. --- litex/soc/cores/hyperbus.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/litex/soc/cores/hyperbus.py b/litex/soc/cores/hyperbus.py index cf94c1adc..426ead2df 100644 --- a/litex/soc/cores/hyperbus.py +++ b/litex/soc/cores/hyperbus.py @@ -103,27 +103,27 @@ class HyperRAM(LiteXModule): self.specials += DifferentialOutput(clk, pads.clk_p, pads.clk_n) # Burst Timer ------------------------------------------------------------------------------ - sys_clk_freq = 10e6 if sys_clk_freq is None else sys_clk_freq - burst_timer = WaitTimer(sys_clk_freq*self.tCSM) - self.burst_timer = burst_timer + if sys_clk_freq is None: + sys_clk_freq = 10e6 # Defaults to 10MHz if not specified. + self.burst_timer = burst_timer = WaitTimer(sys_clk_freq * self.tCSM) # Clock Generation (sys_clk/4) ------------------------------------------------------------- self.sync += clk_phase.eq(clk_phase + 1) cases = {} - cases[1] = clk.eq(cs) # Set pads clk on 90° (if cs is set) - cases[3] = clk.eq(0) # Clear pads clk on 270° + cases[1] = clk.eq(cs) # Set pads Clk on 90° (When CS is set). + cases[3] = clk.eq(0) # Clear pads Clk on 270°. self.sync += Case(clk_phase, cases) # Data Shift-In Register ------------------------------------------------------------------- dqi = Signal(dw) - self.sync += dqi.eq(dq.i) # Sample on 90° and 270° + self.sync += dqi.eq(dq.i) # Sample on 90° and 270° Clk Phases. 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° + self.sync += If(clk_phase[0] == 0, sr.eq(sr_next)) # Shift on 0° and 180° Clk Phases. # Data Shift-Out Register ------------------------------------------------------------------ self.comb += [ @@ -131,7 +131,7 @@ class HyperRAM(LiteXModule): If(dq.oe, dq.o.eq(sr[-dw:]), If(ca_active, - dq.o.eq(sr[-8:]) # Only 8-bit during Command/Address. + dq.o.eq(sr[-8:]) # Only use 8-bit for Command/Address. ) ) ] From 1f71f3d68b5f6d1743cec3c14bb38ec297ee9034 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 20 Aug 2024 10:40:24 +0200 Subject: [PATCH 03/16] soc/cores/hyperbus: Cleanup CSn/Clk generation and add comments. --- litex/soc/cores/hyperbus.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/litex/soc/cores/hyperbus.py b/litex/soc/cores/hyperbus.py index 426ead2df..9b97003ee 100644 --- a/litex/soc/cores/hyperbus.py +++ b/litex/soc/cores/hyperbus.py @@ -91,16 +91,22 @@ class HyperRAM(LiteXModule): self.comb += pads.rst_n.eq(1 & ~self.conf_rst) # CSn. - self.comb += pads.cs_n[0].eq(~cs) - assert len(pads.cs_n) <= 2 - if len(pads.cs_n) == 2: - self.comb += pads.cs_n[1].eq(1) + self.comb += [ + # Set reset value. + pads.cs_n.eq(2**len(pads.cs_n)), + # Set CSn. + pads.cs_n[0].eq(~cs) + ] # Clk. if hasattr(pads, "clk"): + # Single Ended Clk. self.comb += pads.clk.eq(clk) - else: + elif hastattr(pads, "clk_p"): + # Differential Clk. self.specials += DifferentialOutput(clk, pads.clk_p, pads.clk_n) + else: + raise ValueError # Burst Timer ------------------------------------------------------------------------------ if sys_clk_freq is None: From db86ec08b8bbd9207d8387ee32f3d34ef5559833 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 20 Aug 2024 11:12:17 +0200 Subject: [PATCH 04/16] soc/cores/hyperbus: Better split parameters/signals and use intermediate dq_o/oe/i and rwds_o/oe/i signals. --- litex/soc/cores/hyperbus.py | 45 ++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/litex/soc/cores/hyperbus.py b/litex/soc/cores/hyperbus.py index 9b97003ee..2306b1edd 100644 --- a/litex/soc/cores/hyperbus.py +++ b/litex/soc/cores/hyperbus.py @@ -67,6 +67,8 @@ class HyperRAM(LiteXModule): # 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"] # Internal Signals. @@ -78,11 +80,28 @@ class HyperRAM(LiteXModule): ca_active = 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) 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) + self.comb += [ + # DQ. + dq.o.eq( dq_o), + dq.oe.eq(dq_oe), + dq_i.eq( dq.i), - assert dw in [8, 16] + # RWDS. + rwds.o.eq( rwds_o), + rwds.oe.eq(rwds_oe), + rwds_i.eq( rwds.i), + ] # Drive Control Signals -------------------------------------------------------------------- @@ -122,7 +141,7 @@ class HyperRAM(LiteXModule): # Data Shift-In Register ------------------------------------------------------------------- dqi = Signal(dw) - self.sync += dqi.eq(dq.i) # Sample on 90° and 270° Clk Phases. + self.sync += dqi.eq(dq_i) # Sample on 90° and 270° Clk Phases. self.comb += [ sr_next.eq(Cat(dqi, sr[:-dw])), If(ca_active, @@ -134,10 +153,10 @@ class HyperRAM(LiteXModule): # Data Shift-Out Register ------------------------------------------------------------------ self.comb += [ bus.dat_r.eq(sr_next), - If(dq.oe, - dq.o.eq(sr[-dw:]), + If(dq_oe, + dq_o.eq(sr[-dw:]), If(ca_active, - dq.o.eq(sr[-8:]) # Only use 8-bit for Command/Address. + dq_o.eq(sr[-8:]) # Only use 8-bit for Command/Address. ) ) ] @@ -219,7 +238,7 @@ class HyperRAM(LiteXModule): cs.eq(1), # Send Command on DQ. ca_active.eq(1), - dq.oe.eq(1), + dq_oe.eq(1), # Wait for 6*2 cycles... If(cycles == (6*2 - 1), If(reg_write_req, @@ -227,7 +246,7 @@ class HyperRAM(LiteXModule): 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"])), + NextValue(refresh, rwds_i | (latency_mode in ["fixed"])), NextState("WAIT-LATENCY") ) ) @@ -237,7 +256,7 @@ class HyperRAM(LiteXModule): cs.eq(1), # Send Reg on DQ. ca_active.eq(1), - dq.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])), @@ -249,7 +268,7 @@ class HyperRAM(LiteXModule): cs.eq(1), # Send Reg on DQ. ca_active.eq(1), - dq.oe.eq(1), + dq_oe.eq(1), # Wait for 2 cycles... If(cycles == (2 - 1), reg_ep.ready.eq(1), @@ -282,9 +301,9 @@ class HyperRAM(LiteXModule): ca_active.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)], + 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 (since HyperRAM's Clk = sys_clk/4). If(cycles == (2 - 1), From 9c1958d69219509ce2a40f6aa1838ec6c0db6775 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 20 Aug 2024 11:26:58 +0200 Subject: [PATCH 05/16] soc/cores/hyperbus: Simplify/Rework Data Shift-In Register. --- litex/soc/cores/hyperbus.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/litex/soc/cores/hyperbus.py b/litex/soc/cores/hyperbus.py index 2306b1edd..d3f81b844 100644 --- a/litex/soc/cores/hyperbus.py +++ b/litex/soc/cores/hyperbus.py @@ -77,7 +77,7 @@ class HyperRAM(LiteXModule): clk_phase = Signal(2) cs = Signal() ca = Signal(48) - ca_active = Signal() + ca_oe = Signal() sr = Signal(48) sr_next = Signal(48) dq_o = Signal(dw) @@ -143,9 +143,14 @@ class HyperRAM(LiteXModule): dqi = Signal(dw) self.sync += dqi.eq(dq_i) # Sample on 90° and 270° Clk Phases. 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. + # Command/Address: On 8-bit, so 8-bit shift and no input. + If(ca_oe, + sr_next[8:].eq(sr), + # Data: dw-bit shift. + ).Else( + sr_next[:dw].eq(dqi), + sr_next[dw:].eq(sr), + ) ] self.sync += If(clk_phase[0] == 0, sr.eq(sr_next)) # Shift on 0° and 180° Clk Phases. @@ -155,7 +160,7 @@ class HyperRAM(LiteXModule): bus.dat_r.eq(sr_next), If(dq_oe, dq_o.eq(sr[-dw:]), - If(ca_active, + If(ca_oe, dq_o.eq(sr[-8:]) # Only use 8-bit for Command/Address. ) ) @@ -237,7 +242,7 @@ class HyperRAM(LiteXModule): # Set CSn. cs.eq(1), # Send Command on DQ. - ca_active.eq(1), + ca_oe.eq(1), dq_oe.eq(1), # Wait for 6*2 cycles... If(cycles == (6*2 - 1), @@ -255,7 +260,7 @@ class HyperRAM(LiteXModule): # Set CSn. cs.eq(1), # Send Reg on DQ. - ca_active.eq(1), + ca_oe.eq(1), dq_oe.eq(1), # Wait for 2 cycles... If(cycles == (2 - 1), @@ -267,7 +272,7 @@ class HyperRAM(LiteXModule): # Set CSn. cs.eq(1), # Send Reg on DQ. - ca_active.eq(1), + ca_oe.eq(1), dq_oe.eq(1), # Wait for 2 cycles... If(cycles == (2 - 1), @@ -298,7 +303,7 @@ class HyperRAM(LiteXModule): burst_timer.wait.eq(1), # Set CSn. cs.eq(1), - ca_active.eq(reg_read_req), + ca_oe.eq(reg_read_req), # Send Data on DQ/RWDS (for write). If(bus_we, dq_oe.eq(1), From 3a53a92bb2f15d0024b378258e81821aeaa5fdd4 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 20 Aug 2024 11:34:14 +0200 Subject: [PATCH 06/16] soc/cores/hyperbus: Simplify/Rework Data Shift-Out Register. --- litex/soc/cores/hyperbus.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/litex/soc/cores/hyperbus.py b/litex/soc/cores/hyperbus.py index d3f81b844..80cffd058 100644 --- a/litex/soc/cores/hyperbus.py +++ b/litex/soc/cores/hyperbus.py @@ -121,7 +121,7 @@ class HyperRAM(LiteXModule): if hasattr(pads, "clk"): # Single Ended Clk. self.comb += pads.clk.eq(clk) - elif hastattr(pads, "clk_p"): + elif hasattr(pads, "clk_p"): # Differential Clk. self.specials += DifferentialOutput(clk, pads.clk_p, pads.clk_n) else: @@ -146,7 +146,7 @@ class HyperRAM(LiteXModule): # Command/Address: On 8-bit, so 8-bit shift and no input. If(ca_oe, sr_next[8:].eq(sr), - # Data: dw-bit shift. + # Data: On dw-bit, so dw-bit shift. ).Else( sr_next[:dw].eq(dqi), sr_next[dw:].eq(sr), @@ -159,9 +159,12 @@ class HyperRAM(LiteXModule): self.comb += [ bus.dat_r.eq(sr_next), If(dq_oe, - dq_o.eq(sr[-dw:]), + # Command/Address: 8-bit. If(ca_oe, - dq_o.eq(sr[-8:]) # Only use 8-bit for Command/Address. + dq_o.eq(sr[-8:]), + # Data: dw-bit. + ).Else( + dq_o.eq(sr[-dw:]), ) ) ] From 8f5c2dfbca17604984f7ff0a8f0bc94f3d841d6f Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 20 Aug 2024 12:03:40 +0200 Subject: [PATCH 07/16] soc/cores/hyperbus: Fix build with SDRTristate (to prepare tests with it). --- litex/soc/cores/hyperbus.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/litex/soc/cores/hyperbus.py b/litex/soc/cores/hyperbus.py index 80cffd058..3381aacd3 100644 --- a/litex/soc/cores/hyperbus.py +++ b/litex/soc/cores/hyperbus.py @@ -89,8 +89,8 @@ class HyperRAM(LiteXModule): # Tristates. # ---------- - 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 + 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. dq.o.eq( dq_o), @@ -357,10 +357,11 @@ class HyperRAM(LiteXModule): t = TristatePads(len(pad)) if register: for n in range(len(pad)): - self.specials += SDRTristate(pad, - o = t.o[n], - oe = t.oe, - i = t.i[n], + self.specials += SDRTristate(pad[n], + o = t.o[n], + oe = t.oe, + i = t.i[n], + clk = ClockSignal("sys"), ) else: self.specials += Tristate(pad, From 7b413352c20f753cbc9c2d94ebd291697ad8e72e Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 20 Aug 2024 12:04:23 +0200 Subject: [PATCH 08/16] soc/cores/hyperbus: Directly specify default sys_clk_freq in __init__. --- litex/soc/cores/hyperbus.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/litex/soc/cores/hyperbus.py b/litex/soc/cores/hyperbus.py index 3381aacd3..e4427bb88 100644 --- a/litex/soc/cores/hyperbus.py +++ b/litex/soc/cores/hyperbus.py @@ -44,7 +44,7 @@ class HyperRAM(LiteXModule): pads (Record) : Platform pads of HyperRAM. 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.bus = bus = wishbone.Interface(data_width=32, address_width=32, addressing="word") @@ -128,8 +128,6 @@ class HyperRAM(LiteXModule): raise ValueError # Burst Timer ------------------------------------------------------------------------------ - if sys_clk_freq is None: - sys_clk_freq = 10e6 # Defaults to 10MHz if not specified. self.burst_timer = burst_timer = WaitTimer(sys_clk_freq * self.tCSM) # Clock Generation (sys_clk/4) ------------------------------------------------------------- From bfe000150c34cb373f1bd4087a4267bf8afb7ab5 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 20 Aug 2024 14:25:23 +0200 Subject: [PATCH 09/16] soc/cores/hyperbus: Rework Clk generation to allow having using an IO Reg. --- litex/soc/cores/hyperbus.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/litex/soc/cores/hyperbus.py b/litex/soc/cores/hyperbus.py index e4427bb88..e73c46d9d 100644 --- a/litex/soc/cores/hyperbus.py +++ b/litex/soc/cores/hyperbus.py @@ -118,12 +118,14 @@ class HyperRAM(LiteXModule): ] # Clk. + pads_clk = Signal() + self.sync += pads_clk.eq(clk) if hasattr(pads, "clk"): # Single Ended Clk. - self.comb += pads.clk.eq(clk) + self.comb += pads.clk.eq(pads_clk) elif hasattr(pads, "clk_p"): # Differential Clk. - self.specials += DifferentialOutput(clk, pads.clk_p, pads.clk_n) + self.specials += DifferentialOutput(pads_clk, pads.clk_p, pads.clk_n) else: raise ValueError @@ -132,10 +134,13 @@ class HyperRAM(LiteXModule): # Clock Generation (sys_clk/4) ------------------------------------------------------------- self.sync += clk_phase.eq(clk_phase + 1) - cases = {} - cases[1] = clk.eq(cs) # Set pads Clk on 90° (When CS is set). - cases[3] = clk.eq(0) # Clear pads Clk on 270°. - self.sync += Case(clk_phase, cases) + cases = { + 0 : clk.eq(0), # 0° + 1 : clk.eq(cs), # 90° / Set Clk. + 2 : clk.eq(cs), # 180° + 3 : clk.eq(0), # 270° / Clr Clk. + } + self.comb += Case(clk_phase, cases) # Data Shift-In Register ------------------------------------------------------------------- dqi = Signal(dw) From a30651e44e87ef229f53a55df5f2b6ef310ae87b Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 20 Aug 2024 14:44:33 +0200 Subject: [PATCH 10/16] soc/cores/hyperbus: Avoid waiting for clk_phase in IDLE state to reduce latency. --- litex/soc/cores/hyperbus.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/litex/soc/cores/hyperbus.py b/litex/soc/cores/hyperbus.py index e73c46d9d..1895186c1 100644 --- a/litex/soc/cores/hyperbus.py +++ b/litex/soc/cores/hyperbus.py @@ -133,12 +133,20 @@ class HyperRAM(LiteXModule): self.burst_timer = burst_timer = WaitTimer(sys_clk_freq * self.tCSM) # Clock Generation (sys_clk/4) ------------------------------------------------------------- - self.sync += clk_phase.eq(clk_phase + 1) + self.sync += [ + If(cs, + # Increment Clk Phase on CS. + clk_phase.eq(clk_phase + 1) + ).Else( + # Else set Clk Phase to default value. + clk_phase.eq(0b01) + ) + ] cases = { - 0 : clk.eq(0), # 0° - 1 : clk.eq(cs), # 90° / Set Clk. - 2 : clk.eq(cs), # 180° - 3 : clk.eq(0), # 270° / Clr Clk. + 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) @@ -237,11 +245,9 @@ class HyperRAM(LiteXModule): self.fsm = fsm = FSM(reset_state="IDLE") fsm.act("IDLE", NextValue(first, 1), - If(clk_phase == 0, - If((bus.cyc & bus.stb) | reg_write_req | reg_read_req, - NextValue(sr, ca), - NextState("SEND-COMMAND-ADDRESS") - ) + If((bus.cyc & bus.stb) | reg_write_req | reg_read_req, + NextValue(sr, ca), + NextState("SEND-COMMAND-ADDRESS") ) ) fsm.act("SEND-COMMAND-ADDRESS", From b0026937c1c49699d1b2e6482024732efd8d4647 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 20 Aug 2024 14:58:51 +0200 Subject: [PATCH 11/16] soc/software/libbase: Move HyperRAM init code to libbase/hyperram.c. --- litex/soc/software/bios/main.c | 85 ++----------------------- litex/soc/software/libbase/Makefile | 3 +- litex/soc/software/libbase/hyperram.c | 89 +++++++++++++++++++++++++++ litex/soc/software/libbase/hyperram.h | 17 +++++ 4 files changed, 112 insertions(+), 82 deletions(-) create mode 100644 litex/soc/software/libbase/hyperram.c create mode 100644 litex/soc/software/libbase/hyperram.h diff --git a/litex/soc/software/bios/main.c b/litex/soc/software/bios/main.c index e576b6878..3a86cca36 100644 --- a/litex/soc/software/bios/main.c +++ b/litex/soc/software/bios/main.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -173,88 +174,10 @@ __attribute__((__used__)) int main(int i, char **c) printf("\n"); #endif - sdr_ok = 1; + sdr_ok = 1; -#ifdef CSR_HYPERRAM_BASE /* FIXME: Move to libbase/hyperram.h/c? */ - /* Helper Functions */ - - printf("HyperRAM init...\n"); - void hyperram_write_reg(uint16_t reg_addr, uint16_t data) { - /* Write data to the register */ - hyperram_reg_wdata_write(data); - hyperram_reg_control_write( - 1 << CSR_HYPERRAM_REG_CONTROL_WRITE_OFFSET | - 0 << CSR_HYPERRAM_REG_CONTROL_READ_OFFSET | - reg_addr << CSR_HYPERRAM_REG_CONTROL_ADDR_OFFSET - ); - /* Wait for write to complete */ - while ((hyperram_reg_status_read() & (1 << CSR_HYPERRAM_REG_STATUS_WRITE_DONE_OFFSET)) == 0); - } - - uint16_t hyperram_read_reg(uint16_t reg_addr) { - /* Read data from the register */ - hyperram_reg_control_write( - 0 << CSR_HYPERRAM_REG_CONTROL_WRITE_OFFSET | - 1 << CSR_HYPERRAM_REG_CONTROL_READ_OFFSET | - reg_addr << CSR_HYPERRAM_REG_CONTROL_ADDR_OFFSET - ); - /* Wait for read to complete */ - while ((hyperram_reg_status_read() & (1 << CSR_HYPERRAM_REG_STATUS_READ_DONE_OFFSET)) == 0); - return hyperram_reg_rdata_read(); - } - - /* Configuration and Utility Functions */ - - uint16_t hyperram_get_core_latency_setting(uint32_t clk_freq) { - /* Raw clock latency settings for the HyperRAM core */ - if (clk_freq <= 85000000) return 3; /* 3 Clock Latency */ - if (clk_freq <= 104000000) return 4; /* 4 Clock Latency */ - if (clk_freq <= 133000000) return 5; /* 5 Clock Latency */ - if (clk_freq <= 166000000) return 6; /* 6 Clock Latency */ - if (clk_freq <= 250000000) return 7; /* 7 Clock Latency */ - return 7; /* Default to highest latency for safety */ - } - - uint16_t hyperram_get_chip_latency_setting(uint32_t clk_freq) { - /* LUT/Translated settings for the HyperRAM chip */ - if (clk_freq <= 85000000) return 0b1110; /* 3 Clock Latency */ - if (clk_freq <= 104000000) return 0b1111; /* 4 Clock Latency */ - if (clk_freq <= 133000000) return 0b0000; /* 5 Clock Latency */ - if (clk_freq <= 166000000) return 0b0001; /* 6 Clock Latency */ - if (clk_freq <= 250000000) return 0b0010; /* 7 Clock Latency */ - return 0b0010; /* Default to highest latency for safety */ - } - - void hyperram_configure_latency(void) { - uint16_t config_reg_0 = 0x8f2f; - uint16_t core_latency_setting; - uint16_t chip_latency_setting; - - /* Compute Latency settings */ - core_latency_setting = hyperram_get_core_latency_setting(CONFIG_CLOCK_FREQUENCY/4); - chip_latency_setting = hyperram_get_chip_latency_setting(CONFIG_CLOCK_FREQUENCY/4); - - /* Write Latency to HyperRAM Core */ - printf("HyperRAM Core Latency: %d CK (X1).\n", core_latency_setting); - hyperram_config_write(core_latency_setting << CSR_HYPERRAM_CONFIG_LATENCY_OFFSET); - - /* Enable Variable Latency on HyperRAM Chip */ - 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"); +#ifdef CSR_HYPERRAM_BASE + hyperram_init(); #endif #if defined(CSR_ETHMAC_BASE) || defined(MAIN_RAM_BASE) || defined(CSR_SPIFLASH_CORE_BASE) diff --git a/litex/soc/software/libbase/Makefile b/litex/soc/software/libbase/Makefile index 6b8736b17..fab41e466 100755 --- a/litex/soc/software/libbase/Makefile +++ b/litex/soc/software/libbase/Makefile @@ -11,7 +11,8 @@ OBJECTS = \ uart.o \ spiflash.o \ i2c.o \ - isr.o + isr.o \ + hyperram.o all: libbase.a diff --git a/litex/soc/software/libbase/hyperram.c b/litex/soc/software/libbase/hyperram.c new file mode 100644 index 000000000..da0cd5242 --- /dev/null +++ b/litex/soc/software/libbase/hyperram.c @@ -0,0 +1,89 @@ +// This file is Copyright (c) 2024 Florent Kermarrec +// License: BSD + +#include + +#include + +#include + +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"); +} diff --git a/litex/soc/software/libbase/hyperram.h b/litex/soc/software/libbase/hyperram.h new file mode 100644 index 000000000..1c8a7d1e7 --- /dev/null +++ b/litex/soc/software/libbase/hyperram.h @@ -0,0 +1,17 @@ +// This file is Copyright (c) 2024 Florent Kermarrec +// License: BSD + +#ifndef __HYPERRAM_H +#define __HYPERRAM_H + +#ifdef __cplusplus +extern "C" { +#endif + +void hyperram_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __HYPERRAM_H */ From 76cf0049132f21c5eceea87a94fcbb1c976883f6 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 20 Aug 2024 15:17:36 +0200 Subject: [PATCH 12/16] test/test_hyperbus: Update. --- test/test_hyperbus.py | 30 ++++++------------------------ 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/test/test_hyperbus.py b/test/test_hyperbus.py index e52ca3033..03fc4b805 100644 --- a/test/test_hyperbus.py +++ b/test/test_hyperbus.py @@ -45,8 +45,6 @@ class TestHyperBus(unittest.TestCase): 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)) @@ -71,8 +69,6 @@ class TestHyperBus(unittest.TestCase): dq_o = "002000048d000000000000000000000000000000000000000000000000deadbeef000000" 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)) @@ -97,8 +93,6 @@ class TestHyperBus(unittest.TestCase): 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)) @@ -123,8 +117,6 @@ class TestHyperBus(unittest.TestCase): 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)) @@ -151,8 +143,6 @@ class TestHyperBus(unittest.TestCase): 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)) @@ -179,8 +169,6 @@ class TestHyperBus(unittest.TestCase): dq_o = "00a000048d000000000000000000000000000000000000000000000000000000000000000000000000" dq_i = "0000000000000000000000000000000000000000000000000000000000deadbeefcafefade00000000" 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)) @@ -207,8 +195,6 @@ class TestHyperBus(unittest.TestCase): 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)) @@ -235,8 +221,6 @@ class TestHyperBus(unittest.TestCase): 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)) @@ -261,14 +245,12 @@ class TestHyperBus(unittest.TestCase): yield def hyperram_gen(dut): - clk = "___--__--__--__--___________" - cs_n = "--________________----------" - dq_oe = "__----------------__________" - dq_o = "0060000100000012340000000000" - rwds_oe = "____________________________" - rwds_o = "____________________________" - for i in range(3): - yield + clk = "_____--__--__--__--___________" + cs_n = "----________________----------" + dq_oe = "____----------------__________" + dq_o = "000060000100000012340000000000" + 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)) From 8b86b16077c546aa6ac313cbca66927866f2ae73 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 20 Aug 2024 15:26:26 +0200 Subject: [PATCH 13/16] soc/cores/hyperbus: Make Rst synchronous to allow IO Reg (even if low speed). --- litex/soc/cores/hyperbus.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litex/soc/cores/hyperbus.py b/litex/soc/cores/hyperbus.py index 1895186c1..313030105 100644 --- a/litex/soc/cores/hyperbus.py +++ b/litex/soc/cores/hyperbus.py @@ -107,7 +107,7 @@ class HyperRAM(LiteXModule): # Rst. 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. self.comb += [ From 1998c7454977fc978341430ee4f1c24f72102e7d Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 20 Aug 2024 15:44:37 +0200 Subject: [PATCH 14/16] soc/cores/hyperbus: Make DQ/RWDS input sync explicit to allow IO Reg. --- litex/soc/cores/hyperbus.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/litex/soc/cores/hyperbus.py b/litex/soc/cores/hyperbus.py index 313030105..04670d34f 100644 --- a/litex/soc/cores/hyperbus.py +++ b/litex/soc/cores/hyperbus.py @@ -92,15 +92,20 @@ class HyperRAM(LiteXModule): 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. + # DQ O/OE. dq.o.eq( dq_o), dq.oe.eq(dq_oe), - dq_i.eq( dq.i), - # RWDS. + # RWDS O/OE. rwds.o.eq( rwds_o), rwds.oe.eq(rwds_oe), - rwds_i.eq( rwds.i), + ] + self.sync += [ + # DQ I. + dq_i.eq(dq.i), + + # RWDS I. + rwds_i.eq(rwds.i) ] # Drive Control Signals -------------------------------------------------------------------- @@ -151,20 +156,18 @@ class HyperRAM(LiteXModule): self.comb += Case(clk_phase, cases) # Data Shift-In Register ------------------------------------------------------------------- - dqi = Signal(dw) - self.sync += dqi.eq(dq_i) # Sample on 90° and 270° Clk Phases. 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(dqi), + 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° and 180° Clk Phases. + self.sync += If(clk_phase[0] == 0, sr.eq(sr_next)) # Shift on 0°/180° (and sampled on 90°/270°). # Data Shift-Out Register ------------------------------------------------------------------ self.comb += [ From eb29b40e077f40f00690e6b4922c8ed0568a9ddb Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 20 Aug 2024 16:19:15 +0200 Subject: [PATCH 15/16] soc/cores/hyperbus: Simplify CS and make it synchronous to allow IO Reg. --- litex/soc/cores/hyperbus.py | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/litex/soc/cores/hyperbus.py b/litex/soc/cores/hyperbus.py index 04670d34f..a4d0640e9 100644 --- a/litex/soc/cores/hyperbus.py +++ b/litex/soc/cores/hyperbus.py @@ -115,12 +115,8 @@ class HyperRAM(LiteXModule): self.sync += pads.rst_n.eq(1 & ~self.conf_rst) # CSn. - self.comb += [ - # Set reset value. - pads.cs_n.eq(2**len(pads.cs_n)), - # Set CSn. - pads.cs_n[0].eq(~cs) - ] + pads.cs_n.reset = 2**len(pads.cs_n) - 1 + self.sync += pads.cs_n[0].eq(~cs) # Only supporting 1 CS. # Clk. pads_clk = Signal() @@ -144,7 +140,7 @@ class HyperRAM(LiteXModule): clk_phase.eq(clk_phase + 1) ).Else( # Else set Clk Phase to default value. - clk_phase.eq(0b01) + clk_phase.eq(0b00) ) ] cases = { @@ -254,8 +250,6 @@ class HyperRAM(LiteXModule): ) ) fsm.act("SEND-COMMAND-ADDRESS", - # Set CSn. - cs.eq(1), # Send Command on DQ. ca_oe.eq(1), dq_oe.eq(1), @@ -272,8 +266,6 @@ class HyperRAM(LiteXModule): ) ) fsm.act("REG-WRITE-0", - # Set CSn. - cs.eq(1), # Send Reg on DQ. ca_oe.eq(1), dq_oe.eq(1), @@ -284,8 +276,6 @@ class HyperRAM(LiteXModule): ) ) fsm.act("REG-WRITE-1", - # Set CSn. - cs.eq(1), # Send Reg on DQ. ca_oe.eq(1), dq_oe.eq(1), @@ -297,8 +287,6 @@ class HyperRAM(LiteXModule): ) ) 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). 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). @@ -316,8 +304,6 @@ class HyperRAM(LiteXModule): fsm.act(f"READ-WRITE-DATA{n}", # Enable Burst Timer. burst_timer.wait.eq(1), - # Set CSn. - cs.eq(1), ca_oe.eq(reg_read_req), # Send Data on DQ/RWDS (for write). If(bus_we, @@ -356,6 +342,13 @@ 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() self.sync += cycles.eq(cycles + 1) self.sync += If(fsm.next_state != fsm.state, cycles.eq(0)) From 3a37d3ba98aeb13a9834704c7b6acd24c2c01bad Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 20 Aug 2024 17:11:02 +0200 Subject: [PATCH 16/16] software/libbase/hyperram: Add missing #ifdef. --- litex/soc/software/libbase/hyperram.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/litex/soc/software/libbase/hyperram.c b/litex/soc/software/libbase/hyperram.c index da0cd5242..dc8a3e09c 100644 --- a/litex/soc/software/libbase/hyperram.c +++ b/litex/soc/software/libbase/hyperram.c @@ -7,6 +7,8 @@ #include +#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); @@ -87,3 +89,5 @@ void hyperram_init(void) { hyperram_configure_latency(); printf("\n"); } + +#endif \ No newline at end of file