# Copyright (c) 2019, Xilinx, Inc.
# SPDX-License-Identifier: BSD-3-Clause
import os
import warnings
import pynqutils
from .devicetree import get_dtbo_path
OVERLAYS_GROUP = "pynq.overlays"
def _resolve_bitstream(bitfile_path, device):
if os.path.isfile(bitfile_path):
return bitfile_path
if os.path.isdir(bitfile_path + ".d") and hasattr(device, "name"):
split_bitfile = os.path.split(bitfile_path)
local_bitfile = pynqutils.setup_utils._find_local_overlay_res(
device.name, split_bitfile[1], split_bitfile[0]
)
if local_bitfile is not None:
return local_bitfile
return None
def _find_dtbo_file(dtbo_path, bitfile_path):
if os.path.exists(dtbo_path):
return os.path.abspath(dtbo_path)
relative_path = os.path.join(os.path.dirname(bitfile_path), dtbo_path)
if os.path.exists(relative_path):
return relative_path
return None
[docs]class Bitstream:
"""This class instantiates the meta class for PL bitstream (full/partial).
Attributes
----------
bitfile_name : str
The absolute path or name of the bit file as a string.
dtbo : str
The absolute path of the dtbo file as a string.
partial : bool
Flag to indicate whether or not the bitstream is partial.
bit_data : dict
Dictionary storing information about the bitstream.
binfile_name : str
The absolute path or name of the bin file as a string.
firmware_path : str
The absolute path of the bin file in the firmware folder.
timestamp : str
Timestamp when loading the bitstream. Format:
year, month, day, hour, minute, second, microsecond
"""
def __init__(self, bitfile_name, dtbo=None, partial=False, device=None):
"""Return a new Bitstream object.
Users can either specify an absolute path to the bitstream file
(e.g. '/home/xilinx/pynq/overlays/base/base.bit'),
or a relative path within an overlay folder.
(e.g. 'base.bit' for base/base.bit).
Note
----
`self.bitfile_name` always stores the absolute path of the bitstream.
`self.dtbo` always stores the absolute path of the dtbo file.
Parameters
----------
bitfile_name : str
The absolute path or name of the bit file as a string.
dtbo : str
The relative or absolute path to the device tree segment.
partial : bool
Flag to indicate whether or not the bitstream is partial.
"""
if not isinstance(bitfile_name, str):
raise TypeError("Bitstream name has to be a string.")
if device is None:
from .pl_server.device import Device
device = Device.active_device
self.device = device
# self.xsa = None
# if bitfile_name.endswith(".xsa"):
# self.xsa_filepath = bitfile_name
# self.xsa = pynqutils.build_utils.XsaParser(bitfile_name)
# bitfile_name = self.xsa.bitstreamPaths[0]
# self.xsa.load_bdc_metadata()
bitfile_overlay_abs_lst = []
if os.path.isabs(bitfile_name):
bitfile_abs = _resolve_bitstream(bitfile_name, device)
else:
bitfile_abs = _resolve_bitstream(os.path.abspath(bitfile_name), device)
overlays_ext_man = pynqutils.setup_utils.ExtensionsManager(OVERLAYS_GROUP)
paths = [overlays_ext_man.extension_path(OVERLAYS_GROUP)]
paths += overlays_ext_man.paths
for path in paths:
for p in [os.path.join(path, os.path.splitext(bitfile_name)[0]), path]:
bitfile_overlay_abs = _resolve_bitstream(
os.path.join(p, bitfile_name), device
)
if bitfile_overlay_abs:
bitfile_overlay_abs_lst.append(bitfile_overlay_abs)
if bitfile_abs:
self.bitfile_name = bitfile_abs
elif bitfile_overlay_abs_lst:
self.bitfile_name = bitfile_overlay_abs_lst[0]
else:
raise IOError("Bitstream file {} does not exist.".format(bitfile_name))
if bitfile_abs and bitfile_overlay_abs_lst or len(bitfile_overlay_abs_lst) > 1:
msg = (
"The provided name '{}' resulted in multiple possible "
"matches:\n - ".format(bitfile_name)
)
if bitfile_abs:
msg += "{}\n - ".format(bitfile_abs)
msg += "\n - ".join(bitfile_overlay_abs_lst)
msg += (
"\nThe first entry of this list, '{}', will be used, "
"please provide the full path in case your target file "
"was a different one in this list.".format(self.bitfile_name)
)
warnings.warn(msg, UserWarning)
self.dtbo = None
default_dtbo = get_dtbo_path(self.bitfile_name)
if dtbo is None:
if os.path.exists(default_dtbo):
self.dtbo = default_dtbo
else:
self.dtbo = _find_dtbo_file(dtbo, self.bitfile_name)
if self.dtbo is None:
raise IOError("DTBO file {} does not exist.".format(dtbo))
self.bit_data = dict()
self.binfile_name = ""
self.firmware_path = ""
self.timestamp = ""
self.partial = partial
[docs] def download(self, parser=None):
"""Download the bitstream onto PL and update PL information.
If device tree blob has been specified during initialization, this
method will also insert the corresponding device tree blob into the
system. This is same for both full bitstream and partial bitstream.
Note
----
For partial bitstream, this method does not guarantee isolation between
static and dynamic regions.
Returns
-------
None
"""
self.device.download(self, parser)
[docs] def gen_cache(self, parser=None):
""" Generates the pickled metadata cache in pl_server/ even if no download has occurred """
self.device.gen_cache(self, parser)
[docs] def remove_dtbo(self):
"""Remove dtbo file from the system.
A simple wrapper of the corresponding method in the PL class. This is
very useful for partial bitstream downloading, where loading the
new device tree blob will overwrites the existing device tree blob
in the same partial region.
"""
self.device.remove_device_tree(self.dtbo)
[docs] def insert_dtbo(self, dtbo=None):
"""Insert dtbo file into the system.
A simple wrapper of the corresponding method in the PL class. If
`dtbo` is None, `self.dtbo` will be used to insert the dtbo
file. In most cases, users should just ignore the parameter
`dtbo`.
Parameters
----------
dtbo : str
The relative or absolute path to the device tree segment.
"""
if dtbo:
resolved_dtbo = _find_dtbo_file(dtbo, self.bitfile_name)
if resolved_dtbo:
self.dtbo = resolved_dtbo
else:
raise IOError("DTBO file {} does not exist.".format(dtbo))
if not self.dtbo:
raise ValueError("DTBO path has to be specified.")
self.device.insert_device_tree(self.dtbo)