def remove_port_connection(source: Port, sink: Port): """! @brief Remove/Undo a connection between two Ports.""" source.outgoing.remove(sink) sink.incoming = None source.glue_signal = None sink.glue_signal = None sink.set_connected(False)
def __init__(self, name: str, parent: AsModule, sub_modules: list): super().__init__(name) self.assign_to(parent) if self.parent is not None: self.parent.modules.append(self) self.description = ( "ASTERICS module group '{}' file generated by Automatics" ).format(self.name) self.signals = [] self.constants = [] self.vhdl_libraries = ["helpers"] self.modules = sub_modules self.static_code = {"signals": [], "body": []} self.dynamic_code_generators = [self.__generate_register_vhdl__] self.bundles = {"and": [], "or": [], "xor": [], "xnor": []} self.register_if = None self.reg_assign_map_control = {} self.reg_assign_map_status = {} self.register_count = 0 self.register_config = dict() self.regmod = None # Add standard reset and clk signals and ports self.add_standard_port(Port("reset", port_type="external")) self.add_standard_port(Port("clk", port_type="external")) self.get_port("reset").overwrite_rule("external_port", "none")
def test_add_port_w_pre_suffix(self): LOG.debug("*UNITTEST* <running test_add_port_w_pre_suffix>") # Test with suffixes # Start with fresh regif for this test self.regif = SlaveRegisterInterface() testport_p = Port( code_name="right_slv_ctrl_reg_42", name="slv_ctrl_reg", data_type="slv_reg_data", ) testport_n = Port(name="slv_reg_config", direction="out", data_type="slv_reg_config_table") # Add a port with suffix and prefix. This should work regardless self.assertTrue(self.regif.add_port(testport_p)) testport_p = Port( code_name="right_slv_reg_modify_42", name="slv_reg_modify", data_type="std_logic_vector", direction="out", ) # Add another port using the same fixes. This should work self.assertTrue(self.regif.add_port(testport_p)) # Try to add a port without fixes. self.assertFalse(self.regif.add_port(testport_n)) testport_n = Port( code_name="42_slv_reg_config_right", name="slv_reg_config", direction="out", data_type="slv_reg_config_table", ) # Another negative test using the wrong fixes self.assertFalse(self.regif.add_port(testport_n))
def define_signal( self, name: str, data_type: str = "std_logic", data_width: tuple = None, fixed_value=None, ) -> GenericSignal: """! @brief Define, create and add a signal object to this module group. @param name The name for the signal to create @param data_type The data type to set for the signal @param data_width The data width to define for the data type (optional) @param fixed_value (optional) Set a fixed value to assign to the signal """ if data_width: data_width = Port.DataWidth(*data_width) else: data_width = Port.DataWidth(1, None, None) signal = GenericSignal(name, data_type=data_type, data_width=data_width) if fixed_value: glue = GlueSignal( name=fixed_value, code_name=fixed_value, port_type=signal.port_type, data_width=signal.data_width, optional=False, ) glue.is_signal = False signal.glue_signal = glue signal.incoming.append(glue) glue.set_connected() signal.set_connected() # Assign signal to this module group and return it if self.add_signal(signal): return signal return None
def __init__(self): super().__init__("simple_ram_interface") self.add_port(Port("wr_en")) self.add_port(Port("wr_addr", data_type="std_logic_vector", data_width=Port.DataWidth(a="ADDR_WIDTH - 1", sep="downto", b=0))) self.add_port(Port("rd_addr", data_type="std_logic_vector", data_width=Port.DataWidth(a="ADDR_WIDTH - 1", sep="downto", b=0))) self.add_port(Port("din", data_type="std_logic_vector", data_width=Port.DataWidth(a="DATA_WIDTH - 1", sep="downto", b=0))) self.add_port(Port("dout", direction="out", data_type="std_logic_vector", data_width=Port.DataWidth(a="DATA_WIDTH - 1", sep="downto", b=0)))
def test_add_port_wo_pre_suffix(self): LOG.debug("*UNITTEST* <running test_add_port_wo_pre_suffix>") # Test without pre-/suffixes testport_p = Port(name="slv_ctrl_reg", data_type="slv_reg_data") testport_n = Port(name="slv_reg_config", direction="out", data_type="slv_reg_config_table") # self.regif.list_ports() self.assertTrue(self.regif.add_port(testport_p)) self.assertFalse(self.regif.add_port(testport_p)) self.assertFalse(self.regif.add_port(testport_n))
def __init__(self): super().__init__("slv_reg_interface") self.add_port(Port("slv_ctrl_reg", data_type="slv_reg_data")) self.add_port( Port("slv_status_reg", direction="out", data_type="slv_reg_data")) self.add_port( Port("slv_reg_modify", direction="out", data_type="std_logic_vector")) self.add_port( Port("slv_reg_config", direction="out", data_type="slv_reg_config_table"))
def test_get_ports(self): LOG.debug("*UNITTEST* <running test_get_ports>") # Get the port list ports = self.mut.get_port_list() # Checks for the found ports: # Check the number of ports, name, direction, data type and data width self.assertEqual(len(ports), 7) self.assertEqual( [port.code_name for port in ports], [ "clk", "reset", "ready", "mem_out_data", "wideport", "invwideport", "expdatatest", ], ) self.assertEqual( [port.direction for port in ports], ["in", "inout", "out", "out", "out", "in", "out"], ) self.assertEqual( [port.data_type for port in ports], [ "std_logic", "std_logic", "std_logic", "std_logic_vector", "bitvector", "vector", "anothervector", ], ) sdwtemp = Port.DataWidth(1, None, None) self.assertEqual( [port.data_width for port in ports], [ sdwtemp, sdwtemp, sdwtemp, Port.DataWidth("MEMORY_DATA_WIDTH - 1", "downto", 0), Port.DataWidth(1023, "downto", 0), Port.DataWidth(0, "to", "24 * 11 - 9 + MAX_REGS_PER_MODULE"), Port.DataWidth(65535, "downto", 0), ], )
def parse_gwindow_type(width_str: str, generics: list) -> list: """Parse the generic_window custom VHDL data type.""" gendict = {} for gen in generics: gendict[gen.code_name] = gen.get_value() out = [] parens = width_str.lower().split(",") for par in parens: dt_pos = par.find(" downto ") to_pos = par.find(" to ") if dt_pos > -1: spliton = "downto" elif to_pos > -1: spliton = "to" else: continue values = par.split(spliton, 1) value0 = eval_vhdl_expr(values[0].strip().upper(), "data width", gendict) value1 = eval_vhdl_expr(values[1].strip().upper(), "data width", gendict) out.append(Port.DataWidth(a=value0, sep=spliton, b=value1)) return out
def resolve_data_width(port: Port) -> tuple: """Analyse the data width of port and replace generics in the data width with the value of matching generics found in the module the port belongs to.""" # Is it already resolved? if is_data_width_resolved(port.data_width): return port.data_width # "Variable" dictionary for 'eval_data_width' gvals = {} for gen in port.generics: # Get value val = gen.get_value(top_default=False) # If no static value is available, # use the code name of the linked generic if val is None: val = getattr(gen.value, "code_name", None) # If that couldn't be fetched, skip this generic if val is None: continue gvals[gen.code_name] = val # Evaluate the data width new_data_width = as_help.eval_data_width(port, gvals) # If the evaluation could not complete if not is_data_width_resolved(new_data_width): LOG.info( ("Can't automatically resolve data width of '%s' in '%s'. " "Generic(-s) in '%s' have no value set or is(are) external!"), port.code_name, port.parent.name, port.data_width_to_string(port.data_width), ) return port.data_width return new_data_width
def set_input(self, input_ref: AsConnectionRef): """Set the data input reference for this layer. Each layer's input reference should be @ (0,0), with the layer.offset signifying the layer's offset from the base layer.""" port = input_ref.port # Check data widths if the port's data width is already fixed if is_data_width_resolved(port.data_width): port_data_width = Port.data_width_to_string(port.data_width) if self.data_width != port_data_width: LOG.error( ("Incompatiple layer input detected! '%s'(%s) -> " "'%s'(%s)"), port.code_name, port_data_width, self.name, self.data_width, ) raise AsConnectionError( "Layer and input port data widths differ!") if ((input_ref.row < self.parent.rows) and (input_ref.col < self.parent.columns) and (input_ref.row >= 0 and input_ref.col >= 0)): self.input = input_ref LOG.debug("Set input for layer '%s' to %s.", self.name, str(input_ref)) else: LOG.error("Input '%s' is out of bounds!", str(input_ref))
def assign_port_to_register( self, register_num: int, port: Port, to_bit_index: int ) -> bool: """! @brief Assign a Port as a part of a register as a data sink. @param register_num Define the number of the register to assign to @param port The Port to assign to the register @param to_bit_index The bit offset to assign to. register bit map: (31 downto 0) """ if to_bit_index > 31 or to_bit_index < 0: LOG.error("Index for register assignment is out of bounds!") raise ValueError(to_bit_index, "Out of bounds!", port) # If the register index does not have a register associated, add them while self.register_count <= register_num: if register_num == self.register_count: self.add_register(Register.status) else: # Empty registers for empty spots self.add_register(Register.none) # Update register type if necessary if self.register_config[register_num] == "AS_REG_NONE": self.modify_register_type(register_num, Register.status) elif self.register_config[register_num] == "AS_REG_CONTROL": self.modify_register_type(register_num, Register.both) # Get or create the register assignment signal try: reg_signal = self.reg_assign_map_status[register_num] except KeyError: reg_signal = self.define_signal( "s_register_id{}_status".format(register_num), data_type="std_logic_vector", data_width=Port.DataWidth(31, "downto", 0), ) self._assign_to_register(register_num, reg_signal) # Get or create the source signal if isinstance(port, GenericSignal): source = port else: self.__update_generics_list__() port.glue_signal = self.define_signal_based_on_port(port) self.chain.connect(port, port.glue_signal) source = port.glue_signal # Assign the source signal to the register assignment signal reg_signal.assign_to_this_vector(source, to_bit_index)
def setUp(self): self.regif = SlaveRegisterInterface() testports = [ Port(name="slv_status_reg", data_type="slv_reg_data", direction="out"), Port(name="slv_reg_modify", data_type="std_logic_vector", direction="out"), Port(name="slv_reg_config", data_type="slv_reg_config_table", direction="out"), ] self.regif.ports = testports self.regif.config = Constant( name="slave_register_configuration", value='("11","11","01","01","01","10","10","00");', )
def __init__(self): super().__init__(self.INTERFACE_TYPE_NAME) self.add_port(Port("slv_ctrl_reg", data_type="slv_reg_data")) self.add_port( Port("slv_status_reg", direction="out", data_type="slv_reg_data") ) self.add_port( Port( "slv_reg_modify", direction="out", data_type="std_logic_vector" ) ) self.add_port( Port( "slv_reg_config", direction="out", data_type="slv_reg_config_table", ) )
def __get_constant__(self, file_obj) -> str: # Make sure the current line is the complete constant definition if ";" not in self.line: self.line = self.line.strip(" \n") + self.__next_clean_statement__( file_obj) # Clean current line, remove the 'constant' keyword and split on ':' words = self.line.replace("constant", "", 1).strip(" \n").split(":", 1) if len(words) > 1: # Was there a ':' in this line? # If so, we can extract the constant's name name = words[0].strip() # Split on value assignment words = words[1].strip().split(":=", 1) if len(words) > 1: # Was there a ':=' in this line? # If so, we can extract both the data type and value. data_type = words[0].strip() value = words[1].strip(" ;").upper() data_width = Port.DataWidth(1, None, None) # If the data type is a vector (contains a "(a to/downto b)") if "(" in data_type: words = data_type.strip().split("(", maxsplit=1) data_type = words[0].strip() # Get the data width (remove closing bracket) data_width = self.__get_data_width__(words[1][:-1]) # That's all infos for this constant! Create and add it: self.found_constants.append( Constant( code_name=name, data_type=data_type, value=value, data_width=data_width, )) LOG.debug( "Found constant '%s' of type '%s' with value '%s'", name, data_type, value, ) return "" LOG.error( ("Malformed 'constant' declaration: '%s' in '%s': " "Missing ':='!"), self.line, file_obj.name, ) return "malformed constant declaration" LOG.error( ("Malformed 'constant' declaration: '%s' in '%s': " "Missing ':'!"), self.line, file_obj.name, ) return "malformed constant declaration"
def __init__( self, name: str = "", code_name: str = "", port_type: str = "glue_signal", data_type: str = "std_logic", optional: bool = False, data_width=Port.DataWidth(1, None, None), ): super().__init__(name, code_name, port_type, data_type, optional, data_width)
def resolve_generic(port: Port) -> bool: """! @brief Make sure Generics within port's data width exist in its parent module. This method makes sure that the Generics in port's data width exist in the VDHL entity of port's entity. Port may also be a GlueSignal Process: 1. Extract Generic strings from port's data width 2. Match those strings to Generic objects in Port and the parent module 3. Check if the Generic value is set; Check for and match linked Generic 4. If necessary, substitute the Generic(s) with a match in group module 5. If possible, use the defined value of the Generic 6. Update port's data width and try to evaluate it. @param port: The data width attribute of this port will be resolved. @return True if the resolve function ran, False if nothing was done.""" port.data_width = __resolve_generic__(port.data_width, port) try: port.line_width = __resolve_generic__(port.line_width, port) return all( (dw.is_resolved() for dw in (port.data_width, port.line_width))) except AttributeError: pass return port.data_width.is_resolved()
def test_is_complete(self): LOG.debug("*UNITTEST* <running test_is_complete>") # setUp() adds all required ports except for one, this should fail self.assertFalse(self.regif.is_complete()) # Manually (not using add_port()) add the missing port tport = REGIF_PORTS[1] self.regif.ports.append( Port(name=tport.name, direction=tport.direction, data_type=tport.data_type)) # Now the regif should report itself as complete self.assertTrue(self.regif.is_complete())
def resolve_data_width(port: Port): """! @brief Resolves any equations and generic values in port's data width. Analyse the data width of port and replace generics in the data width with the value of matching generics found in the module the port belongs to.""" if isinstance(port.data_width, list): return [__resolve_data_width__(dw, port) for dw in port.data_width] if getattr(port, "line_width", False): port.line_dw = __resolve_data_width__(port.line_width, port) return __resolve_data_width__(port.data_width, port)
def __port_set_value__(self, port: Port, value: str): if (port.port_type + "_port") in Port.rule_conditions: port.add_rule( (port.port_type + "_port"), "set_value({})".format(value) ) else: port.add_rule("source_present", "set_value({})".format(value)) port.remove_condition("both_present") if port.direction == "in": port.in_entity = False
def define_port( self, name: str, code_name: str = "", direction: str = "in", data_type: str = "std_logic", data_width: tuple = None, fixed_value: str = "", ) -> Port: """! @brief Add a port to this module group. @param name: The ports base name (without pre- or suffixes) @param code_name: The ports name as it appears in VHDL code. [name] @param direction: Direction or data ['in'], 'out' or 'inout'. @param data_type: The ports VHDL data type. [std_logic] @param data_width: The width of vector data types. Use a tuple to define. Example: (7, 'downto', 0); [None] @param fixed_value: Optionally set a fixed value for the port. The value of this parameter is directly copied into code! @return The created GenericSignal object.""" if not code_name: code_name = name if data_width: data_width = Port.DataWidth(*data_width) else: data_width = Port.DataWidth(1, None, None) port = Port( name, code_name, direction, port_type="external", data_type=data_type, optional=False, data_width=data_width, ) if fixed_value: self.__port_set_value__(port, fixed_value) self.add_port(port) return port
def _update_register_params(self): # Update config constant value self.register_if.config.value = ( as_help.generate_register_config_value_string(self.register_config) ) # Update register interface port's data width for port in ittls.chain( self.register_if.ports, [self.regmod, self.register_if.config] ): port.data_width = Port.DataWidth(0, "to", self.register_count - 1) # Update register interface parameters self.register_if.config_applied = False self.register_if.__decode_slvreg_table__()
def __init__(self): super().__init__("iic_interface") self.add_port(Port("scl_in", direction="in")) self.add_port(Port("scl_out", direction="out")) self.add_port(Port("scl_out_enable", direction="out")) self.add_port(Port("sda_in", direction="in")) self.add_port(Port("sda_out", direction="out")) self.add_port(Port("sda_out_enable", direction="out")) self.to_external = True
def resolve_data_width(data_width: Port.DataWidth, generics: list) -> list: """! @brief Resolve data_width using a list of generics.""" if data_width.is_resolved(): return data_width gendict = {} for gen in generics: gendict[gen.code_name] = gen.get_value() spliton = data_width.sep value0 = eval_vhdl_expr( str(data_width.a).strip().upper(), "data width", gendict) value1 = eval_vhdl_expr( str(data_width.b).strip().upper(), "data width", gendict) return Port.DataWidth(a=value0, sep=spliton, b=value1)
def __get_data_width__(parens: str) -> str: parens = parens[1:-1] # Determine the "direction" of the data width assignment dt_pos = parens.find(" downto ") to_pos = parens.find(" to ") if dt_pos > -1: spliton = "downto" elif to_pos > -1: spliton = "to" else: return "" values = parens.split(spliton, 1) value0 = eval_vhdl_expr(values[0].strip().upper(), "data width") value1 = eval_vhdl_expr(values[1].strip().upper(), "data width") return Port.DataWidth(a=value0, sep=spliton, b=value1)
def __init__( self, name: str = "", code_name: str = "", port_type: str = "signal", data_type: str = "std_logic", optional: bool = False, data_width=Port.DataWidth(1, None, None), ): super().__init__(name, code_name, "inout", port_type, data_type, optional, data_width) self.incoming = [] self.is_signal = True self.vector_map_incoming = dict() self.vector_map_outgoing = dict() self.vector_assignment_tasks = []
def build_inout_vectors(self) -> GenericSignal: # Get module ports mod_in_port = self.module.get_port("buff_in") mod_out_port = self.module.get_port("data_out") mod_line_port = self.module.get_port("line_out") foreach(self.module.get_full_port_list(), resolve_generic) # Create intermediate vector signals sig_name = self.name + "_data_" self.in_signal = self.pipe.define_signal( sig_name + "in", data_type="std_logic_vector", data_width=(self.bit_width_in - 1, "downto", 0), ) self.out_signal = self.pipe.define_signal( sig_name + "out", data_type="std_logic_vector", data_width=(self.bit_width_in - 1, "downto", 0), ) # Connect them to the buffers input and output self.pipe.chain.__connect__(self.in_signal, mod_in_port, top=self.pipe) self.pipe.chain.__connect__(mod_out_port, self.out_signal, top=self.pipe) # Define vector signal connections for source in self.inputs: self.in_signal.assign_to_this_vector(source.port, source.start_index) for target in self.outputs: self.out_signal.assign_from_this_vector(target.port, target.start_index) # Create line port signal self.line_signal = self.pipe.define_signal( self.name + "_line_data", data_type="t_generic_line", data_width=(self.bit_width_in - 1, "downto", 0), ) setattr( self.line_signal, "line_width", Port.DataWidth(0, "to", self.window_width - 1), ) # Connect to buffer module line port self.pipe.__connect__(self.line_signal, mod_line_port) return self.line_signal
def __init__(self): super().__init__(self.INTERFACE_TYPE_NAME) self.add_port(Port("reset_n", direction="out")) self.add_port(Port("powerdown", direction="out")) self.add_port(Port("pixclk")) self.add_port(Port("frame_valid")) self.add_port(Port("line_valid")) self.add_port( Port( "data", data_type="std_logic_vector", data_width=Port.DataWidth("SENSOR_DATA_WIDTH - 1", "downto", 0), ) ) self.set_prefix_suffix("sensor_", "") self.to_external = True
def convert_window_port_to_vector(self) -> list: """! @brief Convert as_window to only use standard data types. If this module wrapper contains a window module, the window port's data type must be converted from t_generic_window to std_logic_vector. Generates the conversion code and adds the necessary signals within the wrapper VHDL file (conversion from vector to window type) and converts the port data type.""" # If we wrap a window module, we need to convert t if not isinstance(self.modules[0], AsWindowModule): return None mod = self.modules[0] window_ports = [] conversion_template = ( "f_convert_vector_to_generic_window({vector_name}, {x}, {y})") # Xilinx Vivado OOC Synthesis can't deal with custom data types # For all window interfaces, take the window port using the # t_generic_window data type and convert it to a std_logic_vector # Within this wrapper, take the vectorized port and convert it back # to a t_generic_window port to pass to the filter module. # The conversion from window to vector is done in the 2D Pipeline class for winter in getattr(self, "window_interfaces"): wport = winter.window_port window_dims = winter.window window_elements = window_dims.x * window_dims.y new_vector_width = (window_elements * wport.data_width.get_bit_width()) wport.data_type = "std_logic_vector" wport.data_width = Port.DataWidth(new_vector_width - 1, "downto", 0) mport = mod.get_port(wport.code_name, suppress_error=True) sig = self.define_signal( mport.code_name + "_reconv", data_type=mport.data_type, data_width=mport.data_width, fixed_value=conversion_template.format( vector_name=wport.code_name, x=window_dims.x, y=window_dims.y, ), ) sig.window_config = wport.window_config wport.window_config = None mport.incoming = sig window_ports.append(wport) return window_ports
def assign_register_to_port( self, register_num: int, port: Port, from_bit_index: int ): """! @brief Assign part of a register as a data source to a Port. @param register_num Define the number of the register to assign from @param port The Port to assign the register value to @param from_bit_index The bit offset to assign from. register bit map: (31 downto 0)""" # If the register index does not have a register associated, add them while self.register_count <= register_num: if register_num == self.register_count: self.add_register(Register.control) else: # Empty registers for empty spots self.add_register(Register.none) # Update register type if necessary if self.register_config[register_num] == "AS_REG_NONE": self.modify_register_type(register_num, Register.control) elif self.register_config[register_num] == "AS_REG_STATUS": self.modify_register_type(register_num, Register.both) # Get or create the register assignment signal try: reg_signal = self.reg_assign_map_control[register_num] except KeyError: reg_signal = self.define_signal( "s_register_id{}_control".format(register_num), data_type="std_logic_vector", data_width=Port.DataWidth(31, "downto", 0), ) self._assign_from_register(register_num, reg_signal) self.__update_generics_list__() if isinstance(port, GenericSignal): target = port else: sig = self.define_signal_based_on_port(port) self.chain.connect(port, sig) target = sig # Assign from the register to the target signal reg_signal.assign_from_this_vector(target, from_bit_index)