From 4fe31f0760ca98994d4b8c9b867cfff686412c7d Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Sun, 12 Apr 2020 16:34:33 +0200 Subject: [PATCH] cores: add External Memory Interface (EMIF) Wishbone bridge. Useful to interface Processors/DSPs with LiteX. EMIF is generally used on Texas Instrument DSPs. --- litex/soc/cores/emif.py | 150 ++++++++++++++++++++++++++++++++++++++++ test/test_emif.py | 91 ++++++++++++++++++++++++ 2 files changed, 241 insertions(+) create mode 100644 litex/soc/cores/emif.py create mode 100644 test/test_emif.py diff --git a/litex/soc/cores/emif.py b/litex/soc/cores/emif.py new file mode 100644 index 000000000..db7c48c81 --- /dev/null +++ b/litex/soc/cores/emif.py @@ -0,0 +1,150 @@ +# This file is Copyright (c) 2020 Florent Kermarrec +# 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 index 000000000..c7793f4a2 --- /dev/null +++ b/test/test_emif.py @@ -0,0 +1,91 @@ +# This file is Copyright (c) 2020 Florent Kermarrec +# 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<> 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<>= 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)])