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:
parent
44746870a7
commit
4fe31f0760
|
@ -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")
|
||||||
|
)
|
||||||
|
)
|
|
@ -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)])
|
Loading…
Reference in New Issue