cores: add External Memory Interface (EMIF) Wishbone bridge.
authorFlorent Kermarrec <florent@enjoy-digital.fr>
Sun, 12 Apr 2020 14:34:33 +0000 (16:34 +0200)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Sun, 12 Apr 2020 14:34:33 +0000 (16:34 +0200)
Useful to interface Processors/DSPs with LiteX. EMIF is generally used on Texas Instrument DSPs.

litex/soc/cores/emif.py [new file with mode: 0644]
test/test_emif.py [new file with mode: 0644]

diff --git a/litex/soc/cores/emif.py b/litex/soc/cores/emif.py
new file mode 100644 (file)
index 0000000..db7c48c
--- /dev/null
@@ -0,0 +1,150 @@
+# This file is Copyright (c) 2020 Florent Kermarrec <florent@enjoy-digital.fr>
+# License: BSD
+
+from migen import *
+from migen.genlib.cdc import MultiReg
+
+from litex.soc.interconnect import wishbone
+
+
+class EMIF(Module):
+    """External Memory Interface core
+
+    Provides a simple EMIF to Wishbone Master bridge.
+    """
+    def __init__(self, pads):
+        self.bus = bus = wishbone.Interface()
+
+        # # #
+
+        # Resynchronization ------------------------------------------------------------------------
+        cs_n   = Signal(reset=1)
+        oe_n   = Signal(reset=1)
+        we_n   = Signal(reset=1)
+        ba     = Signal(2)
+        addr   = Signal(22)
+        dqm_n  = Signal(2)
+        data   = self.add_tristate(pads.data) if not hasattr(pads.data, "oe") else pads.data
+        data_i = Signal(16)
+        self.specials += [
+            MultiReg(pads.cs_n, cs_n),
+            MultiReg(pads.oe_n, oe_n),
+            MultiReg(pads.we_n, we_n),
+            MultiReg(pads.ba,   ba),
+            MultiReg(pads.addr, addr),
+            MultiReg(data.i,    data_i),
+        ]
+
+        # EMIF <--> Wishbone -----------------------------------------------------------------------
+        access = Signal()
+        we_n_d = Signal()
+        oe_n_d = Signal()
+        self.sync += [
+            we_n_d.eq(we_n),
+            oe_n_d.eq(oe_n),
+            If(~we_n & we_n_d,
+                access.eq(1)
+            ).Elif(~oe_n & oe_n_d,
+                access.eq(1)
+            ).Elif(bus.ack,
+                access.eq(0)
+            )
+        ]
+        self.comb += [
+            bus.stb.eq(~cs_n  & access),
+            bus.cyc.eq(~cs_n  & access),
+            bus.we.eq(~we_n),
+            bus.adr.eq(addr),
+            data.oe.eq(~oe_n),
+            If(ba[1],
+                bus.dat_w[:16].eq(data_i),
+                bus.sel[:2].eq(~dqm_n)
+            ).Else(
+                bus.dat_w[16:].eq(data_i),
+                bus.sel[2:].eq(~dqm_n)
+            )
+        ]
+        self.sync += [
+            If(bus.ack,
+                If(ba[1],
+                    data.o.eq(bus.dat_r[:16])
+                ).Else(
+                    data.o.eq(bus.dat_r[16:])
+                )
+            )
+        ]
+
+    def add_tristate(self, pad):
+        t = TSTriple(len(pad))
+        self.specials += t.get_tristate(pad)
+        return t
+
+
+class EMIF16To32Adapter(Module):
+    def __init__(self, emif):
+        self.bus = bus = wishbone.Interface()
+
+        # # #
+
+        self.submodules.fsm = fsm = FSM(reset_state="IDLE")
+        fsm.act("IDLE",
+            If(emif.bus.stb & emif.bus.cyc,
+                If(emif.bus.we,
+                    NextState("WRITE0")
+                ).Else(
+                    NextState("READ")
+                )
+            )
+        )
+        emif_bus_dat_w = Signal(32)
+        fsm.act("WRITE0",
+            emif.bus.ack.eq(1),
+            If(emif.bus.sel[0],
+                NextValue(emif_bus_dat_w[8*0:8*1], emif.bus.dat_w[8*0:8*1])
+            ),
+            If(emif.bus.sel[1],
+                NextValue(emif_bus_dat_w[8*1:8*2], emif.bus.dat_w[8*1:8*2])
+            ),
+            If(emif.bus.sel[2],
+                NextValue(emif_bus_dat_w[8*2:8*3], emif.bus.dat_w[8*2:8*3])
+            ),
+            If(emif.bus.sel[3],
+                NextValue(emif_bus_dat_w[8*3:8*4], emif.bus.dat_w[8*3:8*4])
+            ),
+            NextState("WRITE1"),
+        )
+        fsm.act("WRITE1",
+            bus.stb.eq(emif.bus.stb & emif.bus.cyc),
+            bus.we.eq(1),
+            bus.cyc.eq(emif.bus.stb & emif.bus.cyc),
+            bus.adr.eq(emif.bus.adr),
+            bus.sel.eq(0b1111),
+            bus.dat_w.eq(emif_bus_dat_w),
+            If(emif.bus.sel[0],
+                bus.dat_w[8*0:8*1].eq(emif.bus.dat_w[8*0:8*1])
+            ),
+            If(emif.bus.sel[1],
+                bus.dat_w[8*1:8*2].eq(emif.bus.dat_w[8*1:8*2])
+            ),
+            If(emif.bus.sel[2],
+                bus.dat_w[8*2:8*3].eq(emif.bus.dat_w[8*2:8*3])
+            ),
+            If(emif.bus.sel[3],
+                bus.dat_w[8*3:8*4].eq(emif.bus.dat_w[8*3:8*4])
+            ),
+            If(bus.stb & bus.ack,
+                emif.bus.ack.eq(1),
+                NextState("IDLE")
+            )
+        )
+        fsm.act("READ",
+            bus.stb.eq(1),
+            bus.we.eq(0),
+            bus.cyc.eq(1),
+            bus.adr.eq(emif.bus.adr),
+            If(bus.ack,
+                emif.bus.ack.eq(1),
+                emif.bus.dat_r.eq(bus.dat_r),
+                NextState("IDLE")
+            )
+        )
diff --git a/test/test_emif.py b/test/test_emif.py
new file mode 100644 (file)
index 0000000..c7793f4
--- /dev/null
@@ -0,0 +1,91 @@
+# This file is Copyright (c) 2020 Florent Kermarrec <florent@enjoy-digital.fr>
+# License: BSD
+
+import unittest
+
+from migen import *
+
+from litex.soc.interconnect import wishbone
+
+from litex.soc.cores.emif import EMIF
+
+
+class EMIFPads:
+    def __init__(self):
+        self.cs_n   = Signal(reset=1)
+        self.we_n   = Signal(reset=1)
+        self.oe_n   = Signal(reset=1)
+        self.wait_n = Signal(reset=1)
+        self.ba     = Signal(2)
+        self.addr   = Signal(22)
+        self.dqm_n  = Signal(2)
+        self.data   = Record([("oe", 1), ("o", 16), ("i", 16)])
+
+
+def emif_write(pads, addr, data, release_cs=True):
+    for i in range(2):
+        yield pads.cs_n.eq(0)
+        yield pads.we_n.eq(1)
+        yield pads.oe_n.eq(1)
+        yield pads.ba.eq(1<<i)
+        yield pads.addr.eq(addr)
+        yield pads.data.i.eq((data >> 16*i) & 0xffff)
+        yield
+        yield pads.we_n.eq(0)
+        for i in range(8):
+            yield
+        yield pads.we_n.eq(1)
+        yield
+        yield pads.cs_n.eq(release_cs)
+        yield
+
+
+def emif_read(pads, addr, release_cs=True, release_oe=True):
+    data = 0
+    for i in range(2):
+        yield pads.cs_n.eq(0)
+        yield pads.we_n.eq(1)
+        yield pads.oe_n.eq(release_oe)
+        yield pads.ba.eq(1<<i)
+        yield pads.addr.eq(addr)
+        yield
+        yield pads.oe_n.eq(0)
+        for i in range(8):
+            yield
+        data >>= 16
+        data |= (yield pads.data.o) << 16
+        yield pads.oe_n.eq(release_oe)
+        yield
+        yield pads.cs_n.eq(release_cs)
+        yield
+    return data
+
+
+class TestEMIF(unittest.TestCase):
+    def test_emif(self):
+        pads = EMIFPads()
+        def generator(dut):
+            # Test writes/reads with cs release between accesses
+            yield from emif_write(pads, 0, 0xdeadbeef, True)
+            yield from emif_write(pads, 1, 0x12345678, True)
+            yield from emif_write(pads, 2, 0x5aa55aa5, True)
+            self.assertEqual((yield from emif_read(pads, 0, True)), 0xdeadbeef)
+            self.assertEqual((yield from emif_read(pads, 1, True)), 0x12345678)
+            self.assertEqual((yield from emif_read(pads, 2, True)), 0x5aa55aa5)
+
+            # Test writes/reads without cs release between accesses
+            yield from emif_write(pads, 0, 0xdeadbeef, False)
+            yield from emif_write(pads, 1, 0x12345678, False)
+            yield from emif_write(pads, 2, 0x5aa55aa5, False)
+            self.assertEqual((yield from emif_read(pads, 0, False)), 0xdeadbeef)
+            self.assertEqual((yield from emif_read(pads, 1, False)), 0x12345678)
+            self.assertEqual((yield from emif_read(pads, 2, False)), 0x5aa55aa5)
+
+        class DUT(Module):
+            def __init__(self, pads):
+                emif = EMIF(pads)
+                self.submodules += emif
+                mem = wishbone.SRAM(16, bus=emif.bus)
+                self.submodules += mem
+        dut = DUT(pads)
+        run_simulation(dut, [generator(dut)])