- rename core to hyperbus.
- change layout (cs_n with variable length instead of cs0_n, cs1_n).
- use DifferentialOutput when differential clock is used.
- add test (python3 -m unittest test.test_hyperbus).
Usage example:
from litex.soc.cores.hyperbus import HyperRAM
self.submodules.hyperram = HyperRAM(platform.request("hyperram"))
self.add_wb_slave(mem_decoder(self.mem_map["hyperram"]), self.hyperram.bus)
self.add_memory_region("hyperram", self.mem_map["hyperram"], 8*1024*1024)
+++ /dev/null
-# This file is Copyright (c) 2019 Antti Lukats <antti.lukats@gmail.com>
-# This file is Copyright (c) 2016-2019 Florent Kermarrec <florent@enjoy-digital.fr>
-
-# License: BSD
-
-#
-#
-#
-
-from migen import *
-from migen.genlib.misc import timeline
-
-from litex.gen import *
-
-from litex.soc.interconnect import wishbone
-from litex.soc.interconnect.csr import *
-
-
-class HyperMemporyCommon(Module):
- def __init__(self, pads):
- self.pads = pads
-
-class HyperRAM(HyperMemporyCommon):
- def __init__(self, pads):
- """
- HyperRAM simple core for LiteX
- This core should always just work on any FPGA platorm it is fully vendor neutral
- No configuration, no software setup, ready after poweron, fixed latency
-
- """
- HyperMemporyCommon.__init__(self, pads)
-
- if hasattr(pads, "rst_n"):
- self.comb += pads.rst_n.eq(1)
- if hasattr(pads, "cs1_n"):
- self.comb += pads.cs1_n.eq(1)
-
- # Tristate pads
- dq = TSTriple(8)
- self.specials.dq = dq.get_tristate(pads.dq)
- rwds = TSTriple(1)
- self.specials.rwds = rwds.get_tristate(pads.rwds)
-
- # Wishbone
- self.bus = bus = wishbone.Interface()
- sr = Signal(48)
-
- dq_oe = Signal(reset=0)
- rwds_oe = Signal(reset=0)
- cs_int = Signal(reset=1)
-
- self.comb += [
- bus.dat_r.eq(sr),
- dq.oe.eq(dq_oe),
- dq.o.eq(sr[-8:]),
- rwds.oe.eq(rwds_oe),
- pads.cs0_n.eq(cs_int)
- ]
-
- # we generate complementaty clk out for emulated differential output
- clk_p = Signal(1)
- clk_n = Signal(1)
-
- self.comb += pads.clk.eq(clk_p)
- # if negative is defined drive complementary clock out
- if hasattr(pads, "clk_n"):
- self.comb += pads.clk_n.eq(clk_n)
- # 1 sys clock delay needed to adjust input timings?
- dqi = Signal(8)
- self.sync += [
- dqi.eq(dq.i)
- ]
- # hyper RAM clock generator and 48 bit byte shifter
- i = Signal(max=4)
- self.sync += [
- If(i == 0,
- sr.eq(Cat(dqi, sr[:-8])),
- ),
- If(i == 1,
- clk_p.eq(~cs_int), # 1
- clk_n.eq(cs_int) # 0
- ),
- If(i == 2,
- sr.eq(Cat(dqi, sr[:-8]))
- ),
- If(i == 3,
- i.eq(0),
- clk_p.eq(0), # 1
- clk_n.eq(1) # 0
- ).Else(
- i.eq(i + 1)
- )
- ]
- # signals to use CA or data to write
- CA = Signal(48)
- # combine bits to create CA bytes
- self.comb += [
- CA[47].eq(~self.bus.we),
- CA[45].eq(1),
- CA[16:35].eq(self.bus.adr[2:21]),
- CA[1:3].eq(self.bus.adr[0:2]),
- CA[0].eq(0),
- ]
- z = Replicate(0, 16)
- seq = [
- (3, []),
- (12, [cs_int.eq(0), dq_oe.eq(1), sr.eq(CA)]), # 6 clock edges for command transmit
- (44, [dq_oe.eq(0)]), # 6+6 latency default
- (2, [dq_oe.eq(self.bus.we), rwds_oe.eq(self.bus.we), rwds.o.eq(~bus.sel[0]), sr.eq(Cat(z, self.bus.dat_w))]), # 4 edges to write data
- (2, [rwds.o.eq(~bus.sel[1])]), # 4 edges to write data
- (2, [rwds.o.eq(~bus.sel[2])]), # 4 edges to write data
- (2, [rwds.o.eq(~bus.sel[3])]), # 4 edges to write data
- (2, [cs_int.eq(1), rwds_oe.eq(0), dq_oe.eq(0)]),
- (1, [bus.ack.eq(1)]), # is 1 also OK?
- (1, [bus.ack.eq(0)]), #
- (0, []),
- ]
-
- t, tseq = 0, []
- for dt, a in seq:
- tseq.append((t, a))
- t += dt
-
- self.sync += timeline(bus.cyc & bus.stb & (i == 1), tseq)
-
--- /dev/null
+# This file is Copyright (c) 2019 Antti Lukats <antti.lukats@gmail.com>
+# This file is Copyright (c) 2019 Florent Kermarrec <florent@enjoy-digital.fr>
+# License: BSD
+
+from migen import *
+from migen.genlib.misc import timeline
+from migen.genlib.io import DifferentialOutput
+
+from litex.soc.interconnect import wishbone
+
+# HyperRAM -----------------------------------------------------------------------------------------
+
+class HyperRAM(Module):
+ """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).
+
+ This core favrors portability and ease of use over performance.
+ """
+ def __init__(self, pads):
+ self.pads = pads
+ self.bus = bus = wishbone.Interface()
+
+ # # #
+
+ clk = Signal()
+ clk_phase = Signal(2)
+ cs = Signal()
+ ca = Signal(48)
+ sr = 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
+
+ # Drive rst_n, cs_n, clk from internal signals ---------------------------------------------
+ if hasattr(pads, "rst_n"):
+ self.comb += pads.rst_n.eq(1)
+ 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)
+ if hasattr(pads, "clk"):
+ self.comb += pads.clk.eq(clk)
+ else:
+ self.specials += DifferentialOutput(clk, pads.clk_p, pads.clk_n)
+
+ # 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°
+ self.sync += Case(clk_phase, cases)
+
+ # Data Shift Register (for write and read) -------------------------------------------------
+ dqi = Signal(8)
+ self.sync += dqi.eq(dq.i) # Sample on 90° and 270°
+ cases = {}
+ cases[0] = sr.eq(Cat(dqi, sr[:-8])) # Shift on 0°
+ cases[2] = sr.eq(Cat(dqi, sr[:-8])) # Shift on 180°
+ self.sync += Case(clk_phase, cases)
+ self.comb += [
+ bus.dat_r.eq(sr), # To Wisbone
+ dq.o.eq(sr[-8:]), # To HyperRAM
+ ]
+
+ # Command generation -----------------------------------------------------------------------
+ self.comb += [
+ ca[47].eq(~self.bus.we), # R/W#
+ ca[45].eq(1), # Burst Type (Linear)
+ ca[16:35].eq(self.bus.adr[2:21]), # Row & Upper Column Address
+ ca[1:3].eq(self.bus.adr[0:2]), # Lower Column Address
+ ca[0].eq(0), # Lower Column Address
+ ]
+
+ # Sequencer --------------------------------------------------------------------------------
+ dt_seq = [
+ # DT, Action
+ (3, []),
+ (12, [cs.eq(1), dq.oe.eq(1), sr.eq(ca)]), # Command: 6 clk
+ (44, [dq.oe.eq(0)]), # Latency(default): 2*6 clk
+ (2, [dq.oe.eq(self.bus.we), # Write/Read data byte: 2 clk
+ sr[:16].eq(0),
+ sr[16:].eq(self.bus.dat_w),
+ rwds.oe.eq(self.bus.we),
+ rwds.o.eq(~bus.sel[0])]),
+ (2, [rwds.o.eq(~bus.sel[1])]), # Write/Read data byte: 2 clk
+ (2, [rwds.o.eq(~bus.sel[2])]), # Write/Read data byte: 2 clk
+ (2, [rwds.o.eq(~bus.sel[3])]), # Write/Read data byte: 2 clk
+ (2, [cs.eq(0), rwds.oe.eq(0), dq.oe.eq(0)]),
+ (1, [bus.ack.eq(1)]),
+ (1, [bus.ack.eq(0)]),
+ (0, []),
+ ]
+ # Convert delta-time sequencer to time sequencer
+ t_seq = []
+ t_seq_start = (clk_phase == 1)
+ t = 0
+ for dt, a in dt_seq:
+ t_seq.append((t, a))
+ t += dt
+ self.sync += timeline(bus.cyc & bus.stb & t_seq_start, t_seq)
+
+ def add_tristate(self, pad):
+ t = TSTriple(len(pad))
+ self.specials += t.get_tristate(pad)
+ return t
--- /dev/null
+# This file is Copyright (c) 2019 Florent Kermarrec <florent@enjoy-digital.fr>
+# License: BSD
+
+import unittest
+
+from migen import *
+
+from litex.soc.cores.hyperbus import HyperRAM
+
+def c2bool(c):
+ return {"-": 1, "_": 0}[c]
+
+
+class Pads: pass
+
+
+class HyperRamPads:
+ def __init__(self):
+ self.clk = Signal()
+ self.cs_n = Signal()
+ self.dq = Record([("oe", 1), ("o", 8), ("i", 8)])
+ self.rwds = Record([("oe", 1), ("o", 1), ("i", 1)])
+
+
+class TestHyperBus(unittest.TestCase):
+ def test_hyperram_syntax(self):
+ pads = Record([("clk", 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)])
+ hyperram = HyperRAM(pads)
+
+ def test_hyperram_write(self):
+ def fpga_gen(dut):
+ yield from dut.bus.write(0x1234, 0xdeadbeef)
+ yield
+
+ def hyperram_gen(dut):
+ clk = "___--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--_______"
+ cs_n = "--________________________________________________________________------"
+ dq_oe = "__------------____________________________________________--------______"
+ 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))
+ 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())
+ run_simulation(dut, [fpga_gen(dut), hyperram_gen(dut)])
+
+ def test_hyperram_read(self):
+ def fpga_gen(dut):
+ dat = yield from dut.bus.read(0x1234)
+ self.assertEqual(dat, 0xdeadbeef)
+
+ def hyperram_gen(dut):
+ clk = "___--__--__--__--__--__--__--__--__--__--__--__--__--__--__--__--_______"
+ cs_n = "--________________________________________________________________------"
+ dq_oe = "__------------__________________________________________________________"
+ dq_o = "00a000048d00000000000000000000000000000000000000000000000000000000000000"
+ dq_i = "0000000000000000000000000000000000000000000000000000000000deadbeef000000"
+ 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())
+ run_simulation(dut, [fpga_gen(dut), hyperram_gen(dut)])