async def valid_cycle(self) -> None: self.log.debug(f"{str(self)} in valid_cycle") channel = self.itf['channel'].capture( ) if self.itf['channel'].instantiated else None data = self.itf['data'].capture( ) if self.itf['data'].instantiated else None empty = self.itf['empty'].capture( ) if self.itf['empty'].instantiated else None error = self.itf['error'].capture( ) if self.itf['error'].instantiated else None sop = self.itf['startofpacket'].capture( ) if self.itf['startofpacket'].instantiated else None eop = self.itf['endofpacket'].capture( ) if self.itf['endofpacket'].instantiated else None # Packet signal checks if self.itf.packets: if sop: if self.in_pkt: raise ci.InterfaceProtocolError( f"Duplicate startofpacket signal ({str(self.itf['startofpacket'])})" ) self.in_pkt = True if not self.in_pkt: raise ci.InterfaceProtocolError( f"Attempted transfer outside of packet") if self.prev_channel is not None and channel != self.prev_channel: raise ci.InterfaceProtocolError( f"Channel changed within packet ({self.prev_channel}->{channel})" ) self.prev_channel = channel # if not self._properties['maxChannel'] >= channel.integer >= 0: # raise ProtocolError( # f"Channel ({channel.integer}) out of valid range " # f"({0}-{self._properties['maxChannel']})" # ) if data is not None: # Apply empty signal if supported if self.in_pkt and (self.itf.empty_within_packet or eop): data = self.itf.mask_data(data, empty) self.buff['data'].append(data) if error is not None: self.buff['error'].append(error) # Transaction completed if not self.itf.packets or eop: if self.prev_channel is not None: self.buff['channel'].append(self.prev_channel) self._release()
async def _event_loop(self) -> None: """ Main event loop for behavioral models. """ # TODO: (redd@) clean; use asyncio.get_event_loop ? # TODO: (redd@) don't block until a reaction is available? self.log.debug(f"{str(self)} looping...") #await ReadOnly() # Stabilize signals prior to sampling await self.trigger('advance') # TODO: (redd@) Reimplement to consider source (shouldn't error out in beginning of sim w/ lots of undefined signals) if self.state == 'TOP_NULL': raise ci.InterfaceProtocolError( f"Control context invariant was violated") # Delete cached values of influences, execute reactions TODO (redd@): revisit # for c in self.get_state(self.state).influences: # self.itf[c].clear() for fn in self.get_state(self.state).reactions: # if fn.smode != ReadOnly: # await NextTimeStep() # await fn.smode() await fn(self) # TODO: (redd@) fix method binding self.log.debug(f"{str(self)} looped!")
def mask_data(self, data: BinaryValue, empty: int) -> BinaryValue: """Returns data signal masked according to empty signal. """ be = self.first_symbol_in_higher_order_bits vec = BinaryValue(bigEndian=be) val = data.value.binstr[:-empty] if be else data.value.binstr[empty:] vec.assign(val) if not vec.is_resolvable: raise ci.InterfaceProtocolError( f"Signal ({str(self['data'])} is unresolvable.") return vec
async def _process(self, trig: Awaitable) -> None: if not self.busy: raise ci.InterfaceProtocolError(f"{str(self)} not busy") self.log.debug(f"{str(self)} processing (trig={trig})...") while self.busy: await trig await self._event_loop() self.log.debug(f"{str(self)} processed!")
def _release(self, data: Optional[Dict] = None) -> None: """ Relinquishes control of `self.lock`; optionally pass data to downstream coroutines. This assumes that the calling task is the current owner of lock. """ if not self.busy: raise ci.InterfaceProtocolError( f"{str(self)} attempted release of non-existent busy-lock") self._busy = False self.lock.set(data) self.log.debug(f"{str(self)} released lock (data={data})")
def _specify(self, spec: Set[ci.signal.Signal], precedes: bool = False, bus_name: Optional[str] = None, bus_separator: str = "_"): """ Incorporate specifications into interface. Args: spec: `Signal` instances to add to interface specification. precedes: Asserted if `Control` instances within `spec` behaviorally-precede those currently specified in self.controls. """ # TODO: (redd@) array_idx; account for naming variations e.g. w/ _n suffix def alias(s: ci.signal.Signal): return f"{bus_name}{bus_separator}{s.name}" if bus_name else s.name if not hasattr(self, '_signals'): self._signals = set() if any(any(s.name == t.name for t in spec) for s in self.signals): raise ValueError(f"Duplicate signals specified: {repr(spec)}") # Consider relative precedence for new Controls if any(isinstance(s, ci.signal.Control) for s in spec): offset = max(c for c in spec if isinstance(c, ci.signal.Control)).precedence if precedes else self.pmax if precedes: for c in self.controls: if offset is not None: c.precedence += (offset + 1) else: for c in spec: if isinstance(c, ci.signal.Control): if offset is not None: c.precedence += (offset + 1) # Instantiate signals, bind filters for s in spec: if not hasattr(self.entity, alias(s)): if s.required: raise ci.InterfaceProtocolError(f"{str(self)} missing required signal: {str(s)}") self.log.info(f"{str(self)} ignoring optional: {str(s)}") elif not s.instantiated: s.handle = getattr(self.entity, alias(s)) for f in self._filters: if f.cname == s.name: s.filter = f self._signals.add(s) self.log.debug(f"{str(self)} applied: {str(spec)}")
def capture(self) -> _allowed: if not self.instantiated: raise AttributeError(f"Signal ({str(self)}) not instantiated") val = self.handle.value if not val.is_resolvable: raise ci.InterfaceProtocolError( f"Signal ({str(self)}) is unresolvable") if not self.logic_active_high: val.assign(~val.integer & len(self.handle)) if self.filter is not None: self.filter(val) if self.logical_type == int: val = val.integer elif self.logical_type == bool: val = bool(val.integer) self.log.debug(f"{str(self)} captured sample: {repr(val)}") return val