diff --git a/src/gateware/cxp_pipeline.py b/src/gateware/cxp_pipeline.py index c254600..70b0b33 100644 --- a/src/gateware/cxp_pipeline.py +++ b/src/gateware/cxp_pipeline.py @@ -32,16 +32,16 @@ def _bytes2word(bytes, big_endian=True): return struct.unpack("4B", *bytes))[0] class Code_Source(Module): - def __init__(self, layout, counts=4): + def __init__(self, layout, data, k): self.source = stream.Endpoint(layout) self.stb = Signal() - self.data = Signal.like(self.source.data) - self.k = Signal.like(self.source.k) # # # + assert len(data) == len(k) > 0 + counts = len(data) - cnt = Signal(max=counts) + cnt = Signal() if counts == 1 else Signal(max=counts) clr_cnt = Signal() inc_cnt = Signal() @@ -64,8 +64,8 @@ class Code_Source(Module): fsm.act("WRITE", self.source.stb.eq(1), - self.source.data.eq(self.data), - self.source.k.eq(self.k), + self.source.data.eq(Array(data)[cnt]), + self.source.k.eq(Array(k)[cnt]), If(cnt == counts - 1, self.source.eop.eq(1), If(self.source.ack, NextState("IDLE")) @@ -76,7 +76,7 @@ class Code_Source(Module): class Code_Inserter(Module): - def __init__(self, layout, insert_infront=True, counts=4): + def __init__(self, layout, data, k, insert_infront=True): self.sink = stream.Endpoint(layout) self.source = stream.Endpoint(layout) @@ -84,7 +84,8 @@ class Code_Inserter(Module): self.k = Signal.like(self.sink.k) # # # - assert counts > 0 + assert len(data) == len(k) > 0 + counts = len(data) cnt = Signal() if counts == 1 else Signal(max=counts) clr_cnt = Signal() @@ -99,84 +100,49 @@ class Code_Inserter(Module): ] self.submodules.fsm = fsm = FSM(reset_state="IDLE") - - if insert_infront: - fsm.act("IDLE", - self.sink.ack.eq(1), - clr_cnt.eq(1), - If(self.sink.stb, - self.sink.ack.eq(0), - NextState("INSERT"), - ) - ) - - fsm.act("INSERT", + remove_sink_oep = 0 if insert_infront else 1 + + # add code in front: IDLE -> INSERT -> COPY + # add code at end: IDLE -> COPY -> INSERT + fsm.act("IDLE", + self.sink.ack.eq(1), + clr_cnt.eq(1), + If(self.sink.stb, self.sink.ack.eq(0), - self.source.stb.eq(1), - self.source.data.eq(self.data), - self.source.k.eq(self.k), - If(cnt == counts - 1, - If(self.source.ack, NextState("COPY")) - ).Else( - inc_cnt.eq(self.source.ack) - ) + NextState("INSERT" if insert_infront else "COPY"), ) + ) - fsm.act("COPY", - self.sink.connect(self.source), - If(self.sink.stb & self.sink.eop & self.source.ack, - NextState("IDLE"), - ) + fsm.act("INSERT", + self.sink.ack.eq(0), + self.source.stb.eq(1), + self.source.data.eq(Array(data)[cnt]), + self.source.k.eq(Array(k)[cnt]), + If(cnt == counts - 1, + If(remove_sink_oep, self.source.eop.eq(1)), + If(self.source.ack, NextState("COPY" if insert_infront else "IDLE")) + ).Else( + inc_cnt.eq(self.source.ack) ) + ) - else: - fsm.act("IDLE", - self.sink.ack.eq(1), - clr_cnt.eq(1), - If(self.sink.stb, - self.sink.ack.eq(0), - NextState("COPY"), - ) - ) - - fsm.act("COPY", - self.sink.connect(self.source), - self.source.eop.eq(0), - If(self.sink.stb & self.sink.eop & self.source.ack, - NextState("INSERT"), - ) - ) - - fsm.act("INSERT", - self.sink.ack.eq(0), - self.source.stb.eq(1), - self.source.data.eq(self.data), - self.source.k.eq(self.k), - If(cnt == counts - 1, - self.source.eop.eq(1), - If(self.source.ack, NextState("IDLE")) - ).Else( - inc_cnt.eq(self.source.ack) - ), + fsm.act("COPY", + self.sink.connect(self.source), + If(remove_sink_oep, self.source.eop.eq(0)), + If(self.sink.stb & self.sink.eop & self.source.ack, + NextState("IDLE" if insert_infront else "INSERT"), ) + ) class Packet_Wrapper(Module): def __init__(self, layout): - self.submodules.pak_start = pak_start = Code_Inserter(layout) - self.submodules.pak_end = pak_end = Code_Inserter(layout, insert_infront=False) + self.submodules.pak_start = pak_start = Code_Inserter(layout, [KCode["pak_start"]]*4, [1]*4) + self.submodules.pak_end = pak_end = Code_Inserter(layout, [KCode["pak_end"]]*4, [1]*4, insert_infront=False) + self.comb += pak_start.source.connect(pak_end.sink), self.sink = pak_start.sink self.source = pak_end.source - - self.comb += [ - pak_start.data.eq(KCode["pak_start"]), - pak_start.k.eq(1), - pak_end.data.eq(KCode["pak_end"]), - pak_end.k.eq(1), - - pak_start.source.connect(pak_end.sink), - ] @ResetInserter() @CEInserter() @@ -216,33 +182,28 @@ class TX_Trigger(Module, AutoCSR): # # # - self.submodules.code_src = code_src = Code_Source(upconn_layout, counts=3) - self.comb += [ - code_src.stb.eq(self.trig_stb), - code_src.data.eq(self.delay), - code_src.k.eq(0) - ] + # Table 15 & 16 (CXP-001-2021) + # Send [K28.2, K28.4, K28.4] or [K28.4, K28.2, K28.2] and 3x delay as trigger packet - self.submodules.inserter_once = inserter_once = Code_Inserter(upconn_layout, counts=1) - self.submodules.inserter_twice = inserter_twice = Code_Inserter(upconn_layout, counts=2) - self.comb += [ - inserter_once.k.eq(1), - inserter_twice.k.eq(1), + self.submodules.code_src = code_src = Code_Source(upconn_layout, [self.delay]*3, [0]*3) + self.comb += code_src.stb.eq(self.trig_stb), + + header = [Signal(8) for _ in range(3)] + self.comb += \ If((self.linktrig_mode == 0) | (self.linktrig_mode == 2), - inserter_once.data.eq(KCode["trig_indic_28_2"]), - inserter_twice.data.eq(KCode["trig_indic_28_4"]), + header[0].eq(KCode["trig_indic_28_2"]), + header[1].eq(KCode["trig_indic_28_4"]), + header[2].eq(KCode["trig_indic_28_4"]), ).Else( - inserter_once.data.eq(KCode["trig_indic_28_4"]), - inserter_twice.data.eq(KCode["trig_indic_28_2"]), + header[0].eq(KCode["trig_indic_28_4"]), + header[1].eq(KCode["trig_indic_28_2"]), + header[2].eq(KCode["trig_indic_28_2"]), ) - ] - tx_pipeline = [ code_src, inserter_twice, inserter_once] + self.submodules.inserter = inserter = Code_Inserter(upconn_layout, header, [1]*3) - for s, d in zip(tx_pipeline, tx_pipeline[1:]): - self.comb += s.source.connect(d.sink) - - self.source = tx_pipeline[-1].source + self.comb += code_src.source.connect(inserter.sink) + self.source = inserter.source class Trigger_ACK(Module): def __init__(self): @@ -252,19 +213,14 @@ class Trigger_ACK(Module): # Section 9.3.2 (CXP-001-2021) # Send 4x K28.6 and 4x 0x01 as trigger packet ack - self.submodules.code_src = code_src = Code_Source(upconn_layout) - self.submodules.k_code_inserter = k_code_inserter = Code_Inserter(upconn_layout) + self.submodules.code_src = code_src = Code_Source(upconn_layout, [0x01]*4, [0]*4) + self.submodules.inserter = inserter = Code_Inserter(upconn_layout, [KCode["io_ack"]]*4, [1]*4) self.comb += [ code_src.stb.eq(self.ack), - code_src.data.eq(0x01), - code_src.k.eq(0), - k_code_inserter.data.eq(KCode["io_ack"]), - k_code_inserter.k.eq(1), - - code_src.source.connect(k_code_inserter.sink) + code_src.source.connect(inserter.sink) ] - self.source = k_code_inserter.source + self.source = inserter.source class TX_Command_Packet(Module, AutoCSR): # Section 12.1.2 (CXP-001-2021) @@ -309,63 +265,64 @@ class TX_Test_Packet(Module, AutoCSR): # # # - testdata_src = stream.Endpoint(upconn_layout) - + # testdata_src = stream.Endpoint(upconn_layout) - # Section 9.9.2 (CXP-001-2021) - # 0x00, 0x01 ... 0xFF need to be send 16 times - # cnt[8:12] is used to count up 16 times while cnt[:8] is the data - cnt = Signal(max=0x1000) - clr_cnt = Signal() - inc_cnt = Signal() + # # Section 9.9.2 (CXP-001-2021) + # # 0x00, 0x01 ... 0xFF need to be send 16 times + # # cnt[8:12] is used to count up 16 times while cnt[:8] is the data + # cnt = Signal(max=0x1000) + # clr_cnt = Signal() + # inc_cnt = Signal() - self.sync += [ - If(clr_cnt, - cnt.eq(cnt.reset), - ).Elif(inc_cnt, - cnt.eq(cnt + 1), - ), - ] + # self.sync += [ + # If(clr_cnt, + # cnt.eq(cnt.reset), + # ).Elif(inc_cnt, + # cnt.eq(cnt + 1), + # ), + # ] - self.submodules.fsm = fsm = FSM(reset_state="IDLE") + # self.submodules.fsm = fsm = FSM(reset_state="IDLE") - fsm.act("IDLE", - clr_cnt.eq(1), - If(self.stb.re, - NextState("WRITE") - ) - ) + # fsm.act("IDLE", + # clr_cnt.eq(1), + # If(self.stb.re, + # NextState("WRITE") + # ) + # ) - fsm.act("WRITE", - testdata_src.stb.eq(1), - testdata_src.data.eq(cnt[:8]), - testdata_src.k.eq(0), - If(cnt == 0xFFF, - testdata_src.eop.eq(1), - If(testdata_src.ack, NextState("IDLE")) - ).Else( - inc_cnt.eq(testdata_src.ack) - ) - ) + # fsm.act("WRITE", + # testdata_src.stb.eq(1), + # testdata_src.data.eq(cnt[:8]), + # testdata_src.k.eq(0), + # If(cnt == 0xFFF, + # testdata_src.eop.eq(1), + # If(testdata_src.ack, NextState("IDLE")) + # ).Else( + # inc_cnt.eq(testdata_src.ack) + # ) + # ) - self.submodules.pak_type_inserter = pak_type_inserter = Code_Inserter(upconn_layout) + # TODO: figure out why only 16 times doesn't work on the decoder + + self.submodules.test_pattern_src = test_pattern_src = Code_Source(upconn_layout, [*range(0x100)]*1, [0]*0x100*1) + self.submodules.pak_type_inserter = pak_type_inserter = Code_Inserter(upconn_layout, [0x04]*4, [0]*4) self.submodules.pak_wrp = pak_wrp = Packet_Wrapper(upconn_layout) self.comb += [ - pak_type_inserter.data.eq(0x04), - pak_type_inserter.k.eq(0), - - testdata_src.connect(pak_type_inserter.sink), + test_pattern_src.source.connect(pak_type_inserter.sink), pak_type_inserter.source.connect(pak_wrp.sink), ] self.source = pak_wrp.source - self.sync += \ + self.sync += [ + test_pattern_src.stb.eq(self.stb.re), If(self.stb.re, self.busy.status.eq(1), ).Elif(self.source.eop & self.source.ack, self.busy.status.eq(0) ) + ] class RX_Debug_Buffer(Module,AutoCSR): def __init__(self): @@ -390,16 +347,20 @@ class Receiver_Path(Module, AutoCSR): self.trig_ack = Signal() self.trig_clr = Signal() - # TODO: self.packet_type = Signal(8) self.decoder_err = Signal() self.decoder_err_clr = Signal() + self.test_err = Signal() + self.test_err_clr = Signal() + # # # self.submodules.trig_ack_checker = trig_ack_checker = CXP_Trig_Ack_Checker() self.submodules.packet_decoder = packet_decoder = CXP_Data_Packet_Decode() + + # Error are latched self.sync += [ If(trig_ack_checker.ack, self.trig_ack.eq(1), @@ -411,6 +372,12 @@ class Receiver_Path(Module, AutoCSR): self.decoder_err.eq(1), ).Elif(self.decoder_err_clr, self.decoder_err.eq(0), + ), + + If(packet_decoder.test_err, + self.test_err.eq(1), + ).Elif(self.test_err_clr, + self.test_err.eq(0), ) ] self.comb += [ @@ -437,6 +404,8 @@ class CXP_Data_Packet_Decode(Module): self.packet_type = Signal(8) self.decode_err = Signal() + self.buffer = Signal(40*downconn_dw) + self.test_err = Signal() # # # # decoder -> priorities mux(normal packet vs trigger ack) -> data packet mux (control ack, data stream, heartbeat, testmode, (optional Genlcam event)) @@ -457,31 +426,84 @@ class CXP_Data_Packet_Decode(Module): self.sink.ack.eq(1), # TODO: add error correction? If((self.sink.stb & (self.sink.data == _bytes2word([KCode["pak_start"]]*4)) & (self.sink.k == 0b1111)), - NextState("DECODE_PAK"), + NextState("DECODE"), ) ) # TODO: decode packet type here - fsm.act("DECODE_PAK", + + cnt = Signal(max=0x100) + + fsm.act("DECODE", self.sink.ack.eq(1), If(self.sink.stb, NextValue(self.packet_type, self.sink.data[:8]), - NextState("STREAMING"), + + Case(self.sink.data[:8],{ + type["data_stream"]: NextState("STREAMING"), + type["debug"]: NextState("STREAMING"), + type["test_packet"]: [ + NextValue(cnt, 0), + NextState("VERIFY_TEST_PATTERN"), + ], + "default": [ + self.decode_err.eq(1), + # wait till next valid packet + NextState("IDLE"), + ], + }), ) ) + fsm.act("VERIFY_TEST_PATTERN", + self.sink.ack.eq(1), + If(self.sink.stb, + If(((self.sink.data == _bytes2word([KCode["pak_end"]]*4)) & (self.sink.k == 0b1111)), + NextState("IDLE"), + ).Else( + If(((self.sink.data != Cat(cnt, cnt+1, cnt+2, cnt+3))), + self.test_err.eq(1), + ), + If(cnt == 0xFC, + NextValue(cnt, cnt.reset), + ).Else( + NextValue(cnt, cnt + 4) + ) + ) + ) + + ) + fsm.act("STREAMING", If((self.sink.stb & (self.sink.data == _bytes2word([KCode["pak_end"]]*4)) & (self.sink.k == 0b1111)), # discard K29,7 self.sink.ack.eq(1), NextState("IDLE") - ).Elif(self.packet_type == type["debug"], - self.sink.connect(self.source), ).Else( - self.sink.ack.eq(1), - self.decode_err.eq(1), - ) + self.sink.connect(self.source), + ) ) + # # input pipeline stage - determine packet length based on type + # self.sync += [ + # packet_start.eq((self.sink.data[0] == K(27, 7)) & (self.sink.k[0] == 1)), + # packet_end.eq((self.sink.data[0] == K(29, 7)) & (self.sink.k[0] == 1)), + + # If((self.sink.data[0] == K(27, 7)) & (self.sink.k[0] == 1), + # packet_buffer_load.eq(1), + # ), + + + # trig_ack.eq((self.sink.data[0] == K(28, 6)) & (self.sink.k[0] == 1)), + # If(trig_ack, + # self.trig_ack.eq(self.sink.data[0]), + # trig_ack.eq(0), + # ).Elif(packet_buffer_load, + # # TODO: add test packet counting + # Case(buffer_count, + # {i: buffer[i*downconn_dw:(i+1)*downconn_dw].eq(self.sink.data) + # for i in range(40)}), + # buffer_count.eq(buffer_count + 1), + class CXP_Trig_Ack_Checker(Module, AutoCSR): def __init__(self):