cores: add External Memory Interface (EMIF) Wishbone bridge.

Useful to interface Processors/DSPs with LiteX. EMIF is generally used on Texas Instrument DSPs.
This commit is contained in:
Florent Kermarrec 2020-04-12 16:34:33 +02:00
parent 44746870a7
commit 4fe31f0760
2 changed files with 241 additions and 0 deletions

150
litex/soc/cores/emif.py Normal file
View File

@ -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")
)
)

91
test/test_emif.py Normal file
View File

@ -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)])