def _gen_input_to_array(ports):
        if isinstance(ports[0], (Wire, InPort, OutPort)):
            stmts = '  wire   [{:4}:0] {}[0:{}];\n' \
                    .format(ports[0].nbits-1, ports.name, len(ports)-1)
            for i, port in enumerate(ports):
                stmts += '  assign {1}[{2:3}] = {0};\n' \
                         .format(signal_to_str(port, None, model), ports.name, i)

        elif isinstance(ports[0], (PortList, WireList)):
            if not isinstance(ports[0][0], (Wire, InPort, OutPort)):
                raise VerilogTranslationError(
                    'Port lists with more than 2 dimenstions are not supported!'
                )
            stmts = '  wire   [{bw:4}:0] {name}[0:{sz0}][0:{sz1}];\n' \
                    .format( bw   = ports[0][0].nbits-1,
                             name = ports.name,
                             sz0  = len(ports)-1,
                             sz1  = len(ports[0])-1 )
            for i, plist in enumerate(ports):
                for j, port in enumerate(plist):
                    stmts += '  assign {1}[{2:3}][{3:3}] = {0};\n' \
                             .format(signal_to_str(port, None, model),
                                     ports.name, i, j)

        else:
            raise VerilogTranslationError(
                'An internal error occured when translating array accesses!\n'
                'Expected a list of Wires or Ports, instead got: {}'.format(
                    type(ports[0])))
        return stmts
Exemple #2
0
    def visit_Slice(self, node):
        if node.step:
            raise VerilogTranslationError(
                'An unexpected error when translating Slices was encountered!\n'
                'Please inform the PyMTL developers!', node.lineno)

        # handle open slices [:upper], [lower:], and [:]
        if node.lower == None: node.lower = ast.Num(0)
        if node.upper == None: node.upper = ast.Num(node._nbits)

        # Special handling of variable part-selects
        # TODO: make this more resilient?
        # http://www.sutherland-hdl.com/papers/2000-HDLCon-paper_Verilog-2000.pdf
        if isinstance(node.upper, _ast.BinOp):

            if not isinstance(node.upper.op, (_ast.Add, _ast.Sub)):
                raise VerilogTranslationError(
                    'Slicing in behavioral blocks cannot contain arbitrary arithmetic!\n'
                    'Variable slices must be of the form [x:x+N] or [x:x-N]!\n'
                    '(and N must be constant!)\n', node.lineno)

            lower = self.visit(node.lower)
            left_op = self.visit(node.upper.left)
            right_num = node.upper.right

            if lower != left_op:
                raise VerilogTranslationError(
                    'Slicing in behavioral blocks cannot contain arbitrary arithmetic!\n'
                    'Variable slices must be of the form [x:x+N] or [x:x-N]!\n'
                    '(and N must be constant!)\n', node.lineno)

            # Determine the width of the part-select.
            # FIXME: elif needed b/c Num AST nodes dont have _objects...
            if hasattr(right_num, '_object'): width = right_num._object
            elif isinstance(node.upper.right, _ast.Num): width = right_num.n
            else:
                raise VerilogTranslationError(
                    'Slicing in behavioral blocks cannot contain arbitrary arithmetic!\n'
                    'Variable slices must be of the form [x:x+N] or [x:x-N]!\n'
                    '(and N must be constant!)\n', node.lineno)

            # Widths for part selects can't be 0 (ie. select a single bit), work
            # around this limitation by converting into a normal index access.
            if width == 1:
                return '{}'.format(lower)
            else:
                op = opmap[type(node.upper.op)]
                upper = self.visit(node.upper.right)
                return '{} {}: {}'.format(lower, op, upper)

        # Normal slices
        lower = self.visit(node.lower)
        upper = self.visit(node.upper)
        return '({})-1:{}'.format(upper, lower)
Exemple #3
0
  def visit_For( self, node ):
    self.generic_visit( node )

    if not ( isinstance( node.iter,      _ast.Call ) and
             isinstance( node.iter.func, _ast.Name ) and
             node.iter.func.id in ['range', 'xrange'] ):
      raise VerilogTranslationError(
        'For loops are only translatable when using range or xrange!\n'
        'Please use "for i in range(...)/xrange(...)".',
        node.lineno
      )

    call = node.iter

    if   len( call.args ) == 1:
      start = _ast.Num( n=0 )
      stop  = call.args[0]
      step  = _ast.Num( n=1 )
    elif len( call.args ) == 2:
      start = call.args[0]
      stop  = call.args[1]
      step  = _ast.Num( n=1 ) # TODO: should be an expression
    elif len( call.args ) == 3:
      start = call.args[0]
      stop  = call.args[1]
      step  = call.args[2]
    else:
      raise VerilogTranslationError(
        'An invalid number of arguments provided to (x)range function!\n',
        node.lineno
      )

    # Must know if the step is negative or positive in order to set the
    # correct bound check. This is because of Python's range behavior.
    try:
      if   hasattr( step, '_object' ): step_val = step._object
      elif hasattr( step, 'n'       ): step_val = step.n
      assert step_val != 0
    except (UnboundLocalError,AssertionError):
      raise VerilogTranslationError(
        'An error occurred when translating a "for loop"!\n'
        'The "step" parameter to range must be a constant integer value != 0!',
        node.lineno
      )

    node.iter       = _ast.Slice( lower=start, upper=stop, step=step )
    node.iter.lt_gt = '<' if step_val > 0 else '>'

    return node
Exemple #4
0
  def visit_Assign( self, node ):
    if len(node.targets) != 1:
      raise VerilogTranslationError(
        'Chained assignments are not supported!\n'
        'Please modify "x = y = ..." to be two separate lines.',
        node.lineno
      )

    self._is_lhs = True
    self.visit( node.targets[0] )
    self._is_lhs = False
    self.visit( node.value )

    obj = node.targets[0]._object

    # NOTE:
    # - currently possible to have inferences with different bitwidths
    # - currently possible for a signal to be stored as both a reg and loopvar
    #   handle this in verilog_structural.create_declarations
    if   obj in self.arrayelms:     return
    elif isinstance( obj, Signal ): self.store[ obj.fullname ] = obj
    elif isinstance( obj, tuple ):  self.loopvar.add( obj[0] )
    # FIXME:
    # - if one field of a bitstruct is assigned in a behavioral block,
    #   the **entire** bitstruct is assumed to be a reg!
    elif isinstance( obj, _SignalSlice ):
      self.store[ obj._signal.fullname ] = obj._signal
Exemple #5
0
 def visit_Expr(self, node):
   raise VerilogTranslationError(
     'An unexpected Expr node was encountered!\n'
     'Please inform the PyMTL developers!',
     node.lineno
   )
   return '{}{};\n'.format( self.indent, self.visit(node.value) )
Exemple #6
0
  def visit_For(self, node):

    if not (isinstance( node.iter,   _ast.Slice ) or
            isinstance( node.target, _ast.Name  )):
      raise VerilogTranslationError(
        'An unexpected error occurred when translating a "for loop"!\n'
        'Please inform the PyMTL developers!',
        node.lineno
      )

    i     = self.visit( node.target )
    lower = self.visit( node.iter.lower )
    upper = self.visit( node.iter.upper )
    step  = self.visit( node.iter.step  )
    lt_gt = node.iter.lt_gt

    body  = self.fmt_body( node.body )

    x = fmt("""
    for ({i}={lower}; {i} {lt_gt} {upper}; {i}={i}+{step})
    begin
    {body}
    end
    """, self.indent ).format( **locals() )

    return x
Exemple #7
0
  def _gen_array_to_output( ports ):
    if isinstance( ports[0], (Wire,InPort,OutPort) ):

      # Annotate arrays if vannotate_arrays is available
      annotation = ''
      if model.vannotate_arrays:
        if ports.name in model.vannotate_arrays:
          annotation = model.vannotate_arrays[ports.name] + ' '

      stmts = '  {}reg    [{:4}:0] {}[0:{}];\n' \
              .format(annotation, ports[0].nbits-1, ports.name, len(ports)-1)

      # Generate declarations
      gen_declarations = not model.vmark_as_bram # unless marked as BRAM
      if gen_declarations:
        for i, port in enumerate(ports):
          stmts += '  assign {0} = {1}[{2:3}];\n' \
                   .format(signal_to_str(port, None, model), ports.name, i)

    elif isinstance( ports[0], (PortList,WireList) ):
      if not isinstance( ports[0][0], (Wire,InPort,OutPort) ):
        raise VerilogTranslationError(
          'Port lists with more than 2 dimenstions are not supported!'
        )
      stmts = '  reg    [{bw:4}:0] {name}[0:{sz0}][0:{sz1}];\n' \
              .format( bw   = ports[0][0].nbits-1,
                       name = ports.name,
                       sz0  = len(ports)-1,
                       sz1  = len(ports[0])-1 )
      for i, plist in enumerate(ports):
        for j, port in enumerate(plist):
          stmts += '  assign {0} = {1}[{2:3}][{3:3}];\n' \
                   .format(signal_to_str(port, None, model),
                           ports.name, i, j)

    else:
      raise VerilogTranslationError(
        'An internal error occured when translating array accesses!\n'
        'Expected a list of Wires or Ports, instead got: {}'
        .format( type(ports[0]) )
      )

    return stmts
Exemple #8
0
 def visit_Compare(self, node):
     if len(node.ops) != 1:
         raise VerilogTranslationError(
             'Chained comparisons are currently not translatable!\n'
             'Please change "x < y < z" to the form "(x < y) and (y < z)!',
             node.lineno)
     left = self.visit(node.left)
     op = opmap[type(node.ops[0])]
     right = self.visit(node.comparators[0])
     return '({} {} {})'.format(left, op, right)
Exemple #9
0
 def visit_For( self, node ):
   if not (isinstance( node.iter,   _ast.Slice ) and
           isinstance( node.target, _ast.Name  )):
     raise VerilogTranslationError(
       'An internal error occured when translating a for loop!\n'
       'Please contact the PyMTL developers!',
       node.lineno
     )
   self.loopvar.add( node.target.id )
   self.generic_visit( node )
Exemple #10
0
 def visit_Name( self, node ):
   self.generic_visit( node )
   if isinstance( node._object, slice ):
     if node._object.step:
       raise VerilogTranslationError(
         'Slices with steps ([start:stop:step]) are not translatable!\n',
         node.lineno
       )
     new_node = ast.Slice( ast.Num( node._object.start ),
                           ast.Num( node._object.stop ),
                           None )
     return ast.copy_location( new_node, node )
   return node
Exemple #11
0
    def visit_Assign(self, node):

        # NOTE: this should really be caught earlier
        if len(node.targets) != 1 or isinstance(node.targets[0], (ast.Tuple)):
            raise VerilogTranslationError(
                'Assignments can only have one item on the left-hand side!\n'
                'Please modify "x,y = ..." to be two separate lines.',
                node.lineno)

        lhs = self.visit(node.targets[0])
        rhs = self.visit(node.value)
        assign = '=' if node._is_blocking else '<='
        indent = self.indent

        return '{indent}{lhs} {assign} {rhs};\n'.format(**vars())
Exemple #12
0
 def visit_Attribute( self, node ):
   self.generic_visit( node )
   if isinstance( node._object, _SignalSlice ):
     if node._object.slice.step:
       raise VerilogTranslationError(
         'Slices with steps ([start:stop:step]) are not translatable!\n',
         node.lineno
       )
     new_node = ast.Subscript( node.value,
                  ast.Slice( ast.Num( node._object.slice.start ),
                             ast.Num( node._object.slice.stop ),
                             None ),
                  None,
                )
     new_node._object = node._object
     return ast.copy_location( new_node, node )
   return node
Exemple #13
0
  def visit_Assign( self, node ):

    # catch untranslatable constructs
    if len(node.targets) != 1:
      raise VerilogTranslationError(
        'Chained assignments are not supported!\n'
        'Please modify "x = y = ..." to be two separate lines.',
        node.lineno
      )

    # annotate the assignment with _is_blocking if not sequential update
    lhs = node.targets[0]
    seq = isinstance( lhs, ast.Attribute ) and lhs.attr in ['next','n']
    node._is_blocking = not seq

    self.generic_visit( node )
    return node
Exemple #14
0
    def visit_FunctionDef(self, node):

        # Don't bother translating undecorated functions
        if not node.decorator_list:
            return

        # Combinational Block
        if 'combinational' in node.decorator_list:
            sensitivity = '*'

        # Posedge Clock
        elif ('posedge_clk' in node.decorator_list
              or 'tick_rtl' in node.decorator_list):
            sensitivity = 'posedge clk'

        # Unsupported annotation
        else:

            def get_dec_name(dec):
                if hasattr(dec, 'id'): return dec.id
                elif hasattr(dec, 'attr'): return dec.attr
                else: return dec

            raise VerilogTranslationError(
                'An invalid decorator was encountered!\nOnly @s.combinational,\n'
                '@s.tick_rtl, and @s.posedge_clk are currently translatable.\n'
                'Current decorators: {}'.format(
                    [get_dec_name(x) for x in node.decorator_list]),
                node.lineno)

        # Visit the body of the function
        body = self.fmt_body(node.body)

        return fmt(
            '''
    // logic for {}()
    always @ ({}) begin
    {}
    end
    ''', self.indent).format(node.name, sensitivity, body)
Exemple #15
0
  def visit_FunctionDef( self, node ):
    #self.generic_visit( node ) # visit children, uneeded?

    # TODO: currently assume decorator is of the form 'self.dec_name'
    if len( node.decorator_list ) != 1:
      def get_dec_name( dec ):
        if   hasattr( dec, 'id'   ): return dec.id
        elif hasattr( dec, 'attr' ): return dec.attr
        else:                        return dec
      raise VerilogTranslationError(
        'Translated behavioral blocks should only have one decorator!\n'
        'Current decorators: {}'.format(
          [ get_dec_name( x ) for x in node.decorator_list ]
        ), node.lineno
      )

    dec = node.decorator_list[0].attr

    # create a new FunctionDef node that deletes the decorators
    new_node = ast.FunctionDef( name=node.name, args=node.args,
                                body=node.body, decorator_list=[dec])

    return ast.copy_location( new_node, node )
Exemple #16
0
  def visit_Call(self, node):

    # Can only translate calls generated from Name nodes
    if not isinstance( node.func, _ast.Name ):
      raise VerilogTranslationError(
        'Encountered a non-translatable function call!',
        node.lineno
      )
    if node.keywords:
      raise VerilogTranslationError(
        'Cannot translate function calls with keyword arguments!\n'
        'Please replace "f( a, argname=b )" with "f( a, b )".',
        node.lineno
      )
    if node.starargs:
      raise VerilogTranslationError(
        'Cannot translate function calls with arg unpacking!\n'
        'Please replace "f( *arg_list )" with "f( a, b )".',
        node.lineno
      )
    if node.kwargs:
      raise VerilogTranslationError(
        'Cannot translate function calls with kwarg unpacking!\n'
        'Please replace "f( **arg_dict )" with "f( a, b )".',
        node.lineno
      )

    # Handle sign extension
    func_name = self.visit( node.func )

    # Handle sign extension
    if func_name  == 'sext':
      if len(node.args) != 2:
        raise VerilogTranslationError(
          'Encountered a non-translatable sext call!\n'
          'sext(in, nbits) must have exactly two arguments!',
          node.lineno
        )
      try:
        if isinstance( node.args[1], ast.Num ): nbits = node.args[1].n
        else:                                   nbits = node.args[1]._object
        assert isinstance( nbits, int )
      except (AssertionError,AttributeError):
        raise VerilogTranslationError(
          'Encountered a non-translatable sext call!\n'
          'Argument "nbits" of sext(in,nbits) is not a constant int!',
          node.lineno
        )
      sig_name   = self.visit( node.args[0] )
      sig_nbits  = node.args[0]._object.nbits
      ext_nbits  = self.visit( node.args[1] )
      return '{{ {{ {3}-{1} {{ {0}[{2}] }} }}, {0} }}' \
             .format( sig_name, sig_nbits, sig_nbits-1, ext_nbits )

    # Handle zero extension
    if func_name  == 'zext':
      if len(node.args) != 2:
        raise VerilogTranslationError(
          'Encountered a non-translatable zext call!\n'
          'zext(in, nbits) must have exactly two arguments!',
          node.lineno
        )
      try:
        if isinstance( node.args[1], ast.Num ): nbits = node.args[1].n
        else:                                   nbits = node.args[1]._object
        assert isinstance( nbits, int )
      except (AssertionError,AttributeError):
        raise VerilogTranslationError(
          'Encountered a non-translatable zext call!\n'
          'Argument "nbits" of zext(in,nbits) is not a constant int!',
          node.lineno
        )
      sig_name   = self.visit( node.args[0] )
      sig_nbits  = node.args[0]._object.nbits
      ext_nbits  = self.visit( node.args[1] )
      return "{{ {{ {3}-{1} {{ 1'b0 }} }}, {0} }}" \
             .format( sig_name, sig_nbits, sig_nbits-1, ext_nbits )

    # Handle concatentation
    if func_name  == 'concat':
      signal_names = [ self.visit(x) for x in node.args ]
      return "{{ {signals} }}" \
             .format( signals=','.join(signal_names) )

    # Handle reduce and
    if func_name  == 'reduce_and':
      sig_name = self.visit( node.args[0] )
      return "(&{sig_name})".format( sig_name=sig_name )

    # Handle reduce or
    if func_name  == 'reduce_or':
      sig_name = self.visit( node.args[0] )
      return "(|{sig_name})".format( sig_name=sig_name )

    # Handle reduce xor
    if func_name  == 'reduce_xor':
      sig_name = self.visit( node.args[0] )
      return "(^{sig_name})".format( sig_name=sig_name )

    # Handle Bits
    if func_name  == 'Bits':
      if len(node.args) > 2:
        raise VerilogTranslationError(
          'Encountered a non-translatable Bits call!\n'
          'Bits(nbits,value) must have exactly two arguments!',
          node.lineno
        )
      if not isinstance( node.args[0], (ast.Num, ast.Name, ast.Attribute)):
        raise VerilogTranslationError(
          'Encountered a non-translatable Bits call!\n'
          'Argument "nbits" of Bits(nbits,value) is not a constant int!',
          node.lineno
        )
      if len(node.args) == 2 and \
         not isinstance( node.args[1], (ast.Num, ast.Name, ast.Attribute)):
        raise VerilogTranslationError(
          'Encountered a non-translatable Bits call!\n'
          'Argument "value" of Bits(nbits,value) is not a constant int!',
          node.lineno
        )

      try:
        if   isinstance( node.args[0], ast.Num ): nbits = node.args[0].n
        else:                                     nbits = node.args[0]._object
        if   len(node.args) == 1:                 value = 0
        elif isinstance( node.args[1], ast.Num ): value = node.args[1].n
        else:                                     value = node.args[1]._object
        assert isinstance( nbits, int )
        assert isinstance( value, int )
      except (AssertionError,AttributeError):
        raise VerilogTranslationError(
          'Encountered a non-translatable Bits call!\n'
          'Arguments of Bits(nbits,value) are not both constant ints!',
          node.lineno
        )

      return "{nbits}'d{value}".format( nbits=nbits, value=value )

    raise VerilogTranslationError(
      'Encountered a non-translatable function call: {}!'.format( func_name ),
      node.lineno
    )
Exemple #17
0
  def visit_Assign( self, node ):

    # Catch untranslatable constructs
    if len(node.targets) != 1:
      raise VerilogTranslationError(
        'Chained assignments are not supported!\n'
        'Please modify "x = y = ..." to be two separate lines.',
        node.lineno
      )

    if isinstance(node.targets[0], ast.Tuple):
      raise VerilogTranslationError(
        'Multiple items on the left of an assignment are not supported!\n'
        'Please modify "x,y = ..." to be two separate lines.',
        node.lineno
      )

    # First visit the RHS to update Name nodes that have been inferred
    self.visit( node.value )

    # Need this to visit potential temporaries used in slice indices!
    self.visit( node.targets[0] )

    # The LHS doesn't have a type, we need to infer it
    if node.targets[0]._object == None:

      # The LHS should be a Name node!
      if not isinstance(node.targets[0], _ast.Name):
        raise VerilogTranslationError(
          'An internal error occured when performing type inference!\n'
          'Please contact the PyMTL developers!',
          node.lineno
        )

      # Assign unique name to this temporary in case the same temporary
      # name is used in another concurrent block.
      node.targets[0].id = self._uniq_name( node.targets[0].id )

      # Copy the object returned by the RHS, set the name appropriately
      if   isinstance( node.value, ast.Name ):
        if isinstance( node.value._object, int ):
          self._insert( node, (node.targets[0].id, node.value._object ) )
        else:
          obj = copy.copy( node.value._object )
          obj.name   = node.targets[0].id
          obj.parent = None
          self._insert( node, obj )

      elif isinstance( node.value, ast.Attribute ):
        if isinstance( node.value._object, int ):
          self._insert( node, (node.targets[0].id, node.value._object ) )
        else:
          obj = copy.copy( node.value._object )
          obj.name   = node.targets[0].id
          obj.parent = None
          self._insert( node, obj )

      elif isinstance( node.value, ast.Num ):
        self._insert( node, (node.targets[0].id, int( node.value.n )) )

      elif isinstance( node.value, ast.BoolOp ):
        obj      = Wire( 1 )
        obj.name = node.targets[0].id
        self._insert( node, obj )

      elif isinstance( node.value, ast.Compare ):
        obj      = Wire( 1 )
        obj.name = node.targets[0].id
        self._insert( node, obj )

      elif isinstance( node.value, ast.Subscript ):

        # TODO: assumes ast.Index does NOT contain a slice object
        if not isinstance( node.value.slice, ast.Index ):
          raise VerilogTranslationError(
            'Type inference from slices > 1-bit is not currently supported!'
            '\nCannot infer type of temporary variable "{}".'
             .format( node.targets[0].id ),
            node.lineno
          )

        if   isinstance( node.value._object, Signal ):
          obj = Wire( 1 )
        elif isinstance( node.value._object,    list   ) and \
             isinstance( node.value._object[0], Signal ):
          obj = Wire( node.value._object[0].nbits )
        else:
          raise VerilogTranslationError(
            'Type inference from unsupported list construct!'
            '\nCannot infer type of temporary variable "{}".'
             .format( node.targets[0].id ),
            node.lineno
          )

        obj.name = node.targets[0].id
        self._insert( node, obj )

      elif isinstance( node.value, ast.Call ):

        func_name = node.value.func.id
        if func_name in ['sext', 'zext']:
          nbits_arg = node.value.args[1]
          if isinstance( nbits_arg, ast.Num ): nbits = nbits_arg.n
          else:                                nbits = nbits_arg._object
          if not isinstance( nbits, int ):
            raise VerilogTranslationError(
              'The second argument to function "{}" must be an int!'
              .format( func_name ),
              node.lineno
            )
          obj      = Wire( nbits )
        elif func_name == 'concat':
          nbits    = sum( [x._object.nbits for x in node.value.args ] )
          obj      = Wire( nbits )
        elif func_name in ['reduce_and', 'reduce_or', 'reduce_xor']:
          obj      = Wire( 1 )
        elif func_name == 'Bits':
          nbits_arg = node.value.args[0]
          if isinstance( nbits_arg, ast.Num ): nbits = nbits_arg.n
          else:                                nbits = nbits_arg._object
          if not isinstance( nbits, int ):
            raise VerilogTranslationError(
              'The first argument to the Bits constructor must be an int!',
              node.lineno
            )
          obj      = Wire( nbits )
        else:
          print_simple_ast( node )
          raise VerilogTranslationError(
            'Type inference from the function "{}" is not currently supported!'
            '\nCannot infer type of temporary variable "{}".'
             .format( func_name, node.targets[0].id ),
            node.lineno
          )

        obj.name = node.targets[0].id
        self._insert( node, obj )

      else:
        print_simple_ast( node )
        raise VerilogTranslationError(
          'Type inference of "{}" AST nodes is not currently supported!'
          '\nCannot infer type of temporary variable "{}".'
           .format( type(node.value).__name__, node.targets[0].id ),
          node.lineno
        )

    return node