def visit_Name( s, node ): if node.id in s.closure: # free var from closure obj = s.closure[ node.id ] if isinstance( obj, dsl.Component ): # Component freevars are an L1 thing. if obj is not s.component: raise PyMTLSyntaxError( s.blk, node, f'Component {obj} is not a sub-component of {s.component}!' ) ret = bir.Base( obj ) else: # A closure variable could be a loop index. We need to # generate per-function closure variable instead of assuming # they will have the same value. ret = bir.FreeVar( f"{node.id}_at_{s.blk.__name__}", obj ) ret.ast = node return ret elif node.id in s.globals: # free var from the global name space # For now we can still safely assume all upblks will see the same # value for a free var from the global space? ret = bir.FreeVar( node.id, s.globals[ node.id ] ) ret.ast = node return ret raise PyMTLSyntaxError( s.blk, node, f'Temporary variable {node.id} is not supported at L1!' )
def visit_Assign(s, node): blocking = { 'CombUpblk': True, 'SeqUpblk': False, } if len(node.targets) < 1: raise PyMTLSyntaxError( s.blk, node, 'At least one assignment target should be provided!') if s._upblk_type not in blocking: raise PyMTLSyntaxError( s.blk, node, 'Assignment should be in either a combinational or a sequential update block!' ) value = s.visit(node.value) targets = [s.visit(target) for target in node.targets] ret = bir.Assign(targets, value, blocking=False) # Determine if this is a blocking/non-blocking assignment ret.blocking = s.get_blocking(node, ret) ret.ast = node return ret
def get_call_obj(s, node): if hasattr(node, "starargs") and node.starargs: raise PyMTLSyntaxError(s.blk, node, 'star argument is not supported!') if hasattr(node, "kwargs") and node.kwargs: raise PyMTLSyntaxError(s.blk, node, 'double-star argument is not supported!') if node.keywords: raise PyMTLSyntaxError(s.blk, node, 'keyword argument is not supported!') if not isinstance(node.func, ast.Name): raise PyMTLSyntaxError( s.blk, node, f'{node.func} is called but is not a name!') func = node.func # Find the corresponding object of node.func field # TODO: Support Verilog task? # if func in s.mapping: # The node.func field corresponds to a member of this class # obj = s.mapping[ func ][ 0 ] # else: try: # An object in global namespace is used if func.id in s.globals: obj = s.globals[func.id] # An object in closure is used elif func.id in s.closure: obj = s.closure[func.id] else: raise NameError except NameError: raise PyMTLSyntaxError(s.blk, node, node.func.id + ' function is not found!') return obj
def visit_Subscript(s, node): value = s.visit(node.value) if isinstance(node.slice, ast.Slice): if node.slice.step is not None: raise PyMTLSyntaxError(s.blk, node, 'Slice with steps is not supported!') lower, upper = s.visit(node.slice) ret = bir.Slice(value, lower, upper) ret.ast = node return ret # signal[ index ] # index might be a slice object! if isinstance(node.slice, ast.Index): idx = s.visit(node.slice) # If we have a static slice object then use it if isinstance(idx, bir.FreeVar) and isinstance(idx.obj, slice): slice_obj = idx.obj if slice_obj.step is not None: raise PyMTLSyntaxError( s.blk, node, 'Slice with steps is not supported!') assert isinstance( slice_obj.start, int ) and \ isinstance( slice_obj.stop, int ), \ f"start and stop of slice object {slice_obj} must be integers!" ret = bir.Slice(value, bir.Number(slice_obj.start), bir.Number(slice_obj.stop)) # Else this is a real index else: ret = bir.Index(value, idx) ret.ast = node return ret raise PyMTLSyntaxError(s.blk, node, 'Illegal subscript ' + node + ' encountered!')
def get_call_obj( s, node ): if hasattr(node, "starargs") and node.starargs: raise PyMTLSyntaxError( s.blk, node, 'star argument is not supported!') if hasattr(node, "kwargs") and node.kwargs: raise PyMTLSyntaxError( s.blk, node, 'double-star argument is not supported!') if node.keywords: raise PyMTLSyntaxError( s.blk, node, 'keyword argument is not supported!') obj = s.const_extractor.enter( node.func ) if obj is not None: return obj else: raise PyMTLSyntaxError( s.blk, node, f'{node.func} function is not found!' )
def visit_Compare(s, node): if not type(node.ops[0]) in s.opmap: raise PyMTLSyntaxError( s.blk, node, 'Operator ' + str(node.ops[0]) + ' is not supported!') if len(node.ops) != 1 or len(node.comparators) != 1: raise PyMTLSyntaxError(s.blk, node, 'Comparison can only have 2 operands!') op = s.opmap[type(node.ops[0])] op.ast = node.ops[0] left = s.visit(node.left) right = s.visit(node.comparators[0]) ret = bir.Compare(left, op, right) ret.ast = node return ret
def visit_Call(s, node): """Return behavioral RTLIR of a method call. At L3 we need to support the syntax of struct instantiation in upblks. This is achieved by function calls like `struct( 1, 2, 0 )`. """ obj = s.get_call_obj(node) if is_bitstruct_class(obj): fields = obj.__bitstruct_fields__ nargs = len(node.args) nfields = len(fields.keys()) if nargs == 0: # Infer the values of each field by inspecting the object constructed # with default arguments inst = obj() values = [ s._datatype_to_bir(getattr(inst, field)) for field in fields.keys() ] else: # Otherwise all fields of the struct must be present in the arguments if nargs != nfields: raise PyMTLSyntaxError( s.blk, node, f'BitStruct {obj.__name__} has {nfields} fields but {nargs} arguments are given!' ) values = [s.visit(arg) for arg in node.args] ret = bir.StructInst(obj, values) ret.ast = node return ret else: return super().visit_Call(node)
def visit_Expr( s, node ): """Return the behavioral RTLIR of an expression. ast.Expr might be useful when a statement is only a call to a task or a non-returning function. """ raise PyMTLSyntaxError( s.blk, node, 'Stand-alone expression is not supported yet!' )
def visit_Call(s, node): obj = s.get_call_obj(node) # At L2 we add bool type but we do not support instantiating a bool # value -- that should always be the result of a comparison! if obj is rdt.Bool: raise PyMTLSyntaxError( s.blk, node, 'bool values cannot be instantiated explicitly!') return super().visit_Call(node)
def visit_Module( s, node ): if len( node.body ) != 1 or \ not isinstance( node.body[0], ast.FunctionDef ): raise PyMTLSyntaxError( s.blk, node, 'Update blocks should have exactly one FuncDef!' ) ret = s.visit( node.body[0] ) ret.ast = node return ret
def visit_UnaryOp(s, node): if not type(node.op) in s.opmap: raise PyMTLSyntaxError( s.blk, node, 'Operator ' + str(node.op) + ' is not supported!') op = s.opmap[type(node.op)] op.ast = node.op operand = s.visit(node.operand) ret = bir.UnaryOp(op, operand) ret.ast = node return ret
def visit_Assign(s, node): if len(node.targets) != 1: raise PyMTLSyntaxError( s.blk, node, 'Assigning to multiple targets is not allowed!') value = s.visit(node.value) target = s.visit(node.targets[0]) ret = bir.Assign(target, value, blocking=True) ret.ast = node return ret
def visit_BinOp(s, node): left = s.visit(node.left) right = s.visit(node.right) if not type(node.op) in s.opmap: raise PyMTLSyntaxError( s.blk, node, 'Operator ' + str(node.op) + ' is not supported!') op = s.opmap[type(node.op)] op.ast = node.op ret = bir.BinOp(left, op, right) ret.ast = node return ret
def get_blocking(s, node, bir_node): has_tmpvar = any(isinstance(n, bir.TmpVar) for n in bir_node.targets) all_tmpvar = all(isinstance(n, bir.TmpVar) for n in bir_node.targets) if has_tmpvar and not all_tmpvar: raise PyMTLSyntaxError( s.blk, node, 'all targets have to be tmpvars if any target on LHS is a tmpvar!' ) if has_tmpvar: return True else: return super().get_blocking(node, bir_node)
def visit_AugAssign( s, node ): """Return the behavioral RTLIR of a non-blocking assignment If the given AugAssign is not non-blocking assignment, throw PyMTLSyntaxError """ if isinstance( node.op, ast.LShift ): value = s.visit( node.value ) targets = [ s.visit( node.target ) ] ret = bir.Assign( targets, value, blocking = False ) ret.ast = node return ret raise PyMTLSyntaxError( s.blk, node, 'invalid operation: augmented assignment is not non-blocking assignment!' )
def visit_BoolOp(s, node): if not type(node.op) in s.opmap: raise PyMTLSyntaxError( s.blk, node, 'Operator ' + str(node.op) + ' is not supported!') op = s.opmap[type(node.op)] op.ast = node.op values = [] for value in node.values: values.append(s.visit(value)) ret = bir.BoolOp(op, values) ret.ast = node return ret
def visit_For(s, node): # First fill the loop_var, start, end, step fields if node.orelse != []: raise PyMTLSyntaxError(s.blk, node, "for loops cannot have 'else' branch!") if not isinstance(node.target, ast.Name): raise PyMTLSyntaxError( s.blk, node, "The loop index must be a temporary variable!") loop_var_name = node.target.id # Check whether loop_var_name has been defined before if loop_var_name in s.loop_var_env: raise PyMTLSyntaxError( s.blk, node, "Redefinition of loop index " + loop_var_name + "!") # Add loop_var to the loop variable environment s.loop_var_env.add(loop_var_name) var = bir.LoopVarDecl(node.target.id) if not isinstance(node.iter, ast.Call): raise PyMTLSyntaxError( s.blk, node, "for loops can only use range() after 'in'!") if node.iter.func.id != 'range': raise PyMTLSyntaxError( s.blk, node, "for loops can only use range() after 'in'!") args = node.iter.args if len(args) == 1: # range( end ) start = bir.Number(0) end = s.visit(args[0]) step = bir.Number(1) elif len(args) == 2: # range( start, end ) start = s.visit(args[0]) end = s.visit(args[1]) step = bir.Number(1) elif len(args) == 3: # range( start, end, step ) start = s.visit(args[0]) end = s.visit(args[1]) step = s.visit(args[2]) else: raise PyMTLSyntaxError(s.blk, node, "1~3 arguments should be given to range!") # Then visit all statements inside the loop body = [] for body_stmt in node.body: body.append(s.visit(body_stmt)) # Before we return, clear the loop variable in the loop variable # environment s.loop_var_env.remove(loop_var_name) ret = bir.For(var, start, end, step, body) ret.ast = node return ret
def visit_Name(s, node): if node.id in s.closure: # free var from closure obj = s.closure[node.id] if isinstance(obj, dsl.Component): # Component freevars are an L1 thing. if obj is not s.component: raise PyMTLSyntaxError( s.blk, node, f'Component {obj} is not a sub-component of {s.component}!' ) ret = bir.Base(obj) else: ret = bir.FreeVar(node.id, obj) ret.ast = node return ret elif node.id in s.globals: # free var from the global name space ret = bir.FreeVar(node.id, s.globals[node.id]) ret.ast = node return ret raise PyMTLSyntaxError( s.blk, node, f'Temporary variable {node.id} is not supported at L1!')
def visit_Assign(s, node): if len(node.targets) < 1: raise PyMTLSyntaxError( s.blk, node, 'At least one assignment target should be provided!') value = s.visit(node.value) targets = [s.visit(target) for target in node.targets] ret = bir.Assign(targets, value, False) # Need a handle to bir node # Determine if this is a blocking/non-blocking assignment ret.blocking = s.get_blocking(node, ret) ret.ast = node return ret
def get_blocking(s, node, bir_node): if len(bir_node.targets) == 1: if isinstance(bir_node.targets[0], bir.TmpVar): return True return s._upblk_type is bir.CombUpblk has_tmpvar = any(isinstance(n, bir.TmpVar) for n in bir_node.targets) all_tmpvar = all(isinstance(n, bir.TmpVar) for n in bir_node.targets) if has_tmpvar and not all_tmpvar: raise PyMTLSyntaxError( s.blk, node, 'all targets have to be tmpvars if any target on LHS is a tmpvar!' ) if has_tmpvar: return True else: return super().get_blocking(node, bir_node)
def visit_Call(s, node): """Return behavioral RTLIR of a method call. At L3 we need to support the syntax of struct instantiation in upblks. This is achieved by function calls like `struct( 1, 2, 0 )`. """ obj = s.get_call_obj(node) if is_bitstruct_class(obj): if len(node.args) < 1: raise PyMTLSyntaxError( s.blk, node, 'at least one value should be provided to struct instantiation!' ) values = [s.visit(arg) for arg in node.args] ret = bir.StructInst(obj, values) ret.ast = node return ret else: return super().visit_Call(node)
def visit_FunctionDef( s, node ): """Return the behavioral RTLIR of function node. We do not need to check the decorator list -- the fact that we are visiting this node ensures this node was added to the upblk dictionary through s.update() (or other PyMTL decorators) earlier! """ # Check the arguments of the function if node.args.args or node.args.vararg or node.args.kwarg: raise PyMTLSyntaxError( s.blk, node, 'Update blocks should not have arguments!' ) # Save the name of the upblk s._upblk_name = node.name # Get the type of upblk from ._upblk_type variable ret = eval( 'bir.' + s._upblk_type + '( node.name, [] )' ) for stmt in node.body: ret.body.append( s.visit( stmt ) ) ret.ast = node return ret
def visit_Name(s, node): # temporary variable if (not node.id in s.closure) and (not node.id in s.globals): # check if is a LoopVar or not if node.id in s.loop_var_env: ret = bir.LoopVar(node.id) elif node.id in s.tmp_var_env: ret = bir.TmpVar(node.id, s._upblk_name) elif isinstance(node.ctx, ast.Load): # trying to load an unregistered tmpvar raise PyMTLSyntaxError( s.blk, node, 'tmpvar ' + node.id + ' used before assignment!') else: # This is the first time we see this tmp var s.tmp_var_env.add(node.id) ret = bir.TmpVar(node.id, s._upblk_name) ret.ast = node return ret else: return super().visit_Name(node)
def visit_Break( s, node ): raise PyMTLSyntaxError( s.blk, node, 'invalid operation: break' )
def visit_Global( s, node ): raise PyMTLSyntaxError( s.blk, node, 'invalid operation: global' )
def visit_Pass( s, node ): raise PyMTLSyntaxError( s.blk, node, 'invalid operation: pass' )
def visit_Continue( s, node ): raise PyMTLSyntaxError( s.blk, node, 'invalid operation: continue' )
def visit_While( s, node ): raise PyMTLSyntaxError( s.blk, node, 'invalid operation: while' )
def visit_BoolOp(s, node): raise PyMTLSyntaxError( s.blk, node, 'Boolean operations are not translatable due to inconsistent semantics ' 'between Python and PyMTL. Please consider using bitwise &, |, and ~ instead!' )
def visit_ExtSlice( s, node ): raise PyMTLSyntaxError( s.blk, node, 'invalid operation: extslice' )