frontend: introduce mode on ports: write, read or both

This commit is contained in:
Florent Kermarrec 2016-06-15 17:51:46 +02:00
parent b0382e8776
commit 5823373243
7 changed files with 219 additions and 142 deletions

View File

@ -89,7 +89,8 @@ def rdata_description(dw):
class LiteDRAMPort:
def __init__(self, aw, dw, cd="sys"):
def __init__(self, mode, aw, dw, cd="sys"):
self.mode = mode
self.aw = aw
self.dw = dw
self.cd = cd
@ -101,6 +102,16 @@ class LiteDRAMPort:
self.rdata = stream.Endpoint(rdata_description(dw))
class LiteDRAMWritePort(LiteDRAMPort):
def __init__(self, *args, **kwargs):
LiteDRAMPort.__init__(self, "write", *args, **kwargs)
class LiteDRAMReadPort(LiteDRAMPort):
def __init__(self, *args, **kwargs):
LiteDRAMPort.__init__(self, "read", *args, **kwargs)
def cmd_request_layout(a, ba):
return [
("a", a),

View File

@ -6,44 +6,47 @@ from litedram.common import *
class LiteDRAMPortCDC(Module):
# TODO: check cmd/wdata/rdata fifo depths
def __init__(self, port_from, port_to):
def __init__(self, port_from, port_to,
cmd_depth=4,
wdata_depth=16,
rdata_depth=16):
assert port_from.aw == port_to.aw
assert port_from.dw == port_to.dw
assert port_from.mode == port_to.mode
aw = port_from.aw
dw = port_from.dw
mode = port_from.mode
cd_from = port_from.cd
cd_to = port_to.cd
# # #
cmd_fifo = stream.AsyncFIFO([("we", 1), ("adr", aw)], 4)
cmd_fifo = stream.AsyncFIFO([("we", 1), ("adr", aw)], cmd_depth)
cmd_fifo = ClockDomainsRenamer({"write": cd_from,
"read": cd_to})(cmd_fifo)
"read": cd_to})(cmd_fifo)
self.submodules += cmd_fifo
self.comb += [
port_from.cmd.connect(cmd_fifo.sink),
cmd_fifo.source.connect(port_to.cmd)
]
self.submodules += stream.Pipeline(port_from.cmd,
cmd_fifo,
port_to.cmd)
wdata_fifo = stream.AsyncFIFO([("data", dw), ("we", dw//8)], 16)
wdata_fifo = ClockDomainsRenamer({"write": cd_from,
"read": cd_to})(wdata_fifo)
self.submodules += wdata_fifo
self.comb += [
port_from.wdata.connect(wdata_fifo.sink),
wdata_fifo.source.connect(port_to.wdata)
]
if mode == "write" or mode == "both":
wdata_fifo = stream.AsyncFIFO([("data", dw), ("we", dw//8)], wdata_depth)
wdata_fifo = ClockDomainsRenamer({"write": cd_from,
"read": cd_to})(wdata_fifo)
self.submodules += wdata_fifo
self.submodules += stream.Pipeline(port_from.wdata,
wdata_fifo,
port_to.wdata)
rdata_fifo = stream.AsyncFIFO([("data", dw)], 16)
rdata_fifo = ClockDomainsRenamer({"write": cd_to,
"read": cd_from})(rdata_fifo)
self.submodules += rdata_fifo
self.comb += [
port_to.rdata.connect(rdata_fifo.sink),
rdata_fifo.source.connect(port_from.rdata)
]
if mode == "read" or mode == "both":
rdata_fifo = stream.AsyncFIFO([("data", dw)], rdata_depth)
rdata_fifo = ClockDomainsRenamer({"write": cd_to,
"read": cd_from})(rdata_fifo)
self.submodules += rdata_fifo
self.submodules += stream.Pipeline(port_to.rdata,
rdata_fifo,
port_from.rdata)
class LiteDRAMPortDownConverter(Module):
@ -60,12 +63,14 @@ class LiteDRAMPortDownConverter(Module):
def __init__(self, port_from, port_to):
assert port_from.cd == port_to.cd
assert port_from.dw > port_to.dw
assert port_from.mode == port_to.mode
if port_from.dw % port_to.dw:
raise ValueError("Ratio must be an int")
# # #
ratio = port_from.dw//port_to.dw
mode = port_from.mode
counter = Signal(max=ratio)
counter_reset = Signal()
@ -97,38 +102,38 @@ class LiteDRAMPortDownConverter(Module):
)
)
wdata_converter = stream.StrideConverter(port_from.wdata.description,
port_to.wdata.description)
self.submodules += wdata_converter
self.comb += [
port_from.wdata.connect(wdata_converter.sink),
wdata_converter.source.connect(port_to.wdata)
]
if mode == "write" or mode == "both":
wdata_converter = stream.StrideConverter(port_from.wdata.description,
port_to.wdata.description)
self.submodules += wdata_converter
self.submodules += stream.Pipeline(port_from.wdata,
wdata_converter,
port_to.wdata)
rdata_converter = stream.StrideConverter(port_to.rdata.description,
port_from.rdata.description)
self.submodules += rdata_converter
self.comb += [
port_to.rdata.connect(rdata_converter.sink),
rdata_converter.source.connect(port_from.rdata)
]
if mode == "read" or mode == "both":
rdata_converter = stream.StrideConverter(port_to.rdata.description,
port_from.rdata.description)
self.submodules += rdata_converter
self.submodules += stream.Pipeline(port_to.rdata,
rdata_converter,
port_from.rdata)
class LiteDRAMPortUpConverter(Module):
# TODO:
# - handle all specials cases (incomplete / non aligned bursts)
# - add exceptions on datapath for such cases
"""LiteDRAM port UpConverter
class LiteDRAMWritePortUpConverter(Module):
# TODO: finish and remove hack
"""LiteDRAM write port UpConverter
This module increase user port data width to fit controller data width.
With N = port_to.dw/port_from.dw:
- Address is adapted (divided by N)
- N writes and read from user are regrouped in a single one to the controller
- N writes from user are regrouped in a single one to the controller
(when possible, ie when consecutive and bursting)
"""
def __init__(self, port_from, port_to):
assert port_from.cd == port_to.cd
assert port_from.dw < port_to.dw
assert port_from.mode == port_to.mode
assert port_from.mode == "write"
if port_to.dw % port_from.dw:
raise ValueError("Ratio must be an int")
@ -156,11 +161,7 @@ class LiteDRAMPortUpConverter(Module):
counter_ce.eq(1),
NextValue(we, port_from.cmd.we),
NextValue(address, port_from.cmd.adr),
If(we,
NextState("RECEIVE")
).Else(
NextState("GENERATE") # FIXME
)
NextState("RECEIVE")
)
)
fsm.act("RECEIVE",
@ -168,12 +169,7 @@ class LiteDRAMPortUpConverter(Module):
If(port_from.cmd.valid,
counter_ce.eq(1),
If(counter == ratio-1,
If(we,
NextState("GENERATE")
).Else(
NextState("IDLE"), # FIXME
port_from.cmd.ready.eq(1),
)
NextState("GENERATE")
)
)
)
@ -182,43 +178,110 @@ class LiteDRAMPortUpConverter(Module):
port_to.cmd.we.eq(we),
port_to.cmd.adr.eq(address[log2_int(ratio):]),
If(port_to.cmd.ready,
If(we,
NextState("IDLE"),
port_from.cmd.ready.eq(1)
).Else(
NextState("RECEIVE") # FIXME
)
NextState("IDLE")
)
)
wdata_converter = stream.StrideConverter(port_from.wdata.description,
port_to.wdata.description)
self.submodules += wdata_converter
self.comb += [
port_from.wdata.connect(wdata_converter.sink),
wdata_converter.source.connect(port_to.wdata)
]
self.submodules += stream.Pipeline(port_from.wdata,
wdata_converter,
port_to.wdata)
class LiteDRAMReadPortUpConverter(Module):
# TODO: finish and remove hack
"""LiteDRAM port UpConverter
This module increase user port data width to fit controller data width.
With N = port_to.dw/port_from.dw:
- Address is adapted (divided by N)
- N read from user are regrouped in a single one to the controller
(when possible, ie when consecutive and bursting)
"""
def __init__(self, port_from, port_to):
assert port_from.cd == port_to.cd
assert port_from.dw < port_to.dw
assert port_from.mode == port_to.mode
assert port_from.mode == "read"
if port_to.dw % port_from.dw:
raise ValueError("Ratio must be an int")
# # #
ratio = port_to.dw//port_from.dw
we = Signal()
address = Signal(port_to.aw)
counter = Signal(max=ratio)
counter_reset = Signal()
counter_ce = Signal()
self.sync += \
If(counter_reset,
counter.eq(0)
).Elif(counter_ce,
counter.eq(counter + 1)
)
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
fsm.act("IDLE",
port_from.cmd.ready.eq(1),
If(port_from.cmd.valid,
counter_ce.eq(1),
NextValue(we, port_from.cmd.we),
NextValue(address, port_from.cmd.adr),
NextState("GENERATE")
)
)
fsm.act("RECEIVE",
port_from.cmd.ready.eq(1),
If(port_from.cmd.valid,
counter_ce.eq(1),
If(counter == ratio-1,
NextState("IDLE")
)
)
)
fsm.act("GENERATE",
port_to.cmd.valid.eq(1),
port_to.cmd.we.eq(we),
port_to.cmd.adr.eq(address[log2_int(ratio):]),
If(port_to.cmd.ready,
NextState("RECEIVE")
)
)
rdata_converter = stream.StrideConverter(port_to.rdata.description,
port_from.rdata.description)
self.submodules += rdata_converter
self.comb += [
port_to.rdata.connect(rdata_converter.sink),
rdata_converter.source.connect(port_from.rdata)
]
self.submodules += stream.Pipeline(port_to.rdata,
rdata_converter,
port_from.rdata)
class LiteDRAMPortConverter(Module):
def __init__(self, port_from, port_to):
assert port_from.cd == port_to.cd
assert port_from.mode == port_to.mode
# # #
mode = port_from.mode
if port_from.dw > port_to.dw:
converter = LiteDRAMPortDownConverter(port_from, port_to)
self.submodules += converter
elif port_from.dw < port_to.dw:
converter = LiteDRAMPortUpConverter(port_from, port_to)
if mode == "write":
converter = LiteDRAMWritePortUpConverter(port_from, port_to)
elif mode == "read":
converter = LiteDRAMReadPortUpConverter(port_from, port_to)
else:
raise NotImplementedError
converter
self.submodules += converter
else:
self.comb += [

View File

@ -26,19 +26,19 @@ class LiteDRAMCrossbar(Module):
self.masters = []
def get_port(self, dw=None, cd="sys"):
def get_port(self, mode="both", dw=None, cd="sys"):
if self.finalized:
raise FinalizeError
if dw is None:
dw = self.dw
# crossbar port
port = LiteDRAMPort(self.rca_bits + self.bank_bits, self.dw, "sys")
port = LiteDRAMPort(mode, self.rca_bits + self.bank_bits, self.dw, "sys")
self.masters.append(port)
# clock domain crossing
if cd != "sys":
new_port = LiteDRAMPort(port.aw, port.dw, cd)
new_port = LiteDRAMPort(mode, port.aw, port.dw, cd)
self.submodules += LiteDRAMPortCDC(new_port, port)
port = new_port
@ -48,7 +48,7 @@ class LiteDRAMCrossbar(Module):
adr_shift = log2_int(dw//self.dw)
else:
adr_shift = -log2_int(self.dw//dw)
new_port = LiteDRAMPort(port.aw + adr_shift, dw, cd=cd)
new_port = LiteDRAMPort(mode, port.aw + adr_shift, dw, cd=cd)
self.submodules += ClockDomainsRenamer(cd)(LiteDRAMPortConverter(new_port, port))
port = new_port

View File

@ -58,22 +58,8 @@ class TB(Module):
self.controller.nrowbits)
# write port
write_crossbar_port = self.crossbar.get_port()
write_user_port = LiteDRAMPort(write_crossbar_port.aw,
write_crossbar_port.dw,
cd="write")
self.submodules += LiteDRAMPortCDC(write_user_port,
write_crossbar_port)
read_crossbar_port = self.crossbar.get_port()
read_user_port = LiteDRAMPort(read_crossbar_port.aw,
read_crossbar_port.dw,
cd="read")
# read port
self.submodules += LiteDRAMPortCDC(read_user_port,
read_crossbar_port)
write_user_port = self.crossbar.get_port("write", cd="write")
read_user_port = self.crossbar.get_port("read", cd="read")
# generator / checker
self.submodules.generator = LiteDRAMBISTGenerator(write_user_port)

View File

@ -4,7 +4,7 @@ from litex.gen import *
from litex.soc.interconnect.stream import *
from litedram.common import LiteDRAMPort
from litedram.common import LiteDRAMWritePort, LiteDRAMReadPort
from litedram.frontend.bist import LiteDRAMBISTGenerator
from litedram.frontend.bist import LiteDRAMBISTChecker
@ -12,8 +12,8 @@ from test.common import DRAMMemory
class TB(Module):
def __init__(self):
self.write_port = LiteDRAMPort(aw=32, dw=32)
self.read_port = LiteDRAMPort(aw=32, dw=32)
self.write_port = LiteDRAMWritePort(aw=32, dw=32)
self.read_port = LiteDRAMReadPort(aw=32, dw=32)
self.submodules.generator = LiteDRAMBISTGenerator(self.write_port)
self.submodules.checker = LiteDRAMBISTChecker(self.read_port)

View File

@ -5,56 +5,64 @@ from litex.gen import *
from litex.soc.interconnect.stream import *
from litex.soc.interconnect.stream_sim import check
from litedram.common import LiteDRAMPort
from litedram.common import LiteDRAMWritePort, LiteDRAMReadPort
from litedram.frontend.adaptation import LiteDRAMPortConverter
from test.common import *
class TB(Module):
def __init__(self):
self.user_port = LiteDRAMPort(aw=32, dw=64)
self.crossbar_port = LiteDRAMPort(aw=32, dw=32)
self.submodules.converter = LiteDRAMPortConverter(self.user_port,
self.crossbar_port)
self.write_user_port = LiteDRAMWritePort(aw=32, dw=64)
self.write_crossbar_port = LiteDRAMWritePort(aw=32, dw=32)
self.submodules.write_converter = LiteDRAMPortConverter(self.write_user_port,
self.write_crossbar_port)
self.read_user_port = LiteDRAMReadPort(aw=32, dw=64)
self.read_crossbar_port = LiteDRAMReadPort(aw=32, dw=32)
self.submodules.read_converter = LiteDRAMPortConverter(self.read_user_port,
self.read_crossbar_port)
self.memory = DRAMMemory(32, 128)
write_data = [seed_to_data(i, nbits=64) for i in range(8)]
read_data = []
@passive
def read_generator(dut):
yield dut.user_port.rdata.ready.eq(1)
yield dut.read_user_port.rdata.ready.eq(1)
while True:
if (yield dut.user_port.rdata.valid):
read_data.append((yield dut.user_port.rdata.data))
if (yield dut.read_user_port.rdata.valid):
read_data.append((yield dut.read_user_port.rdata.data))
yield
def main_generator(dut):
# write
for i in range(8):
yield dut.user_port.cmd.valid.eq(1)
yield dut.user_port.cmd.we.eq(1)
yield dut.user_port.cmd.adr.eq(i)
yield dut.user_port.wdata.valid.eq(1)
yield dut.user_port.wdata.data.eq(write_data[i])
yield dut.write_user_port.cmd.valid.eq(1)
yield dut.write_user_port.cmd.we.eq(1)
yield dut.write_user_port.cmd.adr.eq(i)
yield dut.write_user_port.wdata.valid.eq(1)
yield dut.write_user_port.wdata.data.eq(write_data[i])
yield
while (yield dut.user_port.cmd.ready) == 0:
while (yield dut.write_user_port.cmd.ready) == 0:
yield
while (yield dut.user_port.wdata.ready) == 0:
while (yield dut.write_user_port.wdata.ready) == 0:
yield
yield
# read
yield dut.user_port.rdata.ready.eq(1)
yield dut.read_user_port.rdata.ready.eq(1)
for i in range(8):
yield dut.user_port.cmd.valid.eq(1)
yield dut.user_port.cmd.we.eq(0)
yield dut.user_port.cmd.adr.eq(i)
yield dut.read_user_port.cmd.valid.eq(1)
yield dut.read_user_port.cmd.we.eq(0)
yield dut.read_user_port.cmd.adr.eq(i)
yield
while (yield dut.user_port.cmd.ready) == 0:
while (yield dut.read_user_port.cmd.ready) == 0:
yield
yield dut.user_port.cmd.valid.eq(0)
yield dut.read_user_port.cmd.valid.eq(0)
yield
# delay
@ -70,8 +78,8 @@ if __name__ == "__main__":
generators = {
"sys" : [main_generator(tb),
read_generator(tb),
tb.memory.write_generator(tb.crossbar_port),
tb.memory.read_generator(tb.crossbar_port)]
tb.memory.write_generator(tb.write_crossbar_port),
tb.memory.read_generator(tb.read_crossbar_port)]
}
clocks = {"sys": 10}
run_simulation(tb, generators, clocks, vcd_name="sim.vcd")
run_simulation(tb, generators, clocks, vcd_name="sim.vcd")

View File

@ -5,59 +5,68 @@ from litex.gen import *
from litex.soc.interconnect.stream import *
from litex.soc.interconnect.stream_sim import check
from litedram.common import LiteDRAMPort
from litedram.common import LiteDRAMWritePort, LiteDRAMReadPort
from litedram.frontend.adaptation import LiteDRAMPortConverter
from test.common import *
class TB(Module):
def __init__(self):
self.user_port = LiteDRAMPort(aw=32, dw=32)
self.crossbar_port = LiteDRAMPort(aw=32, dw=64)
self.submodules.converter = LiteDRAMPortConverter(self.user_port,
self.crossbar_port)
self.write_user_port = LiteDRAMWritePort(aw=32, dw=32)
self.write_crossbar_port = LiteDRAMWritePort(aw=32, dw=64)
self.submodules.write_converter = LiteDRAMPortConverter(self.write_user_port,
self.write_crossbar_port)
self.read_user_port = LiteDRAMReadPort(aw=32, dw=32)
self.read_crossbar_port = LiteDRAMReadPort(aw=32, dw=64)
self.submodules.read_converter = LiteDRAMPortConverter(self.read_user_port,
self.read_crossbar_port)
self.memory = DRAMMemory(64, 128)
write_data = [seed_to_data(i, nbits=32) for i in range(8)]
read_data = []
@passive
def read_generator(dut):
yield dut.user_port.rdata.ready.eq(1)
yield dut.read_user_port.rdata.ready.eq(1)
while True:
if (yield dut.user_port.rdata.valid):
read_data.append((yield dut.user_port.rdata.data))
if (yield dut.read_user_port.rdata.valid):
read_data.append((yield dut.read_user_port.rdata.data))
yield
def main_generator(dut):
# write
for i in range(8):
yield dut.user_port.cmd.valid.eq(1)
yield dut.user_port.cmd.we.eq(1)
yield dut.user_port.cmd.adr.eq(i)
yield dut.write_user_port.cmd.valid.eq(1)
yield dut.write_user_port.cmd.we.eq(1)
yield dut.write_user_port.cmd.adr.eq(i)
yield
while (yield dut.user_port.cmd.ready) == 0:
while (yield dut.write_user_port.cmd.ready) == 0:
yield
yield dut.user_port.cmd.valid.eq(0)
yield dut.write_user_port.cmd.valid.eq(0)
yield
yield dut.user_port.wdata.valid.eq(1)
yield dut.user_port.wdata.data.eq(write_data[i])
yield dut.write_user_port.wdata.valid.eq(1)
yield dut.write_user_port.wdata.data.eq(write_data[i])
yield
while (yield dut.user_port.wdata.ready) == 0:
while (yield dut.write_user_port.wdata.ready) == 0:
yield
yield dut.user_port.wdata.valid.eq(0)
yield dut.write_user_port.wdata.valid.eq(0)
yield
# read
for i in range(8):
for j in range(2):
yield dut.user_port.cmd.valid.eq(1)
yield dut.user_port.cmd.we.eq(0)
yield dut.user_port.cmd.adr.eq(i)
yield dut.read_user_port.cmd.valid.eq(1)
yield dut.read_user_port.cmd.we.eq(0)
yield dut.read_user_port.cmd.adr.eq(i)
yield
while (yield dut.user_port.cmd.ready) == 0:
while (yield dut.read_user_port.cmd.ready) == 0:
yield
yield dut.user_port.cmd.valid.eq(0)
yield dut.read_user_port.cmd.valid.eq(0)
yield
# delay
@ -74,8 +83,8 @@ if __name__ == "__main__":
generators = {
"sys" : [main_generator(tb),
read_generator(tb),
tb.memory.write_generator(tb.crossbar_port),
tb.memory.read_generator(tb.crossbar_port)]
tb.memory.write_generator(tb.write_crossbar_port),
tb.memory.read_generator(tb.read_crossbar_port)]
}
clocks = {"sys": 10}
run_simulation(tb, generators, clocks, vcd_name="sim.vcd")