116 lines
5.0 KiB
Python
116 lines
5.0 KiB
Python
from functools import reduce
|
|
from operator import or_
|
|
|
|
from nmigen import *
|
|
|
|
from rtio.sed import layouts
|
|
from rtio.sed.output_network import OutputNetwork
|
|
|
|
__all__ = ["OutputDriver"]
|
|
|
|
class OutputDriver(Elaboratable):
|
|
def __init__(self, channels, glbl_fine_ts_width, lane_count, seqn_width):
|
|
self.channels = channels
|
|
self.glbl_fine_ts_width = glbl_fine_ts_width
|
|
self.lane_count = lane_count
|
|
self.seqn_width = seqn_width
|
|
|
|
self.collision = Signal()
|
|
self.collision_channel = Signal(range(len(channels)), reset_less=True)
|
|
self.busy = Signal()
|
|
self.busy_channel = Signal(range(len(channels)), reset_less=True)
|
|
|
|
# output network
|
|
layout_on_payload = layouts.output_network_payload(channels, glbl_fine_ts_width)
|
|
self.output_network = OutputNetwork(lane_count, seqn_width, layout_on_payload)
|
|
self.input = self.output_network.input
|
|
|
|
# detect collisions (adds one pipeline stage)
|
|
layout_lane_data = [
|
|
("valid", 1),
|
|
("collision", 1),
|
|
("payload", layout_on_payload)
|
|
]
|
|
self.lane_datas = [Record(layout_lane_data) for _ in range(lane_count)]
|
|
for lane_data in self.lane_datas:
|
|
lane_data.valid.reset_less = True
|
|
lane_data.collision.reset_less = True
|
|
for field, _ in layout_on_payload:
|
|
getattr(lane_data.payload, field).reset_less = True
|
|
self.en_replaces = [channel.interface.o.enable_replace for channel in channels]
|
|
|
|
def elaborate(self, platform):
|
|
m = Module()
|
|
|
|
channels = self.channels
|
|
output_network = self.output_network
|
|
lane_datas = self.lane_datas
|
|
en_replaces = self.en_replaces
|
|
|
|
m.submodules += self.output_network
|
|
for lane_data, on_output in zip(lane_datas, output_network.output):
|
|
lane_data.valid.reset_less = False
|
|
lane_data.collision.reset_less = False
|
|
replace_occured_r = Signal()
|
|
nondata_replace_occured_r = Signal()
|
|
m.d.sync += lane_data.valid.eq(on_output.valid)
|
|
m.d.sync += lane_data.payload.eq(on_output.payload)
|
|
m.d.sync += replace_occured_r.eq(on_output.replace_occured)
|
|
m.d.sync += nondata_replace_occured_r.eq(on_output.nondata_replace_occured)
|
|
|
|
en_replaces_rom = Memory(width=1, depth=len(en_replaces), init=en_replaces)
|
|
en_replaces_rom_port = en_replaces_rom.read_port()
|
|
m.submodules += en_replaces_rom_port
|
|
m.d.comb += en_replaces_rom_port.addr.eq(on_output.payload.channel)
|
|
m.d.comb += lane_data.collision.eq(replace_occured_r & (~en_replaces_rom_port.data | nondata_replace_occured_r))
|
|
|
|
m.d.sync += self.collision.eq(0)
|
|
m.d.sync += self.collision_channel.eq(0)
|
|
for lane_data in lane_datas:
|
|
with m.If(lane_data.valid & lane_data.collision):
|
|
m.d.sync += self.collision.eq(1)
|
|
m.d.sync += self.collision_channel.eq(lane_data.payload.channel)
|
|
|
|
# demultiplex channels (adds one pipeline stage)
|
|
for n, channel in enumerate(channels):
|
|
oif = channel.interface.o
|
|
|
|
onehot_stb = []
|
|
onehot_fine_ts = []
|
|
onehot_address = []
|
|
onehot_data = []
|
|
for lane_data in lane_datas:
|
|
selected = Signal()
|
|
m.d.comb += selected.eq(lane_data.valid & ~lane_data.collision & (lane_data.payload.channel == n))
|
|
onehot_stb.append(selected)
|
|
if hasattr(lane_data.payload, "fine_ts") and hasattr(oif, "fine_ts"):
|
|
ts_shift = len(lane_data.payload.fine_ts) - len(oif.fine_ts)
|
|
onehot_fine_ts.append(Mux(selected, lane_data.payload.fine_ts[ts_shift:], 0))
|
|
if hasattr(lane_data.payload, "address"):
|
|
onehot_address.append(Mux(selected, lane_data.payload.address, 0))
|
|
if hasattr(lane_data.payload, "data"):
|
|
onehot_data.append(Mux(selected, lane_data.payload.data, 0))
|
|
|
|
m.d.sync += oif.stb.eq(reduce(or_, onehot_stb))
|
|
if hasattr(oif, "fine_ts"):
|
|
m.d.sync += oif.fine_ts.eq(reduce(or_, onehot_fine_ts))
|
|
if hasattr(oif, "address"):
|
|
m.d.sync += oif.address.eq(reduce(or_, onehot_address))
|
|
if hasattr(oif, "data"):
|
|
m.d.sync += oif.data.eq(reduce(or_, onehot_data))
|
|
|
|
# detect busy errors, at lane level to reduce mixing
|
|
m.d.sync += self.busy.eq(0)
|
|
m.d.sync += self.busy_channel.eq(0)
|
|
for lane_data in lane_datas:
|
|
stb_r = Signal()
|
|
channel_r = Signal(range(len(channels)), reset_less=True)
|
|
m.d.sync += stb_r.eq(lane_data.valid & ~lane_data.collision)
|
|
m.d.sync += channel_r.eq(lane_data.payload.channel)
|
|
|
|
with m.If(stb_r & Array(channel.interface.o.busy for channel in channels)[channel_r]):
|
|
m.d.sync += self.busy.eq(1)
|
|
m.d.sync += self.busy_channel.eq(channel_r)
|
|
|
|
return m
|