Source code for FIREQ_SYSTEM.fifo_node

"""FIFO Node class for FIREQ system node representation."""

from __future__ import annotations

import logging

from FIREQ_LL_API import FIFOWrapper

from ._generic_node import _GenericNode
from ._utils import _MutableRef

logger = logging.getLogger(__name__)


[docs] class FIFONode(_GenericNode): """Object representing an AXI-Stream FIFO. Configuration dictionary keys: - ``_name`` (str): Name of the FIFO node instance. - ``_ll_handler`` (FIFOWrapper): Low-level FIFO wrapper handler. """ nodetype = "acquisition_fifo" wraps = [FIFOWrapper.__name__] def __init__( self, name: str, parent: _GenericNode, _ll_handler: FIFOWrapper, ) -> None: """Initialize the FIFO node. :param name: Name of the node :type name: str :param parent: Parent node in the system tree :type parent: _GenericNode :param _ll_handler: Low-level handler for the FIFO :type _ll_handler: FIFOWrapper """ super().__init__(name=name, parent=parent) self._ll_handler = _ll_handler self._size: int = self._ll_handler.fifo_byte_size # get the interface mapping if_map = self.root.get_axi_stream_interface_map(self.name) self._input_interface = if_map["S_AXIS"] self._output_interface = if_map["M_AXIS"] # this node's maximum number of shots (before overflow) and output payload self.max_hw_shots = _MutableRef() self.payload = _MutableRef() self.max_payload_size = _MutableRef(value=self._size) # register update functions self.root.register_update_function(self.root.make_func_label(self, "max_hw_shots"), self.update_max_hw_shots) self.root.register_update_function(f"{self._output_interface}/payload", self.update_payload) # register the variables that other nodes may need self.root.add_reference(self.root.make_func_label(self, "max_hw_shots"), self.max_hw_shots) self.root.add_reference(f"{self._output_interface}/payload", self.payload) self.root.add_reference(f"{self._output_interface}/max_payload_size", self.max_payload_size) # the number of hw shots and input payload, these will be references to real values self._hw_shots = None self._input_payload = None def _reset_all(self) -> None: self.max_hw_shots.reset_hash() self.payload.reset_hash() self.max_payload_size.reset_hash() def _build_dependencies(self) -> None: """Build the dependencies for this node. The FIFO depends on the input node payload to compute the maximum number of shots and on the trigger generator because the payload changes depending on the number of shots. """ # the maximum number of shots is computed based on the input payload size self.root.add_dependency( self.root.make_func_label(self, "max_hw_shots"), depends_on=f"{self._input_interface}/payload", ) # the output payload depends on the input payload and the number of hw shots self.root.add_dependency( f"{self._output_interface}/payload", depends_on=[ self.root.make_func_label(self.root, "hw_shots"), f"{self._input_interface}/payload", ], ) # resolve references from other nodes self._input_payload = self.root.get_reference(f"{self._input_interface}/payload") self._hw_shots = self.root.get_reference(self.root.make_func_label(self.root, "hw_shots")) self.log.debug("Got input payload reference for FIFO node %s", self.name)
[docs] def update_max_hw_shots(self) -> bool: """Calculate the maximum number of shots that can be stored in the FIFO. Depends on the input payload, and modifies :attr:`max_hw_shots` :return: ``True`` if the maximum number of hardware shots changed, ``False`` otherwise :rtype: bool """ # check if the dict is empty and that the payload is in the correct interface if self._input_payload: self.max_hw_shots["value"] = self._size // self._input_payload["size"] self.log.debug("Recomputed max hw shots to %s for FIFO node %s", self.max_hw_shots["value"], self.name) else: self.max_hw_shots.clear() return self.max_hw_shots.hash_and_compare()
[docs] def update_payload(self) -> bool: """Update FIFO payload based on the input node payload and the number of shots. :return: ``True`` if the payload has changed since the last call, ``False`` otherwise :rtype: bool """ # compute the payload self.payload.clear() if self._input_payload and self._hw_shots["value"]: self.payload["size"] = self._hw_shots["value"] * self._input_payload["size"] self.payload["source"] = self._input_payload["source"] self.payload["format"] = self._input_payload["format"] self.log.debug("Recomputed payload for FIFO node %s", self.name) return self.payload.hash_and_compare()