From 6746052a60c1e7213facb481435c9bc0c699d642 Mon Sep 17 00:00:00 2001 From: Donald Sebastian Leung Date: Fri, 9 Oct 2020 11:16:01 +0800 Subject: [PATCH] Add rtio.sed.output_driver --- README.md | 4 +- rtio/sed/output_driver.py | 99 ++++++++++++++++++++++++++++++++++++++ rtio/sed/output_network.py | 4 +- 3 files changed, 103 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index efead6c..6618b49 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,12 @@ Formally verified implementation of the ARTIQ RTIO core in nMigen ## Progress - Devise a suitable migration strategy for `artiq.gateware.rtio` from Migen to nMigen -- [ ] Implement the core in nMigen +- [x] Implement the core in nMigen - - [x] `rtio.cri` (`Interface` and `CRIDecoder` only) - - [x] `rtio.rtlink` - - [x] `rtio.sed.layouts` - - [x] `rtio.sed.output_network` -- - [ ] `rtio.sed.output_driver` +- - [x] `rtio.sed.output_driver` - [ ] Add suitable assertions for verification (BMC / unbounded proof?) ## License diff --git a/rtio/sed/output_driver.py b/rtio/sed/output_driver.py index 8b13789..935edd3 100644 --- a/rtio/sed/output_driver.py +++ b/rtio/sed/output_driver.py @@ -1 +1,100 @@ +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): + m = Module() + self.m = m + 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) + output_network = OutputNetwork(lane_count, seqn_width, layout_on_payload) + m.submodules += output_network + self.input = output_network.input + + # detect collisions (adds one pipeline stage) + layout_lane_data = [ + ("valid", 1), + ("collision", 1), + ("payload", layout_on_payload) + ] + lane_datas = [Record(layout_lane_data, reset_less=True) for _ in range(lane_count)] + en_replaces = [channel.interface.o.enable_replace for channel in channels] + 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.ayload.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_datapayload.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) + + + def elaborate(self, platform): + return self.m diff --git a/rtio/sed/output_network.py b/rtio/sed/output_network.py index f782c02..9d5198c 100644 --- a/rtio/sed/output_network.py +++ b/rtio/sed/output_network.py @@ -78,8 +78,8 @@ class OutputNetwork(Elaboratable): with m.Else(): m.d.sync += step_output[node1].eq(step_input[node1]) m.d.sync += step_output[node2].eq(step_input[node2]) - m.d.sync += step_output[node1].replace_occurred.eq(1) - m.d.sync += step_output[node1].nondata_replace_occurred.eq(nondata_difference) + m.d.sync += step_output[node1].replace_occured.eq(1) + m.d.sync += step_output[node1].nondata_replace_occured.eq(nondata_difference) m.d.sync += step_output[node2].valid.eq(0) with m.Elif(k1 < k2): m.d.sync += step_output[node1].eq(step_input[node1])