mirror of
https://github.com/enjoy-digital/litedram.git
synced 2025-01-04 09:52:25 -05:00
frontend/wishbone: simplify by reusing LiteDRAMNativePortConverter
This commit is contained in:
parent
b0bde294c0
commit
22bd01c014
4 changed files with 68 additions and 70 deletions
|
@ -173,6 +173,7 @@ class LiteDRAMNativePortUpConverter(Module):
|
|||
rdata_finished = Signal()
|
||||
# used to prevent reading old memory value if previous command has written the same address
|
||||
read_lock = Signal()
|
||||
read_unlocked = Signal()
|
||||
rw_collision = Signal()
|
||||
|
||||
# different order depending on read/write:
|
||||
|
@ -239,15 +240,21 @@ class LiteDRAMNativePortUpConverter(Module):
|
|||
# * cmd type changes
|
||||
# * we received all the `ratio` commands
|
||||
# * this is the last command in a sequence
|
||||
next_cmd.eq(addr_changed | (cmd_we != port_from.cmd.we) | (sel == 2**ratio - 1) | cmd_last),
|
||||
# * master requests a flush (even after the command has been sent)
|
||||
next_cmd.eq(addr_changed | (cmd_we != port_from.cmd.we) | (sel == 2**ratio - 1)
|
||||
| cmd_last | port_from.flush),
|
||||
]
|
||||
|
||||
self.sync += [
|
||||
# block sending read command if we have just written to that address
|
||||
If(wdata_finished,
|
||||
read_lock.eq(0),
|
||||
).Elif(rw_collision & ~port_to.cmd.valid,
|
||||
read_unlocked.eq(1),
|
||||
).Elif(rw_collision & ~port_to.cmd.valid & ~read_unlocked,
|
||||
read_lock.eq(1)
|
||||
),
|
||||
If(port_from.cmd.valid & port_from.cmd.ready,
|
||||
read_unlocked.eq(0)
|
||||
)
|
||||
]
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@ from math import log2
|
|||
from migen import *
|
||||
|
||||
from litex.soc.interconnect import stream
|
||||
from litedram.common import LiteDRAMNativePort
|
||||
from litedram.frontend.adaptation import LiteDRAMNativePortConverter
|
||||
|
||||
|
||||
# LiteDRAMWishbone2Native --------------------------------------------------------------------------
|
||||
|
@ -16,74 +18,55 @@ class LiteDRAMWishbone2Native(Module):
|
|||
def __init__(self, wishbone, port, base_address=0x00000000):
|
||||
wishbone_data_width = len(wishbone.dat_w)
|
||||
port_data_width = 2**int(log2(len(port.wdata.data))) # Round to lowest power 2
|
||||
assert wishbone_data_width >= port_data_width
|
||||
|
||||
if wishbone_data_width != port_data_width:
|
||||
if wishbone_data_width > port_data_width:
|
||||
addr_shift = -log2_int(wishbone_data_width//port_data_width)
|
||||
else:
|
||||
addr_shift = log2_int(port_data_width//wishbone_data_width)
|
||||
new_port = LiteDRAMNativePort(
|
||||
mode = port.mode,
|
||||
address_width = port.address_width + addr_shift,
|
||||
data_width = wishbone_data_width
|
||||
)
|
||||
self.submodules += LiteDRAMNativePortConverter(new_port, port)
|
||||
port = new_port
|
||||
|
||||
# # #
|
||||
|
||||
adr_offset = base_address >> log2_int(port.data_width//8)
|
||||
|
||||
# Write Datapath ---------------------------------------------------------------------------
|
||||
wdata_converter = stream.StrideConverter(
|
||||
[("data", wishbone_data_width), ("we", wishbone_data_width//8)],
|
||||
[("data", port_data_width), ("we", port_data_width//8)],
|
||||
)
|
||||
self.submodules += wdata_converter
|
||||
wdata_complete = Signal()
|
||||
self.comb += [
|
||||
wdata_converter.sink.valid.eq(wishbone.cyc & wishbone.stb & wishbone.we & ~wdata_complete),
|
||||
wdata_converter.sink.data.eq(wishbone.dat_w),
|
||||
wdata_converter.sink.we.eq(wishbone.sel),
|
||||
wdata_converter.source.connect(port.wdata)
|
||||
]
|
||||
# latch ready signals of cmd/wdata and then wait until all are ready
|
||||
cmd_consumed = Signal()
|
||||
wdata_consumed = Signal()
|
||||
self.sync += [
|
||||
If(wdata_converter.sink.valid & wdata_converter.sink.ready,
|
||||
wdata_complete.eq(1)
|
||||
)
|
||||
If(wishbone.ack,
|
||||
cmd_consumed.eq(0),
|
||||
wdata_consumed.eq(0),
|
||||
).Else(
|
||||
If(port.cmd.valid & port.cmd.ready, cmd_consumed.eq(1)),
|
||||
If(port.wdata.valid & port.wdata.ready, wdata_consumed.eq(1)),
|
||||
),
|
||||
]
|
||||
|
||||
# Read Datapath ----------------------------------------------------------------------------
|
||||
rdata_converter = stream.StrideConverter(
|
||||
[("data", port_data_width)],
|
||||
[("data", wishbone_data_width)],
|
||||
)
|
||||
self.submodules += rdata_converter
|
||||
ack_cmd = Signal()
|
||||
ack_wdata = Signal()
|
||||
ack_rdata = Signal()
|
||||
self.comb += [
|
||||
port.rdata.connect(rdata_converter.sink),
|
||||
rdata_converter.source.ready.eq(1),
|
||||
wishbone.dat_r.eq(rdata_converter.source.data),
|
||||
]
|
||||
|
||||
# Control ----------------------------------------------------------------------------------
|
||||
ratio = wishbone_data_width//port_data_width
|
||||
count = Signal(max=max(ratio, 2))
|
||||
self.submodules.fsm = fsm = FSM(reset_state="CMD")
|
||||
fsm.act("CMD",
|
||||
port.cmd.valid.eq(wishbone.cyc & wishbone.stb),
|
||||
port.cmd.addr.eq(wishbone.adr - adr_offset),
|
||||
port.cmd.we.eq(wishbone.we),
|
||||
port.cmd.addr.eq(wishbone.adr*ratio + count - adr_offset),
|
||||
port.cmd.last.eq(1),
|
||||
If(port.cmd.valid & port.cmd.ready,
|
||||
NextValue(count, count + 1),
|
||||
If(count == (ratio - 1),
|
||||
NextValue(count, 0),
|
||||
If(wishbone.we,
|
||||
NextState("WAIT-WRITE")
|
||||
).Else(
|
||||
NextState("WAIT-READ")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
fsm.act("WAIT-WRITE",
|
||||
If(wdata_complete,
|
||||
wishbone.ack.eq(1),
|
||||
NextValue(wdata_complete, 0),
|
||||
NextState("CMD")
|
||||
)
|
||||
)
|
||||
fsm.act("WAIT-READ",
|
||||
If(rdata_converter.source.valid,
|
||||
wishbone.ack.eq(1),
|
||||
NextState("CMD")
|
||||
)
|
||||
)
|
||||
port.wdata.data.eq(wishbone.dat_w),
|
||||
port.wdata.we.eq(wishbone.sel),
|
||||
wishbone.dat_r.eq(port.rdata.data),
|
||||
# always wait for reads, flush write when transaction ends
|
||||
port.flush.eq(~wishbone.cyc),
|
||||
port.cmd.last.eq(~wishbone.we),
|
||||
# make sure cmd/wdata won't stay valid after it is consumed
|
||||
port.cmd.valid.eq(wishbone.cyc & wishbone.stb & ~cmd_consumed),
|
||||
port.wdata.valid.eq((port.cmd.valid | cmd_consumed) & port.cmd.we & ~wdata_consumed),
|
||||
port.rdata.ready.eq((port.cmd.valid | cmd_consumed) & ~port.cmd.we),
|
||||
wishbone.ack.eq(ack_cmd & ((wishbone.we & ack_wdata) | (~wishbone.we & ack_rdata))),
|
||||
ack_cmd.eq((port.cmd.valid & port.cmd.ready) | cmd_consumed),
|
||||
ack_wdata.eq((port.wdata.valid & port.wdata.ready) | wdata_consumed),
|
||||
ack_rdata.eq(port.rdata.valid & port.rdata.ready),
|
||||
]
|
||||
|
|
|
@ -217,7 +217,7 @@ class DRAMMemory:
|
|||
mask = reduce(or_, [0xff << (8 * bit) for bit in range(self.width//8)
|
||||
if (we & (1 << bit)) != 0], 0)
|
||||
data = data & mask
|
||||
self.mem[address%self.depth] = data
|
||||
self.mem[address%self.depth] = data | (self.mem[address%self.depth] & ~mask)
|
||||
if self._debug in ["1", "W"]:
|
||||
print("W 0x{:08x}: 0x{:0{dwidth}x}".format(address, self.mem[address%self.depth],
|
||||
dwidth=self.width//4))
|
||||
|
|
|
@ -15,12 +15,6 @@ from test.common import DRAMMemory, MemoryTestDataMixin
|
|||
|
||||
|
||||
class TestWishbone(MemoryTestDataMixin, unittest.TestCase):
|
||||
def test_wishbone_data_width_not_smaller(self):
|
||||
with self.assertRaises(AssertionError):
|
||||
wb = wishbone.Interface(data_width=32)
|
||||
port = LiteDRAMNativePort("both", address_width=32, data_width=wb.data_width * 2)
|
||||
LiteDRAMWishbone2Native(wb, port)
|
||||
|
||||
def wishbone_readback_test(self, pattern, mem_expected, wishbone, port, base_address=0):
|
||||
class DUT(Module):
|
||||
def __init__(self):
|
||||
|
@ -82,6 +76,20 @@ class TestWishbone(MemoryTestDataMixin, unittest.TestCase):
|
|||
port = LiteDRAMNativePort("both", address_width=30, data_width=8)
|
||||
self.wishbone_readback_test(data["pattern"], data["expected"], wb, port)
|
||||
|
||||
def test_wishbone_8bit_to_32bit(self):
|
||||
# Verify Wishbone with 8-bit data width up-converted to 32-bit data width.
|
||||
data = self.pattern_test_data["8bit_to_32bit"]
|
||||
wb = wishbone.Interface(adr_width=30, data_width=8)
|
||||
port = LiteDRAMNativePort("both", address_width=30, data_width=32)
|
||||
self.wishbone_readback_test(data["pattern"], data["expected"], wb, port)
|
||||
|
||||
def test_wishbone_32bit_to_64bit(self):
|
||||
# Verify Wishbone with 32-bit data width up-converted to 64-bit data width.
|
||||
data = self.pattern_test_data["32bit_to_64bit"]
|
||||
wb = wishbone.Interface(adr_width=30, data_width=32)
|
||||
port = LiteDRAMNativePort("both", address_width=30, data_width=64)
|
||||
self.wishbone_readback_test(data["pattern"], data["expected"], wb, port)
|
||||
|
||||
def test_wishbone_32bit_base_address(self):
|
||||
# Verify Wishbone with 32-bit data width and non-zero base address.
|
||||
data = self.pattern_test_data["32bit"]
|
||||
|
|
Loading…
Reference in a new issue