From 64bbb667ece318e6fbcd1180c939fb3e0bb905f4 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Fri, 28 Jul 2023 12:35:50 +0200 Subject: [PATCH] tlp/depacketizer: Make it more modular and add capabilities parameter. By default, we generally only want Request/Completion TLPs support, but for other specific cases (ex PTM) we also want to handle Configuration TLPs. With these change, the module is made more modular to automatically adapt itself to the capabilities provided (and will also allow easily extending capabilities in the future, ex for PTM TLPs). --- litepcie/tlp/depacketizer.py | 223 ++++++++++++++++++++--------------- 1 file changed, 131 insertions(+), 92 deletions(-) diff --git a/litepcie/tlp/depacketizer.py b/litepcie/tlp/depacketizer.py index dc1fadc..d2981ac 100644 --- a/litepcie/tlp/depacketizer.py +++ b/litepcie/tlp/depacketizer.py @@ -295,15 +295,24 @@ def __init__(self): # LitePCIeTLPDepacketizer -------------------------------------------------------------------------- class LitePCIeTLPDepacketizer(LiteXModule): - def __init__(self, data_width, endianness, address_mask=0): - self.sink = stream.Endpoint(phy_layout(data_width)) - self.req_source = req_source = stream.Endpoint(request_layout(data_width)) - self.cmp_source = cmp_source = stream.Endpoint(completion_layout(data_width)) - self.conf_source = conf_source = stream.Endpoint(configuration_layout(data_width)) + def __init__(self, data_width, endianness, address_mask=0, capabilities=["REQUEST", "COMPLETION"]): + # Sink Endpoint. + self.sink = stream.Endpoint(phy_layout(data_width)) + + # Source Endpoints. + for c in capabilities: + assert c in ["REQUEST", "COMPLETION", "CONFIGURATION"] + if "REQUEST" in capabilities: + self.req_source = req_source = stream.Endpoint(request_layout(data_width)) + if "COMPLETION" in capabilities: + self.cmp_source = cmp_source = stream.Endpoint(completion_layout(data_width)) + if "CONFIGURATION" in capabilities: + self.conf_source = conf_source = stream.Endpoint(configuration_layout(data_width)) # # # - # Extract raw header ----------------------------------------------------------------------- + # Extract RAW Header from Sink ------------------------------------------------------------- + header_extracter_cls = { 64 : LitePCIeTLPHeaderExtracter64b, 128 : LitePCIeTLPHeaderExtracter128b, @@ -315,108 +324,138 @@ def __init__(self, data_width, endianness, address_mask=0): self.comb += self.sink.connect(header_extracter.sink) header = header_extracter.source.header - # Dispatch data according to fmt/type ------------------------------------------------------ - dispatch_source = stream.Endpoint(tlp_common_layout(data_width)) - dispatch_sinks = [stream.Endpoint(tlp_common_layout(data_width)) for i in range(4)] - self.comb += dispatch_sinks[0b00].ready.eq(1) # Always ready when unknown. + # Create Dispatcher ------------------------------------------------------------------------ + + # Dispatch Sources + dispatch_sources = {"DISCARD" : stream.Endpoint(tlp_common_layout(data_width))} + for source in capabilities: + dispatch_sources[source] = stream.Endpoint(tlp_common_layout(data_width)) + + def dispatch_source_sel(name): + for n, k in enumerate(dispatch_sources.keys()): + if k == name: + return n + return None + + # Dispatch Sink. + dispatch_sink = stream.Endpoint(tlp_common_layout(data_width)) + + # Dispatcher + self.dispatcher = Dispatcher( + master = dispatch_sink, + slaves = dispatch_sources.values() + ) + # Ensure DISCARD source is always ready. + self.comb += dispatch_sources["DISCARD"].ready.eq(1) + + # Connect Header Extracter to Dispatch Sink. self.comb += [ - dispatch_source.valid.eq(header_extracter.source.valid), - header_extracter.source.ready.eq(dispatch_source.ready), - dispatch_source.first.eq(header_extracter.source.first), - dispatch_source.last.eq(header_extracter.source.last), - tlp_common_header.decode(header, dispatch_source) + header_extracter.source.connect(dispatch_sink, keep={"valid", "ready", "first", "last"}), + tlp_common_header.decode(header, dispatch_sink) ] self.comb += dword_endianness_swap( src = header_extracter.source.dat, - dst = dispatch_source.dat, + dst = dispatch_sink.dat, data_width = data_width, endianness = endianness, mode = "dat", ) self.comb += dword_endianness_swap( src = header_extracter.source.be, - dst = dispatch_source.be, + dst = dispatch_sink.be, data_width = data_width, endianness = endianness, mode = "be", ) - self.dispatcher = Dispatcher(dispatch_source, dispatch_sinks) - - fmt_type = Cat(dispatch_source.type, dispatch_source.fmt) - self.comb += [ - self.dispatcher.sel.eq(0b00), - # Memory Write/Read. - If((fmt_type == fmt_type_dict["mem_rd32"]) | - (fmt_type == fmt_type_dict["mem_wr32"]), - self.dispatcher.sel.eq(0b01), - ), - # Completion. - If((fmt_type == fmt_type_dict["cpld"]) | - (fmt_type == fmt_type_dict["cpl"]), - self.dispatcher.sel.eq(0b10), - ), - # Configuration. - If((fmt_type == fmt_type_dict["cfg_rd0"]) | - (fmt_type == fmt_type_dict["cfg_wr0"]), - self.dispatcher.sel.eq(0b11), - ), - ] - - # Decode TLP request and format local request ---------------------------------------------- - self.tlp_req = tlp_req = stream.Endpoint(tlp_request_layout(data_width)) - self.comb += dispatch_sinks[0b01].connect(tlp_req) - self.comb += tlp_request_header.decode(header, tlp_req) - req_type = Cat(tlp_req.type, tlp_req.fmt) - self.comb += [ - req_source.valid.eq(tlp_req.valid), - req_source.we.eq(tlp_req.valid & (req_type == fmt_type_dict["mem_wr32"])), - tlp_req.ready.eq(req_source.ready), - req_source.first.eq(tlp_req.first), - req_source.last.eq(tlp_req.last), - req_source.adr.eq(tlp_req.address & (~address_mask)), - req_source.len.eq(tlp_req.length), - req_source.req_id.eq(tlp_req.requester_id), - req_source.tag.eq(tlp_req.tag), - req_source.dat.eq(tlp_req.dat) - ] - - # Decode TLP completion and format local completion ---------------------------------------- - self.tlp_cmp = tlp_cmp = stream.Endpoint(tlp_completion_layout(data_width)) - self.comb += dispatch_sinks[0b10].connect(tlp_cmp) - self.comb += tlp_completion_header.decode(header, tlp_cmp) - - self.comb += [ - cmp_source.valid.eq(tlp_cmp.valid), - tlp_cmp.ready.eq(cmp_source.ready), - cmp_source.first.eq(tlp_cmp.first), - cmp_source.last.eq(tlp_cmp.last), - cmp_source.len.eq(tlp_cmp.length), - cmp_source.end.eq(tlp_cmp.length == (tlp_cmp.byte_count[2:])), - cmp_source.adr.eq(tlp_cmp.lower_address), - cmp_source.req_id.eq(tlp_cmp.requester_id), - cmp_source.cmp_id.eq(tlp_cmp.completer_id), - cmp_source.err.eq(tlp_cmp.status != 0), - cmp_source.tag.eq(tlp_cmp.tag), - cmp_source.dat.eq(tlp_cmp.dat) - ] + # Create fmt_type for destination decoding. + fmt_type = Cat(dispatch_sink.type, dispatch_sink.fmt) + # Set default Dispatcher select to DISCARD Sink. + self.comb += self.dispatcher.sel.eq(dispatch_source_sel("DISCARD")) - # Decode TLP configuration and format local configuraiton ---------------------------------- - self.tlp_conf = tlp_conf = stream.Endpoint(tlp_configuration_layout(data_width)) - self.comb += dispatch_sinks[0b11].connect(tlp_conf) - self.comb += tlp_configuration_header.decode(header, tlp_conf) + # Decode/Dispatch TLP Requests ------------------------------------------------------------- - self.comb += [ - conf_source.valid.eq(tlp_conf.valid), - tlp_conf.ready.eq(conf_source.ready), - conf_source.first.eq(tlp_conf.first), - conf_source.last.eq(tlp_conf.last), - conf_source.bus_number.eq(tlp_conf.bus_number), - conf_source.device_no.eq(tlp_conf.device_no), - conf_source.func.eq(tlp_conf.func), - conf_source.ext_reg.eq(tlp_conf.ext_reg), - conf_source.register_no.eq(tlp_conf.register_no), - req_source.dat.eq(tlp_req.dat) - ] + if "REQUEST" in capabilities: + self.comb += [ + If((fmt_type == fmt_type_dict["mem_rd32"]) | + (fmt_type == fmt_type_dict["mem_wr32"]), + self.dispatcher.sel.eq(dispatch_source_sel("REQUEST")), + ) + ] + + self.tlp_req = tlp_req = stream.Endpoint(tlp_request_layout(data_width)) + self.comb += dispatch_sources["REQUEST"].connect(tlp_req) + self.comb += tlp_request_header.decode(header, tlp_req) + + req_type = Cat(tlp_req.type, tlp_req.fmt) + self.comb += [ + req_source.valid.eq(tlp_req.valid), + req_source.we.eq(tlp_req.valid & (req_type == fmt_type_dict["mem_wr32"])), + tlp_req.ready.eq(req_source.ready), + req_source.first.eq(tlp_req.first), + req_source.last.eq(tlp_req.last), + req_source.adr.eq(tlp_req.address & (~address_mask)), + req_source.len.eq(tlp_req.length), + req_source.req_id.eq(tlp_req.requester_id), + req_source.tag.eq(tlp_req.tag), + req_source.dat.eq(tlp_req.dat) + ] + + # Decode/Dispatch TLP Completions ---------------------------------------------------------- + + if "COMPLETION" in capabilities: + self.comb += [ + If((fmt_type == fmt_type_dict["cpld"]) | + (fmt_type == fmt_type_dict["cpl"]), + self.dispatcher.sel.eq(dispatch_source_sel("COMPLETION")), + ) + ] + + self.tlp_cmp = tlp_cmp = stream.Endpoint(tlp_completion_layout(data_width)) + self.comb += dispatch_sources["COMPLETION"].connect(tlp_cmp) + self.comb += tlp_completion_header.decode(header, tlp_cmp) + + self.comb += [ + cmp_source.valid.eq(tlp_cmp.valid), + tlp_cmp.ready.eq(cmp_source.ready), + cmp_source.first.eq(tlp_cmp.first), + cmp_source.last.eq(tlp_cmp.last), + cmp_source.len.eq(tlp_cmp.length), + cmp_source.end.eq(tlp_cmp.length == (tlp_cmp.byte_count[2:])), + cmp_source.adr.eq(tlp_cmp.lower_address), + cmp_source.req_id.eq(tlp_cmp.requester_id), + cmp_source.cmp_id.eq(tlp_cmp.completer_id), + cmp_source.err.eq(tlp_cmp.status != 0), + cmp_source.tag.eq(tlp_cmp.tag), + cmp_source.dat.eq(tlp_cmp.dat) + ] + + + # Decode/Dispatch TLP Configurations ------------------------------------------------------- + + if "CONFIGURATION" in capabilities: + self.comb += [ + If((fmt_type == fmt_type_dict["cfg_rd0"]) | + (fmt_type == fmt_type_dict["cfg_wr0"]), + self.dispatcher.sel.eq(dispatch_source_sel("CONFIGURATION")), + ) + ] + + self.tlp_conf = tlp_conf = stream.Endpoint(tlp_configuration_layout(data_width)) + self.comb += dispatch_sources["CONFIGURATION"].connect(tlp_conf) + self.comb += tlp_configuration_header.decode(header, tlp_conf) + + self.comb += [ + conf_source.valid.eq(tlp_conf.valid), + tlp_conf.ready.eq(conf_source.ready), + conf_source.first.eq(tlp_conf.first), + conf_source.last.eq(tlp_conf.last), + conf_source.bus_number.eq(tlp_conf.bus_number), + conf_source.device_no.eq(tlp_conf.device_no), + conf_source.func.eq(tlp_conf.func), + conf_source.ext_reg.eq(tlp_conf.ext_reg), + conf_source.register_no.eq(tlp_conf.register_no), + conf_source.dat.eq(tlp_conf.dat) + ]