from migen import * from migen.genlib.record import Record, layout_len from misoc.interconnect.csr import * from misoc.interconnect import stream from artiq.gateware.rtio import dma from artiq.gateware.rtio.cri import commands as cri_commands from artiq.coredevice.comm_analyzer import MessageType, ExceptionType __all__ = ["Analyzer"] input_output_layout = [ ("message_type", 2), ("channel", 30), ("timestamp", 64), ("rtio_counter", 64), ("address_padding", 32), ("data", 64) ] exception_layout = [ ("message_type", 2), ("channel", 30), ("padding0", 64), ("rtio_counter", 64), ("exception_type", 8), ("padding1", 88) ] stopped_layout = [ ("message_type", 2), ("padding0", 94), ("rtio_counter", 64), ("padding1", 96) ] message_len = 256 assert layout_len(input_output_layout) == message_len assert layout_len(exception_layout) == message_len assert layout_len(stopped_layout) == message_len class MessageEncoder(Module, AutoCSR): def __init__(self, tsc, cri, enable): self.source = stream.Endpoint([("data", message_len)]) self.overflow = CSRStatus() self.overflow_reset = CSR() # # # read_wait_event = cri.i_status[2] read_wait_event_r = Signal() read_done = Signal() read_overflow = Signal() self.sync += read_wait_event_r.eq(read_wait_event) self.comb += \ If(read_wait_event_r & ~read_wait_event, If(~cri.i_status[0], read_done.eq(1)), If(cri.i_status[1], read_overflow.eq(1)) ) input_output_stb = Signal() input_output = Record(input_output_layout) self.comb += [ input_output.channel.eq(cri.chan_sel), input_output.address_padding.eq(cri.o_address), input_output.rtio_counter.eq(tsc.full_ts_cri), If(cri.cmd == cri_commands["write"], input_output.message_type.eq(MessageType.output.value), input_output.timestamp.eq(cri.o_timestamp), input_output.data.eq(cri.o_data) ).Else( input_output.message_type.eq(MessageType.input.value), input_output.timestamp.eq(cri.i_timestamp), input_output.data.eq(cri.i_data) ), input_output_stb.eq((cri.cmd == cri_commands["write"]) | read_done) ] exception_stb = Signal() exception = Record(exception_layout) self.comb += [ exception.message_type.eq(MessageType.exception.value), exception.channel.eq(cri.chan_sel), exception.rtio_counter.eq(tsc.full_ts_cri), ] just_written = Signal() self.sync += just_written.eq(cri.cmd == cri_commands["write"]) self.comb += [ If(just_written & cri.o_status[1], exception_stb.eq(1), exception.exception_type.eq(ExceptionType.o_underflow.value) ), If(read_overflow, exception_stb.eq(1), exception.exception_type.eq(ExceptionType.i_overflow.value) ) ] stopped = Record(stopped_layout) self.comb += [ stopped.message_type.eq(MessageType.stopped.value), stopped.rtio_counter.eq(tsc.full_ts_cri), ] enable_r = Signal() stopping = Signal() self.sync += [ enable_r.eq(enable), If(~enable & enable_r, stopping.eq(1)), If(~stopping, If(exception_stb, self.source.data.eq(exception.raw_bits()) ).Else( self.source.data.eq(input_output.raw_bits()) ), self.source.eop.eq(0), self.source.stb.eq(enable & (input_output_stb | exception_stb)), If(self.overflow_reset.re, self.overflow.status.eq(0)), If(self.source.stb & ~self.source.ack, self.overflow.status.eq(1) ) ).Else( self.source.data.eq(stopped.raw_bits()), self.source.eop.eq(1), self.source.stb.eq(1), If(self.source.ack, stopping.eq(0)) ) ] class DMAWriter(Module, AutoCSR): def __init__(self, membus, cpu_dw): aw = len(membus.adr) dw = len(membus.dat_w) messages_per_dw = dw//message_len data_alignment = log2_int(dw//8) self.reset = CSR() # only apply when shut down # All numbers in bytes self.base_address = CSRStorage(aw + data_alignment, alignment_bits=data_alignment) self.last_address = CSRStorage(aw + data_alignment, alignment_bits=data_alignment) self.byte_count = CSRStatus(64) # only read when shut down self.sink = stream.Endpoint( [("data", dw), ("valid_token_count", bits_for(messages_per_dw))]) # # # self.comb += [ membus.cyc.eq(self.sink.stb), membus.stb.eq(self.sink.stb), self.sink.ack.eq(membus.ack), membus.we.eq(1), membus.dat_w.eq(dma.convert_signal(self.sink.data, cpu_dw//8)) ] if messages_per_dw > 1: for i in range(dw//8): self.comb += membus.sel[i].eq( self.sink.valid_token_count >= i//(256//8)) else: self.comb += membus.sel.eq(2**(dw//8)-1) self.sync += [ If(self.reset.re, membus.adr.eq(self.base_address.storage)), If(membus.ack, If(membus.adr == self.last_address.storage, membus.adr.eq(self.base_address.storage) ).Else( membus.adr.eq(membus.adr + 1) ), ) ] message_count = Signal(64 - log2_int(message_len//8)) self.comb += self.byte_count.status.eq( message_count << log2_int(message_len//8)) self.sync += [ If(self.reset.re, message_count.eq(0)), If(membus.ack, message_count.eq( message_count + self.sink.valid_token_count)) ] class Analyzer(Module, AutoCSR): def __init__(self, tsc, cri, membus, fifo_depth=128, cpu_dw=32): # shutdown procedure: set enable to 0, wait until busy=0 self.enable = CSRStorage() self.busy = CSRStatus() self.submodules.message_encoder = MessageEncoder( tsc, cri, self.enable.storage) self.submodules.fifo = stream.SyncFIFO( [("data", message_len)], fifo_depth, True) self.submodules.converter = stream.Converter( message_len, len(membus.dat_w), reverse=True, report_valid_token_count=True) self.submodules.dma = DMAWriter(membus, cpu_dw) enable_r = Signal() self.sync += [ enable_r.eq(self.enable.storage), If(self.enable.storage & ~enable_r, self.busy.status.eq(1)), If(self.dma.sink.stb & self.dma.sink.ack & self.dma.sink.eop, self.busy.status.eq(0)) ] self.comb += [ self.message_encoder.source.connect(self.fifo.sink), self.fifo.source.connect(self.converter.sink), self.converter.source.connect(self.dma.sink) ]