def fifo_payload(channels): address_width = max( rtlink.get_address_width(channel.interface.o) for channel in channels) data_width = max( rtlink.get_data_width(channel.interface.o) for channel in channels) layout = [("channel", bits_for(len(channels) - 1)), ("timestamp", 64)] if address_width: layout.append(("address", address_width)) if data_width: layout.append(("data", data_width)) return layout
def output_network_payload(channels, glbl_fine_ts_width): address_width = max(rtlink.get_address_width(channel.interface.o) for channel in channels) data_width = max(rtlink.get_data_width(channel.interface.o) for channel in channels) layout = [("channel", bits_for(len(channels)-1))] if glbl_fine_ts_width: layout.append(("fine_ts", glbl_fine_ts_width)) if address_width: layout.append(("address", address_width)) if data_width: layout.append(("data", data_width)) return layout
def output_network_payload(channels, glbl_fine_ts_width): address_width = max( rtlink.get_address_width(channel.interface.o) for channel in channels) data_width = max( rtlink.get_data_width(channel.interface.o) for channel in channels) layout = [("channel", bits_for(len(channels) - 1))] if glbl_fine_ts_width: layout.append(("fine_ts", glbl_fine_ts_width)) if address_width: layout.append(("address", address_width)) if data_width: layout.append(("data", data_width)) return layout
def fifo_payload(channels): address_width = max(rtlink.get_address_width(channel.interface.o) for channel in channels) data_width = max(rtlink.get_data_width(channel.interface.o) for channel in channels) layout = [ ("channel", bits_for(len(channels)-1)), ("timestamp", 64) ] if address_width: layout.append(("address", address_width)) if data_width: layout.append(("data", data_width)) return layout
def __init__(self, interface, counter, fifo_depth, guard_io_cycles): data_width = rtlink.get_data_width(interface) address_width = rtlink.get_address_width(interface) fine_ts_width = rtlink.get_fine_ts_width(interface) ev_layout = [] if data_width: ev_layout.append(("data", data_width)) if address_width: ev_layout.append(("address", address_width)) ev_layout.append(("timestamp", counter.width + fine_ts_width)) # ev must be valid 1 cycle before we to account for the latency in # generating replace, sequence_error and collision self.ev = Record(ev_layout) self.writable = Signal() self.we = Signal() # maximum throughput 1/2 self.underflow = Signal() # valid 1 cycle after we, pulsed self.sequence_error = Signal() self.collision = Signal() self.busy = Signal() # pulsed # # # # FIFO fifo = ClockDomainsRenamer({ "write": "rsys", "read": "rio" })(AsyncFIFO(layout_len(ev_layout), fifo_depth)) self.submodules += fifo fifo_in = Record(ev_layout) fifo_out = Record(ev_layout) self.comb += [ fifo.din.eq(fifo_in.raw_bits()), fifo_out.raw_bits().eq(fifo.dout) ] # Buffer buf_pending = Signal() buf = Record(ev_layout) buf_just_written = Signal() # Special cases replace = Signal() sequence_error = Signal() collision = Signal() any_error = Signal() if interface.enable_replace: # Note: replace may be asserted at the same time as collision # when addresses are different. In that case, it is a collision. self.sync.rsys += replace.eq(self.ev.timestamp == buf.timestamp) # Detect sequence errors on coarse timestamps only # so that they are mutually exclusive with collision errors. self.sync.rsys += sequence_error.eq( self.ev.timestamp[fine_ts_width:] < buf.timestamp[fine_ts_width:]) if interface.enable_replace: if address_width: different_addresses = self.ev.address != buf.address else: different_addresses = 0 if fine_ts_width: self.sync.rsys += collision.eq( (self.ev.timestamp[fine_ts_width:] == buf.timestamp[fine_ts_width:]) & ((self.ev.timestamp[:fine_ts_width] != buf.timestamp[:fine_ts_width]) | different_addresses)) else: self.sync.rsys += collision.eq(self.ev.timestamp[fine_ts_width:] == buf.timestamp[fine_ts_width:]) self.comb += [ any_error.eq(sequence_error | collision), self.sequence_error.eq(self.we & sequence_error), self.collision.eq(self.we & collision) ] # Buffer read and FIFO write self.comb += fifo_in.eq(buf) in_guard_time = Signal() self.comb += in_guard_time.eq( buf.timestamp[fine_ts_width:] < counter.value_sys + guard_io_cycles) self.sync.rsys += If(in_guard_time, buf_pending.eq(0)) self.comb += \ If(buf_pending, If(in_guard_time, If(buf_just_written, self.underflow.eq(1) ).Else( fifo.we.eq(1) ) ), If(self.we & ~replace & ~any_error, fifo.we.eq(1) ) ) # Buffer write # Must come after read to handle concurrent read+write properly self.sync.rsys += [ buf_just_written.eq(0), If(self.we & ~any_error, buf_just_written.eq(1), buf_pending.eq(1), buf.eq(self.ev)) ] self.comb += self.writable.eq(fifo.writable) # Buffer output of FIFO to improve timing dout_stb = Signal() dout_ack = Signal() dout = Record(ev_layout) self.sync.rio += \ If(fifo.re, dout_stb.eq(1), dout.eq(fifo_out) ).Elif(dout_ack, dout_stb.eq(0) ) self.comb += fifo.re.eq(fifo.readable & (~dout_stb | dout_ack)) # latency compensation if interface.delay: counter_rtio = Signal.like(counter.value_rtio) self.sync.rtio += counter_rtio.eq(counter.value_rtio - interface.delay + 1) else: counter_rtio = counter.value_rtio # FIFO read through buffer self.comb += [ dout_ack.eq(dout.timestamp[fine_ts_width:] == counter_rtio), interface.stb.eq(dout_stb & dout_ack) ] busy_transfer = BlindTransfer() self.submodules += busy_transfer self.comb += [ busy_transfer.i.eq(interface.stb & interface.busy), self.busy.eq(busy_transfer.o), ] if data_width: self.comb += interface.data.eq(dout.data) if address_width: self.comb += interface.address.eq(dout.address) if fine_ts_width: self.comb += interface.fine_ts.eq(dout.timestamp[:fine_ts_width])
def __init__(self, channels, full_ts_width=63, guard_io_cycles=20): data_width = max(rtlink.get_data_width(c.interface) for c in channels) address_width = max(rtlink.get_address_width(c.interface) for c in channels) fine_ts_width = max(rtlink.get_fine_ts_width(c.interface) for c in channels) self.data_width = data_width self.address_width = address_width self.fine_ts_width = fine_ts_width # CSRs self.kcsrs = _KernelCSRs(bits_for(len(channels)-1), data_width, address_width, full_ts_width) # Clocking/Reset # Create rsys, rio and rio_phy domains based on sys and rtio # with reset controlled by CSR. self.clock_domains.cd_rsys = ClockDomain() self.clock_domains.cd_rio = ClockDomain() self.clock_domains.cd_rio_phy = ClockDomain() self.comb += [ self.cd_rsys.clk.eq(ClockSignal()), self.cd_rsys.rst.eq(self.kcsrs.reset.storage) ] self.comb += self.cd_rio.clk.eq(ClockSignal("rtio")) self.specials += AsyncResetSynchronizer( self.cd_rio, self.kcsrs.reset.storage | ResetSignal("rtio", allow_reset_less=True)) self.comb += self.cd_rio_phy.clk.eq(ClockSignal("rtio")) self.specials += AsyncResetSynchronizer( self.cd_rio_phy, self.kcsrs.reset_phy.storage | ResetSignal("rtio", allow_reset_less=True)) # Managers self.submodules.counter = _RTIOCounter(full_ts_width - fine_ts_width) i_datas, i_timestamps = [], [] o_statuses, i_statuses = [], [] sel = self.kcsrs.chan_sel.storage for n, channel in enumerate(channels): if isinstance(channel, LogChannel): i_datas.append(0) i_timestamps.append(0) i_statuses.append(0) continue selected = Signal() self.comb += selected.eq(sel == n) o_manager = _OutputManager(channel.interface.o, self.counter, channel.ofifo_depth, guard_io_cycles) self.submodules += o_manager if hasattr(o_manager.ev, "data"): self.comb += o_manager.ev.data.eq( self.kcsrs.o_data.storage) if hasattr(o_manager.ev, "address"): self.comb += o_manager.ev.address.eq( self.kcsrs.o_address.storage) ts_shift = (len(self.kcsrs.o_timestamp.storage) - len(o_manager.ev.timestamp)) self.comb += o_manager.ev.timestamp.eq( self.kcsrs.o_timestamp.storage[ts_shift:]) self.comb += o_manager.we.eq(selected & self.kcsrs.o_we.re) underflow = Signal() sequence_error = Signal() collision = Signal() busy = Signal() self.sync.rsys += [ If(selected & self.kcsrs.o_underflow_reset.re, underflow.eq(0)), If(selected & self.kcsrs.o_sequence_error_reset.re, sequence_error.eq(0)), If(selected & self.kcsrs.o_collision_reset.re, collision.eq(0)), If(selected & self.kcsrs.o_busy_reset.re, busy.eq(0)), If(o_manager.underflow, underflow.eq(1)), If(o_manager.sequence_error, sequence_error.eq(1)), If(o_manager.collision, collision.eq(1)), If(o_manager.busy, busy.eq(1)) ] o_statuses.append(Cat(~o_manager.writable, underflow, sequence_error, collision, busy)) if channel.interface.i is not None: i_manager = _InputManager(channel.interface.i, self.counter, channel.ififo_depth) self.submodules += i_manager if hasattr(i_manager.ev, "data"): i_datas.append(i_manager.ev.data) else: i_datas.append(0) if channel.interface.i.timestamped: ts_shift = (len(self.kcsrs.i_timestamp.status) - len(i_manager.ev.timestamp)) i_timestamps.append(i_manager.ev.timestamp << ts_shift) else: i_timestamps.append(0) self.comb += i_manager.re.eq(selected & self.kcsrs.i_re.re) overflow = Signal() self.sync.rsys += [ If(selected & self.kcsrs.i_overflow_reset.re, overflow.eq(0)), If(i_manager.overflow, overflow.eq(1)) ] i_statuses.append(Cat(~i_manager.readable, overflow)) else: i_datas.append(0) i_timestamps.append(0) i_statuses.append(0) if data_width: self.comb += self.kcsrs.i_data.status.eq(Array(i_datas)[sel]) self.comb += [ self.kcsrs.i_timestamp.status.eq(Array(i_timestamps)[sel]), self.kcsrs.o_status.status.eq(Array(o_statuses)[sel]), self.kcsrs.i_status.status.eq(Array(i_statuses)[sel]) ] # Counter access self.sync += \ If(self.kcsrs.counter_update.re, self.kcsrs.counter.status.eq(self.counter.value_sys << fine_ts_width) )
def __init__(self, interface, counter, fifo_depth, guard_io_cycles): data_width = rtlink.get_data_width(interface) address_width = rtlink.get_address_width(interface) fine_ts_width = rtlink.get_fine_ts_width(interface) ev_layout = [] if data_width: ev_layout.append(("data", data_width)) if address_width: ev_layout.append(("address", address_width)) ev_layout.append(("timestamp", counter.width + fine_ts_width)) # ev must be valid 1 cycle before we to account for the latency in # generating replace, sequence_error and collision self.ev = Record(ev_layout) self.writable = Signal() self.we = Signal() # maximum throughput 1/2 self.underflow = Signal() # valid 1 cycle after we, pulsed self.sequence_error = Signal() self.collision = Signal() self.busy = Signal() # pulsed # # # # FIFO fifo = ClockDomainsRenamer({"write": "rsys", "read": "rio"})( AsyncFIFO(layout_len(ev_layout), fifo_depth)) self.submodules += fifo fifo_in = Record(ev_layout) fifo_out = Record(ev_layout) self.comb += [ fifo.din.eq(fifo_in.raw_bits()), fifo_out.raw_bits().eq(fifo.dout) ] # Buffer buf_pending = Signal() buf = Record(ev_layout) buf_just_written = Signal() # Special cases replace = Signal() sequence_error = Signal() collision = Signal() any_error = Signal() if interface.enable_replace: # Note: replace may be asserted at the same time as collision # when addresses are different. In that case, it is a collision. self.sync.rsys += replace.eq(self.ev.timestamp == buf.timestamp) # Detect sequence errors on coarse timestamps only # so that they are mutually exclusive with collision errors. self.sync.rsys += sequence_error.eq(self.ev.timestamp[fine_ts_width:] < buf.timestamp[fine_ts_width:]) if interface.enable_replace: if address_width: different_addresses = self.ev.address != buf.address else: different_addresses = 0 if fine_ts_width: self.sync.rsys += collision.eq( (self.ev.timestamp[fine_ts_width:] == buf.timestamp[fine_ts_width:]) & ((self.ev.timestamp[:fine_ts_width] != buf.timestamp[:fine_ts_width]) |different_addresses)) else: self.sync.rsys += collision.eq( self.ev.timestamp[fine_ts_width:] == buf.timestamp[fine_ts_width:]) self.comb += [ any_error.eq(sequence_error | collision), self.sequence_error.eq(self.we & sequence_error), self.collision.eq(self.we & collision) ] # Buffer read and FIFO write self.comb += fifo_in.eq(buf) in_guard_time = Signal() self.comb += in_guard_time.eq( buf.timestamp[fine_ts_width:] < counter.value_sys + guard_io_cycles) self.sync.rsys += If(in_guard_time, buf_pending.eq(0)) self.comb += \ If(buf_pending, If(in_guard_time, If(buf_just_written, self.underflow.eq(1) ).Else( fifo.we.eq(1) ) ), If(self.we & ~replace & ~any_error, fifo.we.eq(1) ) ) # Buffer write # Must come after read to handle concurrent read+write properly self.sync.rsys += [ buf_just_written.eq(0), If(self.we & ~any_error, buf_just_written.eq(1), buf_pending.eq(1), buf.eq(self.ev) ) ] self.comb += self.writable.eq(fifo.writable) # Buffer output of FIFO to improve timing dout_stb = Signal() dout_ack = Signal() dout = Record(ev_layout) self.sync.rio += \ If(fifo.re, dout_stb.eq(1), dout.eq(fifo_out) ).Elif(dout_ack, dout_stb.eq(0) ) self.comb += fifo.re.eq(fifo.readable & (~dout_stb | dout_ack)) # FIFO read through buffer self.comb += [ dout_ack.eq( dout.timestamp[fine_ts_width:] == counter.value_rtio), interface.stb.eq(dout_stb & dout_ack) ] busy_transfer = _BlindTransfer() self.submodules += busy_transfer self.comb += [ busy_transfer.i.eq(interface.stb & interface.busy), self.busy.eq(busy_transfer.o), ] if data_width: self.comb += interface.data.eq(dout.data) if address_width: self.comb += interface.address.eq(dout.address) if fine_ts_width: self.comb += interface.fine_ts.eq(dout.timestamp[:fine_ts_width])
def add_output(self, n, channel): rt_packet = self.rt_packet max_fine_ts_width = self.max_fine_ts_width interface = channel.interface.o data_width = rtlink.get_data_width(interface) address_width = rtlink.get_address_width(interface) fine_ts_width = rtlink.get_fine_ts_width(interface) assert fine_ts_width <= max_fine_ts_width we = Signal() self.comb += we.eq(rt_packet.write_stb & (rt_packet.write_channel == n)) write_timestamp = rt_packet.write_timestamp[max_fine_ts_width - fine_ts_width:] write_timestamp_coarse = rt_packet.write_timestamp[max_fine_ts_width:] write_timestamp_fine = rt_packet.write_timestamp[ max_fine_ts_width - fine_ts_width:max_fine_ts_width] # latency compensation if interface.delay: tsc_comp = Signal.like(self.tsc) self.sync.rtio += tsc_comp.eq(self.tsc - interface.delay + 1) else: tsc_comp = self.tsc # FIFO ev_layout = [] if data_width: ev_layout.append(("data", data_width)) if address_width: ev_layout.append(("address", address_width)) ev_layout.append(("timestamp", len(self.tsc) + fine_ts_width)) fifo = ClockDomainsRenamer("rio")(SyncFIFOBuffered( layout_len(ev_layout), channel.ofifo_depth)) self.submodules += fifo fifo_in = Record(ev_layout) fifo_out = Record(ev_layout) self.comb += [ fifo.din.eq(fifo_in.raw_bits()), fifo_out.raw_bits().eq(fifo.dout) ] # Buffer buf_pending = Signal() buf = Record(ev_layout) buf_just_written = Signal() # Special cases replace = Signal() sequence_error = Signal() collision = Signal() any_error = Signal() if interface.enable_replace: # Note: replace may be asserted at the same time as collision # when addresses are different. In that case, it is a collision. self.sync.rio += replace.eq(write_timestamp == buf.timestamp) # Detect sequence errors on coarse timestamps only # so that they are mutually exclusive with collision errors. self.sync.rio += sequence_error.eq( write_timestamp_coarse < buf.timestamp[fine_ts_width:]) if interface.enable_replace: if address_width: different_addresses = rt_packet.write_address != buf.address else: different_addresses = 0 if fine_ts_width: self.sync.rio += collision.eq( (write_timestamp_coarse == buf.timestamp[fine_ts_width:]) & ((write_timestamp_fine != buf.timestamp[:fine_ts_width]) | different_addresses)) else: self.sync.rio += collision.eq( (write_timestamp == buf.timestamp) & different_addresses) else: self.sync.rio += collision.eq( write_timestamp_coarse == buf.timestamp[fine_ts_width:]) self.comb += any_error.eq(sequence_error | collision) self.sync.rio += [ If(we & sequence_error, self.write_sequence_error.eq(1)), If(we & collision, self.collision.eq(1)) ] # Buffer read and FIFO write self.comb += fifo_in.eq(buf) in_guard_time = Signal() self.comb += in_guard_time.eq( buf.timestamp[fine_ts_width:] < tsc_comp + 4) self.sync.rio += If(in_guard_time, buf_pending.eq(0)) report_underflow = Signal() self.comb += \ If(buf_pending, If(in_guard_time, If(buf_just_written, report_underflow.eq(1) ).Else( fifo.we.eq(1) ) ), If(we & ~replace & ~any_error, fifo.we.eq(1) ) ) self.sync.rio += If(report_underflow, self.write_underflow.eq(1)) # Buffer write # Must come after read to handle concurrent read+write properly self.sync.rio += [ buf_just_written.eq(0), If( we & ~any_error, buf_just_written.eq(1), buf_pending.eq(1), buf.timestamp.eq(write_timestamp), buf.data.eq(rt_packet.write_data) if data_width else [], buf.address.eq(rt_packet.write_address) if address_width else [], ), If(we & ~fifo.writable, self.write_overflow.eq(1)) ] # FIFO level self.sync.rio += \ If(rt_packet.fifo_space_update & (rt_packet.fifo_space_channel == n), rt_packet.fifo_space.eq(channel.ofifo_depth - fifo.level)) # FIFO read self.sync.rio += [ fifo.re.eq(0), interface.stb.eq(0), If( fifo.readable & (fifo_out.timestamp[fine_ts_width:] == tsc_comp), fifo.re.eq(1), interface.stb.eq(1)) ] if data_width: self.sync.rio += interface.data.eq(fifo_out.data) if address_width: self.sync.rio += interface.address.eq(fifo_out.address) if fine_ts_width: self.sync.rio += interface.fine_ts.eq( fifo_out.timestamp[:fine_ts_width]) self.sync.rio += If(interface.stb & interface.busy, self.busy.eq(1))
def __init__(self, interface, counter, fifo_depth, guard_io_cycles): data_width = rtlink.get_data_width(interface) address_width = rtlink.get_address_width(interface) fine_ts_width = rtlink.get_fine_ts_width(interface) ev_layout = [] if data_width: ev_layout.append(("data", data_width)) if address_width: ev_layout.append(("address", address_width)) ev_layout.append(("timestamp", counter.width + fine_ts_width)) # ev must be valid 1 cycle before we to account for the latency in # generating replace, sequence_error and nop self.ev = Record(ev_layout) self.writable = Signal() self.we = Signal() # maximum throughput 1/2 self.underflow = Signal() # valid 1 cycle after we, pulsed self.sequence_error = Signal() self.collision_error = Signal() # # # # FIFO fifo = ClockDomainsRenamer({"write": "rsys", "read": "rio"})( AsyncFIFO(layout_len(ev_layout), fifo_depth)) self.submodules += fifo fifo_in = Record(ev_layout) fifo_out = Record(ev_layout) self.comb += [ fifo.din.eq(fifo_in.raw_bits()), fifo_out.raw_bits().eq(fifo.dout) ] # Buffer buf_pending = Signal() buf = Record(ev_layout) buf_just_written = Signal() # Special cases replace = Signal() sequence_error = Signal() collision_error = Signal() any_error = Signal() nop = Signal() self.sync.rsys += [ # Note: replace does not perform any RTLink address checks, # i.e. a write to a different address will be silently replaced # as well. replace.eq(self.ev.timestamp == buf.timestamp), # Detect sequence errors on coarse timestamps only # so that they are mutually exclusive with collision errors. sequence_error.eq(self.ev.timestamp[fine_ts_width:] < buf.timestamp[fine_ts_width:]) ] if fine_ts_width: self.sync.rsys += collision_error.eq( (self.ev.timestamp[fine_ts_width:] == buf.timestamp[fine_ts_width:]) & (self.ev.timestamp[:fine_ts_width] != buf.timestamp[:fine_ts_width])) self.comb += any_error.eq(sequence_error | collision_error) if interface.suppress_nop: # disable NOP at reset: do not suppress a first write with all 0s nop_en = Signal(reset=0) addresses_equal = [getattr(self.ev, a) == getattr(buf, a) for a in ("data", "address") if hasattr(self.ev, a)] if addresses_equal: self.sync.rsys += nop.eq( nop_en & reduce(and_, addresses_equal)) else: self.comb.eq(nop.eq(0)) self.sync.rsys += [ # buf now contains valid data. enable NOP. If(self.we & ~any_error, nop_en.eq(1)), # underflows cancel the write. allow it to be retried. If(self.underflow, nop_en.eq(0)) ] self.comb += [ self.sequence_error.eq(self.we & sequence_error), self.collision_error.eq(self.we & collision_error) ] # Buffer read and FIFO write self.comb += fifo_in.eq(buf) in_guard_time = Signal() self.comb += in_guard_time.eq( buf.timestamp[fine_ts_width:] < counter.value_sys + guard_io_cycles) self.sync.rsys += If(in_guard_time, buf_pending.eq(0)) self.comb += \ If(buf_pending, If(in_guard_time, If(buf_just_written, self.underflow.eq(1) ).Else( fifo.we.eq(1) ) ), If(self.we & ~replace & ~nop & ~any_error, fifo.we.eq(1) ) ) # Buffer write # Must come after read to handle concurrent read+write properly self.sync.rsys += [ buf_just_written.eq(0), If(self.we & ~nop & ~any_error, buf_just_written.eq(1), buf_pending.eq(1), buf.eq(self.ev) ) ] self.comb += self.writable.eq(fifo.writable) # Buffer output of FIFO to improve timing dout_stb = Signal() dout_ack = Signal() dout = Record(ev_layout) self.sync.rio += \ If(fifo.re, dout_stb.eq(1), dout.eq(fifo_out) ).Elif(dout_ack, dout_stb.eq(0) ) self.comb += fifo.re.eq(fifo.readable & (~dout_stb | dout_ack)) # FIFO read through buffer # TODO: report error on stb & busy self.comb += [ dout_ack.eq( dout.timestamp[fine_ts_width:] == counter.value_rio), interface.stb.eq(dout_stb & dout_ack) ] if data_width: self.comb += interface.data.eq(dout.data) if address_width: self.comb += interface.address.eq(dout.address) if fine_ts_width: self.comb += interface.fine_ts.eq(dout.timestamp[:fine_ts_width])
def __init__(self, rt_packets, channels, max_fine_ts_width, full_ts_width): tsc = Signal(full_ts_width - max_fine_ts_width) self.sync.rtio += \ If(rt_packets.tsc_load, tsc.eq(rt_packets.tsc_value) ).Else( tsc.eq(tsc + 1) ) for n, channel in enumerate(channels): interface = channel.interface.o data_width = rtlink.get_data_width(interface) address_width = rtlink.get_address_width(interface) fine_ts_width = rtlink.get_fine_ts_width(interface) assert fine_ts_width <= max_fine_ts_width # FIFO ev_layout = [] if data_width: ev_layout.append(("data", data_width)) if address_width: ev_layout.append(("address", address_width)) ev_layout.append(("timestamp", len(tsc) + fine_ts_width)) fifo = ClockDomainsRenamer("rio")(SyncFIFOBuffered( layout_len(ev_layout), channel.ofifo_depth)) self.submodules += fifo fifo_in = Record(ev_layout) fifo_out = Record(ev_layout) self.comb += [ fifo.din.eq(fifo_in.raw_bits()), fifo_out.raw_bits().eq(fifo.dout) ] # FIFO level self.sync.rio += \ If(rt_packets.fifo_space_update & (rt_packets.fifo_space_channel == n), rt_packets.fifo_space.eq(channel.ofifo_depth - fifo.level)) # FIFO write self.comb += fifo.we.eq(rt_packets.write_stb & (rt_packets.write_channel == n)) self.sync.rio += [ If(rt_packets.write_overflow_ack, rt_packets.write_overflow.eq(0)), If(rt_packets.write_underflow_ack, rt_packets.write_underflow.eq(0)), If( fifo.we, If(~fifo.writable, rt_packets.write_overflow.eq(1)), If( rt_packets.write_timestamp[max_fine_ts_width:] < (tsc + 4), rt_packets.write_underflow.eq(1))) ] if data_width: self.comb += fifo_in.data.eq(rt_packets.write_data) if address_width: self.comb += fifo_in.address.eq(rt_packets.write_address) self.comb += fifo_in.timestamp.eq( rt_packets.write_timestamp[max_fine_ts_width - fine_ts_width:]) # FIFO read self.sync.rio += [ fifo.re.eq(0), interface.stb.eq(0), If(fifo.readable & (fifo_out.timestamp[fine_ts_width:] == tsc), fifo.re.eq(1), interface.stb.eq(1)) ] if data_width: self.sync.rio += interface.data.eq(fifo_out.data) if address_width: self.sync.rio += interface.address.eq(fifo_out.address) if fine_ts_width: self.sync.rio += interface.fine_ts.eq( fifo_out.timestamp[:fine_ts_width])
def __init__(self, channels, full_ts_width=63, guard_io_cycles=20): data_width = max(rtlink.get_data_width(c.interface) for c in channels) address_width = max( rtlink.get_address_width(c.interface) for c in channels) fine_ts_width = max( rtlink.get_fine_ts_width(c.interface) for c in channels) self.data_width = data_width self.address_width = address_width self.fine_ts_width = fine_ts_width # CSRs self.kcsrs = _KernelCSRs(bits_for(len(channels) - 1), data_width, address_width, full_ts_width) # Clocking/Reset # Create rsys, rio and rio_phy domains based on sys and rtio # with reset controlled by CSR. self.clock_domains.cd_rsys = ClockDomain() self.clock_domains.cd_rio = ClockDomain() self.clock_domains.cd_rio_phy = ClockDomain() self.comb += [ self.cd_rsys.clk.eq(ClockSignal()), self.cd_rsys.rst.eq(self.kcsrs.reset.storage) ] self.comb += self.cd_rio.clk.eq(ClockSignal("rtio")) self.specials += AsyncResetSynchronizer( self.cd_rio, self.kcsrs.reset.storage | ResetSignal("rtio", allow_reset_less=True)) self.comb += self.cd_rio_phy.clk.eq(ClockSignal("rtio")) self.specials += AsyncResetSynchronizer( self.cd_rio_phy, self.kcsrs.reset_phy.storage | ResetSignal("rtio", allow_reset_less=True)) # Managers self.submodules.counter = _RTIOCounter(full_ts_width - fine_ts_width) i_datas, i_timestamps = [], [] o_statuses, i_statuses = [], [] sel = self.kcsrs.chan_sel.storage for n, channel in enumerate(channels): if isinstance(channel, LogChannel): i_datas.append(0) i_timestamps.append(0) i_statuses.append(0) continue selected = Signal() self.comb += selected.eq(sel == n) o_manager = _OutputManager(channel.interface.o, self.counter, channel.ofifo_depth, guard_io_cycles) self.submodules += o_manager if hasattr(o_manager.ev, "data"): self.comb += o_manager.ev.data.eq(self.kcsrs.o_data.storage) if hasattr(o_manager.ev, "address"): self.comb += o_manager.ev.address.eq( self.kcsrs.o_address.storage) ts_shift = (len(self.kcsrs.o_timestamp.storage) - len(o_manager.ev.timestamp)) self.comb += o_manager.ev.timestamp.eq( self.kcsrs.o_timestamp.storage[ts_shift:]) self.comb += o_manager.we.eq(selected & self.kcsrs.o_we.re) underflow = Signal() sequence_error = Signal() collision = Signal() busy = Signal() self.sync.rsys += [ If(selected & self.kcsrs.o_underflow_reset.re, underflow.eq(0)), If(selected & self.kcsrs.o_sequence_error_reset.re, sequence_error.eq(0)), If(selected & self.kcsrs.o_collision_reset.re, collision.eq(0)), If(selected & self.kcsrs.o_busy_reset.re, busy.eq(0)), If(o_manager.underflow, underflow.eq(1)), If(o_manager.sequence_error, sequence_error.eq(1)), If(o_manager.collision, collision.eq(1)), If(o_manager.busy, busy.eq(1)) ] o_statuses.append( Cat(~o_manager.writable, underflow, sequence_error, collision, busy)) if channel.interface.i is not None: i_manager = _InputManager(channel.interface.i, self.counter, channel.ififo_depth) self.submodules += i_manager if hasattr(i_manager.ev, "data"): i_datas.append(i_manager.ev.data) else: i_datas.append(0) if channel.interface.i.timestamped: ts_shift = (len(self.kcsrs.i_timestamp.status) - len(i_manager.ev.timestamp)) i_timestamps.append(i_manager.ev.timestamp << ts_shift) else: i_timestamps.append(0) self.comb += i_manager.re.eq(selected & self.kcsrs.i_re.re) overflow = Signal() self.sync.rsys += [ If(selected & self.kcsrs.i_overflow_reset.re, overflow.eq(0)), If(i_manager.overflow, overflow.eq(1)) ] i_statuses.append(Cat(~i_manager.readable, overflow)) else: i_datas.append(0) i_timestamps.append(0) i_statuses.append(0) if data_width: self.comb += self.kcsrs.i_data.status.eq(Array(i_datas)[sel]) self.comb += [ self.kcsrs.i_timestamp.status.eq(Array(i_timestamps)[sel]), self.kcsrs.o_status.status.eq(Array(o_statuses)[sel]), self.kcsrs.i_status.status.eq(Array(i_statuses)[sel]) ] # Counter access self.sync += \ If(self.kcsrs.counter_update.re, self.kcsrs.counter.status.eq(self.counter.value_sys << fine_ts_width) )
def __init__(self, interface, counter, fifo_depth, guard_io_cycles): data_width = rtlink.get_data_width(interface) address_width = rtlink.get_address_width(interface) fine_ts_width = rtlink.get_fine_ts_width(interface) ev_layout = [] if data_width: ev_layout.append(("data", data_width)) if address_width: ev_layout.append(("address", address_width)) ev_layout.append(("timestamp", counter.width + fine_ts_width)) # ev must be valid 1 cycle before we to account for the latency in # generating replace, sequence_error and nop self.ev = Record(ev_layout) self.writable = Signal() self.we = Signal() # maximum throughput 1/2 self.underflow = Signal() # valid 1 cycle after we, pulsed self.sequence_error = Signal() self.collision_error = Signal() # # # # FIFO fifo = RenameClockDomains(AsyncFIFO(ev_layout, fifo_depth), { "write": "rsys", "read": "rio" }) self.submodules += fifo # Buffer buf_pending = Signal() buf = Record(ev_layout) buf_just_written = Signal() # Special cases replace = Signal() sequence_error = Signal() collision_error = Signal() any_error = Signal() nop = Signal() self.sync.rsys += [ # Note: replace does not perform any RTLink address checks, # i.e. a write to a different address will be silently replaced # as well. replace.eq(self.ev.timestamp == buf.timestamp), # Detect sequence errors on coarse timestamps only # so that they are mutually exclusive with collision errors. sequence_error.eq(self.ev.timestamp[fine_ts_width:] < buf.timestamp[fine_ts_width:]) ] if fine_ts_width: self.sync.rsys += collision_error.eq( (self.ev.timestamp[fine_ts_width:] == buf.timestamp[fine_ts_width:]) & (self.ev.timestamp[:fine_ts_width] != buf.timestamp[:fine_ts_width])) self.comb += any_error.eq(sequence_error | collision_error) if interface.suppress_nop: # disable NOP at reset: do not suppress a first write with all 0s nop_en = Signal(reset=0) self.sync.rsys += [ nop.eq(nop_en & optree("&", [ getattr(self.ev, a) == getattr(buf, a) for a in ("data", "address") if hasattr(self.ev, a) ], default=0)), # buf now contains valid data. enable NOP. If(self.we & ~any_error, nop_en.eq(1)), # underflows cancel the write. allow it to be retried. If(self.underflow, nop_en.eq(0)) ] self.comb += [ self.sequence_error.eq(self.we & sequence_error), self.collision_error.eq(self.we & collision_error) ] # Buffer read and FIFO write self.comb += fifo.din.eq(buf) in_guard_time = Signal() self.comb += in_guard_time.eq( buf.timestamp[fine_ts_width:] < counter.value_sys + guard_io_cycles) self.sync.rsys += If(in_guard_time, buf_pending.eq(0)) self.comb += \ If(buf_pending, If(in_guard_time, If(buf_just_written, self.underflow.eq(1) ).Else( fifo.we.eq(1) ) ), If(self.we & ~replace & ~nop & ~any_error, fifo.we.eq(1) ) ) # Buffer write # Must come after read to handle concurrent read+write properly self.sync.rsys += [ buf_just_written.eq(0), If(self.we & ~nop & ~any_error, buf_just_written.eq(1), buf_pending.eq(1), buf.eq(self.ev)) ] self.comb += self.writable.eq(fifo.writable) # Buffer output of FIFO to improve timing dout_stb = Signal() dout_ack = Signal() dout = Record(ev_layout) self.sync.rio += \ If(fifo.re, dout_stb.eq(1), dout.eq(fifo.dout) ).Elif(dout_ack, dout_stb.eq(0) ) self.comb += fifo.re.eq(fifo.readable & (~dout_stb | dout_ack)) # FIFO read through buffer # TODO: report error on stb & busy self.comb += [ dout_ack.eq(dout.timestamp[fine_ts_width:] == counter.value_rio), interface.stb.eq(dout_stb & dout_ack) ] if data_width: self.comb += interface.data.eq(dout.data) if address_width: self.comb += interface.address.eq(dout.address) if fine_ts_width: self.comb += interface.fine_ts.eq(dout.timestamp[:fine_ts_width])
def add_output(self, n, channel): rt_packet = self.rt_packet max_fine_ts_width = self.max_fine_ts_width interface = channel.interface.o data_width = rtlink.get_data_width(interface) address_width = rtlink.get_address_width(interface) fine_ts_width = rtlink.get_fine_ts_width(interface) assert fine_ts_width <= max_fine_ts_width we = Signal() self.comb += we.eq(rt_packet.write_stb & (rt_packet.write_channel == n)) write_timestamp = rt_packet.write_timestamp[max_fine_ts_width-fine_ts_width:] write_timestamp_coarse = rt_packet.write_timestamp[max_fine_ts_width:] write_timestamp_fine = rt_packet.write_timestamp[max_fine_ts_width-fine_ts_width:max_fine_ts_width] # latency compensation if interface.delay: tsc_comp = Signal.like(self.tsc) self.sync.rtio += tsc_comp.eq(self.tsc - interface.delay + 1) else: tsc_comp = self.tsc # FIFO ev_layout = [] if data_width: ev_layout.append(("data", data_width)) if address_width: ev_layout.append(("address", address_width)) ev_layout.append(("timestamp", len(self.tsc) + fine_ts_width)) fifo = ClockDomainsRenamer("rio")( SyncFIFOBuffered(layout_len(ev_layout), channel.ofifo_depth)) self.submodules += fifo fifo_in = Record(ev_layout) fifo_out = Record(ev_layout) self.comb += [ fifo.din.eq(fifo_in.raw_bits()), fifo_out.raw_bits().eq(fifo.dout) ] # Buffer buf_pending = Signal() buf = Record(ev_layout) buf_just_written = Signal() # Special cases replace = Signal() sequence_error = Signal() collision = Signal() any_error = Signal() if interface.enable_replace: # Note: replace may be asserted at the same time as collision # when addresses are different. In that case, it is a collision. self.sync.rio += replace.eq(write_timestamp == buf.timestamp) # Detect sequence errors on coarse timestamps only # so that they are mutually exclusive with collision errors. self.sync.rio += sequence_error.eq(write_timestamp_coarse < buf.timestamp[fine_ts_width:]) if interface.enable_replace: if address_width: different_addresses = rt_packet.write_address != buf.address else: different_addresses = 0 if fine_ts_width: self.sync.rio += collision.eq( (write_timestamp_coarse == buf.timestamp[fine_ts_width:]) & ((write_timestamp_fine != buf.timestamp[:fine_ts_width]) |different_addresses)) else: self.sync.rio += collision.eq( (write_timestamp == buf.timestamp) & different_addresses) else: self.sync.rio += collision.eq( write_timestamp_coarse == buf.timestamp[fine_ts_width:]) self.comb += any_error.eq(sequence_error | collision) self.sync.rio += [ If(we & sequence_error, self.write_sequence_error.eq(1)), If(we & collision, self.collision.eq(1)) ] # Buffer read and FIFO write self.comb += fifo_in.eq(buf) in_guard_time = Signal() self.comb += in_guard_time.eq( buf.timestamp[fine_ts_width:] < tsc_comp + 4) self.sync.rio += If(in_guard_time, buf_pending.eq(0)) report_underflow = Signal() self.comb += \ If(buf_pending, If(in_guard_time, If(buf_just_written, report_underflow.eq(1) ).Else( fifo.we.eq(1) ) ), If(we & ~replace & ~any_error, fifo.we.eq(1) ) ) self.sync.rio += If(report_underflow, self.write_underflow.eq(1)) # Buffer write # Must come after read to handle concurrent read+write properly self.sync.rio += [ buf_just_written.eq(0), If(we & ~any_error, buf_just_written.eq(1), buf_pending.eq(1), buf.timestamp.eq(write_timestamp), buf.data.eq(rt_packet.write_data) if data_width else [], buf.address.eq(rt_packet.write_address) if address_width else [], ), If(we & ~fifo.writable, self.write_overflow.eq(1)) ] # FIFO level self.sync.rio += \ If(rt_packet.fifo_space_update & (rt_packet.fifo_space_channel == n), rt_packet.fifo_space.eq(channel.ofifo_depth - fifo.level)) # FIFO read self.sync.rio += [ fifo.re.eq(0), interface.stb.eq(0), If(fifo.readable & (fifo_out.timestamp[fine_ts_width:] == tsc_comp), fifo.re.eq(1), interface.stb.eq(1) ) ] if data_width: self.sync.rio += interface.data.eq(fifo_out.data) if address_width: self.sync.rio += interface.address.eq(fifo_out.address) if fine_ts_width: self.sync.rio += interface.fine_ts.eq(fifo_out.timestamp[:fine_ts_width]) self.sync.rio += If(interface.stb & interface.busy, self.busy.eq(1))