"""Serializer for real-time data, common to satellite and master""" from types import SimpleNamespace from migen import * __all__ = ["ReceiveDatapath", "TransmitDatapath", "get_m2s_layouts", "get_s2m_layouts"] def layout_len(l): return sum(e[1] for e in l) class PacketLayoutManager: def __init__(self, alignment): self.alignment = alignment self.layouts = dict() self.types = dict() self.type_names = dict() def add_type(self, name, *fields, pad=True): type_n = len(self.types) self.types[name] = type_n self.type_names[type_n] = name layout = [("ty", 8)] + list(fields) misalignment = layout_len(layout) % self.alignment if misalignment: layout.append(("packet_pad", self.alignment - misalignment)) self.layouts[name] = layout def field_length(self, type_name, field_name): layout = self.layouts[type_name] for name, length in layout: if name == field_name: return length raise KeyError def get_m2s_layouts(alignment): if alignment > 128: short_data_len = alignment - 128 + 16 else: short_data_len = 16 plm = PacketLayoutManager(alignment) plm.add_type("echo_request") plm.add_type("set_time", ("timestamp", 64)) plm.add_type("write", ("timestamp", 64), ("chan_sel", 24), ("address", 8), ("extra_data_cnt", 8), ("short_data", short_data_len)) plm.add_type("buffer_space_request", ("destination", 8)) plm.add_type("read_request", ("chan_sel", 24), ("timeout", 64)) return plm def get_s2m_layouts(alignment): plm = PacketLayoutManager(alignment) plm.add_type("echo_reply") plm.add_type("buffer_space_reply", ("space", 16)) plm.add_type("read_reply", ("timestamp", 64), ("data", 32)) plm.add_type("read_reply_noevent", ("overflow", 1)) # overflow=0→timeout plm.add_type("async_messages_ready") return plm class ReceiveDatapath(Module): def __init__(self, frame, data, plm): ws = len(data) # control self.packet_buffer_load = Signal() # outputs self.frame_r = Signal() self.data_r = Signal(ws) self.packet_type = Signal(8) self.packet_last = Signal() self.packet_as = dict() # # # # input pipeline stage - determine packet length based on type lastword_per_type = [layout_len(plm.layouts[plm.type_names[i]])//ws - 1 for i in range(len(plm.layouts))] packet_last_n = Signal(max=max(lastword_per_type)+1) self.sync += [ self.frame_r.eq(frame), self.data_r.eq(data), If(frame & ~self.frame_r, self.packet_type.eq(data[:8]), packet_last_n.eq(Array(lastword_per_type)[data[:8]]) ) ] # bufferize packet packet_buffer = Signal(max(layout_len(l) for l in plm.layouts.values())) w_in_packet = len(packet_buffer)//ws packet_buffer_count = Signal(max=w_in_packet+1) self.sync += \ If(self.packet_buffer_load, Case(packet_buffer_count, {i: packet_buffer[i*ws:(i+1)*ws].eq(self.data_r) for i in range(w_in_packet)}), packet_buffer_count.eq(packet_buffer_count + 1) ).Else( packet_buffer_count.eq(0) ) self.comb += self.packet_last.eq(packet_buffer_count == packet_last_n) # dissect packet for name, layout in plm.layouts.items(): fields = SimpleNamespace() idx = 0 for field_name, field_size in layout: setattr(fields, field_name, packet_buffer[idx:idx+field_size]) idx += field_size self.packet_as[name] = fields class TransmitDatapath(Module): def __init__(self, frame, data, plm): ws = len(data) assert ws % 8 == 0 self.ws = ws self.plm = plm self.packet_buffer = Signal(max(layout_len(l) for l in plm.layouts.values())) w_in_packet = len(self.packet_buffer)//ws self.packet_last_n = Signal(max=max(w_in_packet, 2)) self.packet_stb = Signal() self.packet_last = Signal() self.raw_stb = Signal() self.raw_data = Signal(ws) # # # self.sync += frame.eq(0) if w_in_packet > 1: packet_buffer_count = Signal(max=w_in_packet) self.comb += self.packet_last.eq(packet_buffer_count == self.packet_last_n) self.sync += [ packet_buffer_count.eq(0), If(self.packet_stb, frame.eq(1), Case(packet_buffer_count, {i: data.eq(self.packet_buffer[i*ws:(i+1)*ws]) for i in range(w_in_packet)}), packet_buffer_count.eq(packet_buffer_count + 1) ) ] else: self.comb += self.packet_last.eq(1) self.sync += \ If(self.packet_stb, frame.eq(1), data.eq(self.packet_buffer) ) self.sync += [ If(self.raw_stb, frame.eq(1), data.eq(self.raw_data) ) ] def send(self, ty, **kwargs): idx = 8 value = self.plm.types[ty] for field_name, field_size in self.plm.layouts[ty][1:]: try: fvalue = kwargs[field_name] del kwargs[field_name] except KeyError: fvalue = 0 value = value | (fvalue << idx) idx += field_size if kwargs: raise ValueError return [ self.packet_stb.eq(1), self.packet_buffer.eq(value), self.packet_last_n.eq(idx//self.ws-1) ]