Keyerror when loading new bit file

Hi support,

PYNQ version: 3.0.1
FPGA Board: RFSOC 4x2
Xilinx version: Vivado 2022.1
Workbook: RF Dataconvertor Introduction

I’ve modified the block design from the standard base design to add an AXI port (M_AXI_MEMMAP) and a clock port to interface with some RTL outside of the block design. I’ve previously built the standard base design and it runs fine in the RF convertor workbook.

If I try and load the new bit file I get the following error.

KeyError: “M_AXI_MEMMAP”

I’m having trouble finding information on keyerrors in general which is why I am here. I assume there is more to do than just adding ports to the block design. I have attached the hwh file. Apologies I havent been able to upload Traceback log images as yet.

Any help would be greatly appreciated.

base_wrapper_dds.hwh (4.1 MB)

Hi @jonnyj_au,

Welcome to the PYNQ community.

It would be good if you can post an image of your block diagram and the traceback of the error.

Mario

Hi Mario,

I wasnt able to post images in the last post - is there something Im missing here?

You should be able to post now, for the traceback is best if you do a code snippet.

Block design image attached.

Here is the traceback:

---------------------------------------------------------------------------
CacheMetadataError                        Traceback (most recent call last)
File /usr/local/share/pynq-venv/lib/python3.10/site-packages/pynq/pl_server/embedded_device.py:252, in BitstreamHandler.get_parser(self, partial)
    251 try:
--> 252     parser = self._get_cache() 
    253 except CacheMetadataError:

File /usr/local/share/pynq-venv/lib/python3.10/site-packages/pynq/pl_server/embedded_device.py:228, in BitstreamHandler._get_cache(self)
    227 else:
--> 228     raise CacheMetadataError(f"No cached metadata present")

CacheMetadataError: No cached metadata present

During handling of the above exception, another exception occurred:

KeyError                                  Traceback (most recent call last)
Input In [13], in <cell line: 5>()
      1 from pynq.overlays.base import BaseOverlay
      3 #base = BaseOverlay('base.bit')
----> 5 base = BaseOverlay('/home/xilinx/jupyter_notebooks/base/rfdc/base_wrapper_dds.bit')

File /usr/local/share/pynq-venv/lib/python3.10/site-packages/pynq/overlays/base/base.py:24, in BaseOverlay.__init__(self, *args, **kwargs)
     23 def __init__(self, *args, **kwargs):
---> 24     super().__init__(*args, **kwargs)
     25     if self.is_loaded():
     26         self.iop_pmod0.mbtype = "Pmod"

File /usr/local/share/pynq-venv/lib/python3.10/site-packages/pynq/overlay.py:319, in Overlay.__init__(self, bitfile_name, dtbo, download, ignore_version, device, gen_cache)
    315 super().__init__(bitfile_name, dtbo, partial=False, device=device)
    317 self._register_drivers()
--> 319 self.device.set_bitfile_name(self.bitfile_name)
    320 self.parser = self.device.parser
    322 self.ip_dict = (
    323     self.gpio_dict
    324 ) = (
    325     self.interrupt_controllers
    326 ) = self.interrupt_pins = self.hierarchy_dict = dict()

File /usr/local/share/pynq-venv/lib/python3.10/site-packages/pynq/pl_server/device.py:118, in Device.set_bitfile_name(self, bitfile_name)
    116 def set_bitfile_name(self, bitfile_name: str) -> None:
    117     self.bitfile_name = bitfile_name
--> 118     self.parser = self.get_bitfile_metadata(self.bitfile_name)
    119     self.mem_dict = self.parser.mem_dict
    120     self.ip_dict = self.parser.ip_dict

File /usr/local/share/pynq-venv/lib/python3.10/site-packages/pynq/pl_server/embedded_device.py:690, in EmbeddedDevice.get_bitfile_metadata(self, bitfile_name, partial)
    689 def get_bitfile_metadata(self, bitfile_name:str, partial:bool=False):
--> 690     parser = _get_bitstream_handler(bitfile_name).get_parser(partial=partial)
    691     if parser is None:
    692         raise RuntimeError("Unable to find metadata for bitstream")

File /usr/local/share/pynq-venv/lib/python3.10/site-packages/pynq/pl_server/embedded_device.py:254, in BitstreamHandler.get_parser(self, partial)
    252     parser = self._get_cache() 
    253 except CacheMetadataError:
--> 254     parser = RuntimeMetadataParser(Metadata(input=self._filepath.with_suffix(".hwh")))
    255 except:
    256     raise RuntimeError(f"Unable to parse metadata")

File /usr/local/share/pynq-venv/lib/python3.10/site-packages/pynqmetadata/frontends/metadata.py:40, in Metadata(input)
     38 if os.path.isfile(input):
     39     if str(input).endswith(".hwh"):
---> 40         return HwhFrontend(_hwhfile=input)
     41     elif str(input).endswith(".xsa"):
     42         return XsaFrontend(input=input) 

File <string>:25, in __init__(self, name, type, generic_type, _parent, _children, ref, ext, _timestamp, hierarchy_name, ports, parameters, blocks, modules, busses, _hierarchies, _hwhfile, _element_tree, _root, _logical2physical_portmap, _physical2logical_portmap, _logical2physical_extern_pm, _physical2logical_extern_pm)

File /usr/local/share/pynq-venv/lib/python3.10/site-packages/pynqmetadata/frontends/hwh_frontend.py:219, in HwhFrontend.__post_init__(self)
    207 """
    208 Performs the parsing of the hwh into the metadata model
    209 * checks to see if the hwhfile is an XML string or a
   (...)
    216 * Performs a connectivity pass
    217 """
    218 if self._hwhfile != "":
--> 219     self.parse()

File /usr/local/share/pynq-venv/lib/python3.10/site-packages/pynqmetadata/frontends/hwh_frontend.py:240, in HwhFrontend.parse(self)
    237 self._construct_logical2physical_extern_pm()
    238 self._create_external_ports()
--> 240 self.resolve_addressing()
    241 self.connect_signals()
    243 self.refresh()

File /usr/local/share/pynq-venv/lib/python3.10/site-packages/pynqmetadata/frontends/hwh_frontend.py:596, in HwhFrontend.resolve_addressing(self)
    589 def resolve_addressing(self) -> None:
    590     """
    591     For all the subordinate ports in the design and manager ports
    592     grab all the addressing information
    593     WARNING: This should only be called after all the cores and ports
    594     have been populated.
    595     """
--> 596     self._resolve_subordinate_addressing()
    597     self._populate_subordinate_regmap()
    598     self._resolve_manager_address_maps()

File /usr/local/share/pynq-venv/lib/python3.10/site-packages/pynqmetadata/frontends/hwh_frontend.py:452, in HwhFrontend._resolve_subordinate_addressing(self)
    450 for i in self._root.iter("MEMRANGE"):
    451     if i.get("MEMTYPE") == "REGISTER" or i.get("MEMTYPE") == "MEMORY":
--> 452         core = self.blocks[i.get("INSTANCE")]
    453         port = core.ports[i.get("SLAVEBUSINTERFACE")]
    454         if isinstance(port, SubordinatePort):

KeyError: 'M_AXI_MEMMAP'



Where is the output of the clock converter connected to? Can you add screenshot?

What IP has the port M_AXI_MEMMAP? Can you add screenshot?

The clock convertor output is M_AXI_MEMMAP. The input to the clock convertor is the ps_interconnect. I was looking to add an AXI interface to the rtl and wanted the clock speed to be the same as the dac clock. I guess I could have done this directly using the ps_interconnect as well.

I see, M_AXI_MEMMAP is an external port. So, there is no IP associated to it.

I’m not sure the API considered this. There is a newer version of the metadata. You can try to update as shown here

If this not work, you could try to edit this file /usr/local/share/pynq-venv/lib/python3.10/site-packages/pynqmetadata/frontends/hwh_frontend.py

To be

core = self.blocks.get(i.get("INSTANCE"), {})
port = core.ports.get(i.get("SLAVEBUSINTERFACE"))

This is not guaranteed to work

Mario

Hi Mario,

I tried both of those and unfortunately they didnt work.

Just checking the version of pynqmetadata is correct at 0.3.1

Here is the traceback from the hwh_frontend.py modification:

---------------------------------------------------------------------------
CacheMetadataError                        Traceback (most recent call last)
File /usr/local/share/pynq-venv/lib/python3.10/site-packages/pynq/pl_server/embedded_device.py:252, in BitstreamHandler.get_parser(self, partial)
    251 try:
--> 252     parser = self._get_cache() 
    253 except CacheMetadataError:

File /usr/local/share/pynq-venv/lib/python3.10/site-packages/pynq/pl_server/embedded_device.py:228, in BitstreamHandler._get_cache(self)
    227 else:
--> 228     raise CacheMetadataError(f"No cached metadata present")

CacheMetadataError: No cached metadata present

During handling of the above exception, another exception occurred:

AttributeError                            Traceback (most recent call last)
Input In [1], in <cell line: 5>()
      1 from pynq.overlays.base import BaseOverlay
      3 #base = BaseOverlay('base.bit')
----> 5 base = BaseOverlay('/home/xilinx/jupyter_notebooks/base/rfdc/base_wrapper_dds.bit')

File /usr/local/share/pynq-venv/lib/python3.10/site-packages/pynq/overlays/base/base.py:24, in BaseOverlay.__init__(self, *args, **kwargs)
     23 def __init__(self, *args, **kwargs):
---> 24     super().__init__(*args, **kwargs)
     25     if self.is_loaded():
     26         self.iop_pmod0.mbtype = "Pmod"

File /usr/local/share/pynq-venv/lib/python3.10/site-packages/pynq/overlay.py:319, in Overlay.__init__(self, bitfile_name, dtbo, download, ignore_version, device, gen_cache)
    315 super().__init__(bitfile_name, dtbo, partial=False, device=device)
    317 self._register_drivers()
--> 319 self.device.set_bitfile_name(self.bitfile_name)
    320 self.parser = self.device.parser
    322 self.ip_dict = (
    323     self.gpio_dict
    324 ) = (
    325     self.interrupt_controllers
    326 ) = self.interrupt_pins = self.hierarchy_dict = dict()

File /usr/local/share/pynq-venv/lib/python3.10/site-packages/pynq/pl_server/device.py:118, in Device.set_bitfile_name(self, bitfile_name)
    116 def set_bitfile_name(self, bitfile_name: str) -> None:
    117     self.bitfile_name = bitfile_name
--> 118     self.parser = self.get_bitfile_metadata(self.bitfile_name)
    119     self.mem_dict = self.parser.mem_dict
    120     self.ip_dict = self.parser.ip_dict

File /usr/local/share/pynq-venv/lib/python3.10/site-packages/pynq/pl_server/embedded_device.py:690, in EmbeddedDevice.get_bitfile_metadata(self, bitfile_name, partial)
    689 def get_bitfile_metadata(self, bitfile_name:str, partial:bool=False):
--> 690     parser = _get_bitstream_handler(bitfile_name).get_parser(partial=partial)
    691     if parser is None:
    692         raise RuntimeError("Unable to find metadata for bitstream")

File /usr/local/share/pynq-venv/lib/python3.10/site-packages/pynq/pl_server/embedded_device.py:254, in BitstreamHandler.get_parser(self, partial)
    252     parser = self._get_cache() 
    253 except CacheMetadataError:
--> 254     parser = RuntimeMetadataParser(Metadata(input=self._filepath.with_suffix(".hwh")))
    255 except:
    256     raise RuntimeError(f"Unable to parse metadata")

File /usr/local/share/pynq-venv/lib/python3.10/site-packages/pynqmetadata/frontends/metadata.py:40, in Metadata(input)
     38 if os.path.isfile(input):
     39     if str(input).endswith(".hwh"):
---> 40         return HwhFrontend(_hwhfile=input)
     41     elif str(input).endswith(".xsa"):
     42         return XsaFrontend(input=input) 

File <string>:25, in __init__(self, name, type, generic_type, _parent, _children, ref, ext, _timestamp, hierarchy_name, ports, parameters, blocks, modules, busses, _hierarchies, _hwhfile, _element_tree, _root, _logical2physical_portmap, _physical2logical_portmap, _logical2physical_extern_pm, _physical2logical_extern_pm)

File /usr/local/share/pynq-venv/lib/python3.10/site-packages/pynqmetadata/frontends/hwh_frontend.py:219, in HwhFrontend.__post_init__(self)
    207 """
    208 Performs the parsing of the hwh into the metadata model
    209 * checks to see if the hwhfile is an XML string or a
   (...)
    216 * Performs a connectivity pass
    217 """
    218 if self._hwhfile != "":
--> 219     self.parse()

File /usr/local/share/pynq-venv/lib/python3.10/site-packages/pynqmetadata/frontends/hwh_frontend.py:240, in HwhFrontend.parse(self)
    237 self._construct_logical2physical_extern_pm()
    238 self._create_external_ports()
--> 240 self.resolve_addressing()
    241 self.connect_signals()
    243 self.refresh()

File /usr/local/share/pynq-venv/lib/python3.10/site-packages/pynqmetadata/frontends/hwh_frontend.py:596, in HwhFrontend.resolve_addressing(self)
    589 def resolve_addressing(self) -> None:
    590     """
    591     For all the subordinate ports in the design and manager ports
    592     grab all the addressing information
    593     WARNING: This should only be called after all the cores and ports
    594     have been populated.
    595     """
--> 596     self._resolve_subordinate_addressing()
    597     self._populate_subordinate_regmap()
    598     self._resolve_manager_address_maps()

File /usr/local/share/pynq-venv/lib/python3.10/site-packages/pynqmetadata/frontends/hwh_frontend.py:453, in HwhFrontend._resolve_subordinate_addressing(self)
    451 if i.get("MEMTYPE") == "REGISTER" or i.get("MEMTYPE") == "MEMORY":
    452     core = self.blocks.get(i.get("INSTANCE"),{})
--> 453     port = core.ports.get(i.get("SLAVEBUSINTERFACE"))
    454     if isinstance(port, SubordinatePort):
    455         port.baseaddr = int(i.get("BASEVALUE"), 16)

AttributeError: 'dict' object has no attribute 'ports'

You may need to create a fake ports dict.

core = self.blocks.get(i.get("INSTANCE"),{'ports' : {}})

As I said earlier, having external AXI ports was not considered in the API. So, this may not work.

I am trying to get you to a point where you can download the bitstream.

An alternative, would be to bring the IP that is connected to that external port inside of the block design.

Mario

Unfortunately the fake ports dict didnt work either → it came up with the same error:

AttributeError: ‘dict’ object has no attribute ‘ports’

Any other ideas there would be greatly appreciated.

I would like to avoid bringing more into the block design and I believe the ability to have external AXI ports would be very valueable to the API. Might be worth considering in any case.

Just checking if there is anything else I can try here?

Hi @jonnyj_au,

Not really, supporting the external interface will need some kind of redesign of the parsing logic. And this is something that we are not looking at the moment.

My suggestion would be to bring the IP to the block design.

Mario

Hi @marioruiz,

Is there any other way you can think of to ignore a port being added to the block design?

It is impossible to migrate the code I have to the block design due its dynamic port assignments.

This functionality would be a highly valued feature in a future update - that being a way to either ignore certain ports or recognise external AXI ports.

Regards,
Jonathan

Is there any other way you can think of to ignore a port being added to the block design?

No in the API. There are two potential workarounds:

  1. Remove the external port in the hwh file
  2. Modify the design to add an IP in that interface to get a working hwh file, then use that hwh file instead of the on generated with the external port.

This functionality would be a highly valued feature in a future update - that being a way to either ignore certain ports or recognise external AXI ports.

I suggest to open an issue requesting this feature: Issues · Xilinx/PYNQ-Metadata · GitHub

Mario