def is_rtlir_convertible(obj): """Return if `obj` can be converted into an RTLIR instance.""" pymtl_constructs = ( dsl.InPort, dsl.OutPort, dsl.Wire, Bits, dsl.Interface, dsl.Component, ) # TODO: improve this long list of isinstance check if isinstance(obj, list): while isinstance(obj, list): # Empty lists will be dropped if len(obj) == 0: return True obj = obj[0] return is_rtlir_convertible(obj) elif isinstance(obj, pymtl_constructs): return True elif is_bitstruct_inst(obj): return True elif isinstance(obj, int): return True else: return False
def _get_rtlir_dtype_struct(obj): # Vector field if isinstance(obj, Bits): return Vector(obj.nbits) # PackedArray field elif isinstance(obj, list): assert len(obj) > 0, 'list length should be greater than 0!' ref_type = _get_rtlir_dtype_struct(obj[0]) assert all( _get_rtlir_dtype_struct(i) == ref_type for i in obj ), \ f'all elements of array {obj} must have the same type {ref_type}!' dim_sizes = [] while isinstance(obj, list): assert len(obj) > 0, 'list length should be greater than 0!' dim_sizes.append(len(obj)) obj = obj[0] return PackedArray(dim_sizes, _get_rtlir_dtype_struct(obj)) # Struct field elif is_bitstruct_inst(obj): cls = obj.__class__ properties = { name: _get_rtlir_dtype_struct(getattr(obj, name)) for name in cls.__bitstruct_fields__.keys() } return Struct(cls.__name__, properties, cls) else: assert False, str(obj) + ' is not allowed as a field of struct!'
def _get_rtlir_cached(self, _obj): """Return an RTLIR instance corresponding to `obj`.""" obj = _freeze(_obj) if obj in self._rtlir_cache: # self.cache_hit += 1 # if self.cache_hit % 100 == 0: # print("hit", self.cache_hit, repr(_obj)) return self._rtlir_cache[obj] else: # self.cache_miss += 1 # if self.cache_miss % 100 == 0: # print("miss", self.cache_miss, repr(_obj)) try: for Type, handler in self._RTLIR_handlers: if isinstance(_obj, Type): ret = self._rtlir_cache[obj] = handler(NA, _obj) return ret if is_bitstruct_inst(_obj): ret = self._rtlir_cache[obj] = self._handle_Const(NA, _obj) return ret # Cannot convert `obj` into RTLIR representation assert False, f'unrecognized object {_obj}!' except AssertionError as e: msg = '' if e.args[0] is None else e.args[0] raise RTLIRConversionError(_obj, msg)
def get_rtlir_dtype(obj): """Return the RTLIR data type of obj.""" try: assert not isinstance(obj, list), \ 'array datatype object should be a field of some struct!' # Signals might be parameterized with different data types if isinstance(obj, (dsl.Signal, dsl.Const)): Type = obj._dsl.Type assert isinstance(Type, type) # Vector data type if issubclass(Type, Bits): return Vector(Type.nbits) # python int object elif Type is int: return Vector(32) # Struct data type elif is_bitstruct_class(Type): try: return _get_rtlir_dtype_struct(Type()) except TypeError: assert False, \ f'__init__() of supposed struct {Type.__name__} should take 0 argument ( you can \ achieve this by adding default values to your arguments )!' else: assert False, f'cannot convert object of type {Type} into RTLIR!' # Python integer objects elif isinstance(obj, int): # Following the Verilog bitwidth rule: number literals have 32 bit width # by default. return Vector(32) # PyMTL Bits objects elif isinstance(obj, Bits): return Vector(obj.nbits) # PyMTL BitStruct objects elif is_bitstruct_inst(obj): return _get_rtlir_dtype_struct(obj) else: assert False, 'cannot infer the data type of the given object!' except AssertionError as e: msg = '' if e.args[0] is None else e.args[0] raise RTLIRConversionError(obj, msg)
def _get_arg_str(name, obj): # Support common python types and Bits/BitStruct if isinstance(obj, (int, bool, str)): return str(obj) elif obj == None: return 'None' elif isinstance(obj, Bits): nbits = obj.nbits value = int(obj) Bits_name = f"Bits{nbits}" Bits_arg_str = f"{Bits_name}( {value} )" if Bits_name not in symbols and nbits >= 256: Bits_class = mk_bits(nbits) symbols.update({Bits_name: Bits_class}) return Bits_arg_str elif is_bitstruct_inst(obj): raise TypeError( "Do you really want to pass in an instance of " "a BitStruct? Contact PyMTL developers!") # This is hacky: we don't know how to construct an object that # is the same as `obj`, but we do have the object itself. If we # add `obj` to the namespace of `construct` everything works fine # but the user cannot tell what object is passed to the constructor # just from the code. # Do not use a double underscore prefix because that will be # interpreted differently by the Python interpreter # bs_name = ("_" if name[0] != "_" else "") + name + "_obj" # if bs_name not in symbols: # symbols.update( { bs_name : obj } ) # return bs_name elif isinstance(obj, type) and issubclass(obj, Bits): nbits = obj.nbits Bits_name = f"Bits{nbits}" if Bits_name not in symbols and nbits >= 256: Bits_class = mk_bits(nbits) symbols.update({Bits_name: Bits_class}) return Bits_name elif is_bitstruct_class(obj): BitStruct_name = obj.__name__ if BitStruct_name not in symbols: symbols.update({BitStruct_name: obj}) return BitStruct_name # FIXME formalize this elif isinstance(obj, type) and hasattr( obj, 'req') and hasattr(obj, 'resp'): symbols.update({obj.__name__: obj}) return obj.__name__ raise TypeError( f"Interface constructor argument {obj} is not an int/Bits/BitStruct/TypeTuple!" )
def _connect_signal_const(s, o1, o2): Type = o1._dsl.Type if isinstance(o2, int): if not issubclass(Type, (int, Bits)): raise InvalidConnectionError( f"We don't support connecting an integer constant " f"to non-int/Bits type {Type}") o2 = Const(Type, Type(o2), s) elif isinstance(o2, Bits): Type2 = type(o2) if not issubclass(Type, Bits): raise InvalidConnectionError( f"We don't support connecting a {Type2} constant " f"to non-Bits type {Type}") if Type is not Type2: raise InvalidConnectionError( f"Bitwidth mismatch when connecting a {Type2} constant " f"to signal {o1} with type {Type}.") o2 = Const(Type, o2, s) elif is_bitstruct_inst(o2): Type2 = type(o2) if Type is not Type2: raise InvalidConnectionError( f"We don't support connecting a {Type2} constant bitstruct" f"to non-bitstruct type {Type}") o2 = Const(Type, o2, s) else: raise InvalidConnectionError( f"\n>>> {o2} of type {type(o2)} is not a const! \n" f">>> It cannot be connected to signal {o1} of type {o1._dsl.Type}!\n" f"Suggestion: fix the RHS of connection to be an instance of {o1._dsl.Type}." ) # TODO implement connecting a const struct host = o1.get_host_component() if isinstance(o1, InPort): # connecting constant to inport should be at the parent level host = host.get_parent_object() o2._dsl.parent_obj = s s._dsl.consts.add(o2) # o2 should be a new object s._dsl.adjacency[o1].add(o2) s._dsl.adjacency[o2].add(o1) s._dsl.connect_order.append((o1, o2))
def visit_Attribute(s, node): """Return the SystemVerilog representation of an attribute. Add support for accessing struct attribute in L3. """ if isinstance(node.value.Type, rt.Signal): if isinstance(node.value.Type, rt.Const): try: obj = node.Type.get_object() except AttributeError: obj = None if obj is None: raise SVerilogTranslationError( s.blk, node, f"attribute ({node.attr}) of constant struct instance ({node.value}) is not supported!" ) else: if isinstance(obj, Bits): s.signal_expr_prologue(node) node.sexpr['s_attr'] = \ s._literal_number(obj.nbits, int(obj)) node.sexpr["s_index"] = "" attr = node.attr return s.signal_expr_epilogue(node, f"{attr}") elif is_bitstruct_inst(obj): s.signal_expr_prologue(node) node.sexpr['s_attr'] = \ s._struct_instance(node.Type.get_dtype(), obj) node.sexpr["s_index"] = "" attr = node.attr return s.signal_expr_epilogue(node, f"{attr}") else: raise SVerilogTranslationError( s.blk, node, f"attribute ({node.attr}) of constant struct instance ({node.value}) is not supported!" ) elif isinstance(node.value.Type.get_dtype(), rdt.Struct): value = s.visit(node.value) s.signal_expr_prologue(node) attr = node.attr s.check_res(node, attr) node.sexpr['s_attr'] += "__{}" node.sexpr['attr'].append(attr) return s.signal_expr_epilogue(node, f"{value}.{attr}") return super().visit_Attribute(node)
def enter( s, node ): ret = s.visit( node ) # Constant objects that are recognized # 1. int, BitsN( X ) # 2. BitsN # 3. BitStruct, BitStruct() # 4. Functions, including concat, zext, sext, etc. is_value = isinstance(ret, (int, Bits)) or is_bitstruct_inst(ret) is_type = isinstance(ret, type) and (issubclass(ret, Bits) or is_bitstruct_class(ret)) try: is_function = ret in s.pymtl_functions except: is_function = False if is_value or is_type or is_function: return ret else: return None
def _get_rtlir_uncached( self, _obj ): # global uncached """Return an RTLIR instance corresponding to `obj`.""" obj = _freeze( _obj ) try: for Type, handler in self._RTLIR_handlers: if isinstance( _obj, Type ): # uncached +=1 # if uncached % 100 == 0: # print('uncached', uncached, repr(_obj), handler) return handler( "<NA>", _obj ) if is_bitstruct_inst( _obj ): return self._handle_Const( "<NA>", _obj ) # Cannot convert `obj` into RTLIR representation assert False, f'unrecognized object {_obj}!' except AssertionError as e: msg = '' if e.args[0] is None else e.args[0] raise RTLIRConversionError( _obj, msg )
def visit_Attribute(s, node): s.signal_expr_prologue(node) Type = node.Type if isinstance(Type, rt.Const): obj = Type.get_object() if isinstance(obj, int): nbits = node.Type.get_dtype().get_length() node.sexpr['s_attr'] = f"{nbits}'d{obj}" node.sexpr['s_index'] = "" elif isinstance(obj, Bits): # nbits = obj.nbits nbits = node.Type.get_dtype().get_length() value = int(obj) node.sexpr['s_attr'] = f"{nbits}'d{value}" node.sexpr['s_index'] = "" elif is_bitstruct_inst(obj): node.sexpr['s_attr'] = s._struct_instance( node.Type.get_dtype(), obj) node.sexpr['s_index'] = "" else: raise VerilogTranslationError( s.blk, node, f"{node.attr} {obj} is not an integer!") elif isinstance(node.value, bir.Base): # The base of this attribute node is the component 's'. # Example: s.out, s.STATE_IDLE # assert node.value.base is s.component s.check_res(node, node.attr) node.sexpr['s_attr'] = "{}" node.sexpr['attr'].append(node.attr) else: raise VerilogTranslationError( s.blk, node, "sub-components are not supported at L1") return s.signal_expr_epilogue(node, node.attr)
def get_rtlir(_obj): """Return an RTLIR instance corresponding to `obj`.""" obj = _freeze(_obj) if obj in _rtlir_cache: return _rtlir_cache[obj] else: try: for Type, handler in _RTLIR_handlers: if isinstance(_obj, Type): ret = _rtlir_cache[obj] = handler("<name not available>", _obj) return ret if is_bitstruct_inst(_obj): ret = _rtlir_cache[obj] = _handle_Const( "<name not available>", _obj) return ret # Cannot convert `obj` into RTLIR representation assert False, f'unrecognized object {_obj}!' except AssertionError as e: msg = '' if e.args[0] is None else e.args[0] raise RTLIRConversionError(_obj, msg)
def gen_signal_expr(cur_component, signal): """Return an RTLIR signal expression for the given signal.""" try: if isinstance(signal, dsl.Const): c = signal._dsl.const assert isinstance( c, ( int, Bits )) or is_bitstruct_inst( c ), \ f'{c} is not an integer/Bits/BitStruct const!' return ConstInstance(signal, c) assert isinstance(signal, dsl.Signal), f'{signal} is not supported!' # Strip off all the tokens from the signal to the component tmp = signal stack = [] if tmp.is_sliced_signal(): stack.append((construct_slice, tmp._dsl.slice)) tmp = tmp.get_parent_object() while tmp is not cur_component: if tmp._dsl._my_indices: for x in reversed(tmp._dsl._my_indices): stack.append((construct_index, x)) stack.append((construct_attr, tmp._dsl._my_name)) tmp = tmp.get_parent_object() cur_node = construct_base(tmp, tmp._dsl.my_name) for f, token in reversed(stack): cur_node = f(cur_node, token) return cur_node except AssertionError as e: msg = '' if e.args[0] is None else e.args[0] raise RTLIRConversionError(signal, msg)
def gen_signal_expr( cur_component, signal ): """Return an RTLIR signal expression for the given signal.""" def get_next_op( expr, cur_pos, my_name, full_name ): """Return the next signal operation in string `expr`. Return value: ( op, *data ) op is one of [ 'Attr', 'Slice', 'Index', 'Base', 'Done' ]. *data is one to two variables that describes the next signal operation. """ if not expr[cur_pos:]: return ( 'Done', '' ), 0 pos = len( expr ) dot_pos = expr.find( '.', cur_pos+1 ) lb_pos = expr.find( '[', cur_pos+1 ) pos = dot_pos if dot_pos != -1 and dot_pos < pos else pos pos = lb_pos if lb_pos != -1 and lb_pos < pos else pos # Attribute operation if expr[cur_pos] == '.': return ( 'Attr', expr[cur_pos+1:pos] ), pos # Index or slice operation elif expr[cur_pos] == '[': rb_pos = expr.find( ']', cur_pos ) colon_pos = expr.find( ':', cur_pos ) assert rb_pos != -1 and pos == rb_pos+1, \ f"unrecognized expression {expr}" if cur_pos < colon_pos < rb_pos: start = int( expr[cur_pos+1:colon_pos] ) stop = int( expr[colon_pos+1:rb_pos] ) return ( 'Slice', start, stop ), pos else: return ( 'Index', int(expr[cur_pos+1:rb_pos]) ), pos # The current component ( base of attribute ) else: base_pos = expr.find( full_name ) assert base_pos >= 0, \ f"cannot find the base of attribute {full_name} in {expr}" return ( 'Base', my_name ), base_pos + len( full_name ) def get_cls_inst( func_list, cur_node, ops ): """Return an IR instance of the given signal operation.""" classes = [ c for c in ( f( cur_node, *ops ) for f in func_list ) if c ] assert len(classes) <= 1, f"internal error: not unique class {classes}!" assert classes, f"internal error: no available expression nodes for {cur_node}!" return classes[0]( cur_node, *ops ) try: try: expr = signal._dsl.full_name base_comp = signal except AttributeError: # Special case for a ConstInstance because it has no name assert hasattr( signal._dsl, 'const' ), f'{signal} is not supported!' c = signal._dsl.const assert isinstance( c, ( int, Bits )) or is_bitstruct_inst( c ), \ f'{signal._dsl.const} is not an integer/Bits/BitStruct const!' return ConstInstance( signal, c ) # Get the base component base_comp = cur_component full_name = base_comp._dsl.full_name my_name = base_comp._dsl.my_name assert expr.find( full_name ) >= 0, \ f"cannot find the base of attribute {full_name} in {expr}" # Start from the base component and process one operation per iteration cur_pos, cur_node = 0, base_comp while cur_pos < len( expr ): op, next_pos = get_next_op( expr, cur_pos, my_name, full_name ) if op[0] == 'Done': break cur_node = get_cls_inst( signal_expr_classes[ op[0] ], cur_node, op[1:] ) cur_pos = next_pos return cur_node except AssertionError as e: msg = '' if e.args[0] is None else e.args[0] raise RTLIRConversionError( signal, msg )