Ejemplo n.º 1
0
    def _runSeqProcesses(self) -> Generator[None, None, None]:
        """
        Delta step for event dependent processes
        """
        updates = []
        for proc in self._seqProcsToRun:
            try:
                outContainer = self._outputContainers[proc]
            except KeyError:
                # processes does not have to have outputs
                outContainer = None

            proc(self, outContainer)

            if outContainer is not None:
                updates.append(outContainer)

        self._seqProcsToRun = UniqList()
        self._runSeqProcessesPlaned = False

        for cont in updates:
            for sigName, sig in cont._all_signals:
                newVal = getattr(cont, sigName)
                if newVal is not None:
                    v = self._conflictResolveStrategy(newVal)
                    updater, _ = v
                    sig.simUpdateVal(self, updater)
                    setattr(cont, sigName, None)
        return
        yield
Ejemplo n.º 2
0
class SaveToFilesFlat(StoreManager):
    """
    Store all produced code to a single directory, file per component.
    """

    def __init__(self,
                 serializer_cls: DummySerializerConfig,
                 root: str,
                 _filter: "SerializerFilter"=None,
                 name_scope: Optional[NameScope]=None):
        super(SaveToFilesFlat, self).__init__(
            serializer_cls, _filter=_filter, name_scope=name_scope)
        self.root = root
        self.files = UniqList()
        self.constraints_file_spoted = False
        os.makedirs(root, exist_ok=True)

    def write(self, obj: Union[iHdlObj, HdlConstraintList]):
        if isinstance(obj, HdlConstraintList):
            fName = "constraints" + self.serializer_cls.TO_CONSTRAINTS.fileExtension
        else:
            fName = obj.name + self.serializer_cls.fileExtension

        fp = os.path.join(self.root, fName)
        self.files.append(fp)
        if self.constraints_file_spoted:
            m = 'a'
        else:
            m = 'w'
            self.constraints_file_spoted = True
        with open(fp, m) as f:
            s = SaveToStream(self.serializer_cls, f,
                             self.filter, self.name_scope)
            s.write(obj)
Ejemplo n.º 3
0
    def toElkJson(self, idStore: "ElkIdStore", path_prefix: ComponentPath=ComponentPath(), isTop=True):
        props = {
            "org.eclipse.elk.portConstraints": self.portConstraints.name,
            'org.eclipse.elk.layered.mergeEdges': 1,
        }
        hw_meta = {
            "name": self.name,
            "cls": self.cls,
        }
        d = {
            "hwMeta": hw_meta,
            "properties": props
        }
        hideChildren = False
        if self.bodyText is not None:
            hw_meta["bodyText"] = self.bodyText

        if isTop:
            self.toElkJson_registerNodes(idStore, path_prefix)
            self.toElkJson_registerPorts(idStore, path_prefix)
        else:
            d["id"] = str(idStore[path_prefix / self])
            hideChildren = True
            # if self.parent.parent is not None:
            #    props["org.eclipse.elk.noLayout"] = True

        d["ports"] = [p.toElkJson(idStore, path_prefix)
                      for p in self.iterPorts()]

        children, path_prefix = self._getUniqRefChildren(path_prefix)
        if children:
            assert isinstance(children, list)
            formalParent = self._shared_component_with
            if formalParent is None:
                formalParent = self
            nodes = []
            edges = UniqList()
            for ch in children:
                for e in ch.iterEdges():
                    if e.parentNode is formalParent:
                        edges.append(e)

            for ch in children:
                nodes.append(ch.toElkJson(idStore, isTop=False, path_prefix=path_prefix))

            nodes.sort(key=lambda n: n["id"])
            d["_children" if hideChildren else "children"] = nodes

            for e in edges:
                idStore.registerEdge(path_prefix / e)

            d["_edges" if hideChildren else "edges"] = [e.toElkJson(idStore, path_prefix) for e in edges]

        hw_meta["maxId"] = idStore.getMaxId()

        return d
Ejemplo n.º 4
0
    def __init__(self, parentStm=None, sensitivity=None,
                 is_completly_event_dependent=False):
        self._is_completly_event_dependent = is_completly_event_dependent
        self._now_is_event_dependent = is_completly_event_dependent
        self.parentStm = parentStm
        self._inputs = UniqList()
        self._outputs = UniqList()
        self._enclosed_for = None

        self._sensitivity = sensitivity
        self.rank = 0
Ejemplo n.º 5
0
 def __init__(self,
              serializer_cls: DummySerializerConfig,
              root: str,
              _filter: "SerializerFilter"=None,
              name_scope: Optional[NameScope]=None):
     super(SaveToFilesFlat, self).__init__(
         serializer_cls, _filter=_filter, name_scope=name_scope)
     self.root = root
     self.files = UniqList()
     self.module_path_prefix = None
     os.makedirs(root, exist_ok=True)
Ejemplo n.º 6
0
    def __init__(self,
                 parentStm: Optional["HdlStatement"] = None,
                 sensitivity: Optional[UniqList] = None,
                 event_dependent_from_branch: Optional[int] = None):
        assert event_dependent_from_branch is None or isinstance(
            event_dependent_from_branch, int), event_dependent_from_branch
        self._event_dependent_from_branch = event_dependent_from_branch
        self.parentStm = parentStm
        self._inputs = UniqList()
        self._outputs = UniqList()
        self._enclosed_for = None

        self._sensitivity = sensitivity
        self.rank = 0
Ejemplo n.º 7
0
    def __init__(self, others: NetCtxs, actualKey, seqNo: int):
        """
        :param seqNo: sequential number used to sustain determinism while iterating over instances of this class in dictionary

        """
        self.parentNode = others.parentNode
        assert isinstance(self.parentNode, LNode), self.parentNode
        self.actualKeys = [
            actualKey,
        ]
        self.others = others
        self.drivers = UniqList()
        self.endpoints = UniqList()
        self.seqNo = seqNo
Ejemplo n.º 8
0
    def __init__(self,
                 ctx,
                 name,
                 dtype,
                 def_val=None,
                 nop_val=NO_NOPVAL,
                 virtual_only=False,
                 is_const=False):
        """
        :param ctx: context - RtlNetlist which is this signal part of
        :param name: name hint for this signal, if is None name
            is chosen automatically
        :param def_val: value which is used for reset and as default value
            in hdl
        :param nop_val: value which is used to fill up statements when no other
            value is assigned, use NO_NOPVAL to dissable
        :param is_const: flag which tell that this signal can not have any other driver
            than a default value
        """

        if name is None:
            name = "sig_"
            self.hasGenericName = True
        else:
            self.hasGenericName = False

        assert isinstance(dtype, HdlType)
        super(RtlSignal, self).__init__(name,
                                        dtype,
                                        def_val,
                                        virtual_only=virtual_only)
        self.ctx = ctx

        if ctx:
            # params does not have any context on created
            # and it is assigned after param is bounded to unit or interface
            ctx.signals.add(self)

        # set can not be used because hash of items are changing
        self.endpoints = UniqList()
        self.drivers = UniqList()
        self._usedOps = {}
        self._usedOpsAlias = {}
        self.hidden = True
        self._instId = RtlSignal._nextInstId()

        self._nop_val = nop_val
        self._const = is_const
        self.origin = None
Ejemplo n.º 9
0
    def _runCombProcesses(self) -> None:
        """
        Delta step for combinational processes
        """
        for proc in self._combProcsToRun:
            cont = self._outputContainers[proc]
            proc(self, cont)
            for sigName, sig in cont._all_signals:
                newVal = getattr(cont, sigName)
                if newVal is not None:
                    res = self._conflictResolveStrategy(newVal)
                    # prepare update
                    updater, isEvDependent = res
                    self._valuesToApply.append(
                        (sig, updater, isEvDependent, proc))
                    setattr(cont, sigName, None)
                    # else value is latched

        self._combProcsToRun = UniqList()
Ejemplo n.º 10
0
    def _on_reduce(self, self_reduced: bool, io_changed: bool,
                   result_statements: List["HdlStatement"]) -> None:
        """
        Update signal IO after reduce attempt

        :param self_reduced: if True this object was reduced
        :param io_changed: if True IO of this object may changed
            and has to be updated
        :param result_statements: list of statements which are result
            of reduce operation on this statement
        """

        parentStm = self.parentStm
        if self_reduced:
            was_top = parentStm is None
            # update signal drivers/endpoints
            if was_top:
                # disconnect self from signals
                ctx = self._get_rtl_context()
                ctx.statements.remove(self)
                ctx.statements.update(result_statements)

                for i in self._inputs:
                    i.endpoints.discard(self)
                for o in self._outputs:
                    o.drivers.remove(self)

            for stm in result_statements:
                stm.parentStm = parentStm
                if parentStm is None:
                    # connect signals to child statements
                    for inp in stm._inputs:
                        inp.endpoints.append(stm)
                    for outp in stm._outputs:
                        outp.drivers.append(stm)
        else:
            # parent has to update it's inputs/outputs
            if io_changed:
                self._inputs = UniqList()
                self._outputs = UniqList()
                self._collect_io()
Ejemplo n.º 11
0
    def __init__(self, config=None):
        super(HdlSimulator, self).__init__()
        if config is None:
            # default config
            config = HdlSimConfig()

        self.config = config
        self.now = 0.0

        self._combUpdateDonePlaned = False
        self._applyValPlaned = False
        self._runSeqProcessesPlaned = False

        # (signal, value) tuples which should be applied before
        # new round of processes
        #  will be executed
        self._valuesToApply = []
        self._seqProcsToRun = UniqList()
        self._combProcsToRun = UniqList()
        # container of outputs for every process
        self._outputContainers = {}
        self._events = SimCalendar()
Ejemplo n.º 12
0
def unhideResultsOfIndexingAndConcatOnPublicSignals(netlist: RtlNetlist):
    openset = UniqList(
        sorted((s for s in netlist.signals if not s.hidden),
               key=RtlSignal_sort_key))
    epsToReplace = []
    while openset:
        s = openset.pop()
        s: RtlSignal
        for ep in s.endpoints:
            # search for index ops
            if isinstance(ep, Operator)\
                    and ep.operator == AllOps.INDEX\
                    and ep.operands[0] is s:
                ep: Operator
                isIndexInBramWrite = isinstance(s._dtype, HArray)\
                    and arr_all(ep.result.endpoints,
                                lambda ep: isinstance(ep, HdlStatement)\
                                           and ep._event_dependent_from_branch == 0)
                if not isIndexInBramWrite and ep.result.hidden:
                    epsToReplace.append(ep)

        for ep in epsToReplace:
            ep: Operator
            r = ep.result
            assert len(r.drivers) == 1, r
            r.hidden = False
            i = ep.operands[1]
            ep._destroy()

            # instantiate new hidden signal for result of index
            new_r = s[i]
            assert new_r is not r, r
            # and instantiate HdlAssignmentContainer to this new signal from the
            # old one
            r(new_r)
            openset.append(r)

        epsToReplace.clear()
Ejemplo n.º 13
0
    def __init__(self,
                 topUnit: Unit,
                 name: str = None,
                 extraVhdlFiles: List[str] = [],
                 extraVerilogFiles: List[str] = [],
                 serializer=VhdlSerializer,
                 targetPlatform=DummyPlatform()):
        assert not topUnit._wasSynthetised()
        self.topUnit = topUnit
        self.serializer = serializer
        self.targetPlatform = targetPlatform
        if name:
            self.name = name
        else:
            self.name = self.topUnit._getDefaultName()

        self.hdlFiles = UniqList()

        for f in extraVhdlFiles:
            self.hdlFiles.append(f)

        for f in extraVerilogFiles:
            self.hdlFiles.append(f)
Ejemplo n.º 14
0
class NetCtx():
    def __init__(self, others: NetCtxs, actualKey, seqNo: int):
        """
        :param seqNo: sequential number used to sustain determinism while iterating over instances of this class in dictionary

        """
        self.parentNode = others.parentNode
        assert isinstance(self.parentNode, LNode), self.parentNode
        self.actualKeys = [
            actualKey,
        ]
        self.others = others
        self.drivers = UniqList()
        self.endpoints = UniqList()
        self.seqNo = seqNo

    def extend(self, other: "NetCtx"):
        self.drivers.extend(other.drivers)
        self.endpoints.extend(other.endpoints)

    def addDriver(self, src: Union[RtlSignalBase, LPort]):
        if isinstance(src, RtlSignalBase):
            return self.others.joinNetsByKeyVal(src, self)
        else:
            if self.parentNode is src.parentNode:
                # connection between input and output on nodes with same parent
                assert src.direction == PortType.INPUT, src
            elif self.parentNode.parent is src.parentNode:
                # source is parent input port
                assert src.direction == PortType.INPUT, src
            else:
                # source is child output port
                assert self.parentNode is src.parentNode.parent, src
                assert src.direction == PortType.OUTPUT, src

            return self.drivers.append(src)

    def addEndpoint(self, dst: Union[RtlSignalBase, LPort]):
        if isinstance(dst, RtlSignalBase):
            return self.others.joinNetsByValKey(self, dst)
        else:
            if self.parentNode is dst.parentNode:
                # connection between input and output on nodes with same parent
                assert dst.direction == PortType.OUTPUT, dst
            elif self.parentNode.parent is dst.parentNode:
                # target is parent output port
                assert dst.direction == PortType.INPUT, dst
            else:
                # target is child input port
                assert self.parentNode is dst.parentNode.parent, dst
                assert dst.direction == PortType.INPUT, dst

            return self.endpoints.append(dst)
Ejemplo n.º 15
0
    def _collectPortTypeVariants(
        self
    ) -> List[Tuple[HdlPortItem, Dict[Tuple[Param, HValue], List[HdlType]]]]:
        res = []
        param_variants = [paramsToValTuple(u) for u in self._units]
        for parent_port, port_variants in zip(
                self._ctx.ent.ports,
                zip(*(u._ctx.ent.ports for u in self._units))):
            param_val_to_t = {}
            for port_variant, params in zip(port_variants, param_variants):
                assert port_variant.name == parent_port.name, (
                    port_variant.name, parent_port.name)
                t = port_variant._dtype
                assert len(params) == len(self._params), (params, self._params)
                params = params._asdict()
                for p in self._params:
                    p_val = params[p.hdl_name]
                    types = param_val_to_t.setdefault((p, p_val), UniqList())
                    types.append(t)

            res.append((parent_port, param_val_to_t))

        return res
Ejemplo n.º 16
0
def reduceUselessAssignments(root: LNode):
    """
    Remove assignments if it is only a direct connection and can be replaced with direct link
    """
    for n in root.children:
        if n.children:
            reduceUselessAssignments(n)

    to_remove = set()
    for n in root.children:
        if isinstance(n.originObj, HdlAssignmentContainer)\
                and not n.originObj.indexes\
                and len(n.west) == 1:
            src = n.originObj.src
            if isinstance(src, RtlSignalBase) and src.hidden:
                continue

            to_remove.add(n)

            srcPorts = []
            dstPorts = []
            edgesToRemove = UniqList()

            inP = getSinglePort(n.west)
            outP = getSinglePort(n.east)
            for e in inP.incomingEdges:
                sPort = e.src
                srcPorts.append((sPort, e.originObj))
                edgesToRemove.append(e)

            for e in outP.outgoingEdges:
                dPort = e.dst
                dstPorts.append(dPort)
                edgesToRemove.append(e)

            for e in edgesToRemove:
                e.remove()

            for srcPort, originObj in srcPorts:
                for dstPort in dstPorts:
                    root.addEdge(srcPort, dstPort, originObj=originObj)

    if to_remove:
        root.children = [n for n in root.children if n not in to_remove]
Ejemplo n.º 17
0
def portTryReduce(root: LNode, port: LPort):
    """
    Check if majority of children is connected to same port
    if it is the case reduce children and connect this port instead children

    :note: use reduceUselessAssignments, extractSplits, flattenTrees before this function
        to maximize it's effect
    """
    if not port.children:
        return

    for p in port.children:
        portTryReduce(root, p)

    target_nodes = {}
    ch_cnt = countDirectlyConnected(port, target_nodes)
    if not target_nodes:
        # disconnected port
        return

    new_target, children_edge_to_destroy = max(target_nodes.items(),
                                               key=lambda x: len(x[1]))
    if port.direction == new_target.direction:
        return
        # , (port, new_target, children_edge_to_destroy)
    cnt = len(children_edge_to_destroy)
    if cnt < ch_cnt / 2 or cnt == 1 and ch_cnt == 2:
        # too small or few shared connection to reduce
        return

    children_to_destroy = UniqList()
    on_target_children_to_destroy = UniqList()
    for child, edge in children_edge_to_destroy:
        if child.direction == PortType.OUTPUT:
            target_ch = edge.dsts
        elif child.direction == PortType.INPUT:
            target_ch = edge.srcs
        else:
            raise ValueError(child.direction)
        if len(target_ch) != 1:
            raise NotImplementedError("multiple connected nodes", target_ch)
        target_ch = target_ch[0]

        assert target_ch.parent is new_target, (target_ch, target_ch.parent,
                                                new_target, 'Wrong target:\n',
                                                edge.src, "\n", edge.dst, "\n",
                                                target_ch.parent, "\n",
                                                new_target)

        if child.direction == PortType.OUTPUT:
            edge.removeTarget(target_ch)
        elif child.direction == PortType.INPUT:
            edge.removeTarget(child)

        if not edge.srcs or not edge.dsts:
            edge.remove()

        if not target_ch.incomingEdges and not target_ch.outgoingEdges:
            # disconnect selected children from this port and target
            on_target_children_to_destroy.append(target_ch)

        if not child.incomingEdges and not child.outgoingEdges:
            children_to_destroy.append(child)

    for p in chain(children_to_destroy, on_target_children_to_destroy):
        p.connectedAsParent = True

    # destroy children of new target and this port if possible
    # port.children = [
    #    ch for ch in port.children if ch not in children_to_destroy]
    # new_target.children = [
    #    ch for ch in new_target.children if ch not in on_target_children_to_destroy]

    # if the port does have some sub ports which are an exceptions
    # from main port connection we have to add them
    # merge_non_reduced_ports(port, children_to_destroy)
    # merge_non_reduced_ports(new_target, on_target_children_to_destroy)

    # connect this port to new target as it was connected by children before
    # [TODO] names for new edges
    if port.direction == PortType.OUTPUT:
        root.addEdge(port, new_target)
    elif port.direction == PortType.INPUT:
        root.addEdge(new_target, port)
    else:
        raise NotImplementedError(port.direction)
Ejemplo n.º 18
0
class HdlSimulator():
    """
    Circuit simulator with support for external agents

    .. note:: *Signals without driver, constant driver, initial value*
        Every signal is initialized at start with its default value

    .. note:: *Communication between processes*
        For each output for every process there is an IoContainer instance
        which is container container of updates to signals.
        Every process has (generated) sensitivity-list.
        Process is reevaluated when there is a new value on any signal
        from sensitivity list.

    .. note: *Delta steps*
        Delta step is minimum quantum of changes in simulation, on the begining
        of delta step all reads are performed and on the end all writes
        are performed. Writes are causing revalution of HWprocesses
        which are schedueled into next delta step.
        Delta steps does not update time.
        When there is no process to reevaluate that means thereis nothing to do
        in delta step this delta step is considered as last in this time
        and time is shifted on begining of next event by simulator.

    .. note:: *Simulation inputs*
        HWprocess can not contain any blocking statement
        Simulation processes are written in python and can contain anything
        including blocking statements realized by yield sim.wait(time).
        (Using hdl as main simulator driver is not efficient.)

    .. note::
        HWprocesses have lower priority than simulation processes
        this allows simplify logic of agents.
        When simulation process is executed, HW part did not anything
        in this time, Simulation process can prepare anything for HW part
        (= can write) if simulation process need to read, it has to yield
        simulator.updateComplete event first, process then will be wakened
        after reaction of HW in this time:
        agents are greatly simplified, they just need to yield
        simulator.waitOnCombUpdate() before first read
        and then can not write in this time. (Agent can be realized by multiple
        simulation processes.)

    :ivar now: actual simulation time
    :ivar _combUpdateDonePlaned: flag, True if event for combinational
        update is planed, this event is triggered
        when there are not any combinational signal updates in this time
    :ivar _applyValPlaned: flag, True if there is planed event for write
        stashed signal updates to signals
    :ivar _runSeqProcessesPlaned: flag, True if there is planed event for
        running sequential (rising/falling event) dependent processes to reevaluate
    :ivar _valuesToApply: is container of values
        which should be applied in this delta step
    :ivar _combProcsToRun: list of hdl processes to run
    :ivar _seqProcsToRun: list of rising/falling event dependent processes
        which should be evaluated after all combinational changes are applied
    :ivar _outputContainers: dictionary {SimSignal:IoContainer} for each hdl process
    :ivar _events: heap of simulation events and processes
    """

    wait = Wait

    def __init__(self, config=None):
        super(HdlSimulator, self).__init__()
        if config is None:
            # default config
            config = HdlSimConfig()

        self.config = config
        self.now = 0.0

        self._combUpdateDonePlaned = False
        self._applyValPlaned = False
        self._runSeqProcessesPlaned = False

        # (signal, value) tuples which should be applied before
        # new round of processes
        #  will be executed
        self._valuesToApply = []
        self._seqProcsToRun = UniqList()
        self._combProcsToRun = UniqList()
        # container of outputs for every process
        self._outputContainers = {}
        self._events = SimCalendar()

    @internal
    def _add_process(self, proc, priority) -> None:
        """
        Schedule process on actual time with specified priority
        """
        self._events.push(self.now, priority, proc)

    def waitOnCombUpdate(self) -> Event:
        """
        Sim processes can wait on combUpdateDone by:
        yield sim.waitOnCombUpdate()

        Sim process is then woken up when all combinational updates
        are done in this delta step
        """
        if not self._combUpdateDonePlaned:
            return self._scheduleCombUpdateDoneEv()
        else:
            return self.combUpdateDoneEv

    @internal
    def _addHdlProcToRun(self, trigger: SimSignal, proc) -> None:
        """
        Add hdl process to execution queue

        :param trigger: instance of SimSignal
        :param proc: python generator function representing HDL process
        """
        # first process in time has to plan executing of apply values on the
        # end of this time
        if not self._applyValPlaned:
            # (apply on end of this time to minimalize process reevaluation)
            self._scheduleApplyValues()

        if isEvDependentOn(trigger, proc):
            if self.now == 0:
                return  # pass event dependent on startup
            self._seqProcsToRun.append(proc)
        else:
            self._combProcsToRun.append(proc)

    @internal
    def _initUnitSignals(self, unit: Unit) -> None:
        """
        * Inject default values to simulation

        * Instantiate IOs for every process
        """
        # set initial value to all signals and propagate it
        for s in unit._ctx.signals:
            if s.defVal.vldMask:
                v = s.defVal.clone()
                s.simUpdateVal(self, mkUpdater(v, False))

        for u in unit._units:
            self._initUnitSignals(u)

        for p in unit._processes:
            self._addHdlProcToRun(None, p)

        for p, outputs in unit._outputs.items():
            # name has to be explicit because it may be possible that signal
            # with has this name was replaced by signal from parent/child
            containerNames = list(map(lambda x: x[0], outputs))

            class SpecificIoContainer(IoContainer):
                __slots__ = containerNames

            self._outputContainers[p] = SpecificIoContainer(outputs)

    @internal
    def __deleteCombUpdateDoneEv(self) -> Generator[None, None, None]:
        """
        Callback called on combUpdateDoneEv finished
        """
        self._combUpdateDonePlaned = False
        return
        yield

    @internal
    def _scheduleCombUpdateDoneEv(self) -> Event:
        """
        Schedule combUpdateDoneEv event to let agents know that current
        delta step is ending and values from combinational logic are stable
        """
        assert not self._combUpdateDonePlaned, self.now
        cud = Event(self)
        cud.process_to_wake.append(self.__deleteCombUpdateDoneEv())
        self._add_process(cud, PRIORITY_AGENTS_UPDATE_DONE)
        self._combUpdateDonePlaned = True
        self.combUpdateDoneEv = cud
        return cud

    @internal
    def _scheduleApplyValues(self) -> None:
        """
        Apply stashed values to signals
        """
        assert not self._applyValPlaned, self.now
        self._add_process(self._applyValues(), PRIORITY_APPLY_COMB)
        self._applyValPlaned = True

        if self._runSeqProcessesPlaned:
            # if runSeqProcesses is already scheduled
            return

        assert not self._seqProcsToRun and not self._runSeqProcessesPlaned, self.now
        self._add_process(self._runSeqProcesses(), PRIORITY_APPLY_SEQ)
        self._runSeqProcessesPlaned = True

    @internal
    def _conflictResolveStrategy(self, newValue: set)\
            -> Tuple[Callable[[Value], bool], bool]:
        """
        This functions resolves write conflicts for signal

        :param actionSet: set of actions made by process
        """

        invalidate = False
        resLen = len(newValue)
        if resLen == 3:
            # update for item in array
            val, indexes, isEvDependent = newValue
            return (mkArrayUpdater(val, indexes, invalidate), isEvDependent)
        else:
            # update for simple signal
            val, isEvDependent = newValue
            return (mkUpdater(val, invalidate), isEvDependent)

    @internal
    def _runCombProcesses(self) -> None:
        """
        Delta step for combinational processes
        """
        for proc in self._combProcsToRun:
            cont = self._outputContainers[proc]
            proc(self, cont)
            for sigName, sig in cont._all_signals:
                newVal = getattr(cont, sigName)
                if newVal is not None:
                    res = self._conflictResolveStrategy(newVal)
                    # prepare update
                    updater, isEvDependent = res
                    self._valuesToApply.append(
                        (sig, updater, isEvDependent, proc))
                    setattr(cont, sigName, None)
                    # else value is latched

        self._combProcsToRun = UniqList()

    @internal
    def _runSeqProcesses(self) -> Generator[None, None, None]:
        """
        Delta step for event dependent processes
        """
        updates = []
        for proc in self._seqProcsToRun:
            try:
                outContainer = self._outputContainers[proc]
            except KeyError:
                # processes does not have to have outputs
                outContainer = None

            proc(self, outContainer)

            if outContainer is not None:
                updates.append(outContainer)

        self._seqProcsToRun = UniqList()
        self._runSeqProcessesPlaned = False

        for cont in updates:
            for sigName, sig in cont._all_signals:
                newVal = getattr(cont, sigName)
                if newVal is not None:
                    v = self._conflictResolveStrategy(newVal)
                    updater, _ = v
                    sig.simUpdateVal(self, updater)
                    setattr(cont, sigName, None)
        return
        yield

    @internal
    def _applyValues(self) -> Generator[None, None, None]:
        """
        Perform delta step by writing stacked values to signals
        """
        va = self._valuesToApply
        self._applyValPlaned = False

        # log if there are items to log
        lav = self.config.logApplyingValues
        if va and lav:
            lav(self, va)
        self._valuesToApply = []

        # apply values to signals, values can overwrite each other
        # but each signal should be driven by only one process and
        # it should resolve value collision
        addSp = self._seqProcsToRun.append
        for s, vUpdater, isEventDependent, comesFrom in va:
            if isEventDependent:
                # now=0 and this was process initialization or async reg
                addSp(comesFrom)
            else:
                # regular combinational process
                s.simUpdateVal(self, vUpdater)

        self._runCombProcesses()

        # processes triggered from simUpdateVal can add new values
        if self._valuesToApply and not self._applyValPlaned:
            self._scheduleApplyValues()

        return
        yield

    def read(self, sig) -> Value:
        """
        Read value from signal or interface
        """
        try:
            v = sig._val
        except AttributeError:
            v = sig._sigInside._val

        return v.clone()

    def write(self, val, sig: SimSignal) -> None:
        """
        Write value to signal or interface.
        """
        # get target RtlSignal
        try:
            simSensProcs = sig.simSensProcs
        except AttributeError:
            sig = sig._sigInside
            simSensProcs = sig.simSensProcs

        # type cast of input value
        t = sig._dtype

        if isinstance(val, Value):
            v = val.clone()
            v = v._auto_cast(t)
        else:
            v = t.fromPy(val)

        # can not update value in signal directly due singnal proxies
        sig.simUpdateVal(self, lambda curentV:
                         (valueHasChanged(curentV, v), v))

        if not self._applyValPlaned:
            if not (simSensProcs or sig.simRisingSensProcs
                    or sig.simFallingSensProcs):
                # signal value was changed but there are no sensitive processes
                # to it because of this _applyValues is never planed
                # and should be
                self._scheduleApplyValues()
            elif (sig._writeCallbacks or sig._writeCallbacksToEn):
                # signal write did not caused any change on any other signal
                # but there are still simulation agets waiting on
                # updateComplete event
                self._scheduleApplyValues()

    def run(self, until: float) -> None:
        """
        Run simulation until specified time
        :note: can be used to run simulation again after it ends from time when it ends
        """
        assert until > self.now
        events = self._events
        schedule = events.push
        next_event = events.pop

        # add handle to stop simulation
        schedule(until, PRIORITY_URGENT, raise_StopSimulation(self))

        try:
            # for all events
            while True:
                nextTime, priority, process = next_event()
                self.now = nextTime
                # process is python generator or Event
                if isinstance(process, Event):
                    process = iter(process)

                # run process or activate processes dependent on Event
                while True:
                    try:
                        ev = next(process)
                    except StopIteration:
                        break

                    # if process requires waiting put it back in queue
                    if isinstance(ev, Wait):
                        # nextTime is self.now
                        schedule(nextTime + ev.time, priority, process)
                        break
                    elif isinstance(ev, Event):
                        # process going to wait for event
                        # if ev.process_to_wake is None event was already
                        # destroyed
                        ev.process_to_wake.append(process)
                        break
                    else:
                        # else this process spoted new process
                        # and it has to be put in queue
                        schedule(nextTime, priority, ev)

        except StopSimumulation:
            return

    def add_process(self, proc) -> None:
        """
        Add process to events with default priority on current time
        """
        self._events.push(self.now, PRIORITY_NORMAL, proc)

    def simUnit(self, synthesisedUnit: Unit, until: float, extraProcesses=[]):
        """
        Run simulation for Unit instance
        """
        beforeSim = self.config.beforeSim
        if beforeSim is not None:
            beforeSim(self, synthesisedUnit)

        add_proc = self.add_process
        for p in extraProcesses:
            add_proc(p(self))

        self._initUnitSignals(synthesisedUnit)
        self.run(until)
Ejemplo n.º 19
0
class HdlStatement(HdlObject):
    """
    :ivar _is_completly_event_dependent: statement does not have
         any combinational statement
    :ivar _now_is_event_dependent: statement is event (clk) dependent
    :ivar parentStm: parent instance of HdlStatement or None
    :ivar _inputs: UniqList of input signals for this statement
    :ivar _outputs: UniqList of output signals for this statement
    :ivar _sensitivity: UniqList of input signals
        or (rising/falling) operator
    :ivar _enclosed_for: set of outputs for which this statement is enclosed
        (for which there is not any unused branch)
    :ivar rank: number of used branches in statement, used as prefilter
        for statement comparing
    """
    def __init__(self,
                 parentStm=None,
                 sensitivity=None,
                 is_completly_event_dependent=False):
        self._is_completly_event_dependent = is_completly_event_dependent
        self._now_is_event_dependent = is_completly_event_dependent
        self.parentStm = parentStm
        self._inputs = UniqList()
        self._outputs = UniqList()
        self._enclosed_for = None

        self._sensitivity = sensitivity
        self.rank = 0

    @internal
    def _clean_signal_meta(self):
        """
        Clean informations about enclosure for outputs and sensitivity
        of this statement
        """
        self._enclosed_for = None
        self._sensitivity = None
        for stm in self._iter_stms():
            stm._clean_signal_meta()

    @internal
    def _collect_io(self) -> None:
        """
        Collect inputs/outputs from all child statements
        to :py:attr:`~_input` / :py:attr:`_output` attribute on this object
        """
        in_add = self._inputs.extend
        out_add = self._outputs.extend

        for stm in self._iter_stms():
            in_add(stm._inputs)
            out_add(stm._outputs)

    @internal
    @staticmethod
    def _cut_off_drivers_of_list(sig: RtlSignalBase,
                                 statements: List["HdlStatement"],
                                 keep_mask: List[bool],
                                 new_statements: List["HdlStatement"]):
        """
        Cut all logic from statements which drives signal sig.

        :param sig: signal which drivers should be removed
        :param statements: list of statements to filter
        :param keep_mask: list of flags if True statements was driver only of sig
        :param new_statements: output list of filtered statements

        :return: True if all input statements were reduced
        """
        all_cut_off = True
        for stm in statements:
            newStm = stm._cut_off_drivers_of(sig)
            keep = True
            if newStm is None:
                # statement is des not have drivers of sig
                all_cut_off = False
            elif newStm is stm:
                # statement drives only sig
                keep = False
                new_statements.append(newStm)
            else:
                # statement was splited on multiple statements
                all_cut_off = False
                new_statements.append(newStm)

            keep_mask.append(keep)

        return all_cut_off

    @internal
    def _discover_enclosure(self) -> None:
        """
        Discover all outputs for which is this statement enclosed _enclosed_for property
        (has driver in all code branches)
        """
        raise NotImplementedError(
            "This method should be implemented"
            " on class of statement", self.__class__, self)

    @internal
    @staticmethod
    def _discover_enclosure_for_statements(statements: List['HdlStatement'],
                                           outputs: List['RtlSignalBase']):
        """
        Discover enclosure for list of statements

        :param statements: list of statements in one code branch
        :param outputs: list of outputs which should be driven from this statement list
        :return: set of signals for which this statement list have always some driver
            (is enclosed)
        """
        result = set()
        if not statements:
            return result

        for stm in statements:
            stm._discover_enclosure()

        for o in outputs:
            has_driver = False

            for stm in statements:
                if o in stm._outputs:
                    assert not has_driver
                    has_driver = False
                    if o in stm._enclosed_for:
                        result.add(o)
                else:
                    pass

        return result

    @internal
    def _discover_sensitivity(self, seen: set) -> None:
        """
        discover all sensitivity signals and store them to _sensitivity property
        """
        raise NotImplementedError(
            "This method should be implemented"
            " on class of statement", self.__class__, self)

    @internal
    def _discover_sensitivity_sig(self, signal: RtlSignalBase, seen: set,
                                  ctx: SensitivityCtx):
        casualSensitivity = set()
        signal._walk_sensitivity(casualSensitivity, seen, ctx)
        if not ctx.contains_ev_dependency:
            # if event dependent sensitivity found do not add other sensitivity
            ctx.extend(casualSensitivity)

    @internal
    def _discover_sensitivity_seq(self,
                                  signals: List[RtlSignalBase],
                                  seen: set, ctx: SensitivityCtx)\
            -> None:
        """
        Discover sensitivity for list of signals

        """
        casualSensitivity = set()
        for s in signals:
            s._walk_sensitivity(casualSensitivity, seen, ctx)
            if ctx.contains_ev_dependency:
                break

        # if event dependent sensitivity found do not add other sensitivity
        if not ctx.contains_ev_dependency:
            ctx.extend(casualSensitivity)

    @internal
    def _get_rtl_context(self):
        """
        get RtlNetlist context from signals
        """
        for sig in chain(self._inputs, self._outputs):
            if sig.ctx:
                return sig.ctx
            else:
                # Param instances does not have context
                continue
        raise HwtSyntaxError(
            "Statement does not have any signal in any context", self)

    def _iter_stms(self):
        """
        :return: iterator over all children statements
        """
        raise NotImplementedError(
            "This method should be implemented"
            " on class of statement", self.__class__, self)

    @internal
    def _on_reduce(self, self_reduced: bool, io_changed: bool,
                   result_statements: List["HdlStatement"]) -> None:
        """
        Update signal IO after reduce attempt

        :param self_reduced: if True this object was reduced
        :param io_changed: if True IO of this object may changed
            and has to be updated
        :param result_statements: list of statements which are result
            of reduce operation on this statement
        """

        parentStm = self.parentStm
        if self_reduced:
            was_top = parentStm is None
            # update signal drivers/endpoints
            if was_top:
                # disconnect self from signals
                ctx = self._get_rtl_context()
                ctx.statements.remove(self)
                ctx.statements.update(result_statements)

                for i in self._inputs:
                    i.endpoints.discard(self)
                for o in self._outputs:
                    o.drivers.remove(self)

            for stm in result_statements:
                stm.parentStm = parentStm
                if parentStm is None:
                    # connect signals to child statements
                    for inp in stm._inputs:
                        inp.endpoints.append(stm)
                    for outp in stm._outputs:
                        outp.drivers.append(stm)
        else:
            # parent has to update it's inputs/outputs
            if io_changed:
                self._inputs = UniqList()
                self._outputs = UniqList()
                self._collect_io()

    @internal
    def _on_merge(self, other):
        """
        After merging statements update IO, sensitivity and context

        :attention: rank is not updated
        """
        self._inputs.extend(other._inputs)
        self._outputs.extend(other._outputs)

        if self._sensitivity is not None:
            self._sensitivity.extend(other._sensitivity)
        else:
            assert other._sensitivity is None

        if self._enclosed_for is not None:
            self._enclosed_for.update(other._enclosed_for)
        else:
            assert other._enclosed_for is None

        other_was_top = other.parentStm is None
        if other_was_top:
            other._get_rtl_context().statements.remove(other)
            for s in other._inputs:
                s.endpoints.discard(other)
                s.endpoints.append(self)

            for s in other._outputs:
                s.drivers.discard(other)
                s.drivers.append(self)

    @internal
    def _try_reduce(self) -> Tuple[List["HdlStatement"], bool]:
        raise NotImplementedError(
            "This method should be implemented"
            " on class of statement", self.__class__, self)

    def _is_enclosed(self) -> bool:
        """
        :return: True if every branch in statement is covered for all signals else False
        """
        return len(self._outputs) == len(self._enclosed_for)

    @internal
    def _is_mergable(self, other: "HdlStatement") -> bool:
        if self is other:
            raise ValueError("Can not merge statement with itself")
        else:
            raise NotImplementedError(
                "This method should be implemented"
                " on class of statement", self.__class__, self)

    @internal
    @classmethod
    def _is_mergable_statement_list(cls, stmsA, stmsB):
        """
        Walk statements and compare if they can be merged into one statement list
        """
        if stmsA is None and stmsB is None:
            return True

        elif stmsA is None or stmsB is None:
            return False

        a_it = iter(stmsA)
        b_it = iter(stmsB)

        a = _get_stm_with_branches(a_it)
        b = _get_stm_with_branches(b_it)
        while a is not None or b is not None:
            if a is None or b is None or not a._is_mergable(b):
                return False

            a = _get_stm_with_branches(a_it)
            b = _get_stm_with_branches(b_it)

        # lists are empty
        return True

    @internal
    @staticmethod
    def _merge_statements(statements: List["HdlStatement"])\
            -> Tuple[List["HdlStatement"], int]:
        """
        Merge statements in list to remove duplicated if-then-else trees

        :return: tuple (list of merged statements, rank decrease due merging)
        :note: rank decrease is sum of ranks of reduced statements
        :attention: statement list has to me mergable
        """
        order = {}
        for i, stm in enumerate(statements):
            order[stm] = i

        new_statements = []
        rank_decrease = 0

        for rank, stms in groupedby(statements, lambda s: s.rank):
            if rank == 0:
                new_statements.extend(stms)
            else:
                if len(stms) == 1:
                    new_statements.extend(stms)
                    continue

                # try to merge statements if they are same condition tree
                for iA, stmA in enumerate(stms):
                    if stmA is None:
                        continue

                    for iB, stmB in enumerate(islice(stms, iA + 1, None)):
                        if stmB is None:
                            continue

                        if stmA._is_mergable(stmB):
                            rank_decrease += stmB.rank
                            stmA._merge_with_other_stm(stmB)
                            stms[iA + 1 + iB] = None

                    new_statements.append(stmA)

        new_statements.sort(key=lambda stm: order[stm])
        return new_statements, rank_decrease

    @internal
    @staticmethod
    def _merge_statement_lists(stmsA: List["HdlStatement"], stmsB: List["HdlStatement"])\
            -> List["HdlStatement"]:
        """
        Merge two lists of statements into one

        :return: list of merged statements
        """
        if stmsA is None and stmsB is None:
            return None

        tmp = []

        a_it = iter(stmsA)
        b_it = iter(stmsB)

        a = None
        b = None
        a_empty = False
        b_empty = False

        while not a_empty and not b_empty:
            while not a_empty:
                a = next(a_it, None)
                if a is None:
                    a_empty = True
                    break
                elif a.rank == 0:
                    # simple statement does not require merging
                    tmp.append(a)
                    a = None
                else:
                    break

            while not b_empty:
                b = next(b_it, None)
                if b is None:
                    b_empty = True
                    break
                elif b.rank == 0:
                    # simple statement does not require merging
                    tmp.append(b)
                    b = None
                else:
                    break

            if a is not None or b is not None:
                if b is None:
                    a = b
                    b = None

                if a is not None and b is not None:
                    a._merge_with_other_stm(b)

                tmp.append(a)
                a = None
                b = None

        return tmp

    @internal
    @staticmethod
    def _try_reduce_list(statements: List["HdlStatement"]):
        """
        Simplify statements in the list
        """
        io_change = False
        new_statements = []

        for stm in statements:
            reduced, _io_change = stm._try_reduce()
            new_statements.extend(reduced)
            io_change |= _io_change

        new_statements, rank_decrease = HdlStatement._merge_statements(
            new_statements)

        return new_statements, rank_decrease, io_change

    @internal
    def _on_parent_event_dependent(self):
        """
        After parent statement become event dependent
        propagate event dependency flag to child statements
        """
        if not self._is_completly_event_dependent:
            self._is_completly_event_dependent = True
            for stm in self._iter_stms():
                stm._on_parent_event_dependent()

    @internal
    def _set_parent_stm(self, parentStm: "HdlStatement"):
        """
        Assign parent statement and propagate dependency flags if necessary
        """
        was_top = self.parentStm is None
        self.parentStm = parentStm
        if not self._now_is_event_dependent\
                and parentStm._now_is_event_dependent:
            self._on_parent_event_dependent()

        topStatement = parentStm
        while topStatement.parentStm is not None:
            topStatement = topStatement.parentStm

        parent_out_add = topStatement._outputs.append
        parent_in_add = topStatement._inputs.append

        if was_top:
            for inp in self._inputs:
                inp.endpoints.discard(self)
                inp.endpoints.append(topStatement)
                parent_in_add(inp)

            for outp in self._outputs:
                outp.drivers.discard(self)
                outp.drivers.append(topStatement)
                parent_out_add(outp)

            ctx = self._get_rtl_context()
            ctx.statements.discard(self)

        parentStm.rank += self.rank

    @internal
    def _register_stements(self, statements: List["HdlStatement"],
                           target: List["HdlStatement"]):
        """
        Append statements to this container under conditions specified
        by condSet
        """
        for stm in flatten(statements):
            assert stm.parentStm is None, stm
            stm._set_parent_stm(self)
            target.append(stm)

    def isSame(self, other: "HdlStatement") -> bool:
        """
        :return: True if other has same meaning as self
        """
        raise NotImplementedError(
            "This method should be implemented"
            " on class of statement", self.__class__, self)

    @internal
    def _destroy(self):
        """
        Disconnect this statement from signals and delete it from RtlNetlist context

        :attention: signal endpoints/drivers will be altered
            that means they can not be used for iteration
        """
        ctx = self._get_rtl_context()
        for i in self._inputs:
            i.endpoints.discard(self)

        for o in self._outputs:
            o.drivers.remove(self)

        ctx.statements.remove(self)

    @internal
    def _replace_input(self, toReplace: RtlSignalBase,
                       replacement: RtlSignalBase) -> None:
        """
        Replace input signal with another

        :note: sensitivity/endoints are actualized
        """
        raise NotImplementedError()

    @internal
    def _replace_input_update_sensitivity_and_enclosure(
            self, toReplace: RtlSignalBase, replacement: RtlSignalBase):
        if self._sensitivity is not None:
            if self._sensitivity.discard(toReplace):
                self._sensitivity.append(replacement)

        if self._enclosed_for is not None:
            if self._enclosed_for.discard(toReplace):
                self._enclosed_for.append(replacement)
Ejemplo n.º 20
0
 def extend(self, items):
     UniqList.extend(self, items)
     if isinstance(items, SensitivityCtx):
         self.contains_ev_dependency |= items.contains_ev_dependency
Ejemplo n.º 21
0
 def clear(self):
     UniqList.clear(self)
     self.contains_ev_dependency = False
Ejemplo n.º 22
0
def _statements_to_HWProcesses(_statements, tryToSolveCombLoops)\
        -> Generator[HWProcess, None, None]:
    assert _statements
    # try to simplify statements
    proc_statements = []
    for _stm in _statements:
        stms, _ = _stm._try_reduce()
        proc_statements.extend(stms)

    outputs = UniqList()
    _inputs = UniqList()
    sensitivity = UniqList()
    enclosed_for = set()
    for _stm in proc_statements:
        seen = set()
        _stm._discover_sensitivity(seen)
        _stm._discover_enclosure()
        outputs.extend(_stm._outputs)
        _inputs.extend(_stm._inputs)
        sensitivity.extend(_stm._sensitivity)
        enclosed_for.update(_stm._enclosed_for)

    enclosure_values = {}
    for sig in outputs:
        # inject nopVal if needed
        if sig._useNopVal:
            n = sig._nopVal
            enclosure_values[sig] = n

    if enclosure_values:
        do_enclose_for = list(where(outputs,
                                    lambda o: o in enclosure_values))
        fill_stm_list_with_enclosure(None, enclosed_for, proc_statements,
                                     do_enclose_for, enclosure_values)

    if proc_statements:
        for o in outputs:
            assert not o.hidden, o
        seen = set()
        inputs = UniqList()
        for i in _inputs:
            inputs.extend(i._walk_public_drivers(seen))

        intersect = outputs.intersection_set(sensitivity)
        if intersect:
            if not tryToSolveCombLoops:
                raise HwtSyntaxError(
                    "Combinational loop on signal(s)", intersect)

            # try to solve combinational loops by separating drivers of signals
            # from statements
            for sig in intersect:
                proc_statements, proc_stms_select = cut_off_drivers_of(
                    sig, proc_statements)
                yield from _statements_to_HWProcesses(proc_stms_select, False)

            if proc_statements:
                yield from _statements_to_HWProcesses(proc_statements, False)
        else:
            name = name_for_process(outputs)
            yield HWProcess("assig_process_" + name,
                            proc_statements, sensitivity,
                            inputs, outputs)
    else:
        assert not outputs
        # this can happen e.g. when If does not contains any Assignment
        pass
Ejemplo n.º 23
0
 def __init__(self, initSeq=None):
     UniqList.__init__(self, initSeq=initSeq)
     self.contains_ev_dependency = False
Ejemplo n.º 24
0
Archivo: utils.py Proyecto: mgielda/hwt
def toRtl(unitOrCls: Unit, name: str=None,
          serializer: GenericSerializer=VhdlSerializer,
          targetPlatform=DummyPlatform(), saveTo: str=None):
    """
    Convert unit to RTL using specified serializer

    :param unitOrCls: unit instance or class, which should be converted
    :param name: name override of top unit (if is None name is derived
        form class name)
    :param serializer: serializer which should be used for to RTL conversion
    :param targetPlatform: metainformatins about target platform, distributed
        on every unit under _targetPlatform attribute
        before Unit._impl() is called
    :param saveTo: directory where files should be stored
        If None RTL is returned as string.
    :raturn: if saveTo returns RTL string else returns list of file names
        which were created
    """
    if not isinstance(unitOrCls, Unit):
        u = unitOrCls()
    else:
        u = unitOrCls

    u._loadDeclarations()
    if name is not None:
        assert isinstance(name, str)
        u._name = name

    globScope = serializer.getBaseNameScope()
    mouduleScopes = {}

    # unitCls : unitobj
    serializedClasses = {}

    # (unitCls, paramsValues) : unitObj
    # where paramsValues are dict name:value
    serializedConfiguredUnits = {}

    doSerialize = True

    createFiles = saveTo is not None
    if createFiles:
        os.makedirs(saveTo, exist_ok=True)
        files = UniqList()
    else:
        codeBuff = []

    for obj in u._toRtl(targetPlatform):
        doSerialize = serializer.serializationDecision(
            obj,
            serializedClasses,
            serializedConfiguredUnits)
        if doSerialize:
            if isinstance(obj, Entity):
                s = globScope.fork(1)
                s.setLevel(2)
                ctx = serializer.getBaseContext()
                ctx.scope = s
                mouduleScopes[obj] = ctx
                ctx.currentUnit = obj.origin

                sc = serializer.Entity(obj, ctx)
                if createFiles:
                    fName = obj.name + serializer.fileExtension
                    fileMode = 'w'

            elif isinstance(obj, Architecture):
                try:
                    ctx = mouduleScopes[obj.entity]
                except KeyError:
                    raise SerializerException(
                        "Entity should be serialized"
                        " before architecture of %s"
                        % (obj.getEntityName()))

                sc = serializer.Architecture(obj, ctx)
                if createFiles:
                    fName = obj.getEntityName() + serializer.fileExtension
                    fileMode = 'a'
            else:
                if hasattr(obj, "_hdlSources"):
                    for fn in obj._hdlSources:
                        if isinstance(fn, str):
                            shutil.copy2(fn, saveTo)
                            files.append(fn)
                            continue
                else:
                    sc = serializer.asHdl(obj)

            if sc:
                if createFiles:
                    fp = os.path.join(saveTo, fName)
                    files.append(fp)
                    with open(fp, fileMode) as f:
                        if fileMode == 'a':
                            f.write("\n")

                        f.write(
                            serializer.formatter(sc)
                        )
                else:
                    codeBuff.append(sc)

        elif not createFiles:
            try:
                name = '"%s"' % obj.name
            except AttributeError:
                name = ""
            codeBuff.append(serializer.comment(
                "Object of class %s, %s was not serialized as specified" % (
                    obj.__class__.__name__, name)))

    if createFiles:
        return files
    else:
        return serializer.formatter(
            "\n".join(codeBuff)
        )
Ejemplo n.º 25
0
def _statements_to_HdlStatementBlocks(_statements, tryToSolveCombLoops)\
        -> Generator[HdlStatementBlock, None, None]:
    assert _statements
    # try to simplify statements
    proc_statements = []
    for _stm in _statements:
        _stm._clean_signal_meta()
        stms, _ = _stm._try_reduce()
        proc_statements.extend(stms)

    if not proc_statements:
        return

    outputs = UniqList()
    _inputs = UniqList()
    sensitivity = UniqList()
    enclosed_for = set()
    _proc_statements = []
    for _stm in proc_statements:
        seen = set()
        _stm._discover_sensitivity(seen)
        _stm._discover_enclosure()
        if _stm._outputs:
            # remove a statement entirely if it has no ouput
            # (empty if statment or something similar)
            # simulation only processes should not be processed by this function
            # and process should always drive something, unless it is useless
            outputs.extend(_stm._outputs)
            _inputs.extend(_stm._inputs)
            sensitivity.extend(_stm._sensitivity)
            enclosed_for.update(_stm._enclosed_for)
            _proc_statements.append(_stm)

    proc_statements = _proc_statements
    if not proc_statements:
        # this can happen e.g. when If does not contains any Assignment
        return
    sensitivity_recompute = False
    enclosure_recompute = False
    enclosure_values = {}
    for sig in outputs:
        # inject nop_val if needed
        if sig._nop_val is not NO_NOPVAL and sig not in enclosed_for:
            enclosure_recompute = True
            n = sig._nop_val
            enclosure_values[sig] = n
            if not isinstance(n, Value):
                _inputs.append(n)
                sensitivity_recompute = True

    if enclosure_recompute:
        # we have some enclosure values, try fill missing code branches with
        # this values
        do_enclose_for = [o for o in outputs if o in enclosure_values]
        fill_stm_list_with_enclosure(None, enclosed_for, proc_statements,
                                     do_enclose_for, enclosure_values)

    if enclosure_recompute or sensitivity_recompute:
        for _stm in proc_statements:
            _stm._clean_signal_meta()
            seen = set()
            _stm._discover_sensitivity(seen)
            _stm._discover_enclosure()

    if sensitivity_recompute:
        sensitivity = UniqList()
        for _stm in proc_statements:
            sensitivity.extend(_stm._sensitivity)

    for o in outputs:
        assert not o.hidden, o

    seen = set()
    inputs = UniqList()
    for i in _inputs:
        inputs.extend(i._walk_public_drivers(seen))

    intersect = outputs.intersection_set(sensitivity)
    if intersect:
        # there is a combinational loop inside a single process which
        # can not be solved by separation of statments in process
        if not tryToSolveCombLoops:
            raise HwtSyntaxError(
                "Combinational loop on signal(s)", intersect)

        # try to solve combinational loops by separating drivers of signals
        # from statements
        for sig in intersect:
            proc_statements, proc_stms_select = cut_off_drivers_of(
                sig, proc_statements)
            yield from _statements_to_HdlStatementBlocks(proc_stms_select, False)

        if proc_statements:
            yield from _statements_to_HdlStatementBlocks(proc_statements, False)
    else:
        # no combinational loops, wrap current statemetns to a process instance
        name = name_for_process(outputs)
        yield HdlStatementBlock("assig_process_" + name,
                                proc_statements, sensitivity,
                                inputs, outputs)
Ejemplo n.º 26
0
def extract_part_drivers_stm(stm: HdlStatement,
                             signal_parts: Dict[RtlSignal,
                                                List[Tuple[RtlSignal,
                                                           List[HValue]]]]):
    if isinstance(stm, Assignment):
        if stm.indexes and len(stm.indexes) == 1:
            dst = stm.dst
            parts = signal_parts.get(dst, None)
            if parts is None:
                return False
            indexes = _format_indexes(stm.indexes)
            new_dsts, do_remove_stm = parts[indexes]
            if isinstance(new_dsts, list):
                assert len(new_dsts) > 1, (dst, new_dsts, stm)
                assert not do_remove_stm, (dst, new_dsts, stm)
                # the driven slice was split to multiple sub slices
                replacement = []
                dst_offset = new_dsts[0][-1][1]
                for i in new_dsts:
                    new_dst = parts[i][0]
                    new_src = stm.src
                    for _i in i:
                        high, low = _i[0] - dst_offset, _i[1] - dst_offset
                        assert high > 0 and low >= 0, dst_offset
                        assert high > low, (dst, stm, (high, low))
                        new_src = new_src[high:low]
                    a = new_dst(new_src)
                    replacement.append(a)

                # it has to hav parent statement because it needs to be nested
                # because otherwise it would not have some overlapping parts driven diferently
                # inder some condition
                stm.parentStm._replace_child_statement(stm, replacement, False)
            elif do_remove_stm:
                # remove current assignment because we are using src directly
                assert stm.parentStm is None, stm
                stm._destroy()
            else:
                # rewrite the Assignment instance to use new dst
                replacement = [
                    new_dsts(stm.src),
                ]
                stm.parentStm._replace_child_statement(stm, replacement, False)

            return True

    elif isinstance(stm, (IfContainer, SwitchContainer, HdlStatementBlock)):
        modified = False
        for _stm in stm._iter_stms():
            modified |= extract_part_drivers_stm(_stm, signal_parts)
        if modified:
            assert not stm._enclosed_for, "_enclosed_for is expected not to be initialized yet"
            outputs = stm._outputs
            inputs = stm._inputs
            stm._outputs = UniqList()
            stm._inputs = UniqList()
            stm._collect_io()
            if stm.parentStm is None:
                for o in outputs:
                    if o not in stm._outputs:
                        o.drivers.remove(stm)

                for i in inputs:
                    if i not in stm._inputs:
                        i.endpoints.remove(stm)

            return True

    else:
        raise NotImplementedError("Unknown statement ", stm)

    return False
Ejemplo n.º 27
0
class Packager(object):
    """
    Ipcore packager
    """
    def __init__(self,
                 topUnit: Unit,
                 name: str = None,
                 extraVhdlFiles: List[str] = [],
                 extraVerilogFiles: List[str] = [],
                 serializer=VhdlSerializer,
                 targetPlatform=DummyPlatform()):
        assert not topUnit._wasSynthetised()
        self.topUnit = topUnit
        self.serializer = serializer
        self.targetPlatform = targetPlatform
        if name:
            self.name = name
        else:
            self.name = self.topUnit._getDefaultName()

        self.hdlFiles = UniqList()

        for f in extraVhdlFiles:
            self.hdlFiles.append(f)

        for f in extraVerilogFiles:
            self.hdlFiles.append(f)

    def saveHdlFiles(self, srcDir):
        path = os.path.join(srcDir, self.name)
        try:
            os.makedirs(path)
        except OSError:
            # wipe if exists
            shutil.rmtree(path)
            os.makedirs(path)

        files = self.hdlFiles
        self.hdlFiles = toRtl(self.topUnit,
                              saveTo=path,
                              name=self.name,
                              serializer=self.serializer,
                              targetPlatform=self.targetPlatform)

        for srcF in files:
            dst = os.path.join(
                path,
                os.path.relpath(srcF, srcDir).replace('../', ''))
            os.makedirs(os.path.dirname(dst), exist_ok=True)
            shutil.copy(srcF, dst)
            self.hdlFiles.append(dst)

    def mkAutoGui(self):
        gui = GuiBuilder()
        p0 = gui.page("Main")
        handlers = []
        for g in self.topUnit._entity.generics:
            p0.param(g.name)
            for fn in paramManipulatorFns(g.name):
                handlers.append(fn)

        with open(self.guiFile, "w") as f:
            f.write(gui.asTcl())
            for h in handlers:
                f.write('\n\n')
                f.write(str(h))

    def createPackage(self,
                      repoDir,
                      vendor="hwt",
                      library="mylib",
                      description=None):
        '''
        synthetise hdl if needed
        copy hdl files
        create gui file
        create component.xml
        '''
        ip_dir = os.path.join(repoDir, self.name + "/")
        if os.path.exists(ip_dir):
            shutil.rmtree(ip_dir)

        ip_srcPath = os.path.join(ip_dir, "src")
        tclPath = os.path.join(ip_dir, "xgui")
        guiFile = os.path.join(tclPath, "gui.tcl")
        for d in [ip_dir, ip_srcPath, tclPath]:
            os.makedirs(d)
        self.saveHdlFiles(ip_srcPath)

        self.guiFile = guiFile
        self.mkAutoGui()

        c = Component()
        c._files = [relpath(p, ip_dir) for p in sorted(self.hdlFiles)] + \
                   [relpath(guiFile, ip_dir)]

        c.vendor = vendor
        c.library = library
        if description is None:
            c.description = self.name + "_v" + c.version
        else:
            c.description = description

        c.asignTopUnit(self.topUnit)

        xml_str = prettify(c.xml())
        with open(ip_dir + "component.xml", "w") as f:
            f.write(xml_str)

        quartus_tcl_str = c.quartus_tcl()
        with open(ip_dir + "component_hw.tcl", "w") as f:
            f.write(quartus_tcl_str)
Ejemplo n.º 28
0
class HdlStatement(HdlObject):
    """
    :ivar ~._event_dependent_from_branch: index of code branch if statement is event (clk) dependent else None
    :ivar ~.parentStm: parent instance of HdlStatement or None
    :ivar ~._inputs: UniqList of input signals for this statement
    :ivar ~._outputs: UniqList of output signals for this statement
    :ivar ~._sensitivity: UniqList of input signals
        or (rising/falling) operator
    :ivar ~._enclosed_for: set of outputs for which this statement is enclosed
        (for which there is not any unused branch)
    :ivar ~.rank: number of used branches in statement, used as pre-filter
        for statement comparing
    """
    def __init__(self,
                 parentStm: Optional["HdlStatement"] = None,
                 sensitivity: Optional[UniqList] = None,
                 event_dependent_from_branch: Optional[int] = None):
        assert event_dependent_from_branch is None or isinstance(
            event_dependent_from_branch, int), event_dependent_from_branch
        self._event_dependent_from_branch = event_dependent_from_branch
        self.parentStm = parentStm
        self._inputs = UniqList()
        self._outputs = UniqList()
        self._enclosed_for = None

        self._sensitivity = sensitivity
        self.rank = 0

    @internal
    def _clean_signal_meta(self):
        """
        Clean informations about enclosure for outputs and sensitivity
        of this statement
        """
        self._enclosed_for = None
        self._sensitivity = None
        for stm in self._iter_stms():
            stm._clean_signal_meta()

    @internal
    def _collect_io(self) -> None:
        """
        Collect inputs/outputs from all child statements
        to :py:attr:`~_input` / :py:attr:`_output` attribute on this object
        """
        in_add = self._inputs.extend
        out_add = self._outputs.extend

        for stm in self._iter_stms():
            in_add(stm._inputs)
            out_add(stm._outputs)

    @internal
    def _collect_inputs(self) -> None:
        """
        Collect inputs from all child statements
        to :py:attr:`~_input` attribute on this object
        """
        in_add = self._inputs.extend

        for stm in self._iter_stms():
            in_add(stm._inputs)

    @internal
    def _collect_outputs(self) -> None:
        """
        Collect inputs from all child statements
        to :py:attr:`_output` attribute on this object
        """

        out_add = self._outputs.extend

        for stm in self._iter_stms():
            out_add(stm._outputs)

    @internal
    def _cut_off_drivers_of(
        self, sig: RtlSignalBase
    ) -> Union[None, "HdlStatement", List["HdlStatement"]]:
        """
        Cut all logic from statements which drives signal sig.

        :param sig: signal which drivers should be removed
        :return: A statement or statement list which was cut off from the original statement
        """
        raise NotImplementedError(
            "This is an abstract method and it should be implemented in child class"
        )

    @internal
    def _cut_off_drivers_of_regenerate_io(self, cut_off_sig: RtlSignalBase,
                                          cut_of_smt: "HdlStatement"):
        """
        Update _inputs/_outputs after some part of statement was cut of

        :param cut_off_sig: a signal which driver is a cut_of_stm
        :param cut_of_smt: the statement wich was cut off from original statement (selected by cut_off_sig)
        """
        # update io of this
        self._outputs.remove(cut_off_sig)
        if cut_of_smt._inputs:
            # update inputs on this
            self._inputs.clear()
            self._collect_inputs()
            if self.parentStm is None:
                for i in cut_of_smt._inputs:
                    if i not in self._inputs:
                        i.endpoints.remove(self)

        if self.parentStm is None:
            cut_off_sig.drivers.append(cut_of_smt)

    @internal
    def _discover_enclosure(self) -> None:
        """
        Discover all outputs for which is this statement enclosed _enclosed_for property
        (has driver in all code branches)
        """
        raise NotImplementedError(
            "This method should be implemented"
            " on class of statement", self.__class__, self)

    @internal
    def _discover_sensitivity(self, seen: set) -> None:
        """
        discover all sensitivity signals and store them to _sensitivity property
        """
        raise NotImplementedError(
            "This method should be implemented"
            " on class of statement", self.__class__, self)

    @internal
    def _get_rtl_context(self):
        """
        get RtlNetlist context from signals
        """
        for sig in chain(self._inputs, self._outputs):
            if sig.ctx:
                return sig.ctx
            else:
                # Param instances does not have context
                continue
        raise HwtSyntaxError(
            "Statement does not have any signal in any context", self)

    def _iter_stms(self):
        """
        :return: iterator over all children statements
        """
        raise NotImplementedError(
            "This method should be implemented"
            " on class of statement", self.__class__, self)

    @internal
    def _on_reduce(self, self_reduced: bool, io_changed: bool,
                   result_statements: List["HdlStatement"]) -> None:
        """
        Update signal IO after reduce attempt

        :param self_reduced: if True this object was reduced
        :param io_changed: if True IO of this object may changed
            and has to be updated
        :param result_statements: list of statements which are result
            of reduce operation on this statement
        """

        parentStm = self.parentStm
        if self_reduced:
            was_top = parentStm is None
            # update signal drivers/endpoints
            if was_top:
                # disconnect self from signals
                ctx = self._get_rtl_context()
                ctx.statements.remove(self)
                ctx.statements.update(result_statements)

                for i in self._inputs:
                    i.endpoints.discard(self)
                for o in self._outputs:
                    o.drivers.remove(self)

            for stm in result_statements:
                stm.parentStm = parentStm
                if parentStm is None:
                    # connect signals to child statements
                    for inp in stm._inputs:
                        inp.endpoints.append(stm)
                    for outp in stm._outputs:
                        outp.drivers.append(stm)
        else:
            # parent has to update it's inputs/outputs
            if io_changed:
                self._inputs = UniqList()
                self._outputs = UniqList()
                self._collect_io()

    @internal
    def _on_merge(self, other):
        """
        After merging statements update IO, sensitivity and context

        :attention: rank is not updated
        """
        self._inputs.extend(other._inputs)
        self._outputs.extend(other._outputs)

        if self._sensitivity is not None:
            self._sensitivity.extend(other._sensitivity)
        else:
            assert other._sensitivity is None

        if self._enclosed_for is not None:
            self._enclosed_for.update(other._enclosed_for)
        else:
            assert other._enclosed_for is None

        other_was_top = other.parentStm is None
        if other_was_top:
            other._get_rtl_context().statements.remove(other)
            for s in other._inputs:
                s.endpoints.discard(other)
                s.endpoints.append(self)

            for s in other._outputs:
                s.drivers.discard(other)
                s.drivers.append(self)

    @internal
    def _try_reduce(self) -> Tuple[List["HdlStatement"], bool]:
        raise NotImplementedError(
            "This method should be implemented"
            " on class of statement", self.__class__, self)

    def _is_enclosed(self) -> bool:
        """
        :return: True if every branch in statement assignas to all output signals else False
        """
        return len(self._outputs) == len(self._enclosed_for)

    @internal
    def _is_mergable(self, other: "HdlStatement") -> bool:
        if self is other:
            raise ValueError("Can not merge statement with itself")
        else:
            raise NotImplementedError(
                "This method should be implemented"
                " on class of statement", self.__class__, self)

    @internal
    def _on_parent_event_dependent(self):
        """
        After parent statement become event dependent
        propagate event dependency flag to child statements
        """
        if self._event_dependent_from_branch != 0:
            self._event_dependent_from_branch = 0
            for stm in self._iter_stms():
                stm._on_parent_event_dependent()

    @internal
    def _set_parent_stm(self, parentStm: "HdlStatement"):
        """
        Assign parent statement and propagate dependency flags if necessary
        """
        was_top = self.parentStm is None
        self.parentStm = parentStm
        if self._event_dependent_from_branch is None\
                and parentStm._event_dependent_from_branch is not None:
            self._on_parent_event_dependent()

        topStatement = parentStm
        parents = []
        while True:
            parents.append(topStatement)
            if topStatement.parentStm is None:
                break
            topStatement = topStatement.parentStm

        if was_top:
            for inp in self._inputs:
                inp.endpoints.discard(self)
                inp.endpoints.append(topStatement)
                for p in parents:
                    p._inputs.append(inp)

            for outp in self._outputs:
                outp.drivers.discard(self)
                outp.drivers.append(topStatement)
                for p in parents:
                    p._outputs.append(outp)

            ctx = self._get_rtl_context()
            ctx.statements.discard(self)

        parentStm.rank += self.rank

    @internal
    def _register_stements(self, statements: List["HdlStatement"],
                           target: List["HdlStatement"]):
        """
        Append statements to this container
        """
        for stm in flatten(statements):
            assert stm.parentStm is None, (
                "HdlStatement instance has to have only a single parent", stm)
            stm._set_parent_stm(self)
            target.append(stm)

    def isSame(self, other: "HdlStatement") -> bool:
        """
        :return: True if other has same meaning as self
        """
        raise NotImplementedError(
            "This method should be implemented in child class", self.__class__,
            self)

    @internal
    def _destroy(self):
        """
        Disconnect this statement from signals and delete it from RtlNetlist context

        :attention: signal endpoints/drivers will be altered
            that means they can not be used for iteration
        """
        for i in self._inputs:
            i.endpoints.discard(self)

        if self.parentStm is None:
            ctx = self._get_rtl_context()
            for o in self._outputs:
                o.drivers.remove(self)

            ctx.statements.remove(self)
            self.parentStm = None

    @internal
    def _replace_input(self, toReplace: RtlSignalBase,
                       replacement: RtlSignalBase) -> None:
        """
        Replace input signal with another

        :note: sensitivity/endoints are actualized
        """
        raise NotImplementedError(
            "This method should be implemented in child class", self.__class__,
            self)

    @internal
    def _replace_input_update_sensitivity_and_enclosure(
            self, toReplace: RtlSignalBase, replacement: RtlSignalBase):
        if self._sensitivity is not None:
            if self._sensitivity.discard(toReplace):
                self._sensitivity.add(replacement)

        if self._enclosed_for is not None:
            if self._enclosed_for.discard(toReplace):
                self._enclosed_for.add(replacement)

    @internal
    def _replace_child_statement(self, stm: "HdlStatement",
                                 replacement: List["HdlStatement"],
                                 update_io: bool) -> None:
        """
        Replace a child statement with a list of other statements

        :attention: original statement is destroyed and entirely removed from circuit
        :note: sensitivity/endoints are actualized
        """
        raise NotImplementedError(
            "This method should be implemented in child class", self.__class__,
            self)
Ejemplo n.º 29
0
def flattenTrees(root, nodeSelector: Callable[[LNode], bool],
                 reversePortOrder):
    """
    Walk all nodes and discover trees of nodes (usually operators)
    and reduce them to single node with multiple outputs

    :attention: selected nodes has to have single output
                and has to be connected to nets with single driver
    """
    assert isinstance(root.children, list)
    for ch in root.children:
        if ch.children:
            flattenTrees(ch, nodeSelector, reversePortOrder)

    # collect all nodes which can be potentially reduced
    reducibleChildren = UniqList(ch for ch in root.children
                                 if nodeSelector(ch))

    removedNodes = set()
    for _treeRoot in reducibleChildren:
        # try to pick a node from random tree and search it's root
        if _treeRoot in removedNodes:
            continue

        # we need to keep order of inputs, use pre-order
        treeRoot = searchRootOfTree(reducibleChildren, _treeRoot, removedNodes)

        reducedNodes, inputEdges = collectNodesInTree(treeRoot,
                                                      reducibleChildren,
                                                      removedNodes)
        if reversePortOrder:
            inputEdges = tuple(reversed(inputEdges))

        # if tree is big enough for reduction, reduce it to single node
        if len(reducedNodes) > 1:
            assert inputEdges
            newNode = root.addNode(name=reducedNodes[0].name,
                                   cls=reducedNodes[0].cls)

            o = newNode.addPort("", PortType.OUTPUT, PortSide.EAST)

            oEdges = treeRoot.east[0].outgoingEdges
            # intented copy of oEdges
            for outputedge in list(oEdges):
                dsts = list(outputedge.dsts)
                assert len(dsts) > 0
                outputedge.remove()
                root.addHyperEdge([
                    o,
                ], dsts, originObj=outputedge.originObj)

            port_names = []
            bit_offset = 0
            for i, (_, iP, iE) in enumerate(inputEdges):
                name = None
                index = len(inputEdges) - i - 1
                origin_sig = iE.originObj
                if type(origin_sig) is tuple:
                    for _origin_sig in origin_sig:
                        if hasattr(_origin_sig, "_dtype"):
                            origin_sig = _origin_sig

                if hasattr(origin_sig, "_dtype"):
                    w = origin_sig._dtype.bit_length()
                    if w > 1:
                        name = "[%d:%d]" % (w + bit_offset, bit_offset)
                    else:
                        name = f"[{bit_offset:d}]"
                    bit_offset += w

                if name is None:
                    assert bit_offset == 0, (
                        "can not mix implicitly indexed and bit indexed array items",
                        inputEdges)
                    name = f"[{index:d}]"
                port_names.append(name)

            assert len(port_names) == len(inputEdges)
            for name, (_, iP, iE) in zip(port_names, inputEdges):
                inp = newNode.addPort(name, PortType.INPUT, PortSide.WEST)
                iE.removeTarget(iP)
                iE.addTarget(inp)

            _reducedNodes = set(reducedNodes)
            for n in reducedNodes:
                for e in tuple(n.iterEdges()):
                    #raise AssertionError(n, "should be disconnected")
                    for s in e.srcs:
                        assert s.parentNode in _reducedNodes, (n, e, s)

                    for d in e.dsts:
                        assert d.parentNode in _reducedNodes, (n, e, d)
                    e.remove()
                root.children.remove(n)
Ejemplo n.º 30
0
    def _injectParametersIntoPortTypes(
            self, port_type_variants: List[Tuple[HdlPortItem,
                                                 Dict[Tuple[Param, HValue],
                                                      List[HdlType]]]],
            param_signals: List[RtlSignal]):
        updated_type_ids = set()
        param_sig_by_name = {p.name: p for p in param_signals}
        param_value_domain = {}
        for parent_port, param_val_to_t in port_type_variants:
            for (param, param_value), port_types in param_val_to_t.items():
                param_value_domain.setdefault(param, set()).add(param_value)

        for parent_port, param_val_to_t in port_type_variants:
            if id(parent_port._dtype) in updated_type_ids:
                continue
            # check which unique parameter values affects the type of the port
            # if the type changes with any parameter value integrate it in to type of the port
            # print(parent_port, param_val_to_t)
            type_to_param_values = {}
            for (param, param_value), port_types in param_val_to_t.items():
                for pt in port_types:
                    cond = type_to_param_values.setdefault(pt, UniqList())
                    cond.append((param, param_value))

            assert type_to_param_values, parent_port
            if len(type_to_param_values) == 1:
                continue  # type does not change

            # Param: values
            params_used = {}
            for t, param_values in type_to_param_values.items():
                for (param, param_val) in param_values:
                    params_used.setdefault(param, set()).add(param_val)

                # filter out parameters which are not part of type specification process
                for p, p_vals in list(params_used.items()):
                    if len(param_value_domain[p]) == len(p_vals):
                        params_used.pop(p)
                # reset sets used to check parameter values
                for p, p_vals in params_used.items():
                    p_vals.clear()

            if not params_used:
                raise AssertionError(
                    parent_port,
                    "Type changes between the variants but it does not depend on parameter",
                    param_val_to_t)

            if len(params_used) == 1 and list(
                    params_used.keys())[0].get_hdl_type() == INT:
                # try to extract param * x + y
                p_val_to_port_w = {}
                for t, param_values in type_to_param_values.items():
                    for (param, param_val) in param_values:
                        if param not in params_used:
                            continue
                        assert param_val not in p_val_to_port_w or p_val_to_port_w[
                            param_val] == t.bit_length(), parent_port
                        p_val_to_port_w[param_val] = t.bit_length()
                # t_width = n*p + c
                _p_val_to_port_w = sorted(p_val_to_port_w.items())
                t_width0, p0 = _p_val_to_port_w[0]
                t_width1, p1 = _p_val_to_port_w[1]
                # 0 == t_width0 - n*p0 + c
                # 0 == t_width1 - n*p1 + c

                # 0 == t_width0 - n*p0 - c + t_width1 - n*p1 - c
                # 0 == t_width0 + t_width1 - n*(p0 + p1) - 2c
                # c == (t_width0 + t_width1 - n*(p0 + p1) ) //2
                # n has to be int, 0 < n <= t_width0/p0
                # n is something like base size of port which is multipled by parameter
                # we searching n for which we can resolve c
                found_nc = None
                for n in range(1, t_width0 // p0 + 1):
                    c = (t_width0 + t_width1 - n * (p0 + p1)) // 2
                    if t_width0 - n * p0 + c == 0 and t_width1 - n * p1 + c == 0:
                        found_nc = (n, c)
                        break

                if found_nc is None:
                    raise NotImplementedError()
                else:
                    p = list(params_used.keys())[0]
                    p = param_sig_by_name[p._name]
                    (n, c) = found_nc
                    t = parent_port._dtype
                    t._bit_length = INT.from_py(n) * p + c
                    t._bit_length._const = True
                    updated_type_ids.add(id(t))
            else:
                condition_and_type_width = []
                default_width = None
                for t, p_vals in sorted(type_to_param_values.items(),
                                        key=lambda x: x[0].bit_length()):
                    cond = And(*(param_sig_by_name[p.hdl_name]._eq(p_val)
                                 for p, p_val in p_vals if p in params_used))
                    w = t.bit_length()
                    if default_width is None:
                        default_width = w
                    condition_and_type_width.append((cond, w))

                t = parent_port._dtype
                t._bit_length = reduce_ternary(condition_and_type_width,
                                               default_width)
                t._bit_length._const = True
                updated_type_ids.add(id(t))