<commit_before>#=========================================================================
# SimulationTool.py
#=========================================================================
# Tool for simulating MTL models.
#
# This module contains classes which construct a simulator given a MTL model
# for execution in the python interpreter.

import pprint
import collections
import inspect
import copy
import warnings

from SignalValue import SignalValue
from ast_visitor import DetectLoadsAndStores, get_method_ast

#-------------------------------------------------------------------------
# SimulationTool
#-------------------------------------------------------------------------
# User visible class implementing a tool for simulating MTL models.
#
# This class takes a MTL model instance and creates a simulator for execution
# in the python interpreter.
class SimulationTool( object ):

  #-----------------------------------------------------------------------
  # __init__
  #-----------------------------------------------------------------------
  # Construct a simulator based on a MTL model.
  def __init__( self, model ):

    # Check that the model has been elaborated
    if not model.is_elaborated():
      raise Exception( "cannot initialize {0} tool.\n"
                       "Provided model has not been elaborated yet!!!"
                       "".format( self.__class__.__name__ ) )

    self.model              = model
    self._nets              = []
    self._sequential_blocks = []
    self._register_queue    = []
    self._event_queue       = collections.deque()
    self._event_queue_set   = set()
    self._svalue_callbacks  = collections.defaultdict(list)
    self.ncycles            = 0
    self._current_func      = None

    # TODO: temporary hack
    self._slice_connects    = []

    # Actually construct the simulator
    self._construct_sim()

  #-----------------------------------------------------------------------
  # eval_combinational
  #-----------------------------------------------------------------------
  # Evaluates all combinational logic blocks currently in the event queue.
  def eval_combinational( self ):
    while self._event_queue:
      self._current_func = func = self._event_queue.pop()
      self._event_queue_set.discard( func )
      #self.pstats.add_eval_call( func, self.num_cycles )
      try:
        func()
        self._current_func = None
      except TypeError:
        # TODO: can we catch this at static elaboration?
        raise Exception("Concurrent block '{}' must take no parameters!\n"
                        "file: {}\n"
                        "line: {}\n"
                        "".format( func.func_name,
                                   func.func_code.co_filename,
                                   func.func_code.co_firstlineno ) )

  #-----------------------------------------------------------------------
  # cycle
  #-----------------------------------------------------------------------
  # Advance the simulator by a single clock cycle.
  #
  # Executes all sequential @tick and @posedge_clk blocks defined in the
  # design, as well as any @combinational blocks that have been added
  # to the event queue.
  def cycle( self ):
    # Call all events generated by input changes
    self.eval_combinational()

    # TODO: Hacky auto clock generation
    #if self.vcd:
    #  print >> self.o, "#%s" % (10 * self.num_cycles)
    self.model.clk.value = 0

    #if self.vcd:
    #  print >> self.o, "#%s" % ((10 * self.num_cycles) + 5)
    self.model.clk.value = 1

    # Call all rising edge triggered functions
    for func in self._sequential_blocks:
      func()

    # Then flop the shadow state on all registers
    while self._register_queue:
      reg = self._register_queue.pop()
      reg.flop()

    # Call all events generated by synchronous logic
    self.eval_combinational()

    # Increment the simulator cycle count
    self.ncycles += 1

  #-----------------------------------------------------------------------
  # reset
  #-----------------------------------------------------------------------
  # Sets the reset signal high and cycles the simulator.
  def reset( self ):
    self.model.reset.v = 1
    self.cycle()
    self.cycle()
    self.model.reset.v = 0

  #-----------------------------------------------------------------------
  # print_line_trace
  #-----------------------------------------------------------------------
  # Print cycle number and line trace of model.
  def print_line_trace( self ):
    print "{:>3}:".format( self.ncycles ), self.model.line_trace()

  #-----------------------------------------------------------------------
  # add_event
  #-----------------------------------------------------------------------
  # Add an event to the simulator event queue for later execution.
  #
  # This function will check if the written SignalValue instance has any
  # registered events (functions decorated with @combinational), and if
  # so, adds them to the event queue.
  def add_event( self, signal_value ):
    # TODO: debug_event
    #print "    ADDEVENT: VALUE", signal_value.v,
    #print signal_value in self._svalue_callbacks,
    #print [x.fullname for x in signal_value._debug_signals],
    #print self._svalue_callbacks[signal_value]
    if signal_value in self._svalue_callbacks:
      funcs = self._svalue_callbacks[signal_value]
      for func in funcs:
        # TODO: remove this check?  test performance...
        if func != self._current_func and func not in self._event_queue_set:
          self._event_queue.appendleft( func )
          self._event_queue_set.add( func )

  #-----------------------------------------------------------------------
  # _construct_sim
  #-----------------------------------------------------------------------
  # Construct a simulator for the provided model.
  def _construct_sim( self ):
    self._create_nets( self.model )
    self._insert_signal_values()
    self._register_decorated_functions( self.model )
    self._create_slice_callbacks()

  #-----------------------------------------------------------------------
  # _create_nets
  #-----------------------------------------------------------------------
  # Generate nets describing structural connections in the model.  Each
  # net describes a set of Signal objects which have been interconnected,
  # either directly or indirectly, by calls to connect().
  def _create_nets( self, model ):

    # DEBUG<commit_msg>[new_pymtl] create dev and perf impl of cycle()<commit_after>#=========================================================================
# SimulationTool.py
#=========================================================================
# Tool for simulating MTL models.
#
# This module contains classes which construct a simulator given a MTL model
# for execution in the python interpreter.

import pprint
import collections
import inspect
import copy
import warnings

from sys         import flags
from SignalValue import SignalValue
from ast_visitor import DetectLoadsAndStores, get_method_ast

#-------------------------------------------------------------------------
# SimulationTool
#-------------------------------------------------------------------------
# User visible class implementing a tool for simulating MTL models.
#
# This class takes a MTL model instance and creates a simulator for execution
# in the python interpreter.
class SimulationTool( object ):

  #-----------------------------------------------------------------------
  # __init__
  #-----------------------------------------------------------------------
  # Construct a simulator based on a MTL model.
  def __init__( self, model ):

    # Check that the model has been elaborated
    if not model.is_elaborated():
      raise Exception( "cannot initialize {0} tool.\n"
                       "Provided model has not been elaborated yet!!!"
                       "".format( self.__class__.__name__ ) )

    self.model              = model
    self._nets              = []
    self._sequential_blocks = []
    self._register_queue    = []
    self._event_queue       = collections.deque()
    self._event_queue_set   = set()
    self._svalue_callbacks  = collections.defaultdict(list)
    self.ncycles            = 0
    self._current_func      = None

    # TODO: temporary hack
    self._slice_connects    = []

    # If the -O flag was passed to python, use the perf implementation
    # of cycle, otherwise use the dev version.
    self.cycle = self._perf_cycle if flags.optimize else self._dev_cycle

    # Actually construct the simulator
    self._construct_sim()

  #-----------------------------------------------------------------------
  # eval_combinational
  #-----------------------------------------------------------------------
  # Evaluates all combinational logic blocks currently in the event queue.
  def eval_combinational( self ):
    while self._event_queue:
      self._current_func = func = self._event_queue.pop()
      self._event_queue_set.discard( func )
      #self.pstats.add_eval_call( func, self.num_cycles )
      try:
        func()
        self._current_func = None
      except TypeError:
        # TODO: can we catch this at static elaboration?
        raise Exception("Concurrent block '{}' must take no parameters!\n"
                        "file: {}\n"
                        "line: {}\n"
                        "".format( func.func_name,
                                   func.func_code.co_filename,
                                   func.func_code.co_firstlineno ) )

  #-----------------------------------------------------------------------
  # cycle
  #-----------------------------------------------------------------------
  # Advances the simulator by a single clock cycle, executing all
  # sequential @tick and @posedge_clk blocks defined in the design, as
  # well as any @combinational blocks that have been added to the event
  # queue.
  #
  # Note: see _debug_cycle and _perf_cycle for actual implementations.
  def cycle( self ):
    pass

  #-----------------------------------------------------------------------
  # _debug_cycle
  #-----------------------------------------------------------------------
  # Implementation of cycle() for use during develop-test-debug loops.
  def _dev_cycle( self ):
    # Call all events generated by input changes
    self.eval_combinational()

    # TODO: Hacky auto clock generation
    #if self.vcd:
    #  print >> self.o, "#%s" % (10 * self.num_cycles)
    self.model.clk.value = 0

    #if self.vcd:
    #  print >> self.o, "#%s" % ((10 * self.num_cycles) + 5)
    self.model.clk.value = 1

    # Call all rising edge triggered functions
    for func in self._sequential_blocks:
      func()

    # Then flop the shadow state on all registers
    while self._register_queue:
      reg = self._register_queue.pop()
      reg.flop()

    # Call all events generated by synchronous logic
    self.eval_combinational()

    # Increment the simulator cycle count
    self.ncycles += 1

  #-----------------------------------------------------------------------
  # _perf_cycle
  #-----------------------------------------------------------------------
  # Implementation of cycle() for use when benchmarking models.
  def _perf_cycle( self ):

    # Call all events generated by input changes
    self.eval_combinational()

    # Call all rising edge triggered functions
    for func in self._sequential_blocks:
      func()

    # Then flop the shadow state on all registers
    while self._register_queue:
      reg = self._register_queue.pop()
      reg.flop()

    # Call all events generated by synchronous logic
    self.eval_combinational()

    # Increment the simulator cycle count
    self.ncycles += 1

  #-----------------------------------------------------------------------
  # reset
  #-----------------------------------------------------------------------
  # Sets the reset signal high and cycles the simulator.
  def reset( self ):
    self.model.reset.v = 1
    self.cycle()
    self.cycle()
    self.model.reset.v = 0

  #-----------------------------------------------------------------------
  # print_line_trace
  #-----------------------------------------------------------------------
  # Print cycle number and line trace of model.
  def print_line_trace( self ):
    print "{:>3}:".format( self.ncycles ), self.model.line_trace()

  #-----------------------------------------------------------------------
  # add_event
  #-----------------------------------------------------------------------
  # Add an event to the simulator event queue for later execution.
  #
  # This function will check if the written SignalValue instance has any
  # registered events (functions decorated with @combinational), and if
  # so, adds them to the event queue.
  def add_event( self, signal_value ):
    # TODO: debug_event
    #print "    ADDEVENT: VALUE", signal_value.v,
    #print signal_value in self._svalue_callbacks,
    #print [x.fullname for x in signal_value._debug_signals],
    #print self._svalue_callbacks[signal_value]
    if signal_value in self._svalue_callbacks:
      funcs = self._svalue_callbacks[signal_value]
      for func in funcs:
        if func != self._current_func and func not in self._event_queue_set:
          self._event_queue.appendleft( func )
          self._event_queue_set.add( func )

  #-----------------------------------------------------------------------
  # _construct_sim
  #-----------------------------------------------------------------------
  # Construct a simulator for the provided model.
  def _construct_sim( self ):
    self._create_nets( self.model )
    self._insert_signal_values()
    self._register_decorated_functions( self.model )
    self._create_slice_callbacks()

  #-----------------------------------------------------------------------
  # _create_nets
  #-----------------------------------------------------------------------
  # Generate nets describing structural connections in the model.  Each
  # net describes a set of Signal objects which have been interconnected,
  # either directly or indirectly, by calls to connect().
  def _create_nets( self, model ):

    # DEBUG
{'fraction_non_alphanumeric': 0.2836859492514234, 'fraction_numerical': 0.0018977999578266675, 'mean_word_length': 1.4070377262730502, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4909556', 'n_tokens_mistral': 3414, 'n_tokens_neox': 3222, 'n_words': 1055}