<commit_before># This file is part of cclib (http://cclib.sf.net), a library for parsing
# and interpreting the results of computational chemistry packages.
#
# Copyright (C) 2006-2014, the cclib development team
#
# The library is free software, distributed under the terms of
# the GNU Lesser General Public version 2.1 or later. You should have
# received a copy of the license along with cclib. You can also access
# the full license online at http://www.gnu.org/copyleft/lgpl.html.

import io
import collections

try:
    import bz2 # New in Python 2.3.
except ImportError:
    bz2 = None
import fileinput
import gzip
import inspect
import logging
logging.logMultiprocessing =  0 # To avoid a problem with Avogadro
import os
import random
try:
    set # Standard type from Python 2.4+.
except NameError:
    from sets import Set as set
import sys
import zipfile

import numpy

from . import utils
from .data import ccData

class myBZ2File(bz2.BZ2File):
    """Return string instead of bytes"""
    def __next__(self):
        line = super().__next__()
        return line.decode("ascii", "replace")

class myGzipFile(gzip.GzipFile):
    """Return string instead of bytes"""
    def __next__(self):
        line = super().__next__()
        return line.decode("ascii", "replace")

class FileWrapper(object):
    """Wrap a file object so that we can maintain position"""

    def __init__(self, file):
        self.file = file
        self.pos = 0
        self.file.seek(0, 2)
        self.size = self.file.tell()
        self.file.seek(0, 0)

    def next(self):
        line = next(self.file)
        self.pos += len(line)
        return line

    def __next__(self):
        return self.next()

    def __iter__(self):
        return self

    def close(self):
        self.file.close()

    def seek(self, pos, ref):
        self.file.seek(pos, ref)

def openlogfile(filename):
    """Return a file object given a filename.

    Given the filename of a log file or a gzipped, zipped, or bzipped
    log file, this function returns a regular Python file object.

    Given an address starting with http://, this function retrieves the url
    and returns a file object using a temporary file.

    Given a list of filenames, this function returns a FileInput object,
    which can be used for seamless iteration without concatenation.
    """

    # If there is a single string argument given.
    if type(filename) in [str, str]:

        extension = os.path.splitext(filename)[1]

        if extension == ".gz":
            fileobject = myGzipFile(filename, "r")

        elif extension == ".zip":
            zip = zipfile.ZipFile(filename, "r")
            assert len(zip.namelist()) == 1, "ERROR: Zip file contains more than 1 file"
            fileobject = io.StringIO(zip.read(zip.namelist()[0]).decode("ascii", "ignore"))

        elif extension in ['.bz', '.bz2']:
            # Module 'bz2' is not always importable.
            assert bz2 != None, "ERROR: module bz2 cannot be imported"
            fileobject = myBZ2File(filename, "r")

        else:
            fileobject = FileWrapper(io.open(filename, "r", errors='ignore'))

        return fileobject

    elif hasattr(filename, "__iter__"):

        # Compression (gzip and bzip) is supported as of Python 2.5.
        if sys.version_info[0] >= 2 and sys.version_info[1] >= 5:
            fileobject = fileinput.input(filename, openhook=fileinput.hook_compressed)
        else:
            fileobject = fileinput.input(filename)

        return fileobject

class Logfile(object):
    """Abstract class for logfile objects.

    Subclasses defined by cclib:
        ADF, GAMESS, GAMESSUK, Gaussian, Jaguar, Molpro, ORCA

    """

    def __init__(self, source, loglevel=logging.INFO, logname="Log",
                    logstream=sys.stdout, datatype=ccData):
        """Initialise the Logfile object.

        This should be called by a ubclass in its own __init__ method.

        Inputs:
            source - a single logfile, a list of logfiles, or input stream
        """

        # Set the filename to source if it is a string or a list of filenames.
        # In the case of an input stream, set some arbitrary name and the stream.
        # Elsewise, raise an Exception.
        if isinstance(source, str):
            self.filename = source
            self.isstream = False
        elif isinstance(source, list) and all([isinstance(s, str) for s in source]):
            self.filename = source
            self.isstream = False
        elif hasattr(source, "read"):
            self.filename = "stream %s" % str(type(source))
            self.isstream = True
            self.stream = source
        else:
            raise ValueError

        # Set up the logger.
        # Note that calling logging.getLogger() with one name always returns the same instance.
        # Presently in cclib, all parser instances of the same class use the same logger,
        #   which means that care needs to be taken not to duplicate handlers.
        self.loglevel = loglevel
        self.logname  = logname
        self.logger = logging.getLogger('%s %s' % (self.logname, self.filename))
        self.logger.setLevel(self.loglevel)
        if len(self.logger.handlers) == 0:
            handler = logging.StreamHandler(logstream)
            handler.setFormatter(logging.Formatter("[%(name)s %(levelname)s] %(message)s"))
            self.logger.addHandler(handler)

        # Periodic table of elements.
        self.table = utils.PeriodicTable()

        # This is the class that will be used in the data object returned by parse(),
        #   and should normally be ccData or a subclass.
        self.datatype = datatype

    def __setattr__(self, name, value):

        # Send info to logger if the attribute is in the list self._attrlist.
        if name in getattr(self, "_attrlist", {}) and hasattr(self, "logger"):

            # Call logger.info() only if the attribute is new.
            if not hasattr(self, name):
                if type(value) in [numpy.ndarray, list]:
                    self.logger.info("Creating attribute %s[]" %name)
                else:
                    self.logger.info("Creating attribute %s: %s" %(name, str(value)))

        # Set the attribute.
        object.__setattr__(self, name, value)

    def parse(self, progress=None, fupdate=0.05, cupdate=0.002):
        """Parse the logfile, using the assumed extract method of the child."""

        # Check that the sub-class has an extract attribute,
        #  that is callable with the proper number of arguemnts.
        if not hasattr(self, "extract"):
            raise AttributeError("Class %s has no extract() method." %self.__class__.__name__)
        if not callable(self.extract):
            raise AttributeError("Method %s._extract not callable." %self.__class__.__name__)
        if len(inspect.getargspec(self.extract)[0]) != 3:
            raise AttributeError("Method %s._extract takes wrong number of arguments." %self.__class__.__name__)

        # Save the current list of attributes to keep after parsing.
        # The dict of self should be the same after parsing.
        _nodelete = list(set(self.__dict__.keys()))

        # Initiate the FileInput object for the input files.
        # Remember that self.filename can be a list of files.
        if not self.isstream:
            inputfile = openlogfile(self.filename)
        else:
            inputfile = self.stream

        # Intialize self.progress
        if progress and not (isinstance(inputfile, myGzipFile) or
                                isinstance(inputfile, myBZ2File)):
            self.progress = progress
            self.progress.initialize(inputfile.size)
            self.progress.step = 0
        self.fupdate = fupdate
        self.cupdate = cupdate

        # Initialize the ccData object that will be returned.
        # This is normally ccData, but can be changed by passing
        #   the datatype argument to __init__().
        data = self.datatype()

        # Copy the attribute list, so that the parser knows what to expect,
        #   specifically in __setattr__().
        # The class self.datatype (normally ccData) must have this attribute.
        self._attrlist = data._attrlist

        # Maybe the sub-class has something to do before parsing.
        self.before_parsing()

        # Loop over lines in the file object and call extract().
        # This is where the actual parsing is done.
        for line in inputfile:

            self.updateprogress(inputfile, "Unsupported information", cupdate)

            # This call should check if the line begins a section of extracted data.
            # If it does, it parses some lines and sets the relevant attributes (to self).
            # Any attributes can be freely set and used across calls, however only those
            #   in data._attrlist will be moved to final data object that is returned.
            self.extract(inputfile, line)

        # Close input file object.
        if not self.isstream:
            inputfile.close()

        # Maybe the sub-class has something to do after parsing.
        self.after_parsing()

        # If atomcoords were not parsed, but some input coordinates were ("inputcoords").
        # This is originally from the Gaussian parser, a regression fix.
        if not hasattr(self, "atomcoords") and hasattr(self, "inputcoords"):
            self.atomcoords = numpy.array(self.inputcoords, 'd')

        # Set nmo if not set already - to nbasis.
        if not hasattr(self, "nmo") and hasattr(self, "nbasis"):
            self.nmo = self.nbasis

        # Creating deafult coreelectrons array.
        if not hasattr(self, "coreelectrons") and hasattr(self, "natom"):
            self.coreelectrons = numpy.zeros(self.natom, "i")

        # Move all cclib attributes to the ccData object.
        # To be moved, an attribute must be in data._attrlist.
        for attr in data._attrlist:
            if hasattr(self, attr):
                setattr(data, attr, getattr(self, attr))

        # Now make sure that the cclib attributes in the data object
        #   are all the correct type (including arrays and lists of arrays).
        data.arrayify()

        # Delete all temporary attributes (including cclib attributes).
        # All attributes should have been moved to a data object,
        #   which will be returned.
        for attr in list(self.__dict__.keys()):
            if not attr in _nodelete:
                self.__delattr__(attr)

        # Update self.progress as done.
        if hasattr(self, "progress"):
            self.progress.update(inputfile.size, "Done")

        # Return the ccData object that was generated.
        return data

    def before_parsing(self):

        pass

    def after_parsing(self):

        pass

    def updateprogress(self, inputfile, msg, xupdate=0.05):
        """Update progress."""

        if hasattr(self, "progress") and random.random() < xupdate:
            newstep = inputfile.pos
            if newstep != self.progress.step:
                self.progress.update(newstep, msg)
                self.progress.step = newstep

    def normalisesym(self, symlabel):
        """Standardise the symmetry labels between parsers.

        This method should be overwritten by individual parsers, and should
        contain appropriate doctests. If is not overwritten, this is detected
        as an error by unit tests.
        """
        return "ERROR: This should be overwritten by this subclass"

    def float(self, number):
        """Convert a string to a float avoiding the problem with Ds.

        >>> t = Logfile("dummyfile")
        >>> t.float("123.2323E+02")
        12323.23
        >>> t.float("123.2323D+02")
        12323.23
        """
        number = number.replace("D","E")
        return float(number)

if __name__ == "__main__":
    import doctest
    doctest.testmod()
<commit_msg>LogFile: set attributes using ccData.setattributes (cherry picked from commit 3c1032c121c40daf7bbd6c9e2e501832ab475d73)<commit_after># This file is part of cclib (http://cclib.sf.net), a library for parsing
# and interpreting the results of computational chemistry packages.
#
# Copyright (C) 2006-2014, the cclib development team
#
# The library is free software, distributed under the terms of
# the GNU Lesser General Public version 2.1 or later. You should have
# received a copy of the license along with cclib. You can also access
# the full license online at http://www.gnu.org/copyleft/lgpl.html.

import io
import collections

try:
    import bz2 # New in Python 2.3.
except ImportError:
    bz2 = None
import fileinput
import gzip
import inspect
import logging
logging.logMultiprocessing =  0 # To avoid a problem with Avogadro
import os
import random
try:
    set # Standard type from Python 2.4+.
except NameError:
    from sets import Set as set
import sys
import zipfile

import numpy

from . import utils
from .data import ccData

class myBZ2File(bz2.BZ2File):
    """Return string instead of bytes"""
    def __next__(self):
        line = super().__next__()
        return line.decode("ascii", "replace")

class myGzipFile(gzip.GzipFile):
    """Return string instead of bytes"""
    def __next__(self):
        line = super().__next__()
        return line.decode("ascii", "replace")

class FileWrapper(object):
    """Wrap a file object so that we can maintain position"""

    def __init__(self, file):
        self.file = file
        self.pos = 0
        self.file.seek(0, 2)
        self.size = self.file.tell()
        self.file.seek(0, 0)

    def next(self):
        line = next(self.file)
        self.pos += len(line)
        return line

    def __next__(self):
        return self.next()

    def __iter__(self):
        return self

    def close(self):
        self.file.close()

    def seek(self, pos, ref):
        self.file.seek(pos, ref)

def openlogfile(filename):
    """Return a file object given a filename.

    Given the filename of a log file or a gzipped, zipped, or bzipped
    log file, this function returns a regular Python file object.

    Given an address starting with http://, this function retrieves the url
    and returns a file object using a temporary file.

    Given a list of filenames, this function returns a FileInput object,
    which can be used for seamless iteration without concatenation.
    """

    # If there is a single string argument given.
    if type(filename) in [str, str]:

        extension = os.path.splitext(filename)[1]

        if extension == ".gz":
            fileobject = myGzipFile(filename, "r")

        elif extension == ".zip":
            zip = zipfile.ZipFile(filename, "r")
            assert len(zip.namelist()) == 1, "ERROR: Zip file contains more than 1 file"
            fileobject = io.StringIO(zip.read(zip.namelist()[0]).decode("ascii", "ignore"))

        elif extension in ['.bz', '.bz2']:
            # Module 'bz2' is not always importable.
            assert bz2 != None, "ERROR: module bz2 cannot be imported"
            fileobject = myBZ2File(filename, "r")

        else:
            fileobject = FileWrapper(io.open(filename, "r", errors='ignore'))

        return fileobject

    elif hasattr(filename, "__iter__"):

        # Compression (gzip and bzip) is supported as of Python 2.5.
        if sys.version_info[0] >= 2 and sys.version_info[1] >= 5:
            fileobject = fileinput.input(filename, openhook=fileinput.hook_compressed)
        else:
            fileobject = fileinput.input(filename)

        return fileobject

class Logfile(object):
    """Abstract class for logfile objects.

    Subclasses defined by cclib:
        ADF, GAMESS, GAMESSUK, Gaussian, Jaguar, Molpro, ORCA

    """

    def __init__(self, source, loglevel=logging.INFO, logname="Log",
                    logstream=sys.stdout, datatype=ccData):
        """Initialise the Logfile object.

        This should be called by a ubclass in its own __init__ method.

        Inputs:
            source - a single logfile, a list of logfiles, or input stream
        """

        # Set the filename to source if it is a string or a list of filenames.
        # In the case of an input stream, set some arbitrary name and the stream.
        # Elsewise, raise an Exception.
        if isinstance(source, str):
            self.filename = source
            self.isstream = False
        elif isinstance(source, list) and all([isinstance(s, str) for s in source]):
            self.filename = source
            self.isstream = False
        elif hasattr(source, "read"):
            self.filename = "stream %s" % str(type(source))
            self.isstream = True
            self.stream = source
        else:
            raise ValueError

        # Set up the logger.
        # Note that calling logging.getLogger() with one name always returns the same instance.
        # Presently in cclib, all parser instances of the same class use the same logger,
        #   which means that care needs to be taken not to duplicate handlers.
        self.loglevel = loglevel
        self.logname  = logname
        self.logger = logging.getLogger('%s %s' % (self.logname, self.filename))
        self.logger.setLevel(self.loglevel)
        if len(self.logger.handlers) == 0:
            handler = logging.StreamHandler(logstream)
            handler.setFormatter(logging.Formatter("[%(name)s %(levelname)s] %(message)s"))
            self.logger.addHandler(handler)

        # Periodic table of elements.
        self.table = utils.PeriodicTable()

        # This is the class that will be used in the data object returned by parse(),
        #   and should normally be ccData or a subclass.
        self.datatype = datatype

    def __setattr__(self, name, value):

        # Send info to logger if the attribute is in the list self._attrlist.
        if name in getattr(self, "_attrlist", {}) and hasattr(self, "logger"):

            # Call logger.info() only if the attribute is new.
            if not hasattr(self, name):
                if type(value) in [numpy.ndarray, list]:
                    self.logger.info("Creating attribute %s[]" %name)
                else:
                    self.logger.info("Creating attribute %s: %s" %(name, str(value)))

        # Set the attribute.
        object.__setattr__(self, name, value)

    def parse(self, progress=None, fupdate=0.05, cupdate=0.002):
        """Parse the logfile, using the assumed extract method of the child."""

        # Check that the sub-class has an extract attribute,
        #  that is callable with the proper number of arguemnts.
        if not hasattr(self, "extract"):
            raise AttributeError("Class %s has no extract() method." %self.__class__.__name__)
        if not callable(self.extract):
            raise AttributeError("Method %s._extract not callable." %self.__class__.__name__)
        if len(inspect.getargspec(self.extract)[0]) != 3:
            raise AttributeError("Method %s._extract takes wrong number of arguments." %self.__class__.__name__)

        # Save the current list of attributes to keep after parsing.
        # The dict of self should be the same after parsing.
        _nodelete = list(set(self.__dict__.keys()))

        # Initiate the FileInput object for the input files.
        # Remember that self.filename can be a list of files.
        if not self.isstream:
            inputfile = openlogfile(self.filename)
        else:
            inputfile = self.stream

        # Intialize self.progress
        if progress and not (isinstance(inputfile, myGzipFile) or
                                isinstance(inputfile, myBZ2File)):
            self.progress = progress
            self.progress.initialize(inputfile.size)
            self.progress.step = 0
        self.fupdate = fupdate
        self.cupdate = cupdate

        # Initialize the ccData object that will be returned.
        # This is normally ccData, but can be changed by passing
        #   the datatype argument to __init__().
        data = self.datatype()

        # Copy the attribute list, so that the parser knows what to expect,
        #   specifically in __setattr__().
        # The class self.datatype (normally ccData) must have this attribute.
        self._attrlist = data._attrlist

        # Maybe the sub-class has something to do before parsing.
        self.before_parsing()

        # Loop over lines in the file object and call extract().
        # This is where the actual parsing is done.
        for line in inputfile:

            self.updateprogress(inputfile, "Unsupported information", cupdate)

            # This call should check if the line begins a section of extracted data.
            # If it does, it parses some lines and sets the relevant attributes (to self).
            # Any attributes can be freely set and used across calls, however only those
            #   in data._attrlist will be moved to final data object that is returned.
            self.extract(inputfile, line)

        # Close input file object.
        if not self.isstream:
            inputfile.close()

        # Maybe the sub-class has something to do after parsing.
        self.after_parsing()

        # If atomcoords were not parsed, but some input coordinates were ("inputcoords").
        # This is originally from the Gaussian parser, a regression fix.
        if not hasattr(self, "atomcoords") and hasattr(self, "inputcoords"):
            self.atomcoords = numpy.array(self.inputcoords, 'd')

        # Set nmo if not set already - to nbasis.
        if not hasattr(self, "nmo") and hasattr(self, "nbasis"):
            self.nmo = self.nbasis

        # Creating deafult coreelectrons array.
        if not hasattr(self, "coreelectrons") and hasattr(self, "natom"):
            self.coreelectrons = numpy.zeros(self.natom, "i")

        # Move all cclib attributes to the ccData object, but beware that
        # in order to be moved an attribute must be in data._attrlist.
        data.setattributes(self.__dict__)

        # Now make sure that the cclib attributes in the data object
        #   are all the correct type (including arrays and lists of arrays).
        data.arrayify()

        # Delete all temporary attributes (including cclib attributes).
        # All attributes should have been moved to a data object,
        #   which will be returned.
        for attr in list(self.__dict__.keys()):
            if not attr in _nodelete:
                self.__delattr__(attr)

        # Update self.progress as done.
        if hasattr(self, "progress"):
            self.progress.update(inputfile.size, "Done")

        # Return the ccData object that was generated.
        return data

    def before_parsing(self):

        pass

    def after_parsing(self):

        pass

    def updateprogress(self, inputfile, msg, xupdate=0.05):
        """Update progress."""

        if hasattr(self, "progress") and random.random() < xupdate:
            newstep = inputfile.pos
            if newstep != self.progress.step:
                self.progress.update(newstep, msg)
                self.progress.step = newstep

    def normalisesym(self, symlabel):
        """Standardise the symmetry labels between parsers.

        This method should be overwritten by individual parsers, and should
        contain appropriate doctests. If is not overwritten, this is detected
        as an error by unit tests.
        """
        return "ERROR: This should be overwritten by this subclass"

    def float(self, number):
        """Convert a string to a float avoiding the problem with Ds.

        >>> t = Logfile("dummyfile")
        >>> t.float("123.2323E+02")
        12323.23
        >>> t.float("123.2323D+02")
        12323.23
        """
        number = number.replace("D","E")
        return float(number)

if __name__ == "__main__":
    import doctest
    doctest.testmod()
<|endoftext|>


<commit_before>import time
import logging
import numpy as np

from analysis.plotting.plotting import plot_occupancy
from daq.readout import get_col_row_array_from_data_record_array, convert_data_array, data_array_from_data_dict_iterable, is_data_record, is_data_from_channel

from scan.scan import ScanBase
from daq.readout import open_raw_data_file

logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)-8s] (%(threadName)-10s) %(message)s")

scan_configuration = {
    "occupancy_limit": 10 ** (-7),  # 0 will mask any pixel with occupancy greater than zero
    "triggers": 10000000,
    "consecutive_lvl1": 1,
    "disable_for_mask": ['Enable'],
    "enable_for_mask": ['Imon'],
    "overwrite_mask": False,
    "col_span": [1, 80],
    "row_span": [1, 336],
    "timeout_no_data": 10
}

class NoiseOccupancyScan(ScanBase):
    scan_identifier = "noise_occupancy_tuning"

    def scan(self, occupancy_limit=10 ** (-7), triggers=10000000, consecutive_lvl1=1, disable_for_mask=['Enable'], enable_for_mask=['Imon'], overwrite_mask=False, col_span=[1, 80], row_span=[1, 336], timeout_no_data=10, **kwargs):
        '''Masking pixels with occupancy above certain limit.

        Parameters
        ----------
        occupancy_limit : float
            Occupancy limit which is multiplied with measured number of hits for each pixel. Any pixel above 1 will be masked.
        triggers : int
            Total number of triggers sent to FE. From 1 to 4294967295 (32-bit unsigned int).
        consecutive_lvl1 : int
            Number of consecutive LVL1 triggers. From 1 to 16.
        disable_for_mask : list, tuple
            List of masks for which noisy pixels will be disabled.
        enable_for_mask : list, tuple
            List of masks for which noisy pixels will be enabled.
        overwrite_mask : bool
            Overwrite masks (disable_for_mask, enable_for_mask) if set to true. If set to false, make a combination of existing mask and new mask.
        col_span : list, tuple
            Column range (from minimum to maximum value). From 1 to 80.
        row_span : list, tuple
            Row range (from minimum to maximum value). From 1 to 336.
        timeout_no_data : int
            In seconds; if no data, stop scan after given time.
        scan_timeout : int
            In seconds; stop scan after given time.

        Note
        ----
        The total number of trigger is triggers * consecutive_lvl1.
        Please note that a high trigger rate leads to an effective lower threshold.
        '''
        # create restore point
        self.register.create_restore_point()

        if occupancy_limit * triggers * consecutive_lvl1 < 1:
            logging.warning('Number of triggers too low for given occupancy limit')

        commands = []
        mask = self.register_utils.make_box_pixel_mask_from_col_row(column=col_span, row=row_span)
        pixel_reg = "Enable"
        commands.extend(self.register.get_commands("confmode"))
        self.register.set_pixel_register_value(pixel_reg, mask)
        commands.extend(self.register.get_commands("wrfrontend", same_mask_for_all_dc=False, name=pixel_reg))
        # generate mask for Imon mask
        pixel_reg = "Imon"
#         mask = self.register_utils.make_box_pixel_mask_from_col_row(column=col_span, row=row_span, default=1, value=0)
        self.register.set_pixel_register_value(pixel_reg, 1)
        commands.extend(self.register.get_commands("wrfrontend", same_mask_for_all_dc=True, name=pixel_reg))
        # disable C_inj mask
        pixel_reg = "C_High"
        self.register.set_pixel_register_value(pixel_reg, 0)
        commands.extend(self.register.get_commands("wrfrontend", same_mask_for_all_dc=True, name=pixel_reg))
        pixel_reg = "C_Low"
        self.register.set_pixel_register_value(pixel_reg, 0)
        commands.extend(self.register.get_commands("wrfrontend", same_mask_for_all_dc=True, name=pixel_reg))
#         self.register.set_global_register_value("Trig_Lat", 232)  # set trigger latency
        self.consecutive_lvl1 = consecutive_lvl1
        self.register.set_global_register_value("Trig_Count", (0 if consecutive_lvl1 == 16 else consecutive_lvl1))  # set number of consecutive triggers
        commands.extend(self.register.get_commands("wrregister", name=["Trig_Count"]))
        # setting FE into runmode
        commands.extend(self.register.get_commands("runmode"))
        self.register_utils.send_commands(commands)

        self.col_arr = np.array([], dtype=np.dtype('>u1'))
        self.row_arr = np.array([], dtype=np.dtype('>u1'))

        with open_raw_data_file(filename=self.scan_data_filename, title=self.scan_identifier) as raw_data_file:
            self.readout.start()

            # preload command
            command_delay = 400  # 100kHz
            lvl1_command = self.register.get_commands("lv1")[0] + self.register.get_commands("zeros", length=command_delay)[0]
            commnd_lenght = lvl1_command.length()
            logging.info('Estimated scan time: %ds' % int(commnd_lenght * 25 * (10 ** -9) * triggers))
            logging.info('Please stand by...')
            self.register_utils.send_command(lvl1_command, repeat=triggers, wait_for_finish=False, set_length=True, clear_memory=False)

            wait_for_first_data = False
            last_iteration = time.time()
            saw_no_data_at_time = last_iteration
            saw_data_at_time = last_iteration
            no_data_at_time = last_iteration
            while not self.stop_thread_event.wait(self.readout.readout_interval):
                last_iteration = time.time()
                try:
                    data = (self.readout.data.popleft(), )
                    raw_data_file.append(data)
                    col_arr_tmp, row_arr_tmp = convert_data_array(data_array_from_data_dict_iterable(data), filter_func=is_data_record, converter_func=get_col_row_array_from_data_record_array)
                    self.col_arr = np.concatenate((self.col_arr, col_arr_tmp))
                    self.row_arr = np.concatenate((self.row_arr, row_arr_tmp))
                    #logging.info('data words')
                except IndexError:  # no data
                    #logging.info('no data words')
                    no_data_at_time = last_iteration
                    if self.register_utils.is_ready:
                        self.stop_thread_event.set()
                        logging.info('Finished sending %d triggers' % triggers)
                    elif wait_for_first_data == False and saw_no_data_at_time > (saw_data_at_time + timeout_no_data):
                        logging.info('Reached no data timeout. Stopping Scan...')
                        self.stop_thread_event.set()
                    elif wait_for_first_data == False:
                        saw_no_data_at_time = no_data_at_time
                    elif self.reaout_utils.is_ready:
                        self.stop_thread_event.set()

                    continue

                saw_data_at_time = last_iteration

                if wait_for_first_data == True:
                    logging.info('Taking data...')
                    wait_for_first_data = False

            self.readout.stop()

            self.register.restore()

            occ_hist, _, _ = np.histogram2d(self.col_arr, self.row_arr, bins=(80, 336), range=[[1, 80], [1, 336]])

            self.occ_mask = np.zeros(shape=occ_hist.shape, dtype=np.dtype('>u1'))
            # noisy pixels are set to 1
            self.occ_mask[occ_hist > occupancy_limit * triggers * consecutive_lvl1] = 1
            # make inverse
            self.inv_occ_mask = self.register_utils.invert_pixel_mask(self.occ_mask)
            self.disable_for_mask = disable_for_mask
            if overwrite_mask:
                self.register.set_pixel_register_value(disable_for_mask, self.inv_occ_mask)
            else:
                for mask in disable_for_mask:
                    enable_mask = np.logical_and(self.inv_occ_mask, self.register.get_pixel_register_value(mask))
                    self.register.set_pixel_register_value(mask, enable_mask)

            self.enable_for_mask = enable_for_mask
            if overwrite_mask:
                self.register.set_pixel_register_value(enable_for_mask, self.occ_mask)
            else:
                for mask in enable_for_mask:
                    disable_mask = np.logical_or(self.occ_mask, self.register.get_pixel_register_value(mask))
                    self.register.set_pixel_register_value(mask, disable_mask)

#             plot_occupancy(self.col_arr, self.row_arr, max_occ=None, filename=self.scan_data_filename + "_occupancy.pdf")

    def analyze(self):
        from analysis.analyze_raw_data import AnalyzeRawData
        output_file = self.scan_data_filename + "_interpreted.h5"
        with AnalyzeRawData(raw_data_file=scan.scan_data_filename, analyzed_data_file=output_file, create_pdf=True) as analyze_raw_data:
            analyze_raw_data.interpreter.set_trig_count(self.consecutive_lvl1)
            analyze_raw_data.create_source_scan_hist = True
#             analyze_raw_data.create_hit_table = True
#             analyze_raw_data.interpreter.debug_events(0, 0, True)  # events to be printed onto the console for debugging, usually deactivated
            analyze_raw_data.interpreter.set_warning_output(False)
            analyze_raw_data.interpret_word_table(fei4b=scan.register.fei4b)
            analyze_raw_data.interpreter.print_summary()
            analyze_raw_data.plot_histograms()
            plot_occupancy(self.occ_mask.T, title='Noisy Pixels', z_max=1, filename=analyze_raw_data.output_pdf)
            for mask in self.disable_for_mask:
                mask_name = self.register.get_pixel_register_attributes("full_name", do_sort=True, name=[mask])[0]
                plot_occupancy(self.register.get_pixel_register_value(mask).T, title='%s Mask' % mask_name, z_max=1, filename=analyze_raw_data.output_pdf)
            for mask in self.enable_for_mask:
                mask_name = self.register.get_pixel_register_attributes("full_name", do_sort=True, name=[mask])[0]
                plot_occupancy(self.register.get_pixel_register_value(mask).T, title='%s Mask' % mask_name, z_max=1, filename=analyze_raw_data.output_pdf)

if __name__ == "__main__":
    import configuration
    scan = NoiseOccupancyScan(**configuration.device_configuration)
    scan.start(use_thread=False, **scan_configuration)
    scan.stop()
    scan.analyze()
    scan.register.save_configuration(configuration.device_configuration["configuration_file"])
<commit_msg>BUG: fix missing loop ENH: change noise occ to reasonable value<commit_after>import time
import logging
import numpy as np

from analysis.plotting.plotting import plot_occupancy
from daq.readout import get_col_row_array_from_data_record_array, convert_data_array, data_array_from_data_dict_iterable, is_data_record, is_data_from_channel

from scan.scan import ScanBase
from daq.readout import open_raw_data_file

logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)-8s] (%(threadName)-10s) %(message)s")

scan_configuration = {
    "occupancy_limit": 10 ** (-5),  # 0 will mask any pixel with occupancy greater than zero
    "triggers": 10000000,
    "consecutive_lvl1": 1,
    "disable_for_mask": ['Enable'],
    "enable_for_mask": ['Imon'],
    "overwrite_mask": False,
    "col_span": [1, 80],
    "row_span": [1, 336],
    "timeout_no_data": 10
}

class NoiseOccupancyScan(ScanBase):
    scan_identifier = "noise_occupancy_tuning"

    def scan(self, occupancy_limit=10 ** (-7), triggers=10000000, consecutive_lvl1=1, disable_for_mask=['Enable'], enable_for_mask=['Imon'], overwrite_mask=False, col_span=[1, 80], row_span=[1, 336], timeout_no_data=10, **kwargs):
        '''Masking pixels with occupancy above certain limit.

        Parameters
        ----------
        occupancy_limit : float
            Occupancy limit which is multiplied with measured number of hits for each pixel. Any pixel above 1 will be masked.
        triggers : int
            Total number of triggers sent to FE. From 1 to 4294967295 (32-bit unsigned int).
        consecutive_lvl1 : int
            Number of consecutive LVL1 triggers. From 1 to 16.
        disable_for_mask : list, tuple
            List of masks for which noisy pixels will be disabled.
        enable_for_mask : list, tuple
            List of masks for which noisy pixels will be enabled.
        overwrite_mask : bool
            Overwrite masks (disable_for_mask, enable_for_mask) if set to true. If set to false, make a combination of existing mask and new mask.
        col_span : list, tuple
            Column range (from minimum to maximum value). From 1 to 80.
        row_span : list, tuple
            Row range (from minimum to maximum value). From 1 to 336.
        timeout_no_data : int
            In seconds; if no data, stop scan after given time.
        scan_timeout : int
            In seconds; stop scan after given time.

        Note
        ----
        The total number of trigger is triggers * consecutive_lvl1.
        Please note that a high trigger rate leads to an effective lower threshold.
        '''
        # create restore point
        self.register.create_restore_point()

        if occupancy_limit * triggers * consecutive_lvl1 < 1:
            logging.warning('Number of triggers too low for given occupancy limit')

        commands = []
        mask = self.register_utils.make_box_pixel_mask_from_col_row(column=col_span, row=row_span)
        pixel_reg = "Enable"
        commands.extend(self.register.get_commands("confmode"))
        self.register.set_pixel_register_value(pixel_reg, mask)
        commands.extend(self.register.get_commands("wrfrontend", same_mask_for_all_dc=False, name=pixel_reg))
        # generate mask for Imon mask
        pixel_reg = "Imon"
#         mask = self.register_utils.make_box_pixel_mask_from_col_row(column=col_span, row=row_span, default=1, value=0)
        self.register.set_pixel_register_value(pixel_reg, 1)
        commands.extend(self.register.get_commands("wrfrontend", same_mask_for_all_dc=True, name=pixel_reg))
        # disable C_inj mask
        pixel_reg = "C_High"
        self.register.set_pixel_register_value(pixel_reg, 0)
        commands.extend(self.register.get_commands("wrfrontend", same_mask_for_all_dc=True, name=pixel_reg))
        pixel_reg = "C_Low"
        self.register.set_pixel_register_value(pixel_reg, 0)
        commands.extend(self.register.get_commands("wrfrontend", same_mask_for_all_dc=True, name=pixel_reg))
#         self.register.set_global_register_value("Trig_Lat", 232)  # set trigger latency
        self.consecutive_lvl1 = consecutive_lvl1
        self.register.set_global_register_value("Trig_Count", (0 if consecutive_lvl1 == 16 else consecutive_lvl1))  # set number of consecutive triggers
        commands.extend(self.register.get_commands("wrregister", name=["Trig_Count"]))
        # setting FE into runmode
        commands.extend(self.register.get_commands("runmode"))
        self.register_utils.send_commands(commands)

        self.col_arr = np.array([], dtype=np.dtype('>u1'))
        self.row_arr = np.array([], dtype=np.dtype('>u1'))

        with open_raw_data_file(filename=self.scan_data_filename, title=self.scan_identifier) as raw_data_file:
            self.readout.start()

            # preload command
            command_delay = 400  # 100kHz
            lvl1_command = self.register.get_commands("lv1")[0] + self.register.get_commands("zeros", length=command_delay)[0]
            commnd_lenght = lvl1_command.length()
            logging.info('Estimated scan time: %ds' % int(commnd_lenght * 25 * (10 ** -9) * triggers))
            logging.info('Please stand by...')
            self.register_utils.send_command(lvl1_command, repeat=triggers, wait_for_finish=False, set_length=True, clear_memory=False)

            wait_for_first_data = False
            last_iteration = time.time()
            saw_no_data_at_time = last_iteration
            saw_data_at_time = last_iteration
            no_data_at_time = last_iteration
            while not self.stop_thread_event.wait(self.readout.readout_interval):
                last_iteration = time.time()
                try:
                    data = (self.readout.data.popleft(), )
                    raw_data_file.append(data)
                    col_arr_tmp, row_arr_tmp = convert_data_array(data_array_from_data_dict_iterable(data), filter_func=is_data_record, converter_func=get_col_row_array_from_data_record_array)
                    self.col_arr = np.concatenate((self.col_arr, col_arr_tmp))
                    self.row_arr = np.concatenate((self.row_arr, row_arr_tmp))
                    #logging.info('data words')
                except IndexError:  # no data
                    #logging.info('no data words')
                    no_data_at_time = last_iteration
                    if self.register_utils.is_ready:
                        self.stop_thread_event.set()
                        logging.info('Finished sending %d triggers' % triggers)
                    elif wait_for_first_data == False and saw_no_data_at_time > (saw_data_at_time + timeout_no_data):
                        logging.info('Reached no data timeout. Stopping Scan...')
                        self.stop_thread_event.set()
                    elif wait_for_first_data == False:
                        saw_no_data_at_time = no_data_at_time
                    elif self.reaout_utils.is_ready:
                        self.stop_thread_event.set()

                    continue

                saw_data_at_time = last_iteration

                if wait_for_first_data == True:
                    logging.info('Taking data...')
                    wait_for_first_data = False

            self.readout.stop()

            self.register.restore()

            occ_hist, _, _ = np.histogram2d(self.col_arr, self.row_arr, bins=(80, 336), range=[[1, 80], [1, 336]])

            self.occ_mask = np.zeros(shape=occ_hist.shape, dtype=np.dtype('>u1'))
            # noisy pixels are set to 1
            self.occ_mask[occ_hist > occupancy_limit * triggers * consecutive_lvl1] = 1
            # make inverse
            self.inv_occ_mask = self.register_utils.invert_pixel_mask(self.occ_mask)
            self.disable_for_mask = disable_for_mask
            if overwrite_mask:
                for mask in disable_for_mask:
                    self.register.set_pixel_register_value(disable_for_mask, self.inv_occ_mask)
            else:
                for mask in disable_for_mask:
                    enable_mask = np.logical_and(self.inv_occ_mask, self.register.get_pixel_register_value(mask))
                    self.register.set_pixel_register_value(mask, enable_mask)

            self.enable_for_mask = enable_for_mask
            if overwrite_mask:
                for mask in disable_for_mask:
                    self.register.set_pixel_register_value(enable_for_mask, self.occ_mask)
            else:
                for mask in enable_for_mask:
                    disable_mask = np.logical_or(self.occ_mask, self.register.get_pixel_register_value(mask))
                    self.register.set_pixel_register_value(mask, disable_mask)

#             plot_occupancy(self.col_arr, self.row_arr, max_occ=None, filename=self.scan_data_filename + "_occupancy.pdf")

    def analyze(self):
        from analysis.analyze_raw_data import AnalyzeRawData
        output_file = self.scan_data_filename + "_interpreted.h5"
        with AnalyzeRawData(raw_data_file=scan.scan_data_filename, analyzed_data_file=output_file, create_pdf=True) as analyze_raw_data:
            analyze_raw_data.interpreter.set_trig_count(self.consecutive_lvl1)
            analyze_raw_data.create_source_scan_hist = True
#             analyze_raw_data.create_hit_table = True
#             analyze_raw_data.interpreter.debug_events(0, 0, True)  # events to be printed onto the console for debugging, usually deactivated
            analyze_raw_data.interpreter.set_warning_output(False)
            analyze_raw_data.interpret_word_table(fei4b=scan.register.fei4b)
            analyze_raw_data.interpreter.print_summary()
            analyze_raw_data.plot_histograms()
            plot_occupancy(self.occ_mask.T, title='Noisy Pixels', z_max=1, filename=analyze_raw_data.output_pdf)
            for mask in self.disable_for_mask:
                mask_name = self.register.get_pixel_register_attributes("full_name", do_sort=True, name=[mask])[0]
                plot_occupancy(self.register.get_pixel_register_value(mask).T, title='%s Mask' % mask_name, z_max=1, filename=analyze_raw_data.output_pdf)
            for mask in self.enable_for_mask:
                mask_name = self.register.get_pixel_register_attributes("full_name", do_sort=True, name=[mask])[0]
                plot_occupancy(self.register.get_pixel_register_value(mask).T, title='%s Mask' % mask_name, z_max=1, filename=analyze_raw_data.output_pdf)

if __name__ == "__main__":
    import configuration
    scan = NoiseOccupancyScan(**configuration.device_configuration)
    scan.start(use_thread=False, **scan_configuration)
    scan.stop()
    scan.analyze()
    scan.register.save_configuration(configuration.device_configuration["configuration_file"])
<|endoftext|>


<commit_before>import linecache
import os

class TagValidator(object):
    def __init__(self, name):
        self.name = name

    def validate(self, value):
        raise NotImplementedError

class Comment(object):
    def __init__(self, name='', title='', params={}, filename='',
            lineno=-1, endlineno=-1, annotations={}, description='', short_description='',
            tags={}, raw_comment=''):
        self.name = name
        self.title = title
        self.params = params
        self.filename = os.path.abspath(filename)
        self.lineno = lineno
        self.endlineno = endlineno
        self.annotations = annotations
        self.description = description
        self.short_description = short_description
        self.tags = tags

        # FIXME : would be nicer to have the scanner do that ^^
        leading_ws = ''
        if lineno != -1:
            orig = linecache.getline(filename, lineno)
            leading_ws = (len(orig) - len (orig.lstrip(' '))) * ' '

        self.raw_comment = leading_ws + raw_comment

    def add_param_block (self, param_name, block):
        self.params[param_name] = block

    def set_return_block (self, block):
        self.tags['returns'] = block

    def set_description (self, description):
        self.description = description.strip();

class Annotation(object):
    def __init__(self, name, argument=None):
        self.name = name
        self.argument = argument

class Tag(object):
    def __init__(self, name, description, value=None, annotations={}):
        self.name = name
        self.description = description
        self.annotations = annotations

def comment_from_tag(tag):
    if not tag:
        return None
    comment = Comment (name=tag.name,
            description=tag.description,
            annotations=tag.annotations)
    return comment
<commit_msg>comment_block: pylint + pep8<commit_after>"""
This module implements a Comment class, to be used
by code-parsing extensions.
"""

import linecache
import os

# pylint: disable=too-few-public-methods
class TagValidator(object):
    """
    Tag validators may be created by extensions that wish
    to add custom tags. (Returns, Since, etc...)
    """
    def __init__(self, name):
        self.name = name

    def validate(self, value):
        """
        Subclasses should implement this to validate the
        value of a tag.
        """
        raise NotImplementedError

class Comment(object):
    """
    Code-parsing extensions should add instances of this class to
    their doc_tool instance.
    """
    # This constructor is convenient
    # pylint: disable=too-many-instance-attributes
    # pylint: disable=too-many-arguments
    def __init__(self, name='', title='', params=None, filename='',
                 lineno=-1, endlineno=-1, annotations=None,
                 description='', short_description='', tags=None,
                 raw_comment=''):
        self.name = name
        self.title = title
        self.params = params or {}
        self.filename = os.path.abspath(filename)
        self.lineno = lineno
        self.endlineno = endlineno
        self.annotations = annotations or {}
        self.description = description
        self.short_description = short_description
        self.tags = tags or {}

        # FIXME : would be nicer to have the scanner do that ^^
        leading_ws = ''
        if lineno != -1:
            orig = linecache.getline(filename, lineno)
            leading_ws = (len(orig) - len(orig.lstrip(' '))) * ' '

        self.raw_comment = leading_ws + raw_comment

class Annotation(object):
    """
    An annotation is extra information that may or may not be displayed
    to the end-user, depending on the context.
    For example gobject annotations will be displayed for the
    C language, but hidden in python, and interpreted instead.
    """
    def __init__(self, name, argument=None):
        self.name = name
        self.argument = argument

class Tag(object):
    """
    A tag is extra information that shall always be displayed
    to the end-user, independent of the context.
    For example, since tags or return tags.
    """
    def __init__(self, name, description, value=None, annotations=None):
        self.name = name
        self.description = description
        self.value = value
        self.annotations = annotations or {}

def comment_from_tag(tag):
    """
    Convenience function to create a full-fledged comment for a
    given tag, for example it is convenient to assign a Comment
    to a ReturnValueSymbol.
    """
    if not tag:
        return None
    comment = Comment(name=tag.name,
                      description=tag.description,
                      annotations=tag.annotations)
    return comment
<|endoftext|>


<commit_before>from hdl_toolkit.bitmask import Bitmask
from hdl_toolkit.hdlObjects.specialValues import DIRECTION
from hdl_toolkit.hdlObjects.typeShortcuts import vecT
from hdl_toolkit.interfaces.std import Handshaked, Signal, HandshakeSync
from hdl_toolkit.interfaces.utils import addClkRstn, log2ceil, propagateClkRstn
from hdl_toolkit.synthesizer.codeOps import If, Switch, connect
from hdl_toolkit.synthesizer.interfaceLevel.unit import Unit
from hdl_toolkit.synthesizer.param import Param, evalParam
from hwtLib.handshaked.fifo import HandshakedFifo
from hwtLib.interfaces.amba import (Axi4_r, Axi4_addr, AxiStream,
                                    AxiStream_withUserAndStrb)
from hwtLib.interfaces.amba_constants import (BURST_INCR, CACHE_DEFAULT,
                                              LOCK_DEFAULT, PROT_DEFAULT,
                                              QOS_DEFAULT, BYTES_IN_TRANS,
                                              RESP_OKAY)
from hdl_toolkit.interfaces.agents.handshaked import HandshakedAgent

class AddrSizeHsAgent(HandshakedAgent):
    def doRead(self, s):
        intf = self.intf
        r = s.read

        addr = r(intf.addr)
        _len = r(intf.len)
        rem = r(intf.rem)
        user = r(intf.user)

        return (addr, _len, rem, user)

    def mkReq(self, addr, _len, rem=0, user=0):
        return (addr, _len, rem, user)

    def doWrite(self, s, data):
        intf = self.intf
        w = s.w

        if data is None:
            data = [None for _ in range(4)]

        addr, _len, rem, user = data

        w(addr, intf.addr)
        w(_len, intf.len)
        w(rem, intf.rem)
        w(user, intf.user)

class AddrSizeHs(Handshaked):
    def _config(self):
        self.ADDR_WIDTH = Param(32)
        self.MAX_LEN = Param(4096 // 8 - 1)
        self.DATA_WIDTH = Param(64)
        self.USER_WIDTH = Param(2)

    def _declr(self):
        self.addr = Signal(dtype=vecT(self.ADDR_WIDTH))
        #  len is number of words -1
        self.len = Signal(dtype=vecT(log2ceil(self.MAX_LEN)))

        # rem is number of bits in last word which is valid - 1
        self.rem = Signal(dtype=vecT(log2ceil(self.DATA_WIDTH // 8)))

        # signal of generic purpose
        if evalParam(self.USER_WIDTH).val > 0:
            self.user = Signal(dtype=vecT(self.USER_WIDTH))

        self.vld = Signal()
        self.rd = Signal(masterDir=DIRECTION.IN)

    def _getSimAgent(self):
        return AddrSizeHsAgent

class TransEndInfo(HandshakeSync):
    def _config(self):
        self.DATA_WIDTH = Param(64)
        self.USER_WIDTH = Param(0)

    def _declr(self):
        # rem is number of bits in last word which is valid - 1
        self.rem = Signal(dtype=vecT(log2ceil(self.DATA_WIDTH // 8)))

        # signal of generic purpose
        if evalParam(self.USER_WIDTH).val > 0:
            self.user = Signal(dtype=vecT(self.USER_WIDTH))

        self.propagateLast = Signal()
        HandshakeSync._declr(self)

class Axi4_RDataPump(Unit):
    """
    Foward req to axi ar channel
    and collect data to data channel form axi r channel

    This unit simplifies axi interface
    and blocks data channel when there is no request pending

    if req len is wider transaction is internally splited to multiple
    transactions, but readed data are single packet as requested

    rErrFlag stays hihh when there was error on axi r channel
    it will not affect unit functionality
    """

    def _config(self):
        self.DEFAULT_ID = Param(0)
        self.MAX_TRANS_OVERLAP = Param(16)
        self.MAX_LEN = Param(4096 // 8 - 1)

        self.ID_WIDTH = Param(4)
        self.ADDR_WIDTH = Param(32)
        self.DATA_WIDTH = Param(64)
        self.USER_WIDTH = Param(2)  # if 0 is used user signal completly disapears

    def _declr(self):
        with self._asExtern():
            addClkRstn(self)
            with self._paramsShared():
                self.ar = Axi4_addr()
                self.r = Axi4_r()

                # user flag from req will be set in every word on output rOut
                self.req = AddrSizeHs()
                if self.useUserSig():
                    self.rOut = AxiStream_withUserAndStrb()
                else:
                    self.rOut = AxiStream()

                self.rErrFlag = Signal()

        with self._paramsShared():
            f = self.sizeRmFifo = HandshakedFifo(TransEndInfo)
            f.DEPTH.set(self.MAX_TRANS_OVERLAP)

    def getSizeAlignBits(self):
        return log2ceil(self.DATA_WIDTH // 8).val

    def useTransSplitting(self):
        return self.req.len._dtype.bit_length() > self.ar.len._dtype.bit_length()

    def useUserSig(self):
        return evalParam(self.req.USER_WIDTH).val > 0

    def getBurstAddrOffset(self):
        LEN_MAX = Bitmask.mask(self.ar.len._dtype.bit_length())
        return (LEN_MAX + 1) << self.getSizeAlignBits()

    def addrHandler(self, addRmSize):
        ar = self.ar
        req = self.req

        canStartNew = addRmSize.rd

        ar.id ** self.DEFAULT_ID
        ar.burst ** BURST_INCR
        ar.cache ** CACHE_DEFAULT
        ar.lock ** LOCK_DEFAULT
        ar.prot ** PROT_DEFAULT
        ar.qos ** QOS_DEFAULT
        ar.size ** BYTES_IN_TRANS(evalParam(self.DATA_WIDTH).val)

        # if axi len is smaller we have to use transaction splitting
        if self.useTransSplitting():
            LEN_MAX = Bitmask.mask(ar.len._dtype.bit_length())

            lastReqDispatched = self._reg("lastReqDispatched", defVal=1)
            lenDebth = self._reg("lenDebth", req.len._dtype)
            remBackup = self._reg("remBackup", req.rem._dtype)
            rAddr = self._reg("r_addr", req.addr._dtype)

            if self.useUserSig():
                user = self._reg("user", self.req.user._dtype)
                If(lastReqDispatched,
                    user ** req.user,
                    addRmSize.user ** req.user
                ).Else(
                    addRmSize.user ** user
                )

            reqLen = self._sig("reqLen", req.len._dtype)
            reqRem = self._sig("reqRem", req.rem._dtype)
            ack = self._sig("ar_ack")

            If(reqLen > LEN_MAX,
               ar.len ** LEN_MAX,
               addRmSize.rem ** 0,
               addRmSize.propagateLast ** 0,
               If(ack,
                  lenDebth ** (reqLen - LEN_MAX),
                  lastReqDispatched ** 0)
            ).Else(
               connect(reqLen, ar.len, fit=True),  # connect only lower bits of len
               addRmSize.rem ** reqRem,
               addRmSize.propagateLast ** 1,
               If(ack,
                  lastReqDispatched ** 1)
            )

            If(lastReqDispatched,
               ar.valid ** (req.vld & canStartNew),
               ar.addr ** req.addr,
               rAddr ** req.addr,

               req.rd ** (canStartNew & ar.ready),
               reqLen ** req.len,
               reqRem ** req.rem,
               ack ** (req.vld & canStartNew & ar.ready),
               addRmSize.vld ** (req.vld & ar.ready),
               remBackup ** req.rem,
            ).Else(
               ar.addr ** rAddr,
               ar.valid ** canStartNew,
               req.rd ** 0,

               reqLen ** lenDebth,
               reqRem ** remBackup,
               ack ** (canStartNew & ar.ready),
               If(canStartNew & ar.ready,
                  rAddr ** (rAddr + self.getBurstAddrOffset())
               ),
               addRmSize.vld ** ar.ready
            )
        else:
            # if axi len is wider we can directly translate requests to axi
            ar.valid ** (req.vld & canStartNew)
            ar.addr ** req.addr
            connect(req.len, ar.len, fit=True)

            addRmSize.rem ** req.rem
            addRmSize.propagateLast ** 1
            if self.useUserSig():
                addRmSize.user ** req.user
            addRmSize.vld ** (req.vld & ar.ready)

    def remSizeToStrb(self, remSize, strb):
        strbBytes = 2 ** self.getSizeAlignBits()

        Switch(remSize)\
        .Case(0,
              strb ** Bitmask.mask(strbBytes)
        ).addCases(
         [ (i + 1, strb ** Bitmask.mask(i + 1))
           for i in range(strbBytes - 1)]
        )

    def dataHandler(self, rmSizeOut):
        r = self.r
        rOut = self.rOut

        rErrFlag = self._reg("rErrFlag", defVal=0)
        If(r.valid & rOut.ready & (r.resp != RESP_OKAY),
           rErrFlag ** 1
        )
        self.rErrFlag ** rErrFlag

        rOut.data ** r.data
        rOut.last ** (r.last & rmSizeOut.propagateLast)

        if self.useUserSig():
            rOut.user ** rmSizeOut.user

        self.remSizeToStrb(rmSizeOut.rem, rOut.strb)

        rOut.valid ** (r.valid & rmSizeOut.vld)
        r.ready ** (rOut.ready & rmSizeOut.vld)
        rmSizeOut.rd ** (r.valid & r.last & rOut.ready)

    def _impl(self):
        propagateClkRstn(self)

        self.addrHandler(self.sizeRmFifo.dataIn)
        self.dataHandler(self.sizeRmFifo.dataOut)

if __name__ == "__main__":
    from hdl_toolkit.synthesizer.shortcuts import toRtl
    u = Axi4_RDataPump()
    print(toRtl(u))

<commit_msg>codestyle<commit_after>from hdl_toolkit.bitmask import Bitmask
from hdl_toolkit.hdlObjects.specialValues import DIRECTION
from hdl_toolkit.hdlObjects.typeShortcuts import vecT
from hdl_toolkit.interfaces.std import Handshaked, Signal, HandshakeSync
from hdl_toolkit.interfaces.utils import addClkRstn, log2ceil, propagateClkRstn
from hdl_toolkit.synthesizer.codeOps import If, Switch, connect
from hdl_toolkit.synthesizer.interfaceLevel.unit import Unit
from hdl_toolkit.synthesizer.param import Param, evalParam
from hwtLib.handshaked.fifo import HandshakedFifo
from hwtLib.interfaces.amba import (Axi4_r, Axi4_addr, AxiStream,
                                    AxiStream_withUserAndStrb)
from hwtLib.interfaces.amba_constants import (BURST_INCR, CACHE_DEFAULT,
                                              LOCK_DEFAULT, PROT_DEFAULT,
                                              QOS_DEFAULT, BYTES_IN_TRANS,
                                              RESP_OKAY)
from hdl_toolkit.interfaces.agents.handshaked import HandshakedAgent
from hdl_toolkit.serializer.simModelSerializer import SimModelSerializer

class AddrSizeHsAgent(HandshakedAgent):
    def doRead(self, s):
        intf = self.intf
        r = s.read

        addr = r(intf.addr)
        _len = r(intf.len)
        rem = r(intf.rem)
        user = r(intf.user)

        return (addr, _len, rem, user)

    def mkReq(self, addr, _len, rem=0, user=0):
        return (addr, _len, rem, user)

    def doWrite(self, s, data):
        intf = self.intf
        w = s.w

        if data is None:
            data = [None for _ in range(4)]

        addr, _len, rem, user = data

        w(addr, intf.addr)
        w(_len, intf.len)
        w(rem, intf.rem)
        w(user, intf.user)

class AddrSizeHs(Handshaked):
    def _config(self):
        self.ADDR_WIDTH = Param(32)
        self.MAX_LEN = Param(4096 // 8 - 1)
        self.DATA_WIDTH = Param(64)
        self.USER_WIDTH = Param(2)

    def _declr(self):
        self.addr = Signal(dtype=vecT(self.ADDR_WIDTH))
        #  len is number of words -1
        self.len = Signal(dtype=vecT(log2ceil(self.MAX_LEN)))

        # rem is number of bits in last word which is valid - 1
        self.rem = Signal(dtype=vecT(log2ceil(self.DATA_WIDTH // 8)))

        # signal of generic purpose
        if evalParam(self.USER_WIDTH).val > 0:
            self.user = Signal(dtype=vecT(self.USER_WIDTH))

        self.vld = Signal()
        self.rd = Signal(masterDir=DIRECTION.IN)

    def _getSimAgent(self):
        return AddrSizeHsAgent

class TransEndInfo(HandshakeSync):
    def _config(self):
        self.DATA_WIDTH = Param(64)
        self.USER_WIDTH = Param(0)

    def _declr(self):
        # rem is number of bits in last word which is valid - 1
        self.rem = Signal(dtype=vecT(log2ceil(self.DATA_WIDTH // 8)))

        # signal of generic purpose
        if evalParam(self.USER_WIDTH).val > 0:
            self.user = Signal(dtype=vecT(self.USER_WIDTH))

        self.propagateLast = Signal()
        HandshakeSync._declr(self)

class Axi4_RDataPump(Unit):
    """
    Foward req to axi ar channel
    and collect data to data channel form axi r channel

    This unit simplifies axi interface
    and blocks data channel when there is no request pending

    if req len is wider transaction is internally splited to multiple
    transactions, but readed data are single packet as requested

    rErrFlag stays hihh when there was error on axi r channel
    it will not affect unit functionality
    """

    def _config(self):
        self.DEFAULT_ID = Param(0)
        self.MAX_TRANS_OVERLAP = Param(16)
        self.MAX_LEN = Param(4096 // 8 - 1)

        self.ID_WIDTH = Param(4)
        self.ADDR_WIDTH = Param(32)
        self.DATA_WIDTH = Param(64)
        self.USER_WIDTH = Param(2)  # if 0 is used user signal completly disapears

    def _declr(self):
        with self._asExtern():
            addClkRstn(self)
            with self._paramsShared():
                self.ar = Axi4_addr()
                self.r = Axi4_r()

                # user flag from req will be set in every word on output rOut
                self.req = AddrSizeHs()
                if self.useUserSig():
                    self.rOut = AxiStream_withUserAndStrb()
                else:
                    self.rOut = AxiStream()

                self.rErrFlag = Signal()

        with self._paramsShared():
            f = self.sizeRmFifo = HandshakedFifo(TransEndInfo)
            f.DEPTH.set(self.MAX_TRANS_OVERLAP)

    def getSizeAlignBits(self):
        return log2ceil(self.DATA_WIDTH // 8).val

    def useTransSplitting(self):
        return self.req.len._dtype.bit_length() > self.ar.len._dtype.bit_length()

    def useUserSig(self):
        return evalParam(self.req.USER_WIDTH).val > 0

    def getBurstAddrOffset(self):
        LEN_MAX = Bitmask.mask(self.ar.len._dtype.bit_length())
        return (LEN_MAX + 1) << self.getSizeAlignBits()

    def addrHandler(self, addRmSize):
        ar = self.ar
        req = self.req

        canStartNew = addRmSize.rd

        ar.id ** self.DEFAULT_ID
        ar.burst ** BURST_INCR
        ar.cache ** CACHE_DEFAULT
        ar.lock ** LOCK_DEFAULT
        ar.prot ** PROT_DEFAULT
        ar.qos ** QOS_DEFAULT
        ar.size ** BYTES_IN_TRANS(evalParam(self.DATA_WIDTH).val)

        # if axi len is smaller we have to use transaction splitting
        if self.useTransSplitting():
            LEN_MAX = Bitmask.mask(ar.len._dtype.bit_length())

            lastReqDispatched = self._reg("lastReqDispatched", defVal=1)
            lenDebth = self._reg("lenDebth", req.len._dtype)
            remBackup = self._reg("remBackup", req.rem._dtype)
            rAddr = self._reg("r_addr", req.addr._dtype)

            if self.useUserSig():
                user = self._reg("user", self.req.user._dtype)
                If(lastReqDispatched,
                    user ** req.user,
                    addRmSize.user ** req.user
                ).Else(
                    addRmSize.user ** user
                )

            reqLen = self._sig("reqLen", req.len._dtype)
            reqRem = self._sig("reqRem", req.rem._dtype)
            ack = self._sig("ar_ack")

            If(reqLen > LEN_MAX,
               ar.len ** LEN_MAX,
               addRmSize.rem ** 0,
               addRmSize.propagateLast ** 0
            ).Else(
               connect(reqLen, ar.len, fit=True),  # connect only lower bits of len
               addRmSize.rem ** reqRem,
               addRmSize.propagateLast ** 1
            )

            If(ack,
                If(reqLen > LEN_MAX,
                    lenDebth ** (reqLen - LEN_MAX),
                    lastReqDispatched ** 0
                ).Else(
                    lastReqDispatched ** 1
                )
            )

            If(lastReqDispatched,
               ar.valid ** (req.vld & canStartNew),
               ar.addr ** req.addr,
               rAddr ** req.addr,

               req.rd ** (canStartNew & ar.ready),
               reqLen ** req.len,
               reqRem ** req.rem,
               ack ** (req.vld & canStartNew & ar.ready),
               addRmSize.vld ** (req.vld & ar.ready),
               remBackup ** req.rem,
            ).Else(
               ar.addr ** rAddr,
               ar.valid ** canStartNew,
               req.rd ** 0,

               reqLen ** lenDebth,
               reqRem ** remBackup,
               ack ** (canStartNew & ar.ready),
               If(canStartNew & ar.ready,
                  rAddr ** (rAddr + self.getBurstAddrOffset())
               ),
               addRmSize.vld ** ar.ready
            )
        else:
            # if axi len is wider we can directly translate requests to axi
            ar.valid ** (req.vld & canStartNew)
            ar.addr ** req.addr
            connect(req.len, ar.len, fit=True)

            addRmSize.rem ** req.rem
            addRmSize.propagateLast ** 1
            if self.useUserSig():
                addRmSize.user ** req.user
            addRmSize.vld ** (req.vld & ar.ready)

    def remSizeToStrb(self, remSize, strb):
        strbBytes = 2 ** self.getSizeAlignBits()

        Switch(remSize)\
        .Case(0,
              strb ** Bitmask.mask(strbBytes)
        ).addCases(
         [ (i + 1, strb ** Bitmask.mask(i + 1))
           for i in range(strbBytes - 1)]
        )

    def dataHandler(self, rmSizeOut):
        r = self.r
        rOut = self.rOut

        rErrFlag = self._reg("rErrFlag", defVal=0)
        If(r.valid & rOut.ready & (r.resp != RESP_OKAY),
           rErrFlag ** 1
        )
        self.rErrFlag ** rErrFlag

        rOut.data ** r.data
        rOut.last ** (r.last & rmSizeOut.propagateLast)

        if self.useUserSig():
            rOut.user ** rmSizeOut.user

        self.remSizeToStrb(rmSizeOut.rem, rOut.strb)

        rOut.valid ** (r.valid & rmSizeOut.vld)
        r.ready ** (rOut.ready & rmSizeOut.vld)
        rmSizeOut.rd ** (r.valid & r.last & rOut.ready)

    def _impl(self):
        propagateClkRstn(self)

        self.addrHandler(self.sizeRmFifo.dataIn)
        self.dataHandler(self.sizeRmFifo.dataOut)

if __name__ == "__main__":
    from hdl_toolkit.synthesizer.shortcuts import toRtl
    u = Axi4_RDataPump()
    print(toRtl(u, serializer=SimModelSerializer))

<|endoftext|>


<commit_before>#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from hdl_toolkit.bitmask import mask
from hdl_toolkit.interfaces.std import Signal
from hdl_toolkit.interfaces.utils import addClkRstn, propagateClkRstn, log2ceil
from hdl_toolkit.synthesizer.codeOps import connect, If
from hdl_toolkit.synthesizer.interfaceLevel.unit import Unit
from hdl_toolkit.synthesizer.param import Param, evalParam
from hwtLib.axi.axi4_rDatapump import AddrSizeHs
from hwtLib.interfaces.amba import Axi4_w, Axi4_addr, AxiStream, Axi4_b
from hwtLib.interfaces.amba_constants import BURST_INCR, CACHE_DEFAULT, LOCK_DEFAULT, PROT_DEFAULT, QOS_DEFAULT, \
    BYTES_IN_TRANS, RESP_OKAY

def axiHsAck(m, s):
    return m.valid & s.ready

class Axi4_wDatapump(Unit):
    """
    @ivar param MAX_TRANS_OVERLAP: max number of concurent transactios
    """
    def _config(self):
        self.DEFAULT_ID = Param(0)
        self.MAX_TRANS_OVERLAP = Param(16)
        self.MAX_LEN = Param(4096 // 8 - 1)

        self.ID_WIDTH = Param(4)
        self.ADDR_WIDTH = Param(32)
        self.DATA_WIDTH = Param(64)

    def _declr(self):
        with self._asExtern():
            addClkRstn(self)
            with self._paramsShared():
                self.aw = Axi4_addr()
                self.w = Axi4_w()
                self.b = Axi4_b()

                self.req = AddrSizeHs()
                self.wIn = AxiStream()

                self.wErrFlag = Signal()

    def useTransSplitting(self):
        return self.req.len._dtype.bit_length() > self.ar.len._dtype.bit_length()

    def getSizeAlignBits(self):
        return log2ceil(self.DATA_WIDTH // 8).val

    def getAxiLenMax(self):
        return mask(self.ar.len._dtype.bit_length())

    def getBurstAddrOffset(self):
        return (self.getAxiLenMax() + 1) << self.getSizeAlignBits()

    def axiAwHandler(self, enable):
        req = self.req
        aw = self.aw

        aw.id ** self.DEFAULT_ID
        aw.burst ** BURST_INCR
        aw.cache ** CACHE_DEFAULT
        aw.lock ** LOCK_DEFAULT
        aw.prot ** PROT_DEFAULT
        aw.qos ** QOS_DEFAULT
        aw.size ** BYTES_IN_TRANS(evalParam(self.DATA_WIDTH).val)

        if self.useTransSplitting():
            LEN_MAX = mask(aw.len._dtype.bit_length())

            lastReqDispatched = self._reg("lastReqDispatched", defVal=1)

            lenDebth = self._reg("lenDebth", req.len._dtype)
            rAddr = self._reg("r_addr", req.addr._dtype)

            If(lastReqDispatched,
               aw.addr ** req.addr,
               connect(req.len, aw.len, fit=True),
               aw.valid ** (enable & req.vld),
               rAddr ** (req.addr + self.getBurstAddrOffset()),
               lenDebth ** (req.len - LEN_MAX),
               If(enable & (req.len > LEN_MAX) & aw.ready & req.vld,
                  lastReqDispatched ** 1
               )
            ).Else(
               aw.addr ** rAddr,
               connect(lenDebth, aw.len, fit=True),
               aw.valid ** enable,
               If(enable & aw.ready,
                  rAddr ** (rAddr + self.getBurstAddrOffset()),
                  lenDebth ** (lenDebth - LEN_MAX),
                  If(lenDebth <= LEN_MAX,
                     lastReqDispatched ** 1
                  )
               )
            )

            ack = aw.ready & ((lastReqDispatched & req.vld) | ~lastReqDispatched)

        else:
            aw.addr ** req.addr
            connect(req.len, aw.len, fit=True)
            aw.valid ** req.vld
            req.rd ** aw.ready

            ack = aw.ready & req.vld

        return ack

    def axiWHandler(self, enable):
        w = self.w
        wIn = self.wIn

        w.id ** self.DEFAULT_ID
        w.data ** wIn.data
        w.strb ** wIn.strb

        if self.useTransSplitting():
            wordCntr = self._reg("wWordCntr", self.aw.len._dtype, 0)
            If(enable & axiHsAck(wIn, w),
               If(wIn.last,
                   wordCntr ** 0
               ).Else(
                   wordCntr ** (wordCntr + 1)
               )
            )

            w.last ** (wordCntr._eq(self.getAxiLenMax()) | wIn.last)
            w.valid ** (enable & wIn.valid)
            wIn.ready ** (enable & w.ready)

        else:
            w.last ** wIn.last
            w.valid ** (enable & wIn.valid)
            wIn.ready ** w.ready

    def axiBHandler(self, enable):
        wErrFlag = self._reg("wErrFlag", defVal=0)
        b = self.b
        If(enable & b.valid & (b.resp != RESP_OKAY),
           wErrFlag ** 1
        )

        self.wErrFlag * wErrFlag
        b.ready ** enable

        ack = b.valid
        return ack

    def _impl(self):
        propagateClkRstn(self)

        self.axiWHandler()
<commit_msg>rework transaction info passing <commit_after>#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from hdl_toolkit.bitmask import mask
from hdl_toolkit.interfaces.std import Signal, Handshaked, VectSignal, \
    HandshakeSync
from hdl_toolkit.interfaces.utils import addClkRstn, propagateClkRstn, log2ceil
from hdl_toolkit.synthesizer.codeOps import connect, If, Concat
from hdl_toolkit.synthesizer.interfaceLevel.unit import Unit
from hdl_toolkit.synthesizer.param import Param, evalParam
from hwtLib.axi.axi4_rDatapump import AddrSizeHs
from hwtLib.interfaces.amba import Axi4_w, Axi4_addr, AxiStream, Axi4_b
from hwtLib.interfaces.amba_constants import BURST_INCR, CACHE_DEFAULT, LOCK_DEFAULT, PROT_DEFAULT, QOS_DEFAULT, \
    BYTES_IN_TRANS, RESP_OKAY
from hwtLib.handshaked.fifo import HandshakedFifo
from hwtLib.handshaked.builder import HsBuilder

def axiHsAck(m, s):
    return m.valid & s.ready

class WFifoIntf(Handshaked):
    def _config(self):
        self.ID_WIDTH = Param(4)

    def _declr(self):
        self.id = VectSignal(self.ID_WIDTH)
        HandshakeSync._declr(self)

class BFifoIntf(Handshaked):
    def _config(self):
        pass

    def _declr(self):
        self.isLast = Signal()
        HandshakeSync._declr(self)

class Axi4_wDatapump(Unit):
    """
    @ivar param MAX_TRANS_OVERLAP: max number of concurrent transactions

    """
    def _config(self):
        self.MAX_TRANS_OVERLAP = Param(16)
        self.MAX_LEN = Param(4096 // 8 - 1)

        self.ID_WIDTH = Param(4)
        self.ADDR_WIDTH = Param(32)
        self.DATA_WIDTH = Param(64)

    def _declr(self):
        with self._asExtern():
            addClkRstn(self)
            with self._paramsShared():
                self.aw = Axi4_addr()
                self.w = Axi4_w()
                self.b = Axi4_b()

                self.req = AddrSizeHs()

                self.wIn = AxiStream()

                self.wErrFlag = Signal()
            self.reqAck = Handshaked()
            self.reqAck.DATA_WIDTH.set(self.ID_WIDTH)
        with self._paramsShared():
            # fifo for id propagation and frame splitting on axi.w channel
            wf = self.writeInfoFifo = HandshakedFifo(WFifoIntf)
            wf.ID_WIDTH.set(self.ID_WIDTH)
            wf.DEPTH.set(self.MAX_TRANS_OVERLAP)

            # fifo for propagation of end of frame from axi.b channel
            bf = self.bInfoFifo = HandshakedFifo(BFifoIntf)
            bf.DEPTH.set(self.MAX_TRANS_OVERLAP)

    def useTransSplitting(self):
        return evalParam(self.MAX_LEN).val > self.aw.len._dtype.bit_length()

    def getSizeAlignBits(self):
        return log2ceil(self.DATA_WIDTH // 8).val

    def getAxiLenMax(self):
        return mask(self.aw.len._dtype.bit_length())

    def getBurstAddrOffset(self):
        return (self.getAxiLenMax() + 1) << self.getSizeAlignBits()

    def axiAwHandler(self):
        req = self.req
        aw = self.aw

        aw.burst ** BURST_INCR
        aw.cache ** CACHE_DEFAULT
        aw.lock ** LOCK_DEFAULT
        aw.prot ** PROT_DEFAULT
        aw.qos ** QOS_DEFAULT
        aw.size ** BYTES_IN_TRANS(evalParam(self.DATA_WIDTH).val)

        if self.useTransSplitting():
            LEN_MAX = mask(aw.len._dtype.bit_length())
            wInfo = self.writeInfoFifo.dataIn

            lastReqDispatched = self._reg("lastReqDispatched", defVal=1)
            lenDebth = self._reg("lenDebth", req.len._dtype)
            addrBackup = self._reg("addrBackup", req.addr._dtype)
            req_idBackup = self._reg("req_idBackup", self.req.id._dtype)
            _id = self._sig("id", aw.id._dtype)

            en = wInfo.rd

            If(lastReqDispatched,
                _id ** req.id,
                aw.addr ** req.addr,
                connect(req.len, aw.len, fit=True),
                aw.valid ** (en & req.vld),

                wInfo.vld ** (req.vld & aw.ready),

                req.rd ** (wInfo.rd & aw.ready),

                req_idBackup ** req.id,
                addrBackup ** (req.addr + self.getBurstAddrOffset()),
                lenDebth ** (req.len - (LEN_MAX + 1)),
                If(en & (req.len > LEN_MAX) & aw.ready & req.vld,
                   lastReqDispatched ** 1
                )
            ).Else(
                _id ** req_idBackup,
                aw.addr ** addrBackup,
                connect(lenDebth, aw.len, fit=True),
                aw.valid ** en,

                wInfo.vld ** aw.ready,
                req.rd ** 0,

                If(en & aw.ready,
                   addrBackup ** (addrBackup + self.getBurstAddrOffset()),
                   lenDebth ** (lenDebth - LEN_MAX),
                   If(lenDebth <= LEN_MAX,
                      lastReqDispatched ** 1
                   )
                )
            )
            aw.id ** _id
            wInfo.id ** _id

        else:
            aw.id ** req.id
            aw.addr ** req.addr
            connect(req.len, aw.len, fit=True)
            aw.valid ** req.vld
            req.rd ** aw.ready

    def axiWHandler(self):
        w = self.w
        wIn = self.wIn
        wInfo = HsBuilder(self, self.writeInfoFifo.dataOut).reg().end
        bInfo = self.bInfoFifo.dataIn

        enable = wInfo.vld & bInfo.rd

        w.id ** wInfo.id
        w.data ** wIn.data
        w.strb ** wIn.strb

        if self.useTransSplitting():
            wordCntr = self._reg("wWordCntr", self.aw.len._dtype, 0)
            doSplit = wordCntr._eq(self.getAxiLenMax())

            If(enable & axiHsAck(wIn, w),
               If(wIn.last,
                   wordCntr ** 0
               ).Else(
                   wordCntr ** (wordCntr + 1)
               )
            )
            wInfo.rd ** (axiHsAck(wIn, w) & (doSplit | wIn.last) & bInfo.rd)
            bInfo.isLast ** wIn.last
            bInfo.vld ** (axiHsAck(wIn, w) & (doSplit | wIn.last) & wInfo.vld)

            w.last ** (doSplit | wIn.last)
            w.valid ** (enable & wIn.valid)
            wIn.ready ** (enable & w.ready)

        else:
            w.last ** wIn.last
            w.valid ** (enable & wIn.valid)
            wIn.ready ** w.ready

    def axiBHandler(self):
        wErrFlag = self._reg("wErrFlag", defVal=0)
        b = self.b
        reqAck = self.reqAck

        lastFlags = HsBuilder(self, self.bInfoFifo.dataOut).reg().end

        If(lastFlags.vld & reqAck.rd & b.valid & (b.resp != RESP_OKAY),
           wErrFlag ** 1
        )

        self.wErrFlag ** wErrFlag
        b.ready ** (lastFlags.vld & reqAck.rd)
        lastFlags.rd ** (reqAck.rd & b.valid)

        reqAck.vld ** (lastFlags.vld & lastFlags.isLast & b.valid)
        reqAck.data ** b.id

    def _impl(self):
        propagateClkRstn(self)

        self.axiAwHandler()
        self.axiWHandler()
        self.axiBHandler()

if __name__ == "__main__":
    from hdl_toolkit.synthesizer.shortcuts import toRtl
    u = Axi4_wDatapump()
    print(toRtl(u))
<|endoftext|>