def manage_data_widths(source: Port, sink: Port) -> bool: """This function checks if the data widths of both ports are compatible. It resolves the data widths by replacing the generics with their respective values and compares the resolved versions. The port objects aren't modified. Exception: For toplevel modules an automatic propagation of generics is attempted if the specific case is clear cut. Parameters: source and sink: Both Port objects to compare and adjust. Returns: Boolean value: True on success, else False. """ # Determine data flow direction if source.get_direction_normalized() == "in": pin = source pout = sink else: pin = sink pout = source # Get "state" of data widths (are they fixed?) pin_res = is_data_width_resolved(pin.data_width) pout_res = is_data_width_resolved(pout.data_width) # If fixed and matching -> OK if pin_res and pout_res: if pin.data_width == pout.data_width: return True LOG.debug( "Data widths of '%s' and '%s' not resolved, resolving...", pin.code_name, pout.code_name, ) # Evaluate data widths pin_rdw = resolve_data_width(pin) pout_rdw = resolve_data_width(pout) # Do they match now? Yes -> OK if pin_rdw == pout_rdw: LOG.debug("Data widths resolved successfully!") return True # Else: Warn user; automatic adjustment requires more development! # The simple "beta" version caused more problems than it was worth... # For toplevel modules (AXI Master / Slave): # Attempt automatic management of the generics in very simple cases # as it is inconvenient for the user to manually resolve toplevel # problems (especially for auto-instantiated modules like AXI_Masters) pin_mod = AsModule.get_parent_module(pin) pout_mod = AsModule.get_parent_module(pout) # If either port's module is directly instantiated in toplevel if (pin_mod.modlevel < 2) or (pout_mod.modlevel < 2): igen = pin.generics ogen = pout.generics # If both ports have exactly one generic if len(igen) == 1 and len(ogen) == 1: igen = igen[0] ogen = ogen[0] # Store current values igen_v = igen.value ogen_v = ogen.value # Propagate the source generic to the sink port igen.value = ogen pin_rdw = resolve_data_width(pin) # Did that resolve the difference? if pin_rdw == pout_rdw: LOG.debug( ("Generic Auto-Propagation: For ports '%s' and " "'%s' of modules '%s' and '%s': Set value of " "generic '%s' to generic '%s' of module '%s'."), pout.code_name, pin.code_name, pin_mod.name, pout_mod.name, igen.code_name, ogen.code_name, pout_mod.name, ) return True # If not, reverse the propagation and try the other way around igen.value = igen_v ogen.value = igen pin_rdw = resolve_data_width(pin) pout_rdw = resolve_data_width(pout) # Did that work? if pin_rdw == pout_rdw: LOG.debug( ("Generic Auto-Propagation: For ports '%s' and " "'%s' of modules '%s' and '%s': Set value of " "generic '%s' to generic '%s' of module '%s'."), pout.code_name, pin.code_name, pin_mod.name, pout_mod.name, ogen.code_name, igen.code_name, pin_mod.name, ) return True # If not, reverse the propagation and print the user warning ogen.value = ogen_v pout_rdw = resolve_data_width(pout) # Gather some data to generate a useful user message # Involved Generic names all_gens = [] all_gens.extend(pout.generics) all_gens.extend(pin.generics) all_gens = [gen.code_name for gen in all_gens] # Module names pout_mod = pout_mod.name pin_mod = pin_mod.name # Data width strings pout_dw = Port.data_width_to_string(pout.data_width) pout_rdw = Port.data_width_to_string(pout_rdw) pin_dw = Port.data_width_to_string(pin.data_width) pin_rdw = Port.data_width_to_string(pin_rdw) # User warning message LOG.error( ("Data widths between ports '%s' and '%s' of modules " "'%s' and '%s' differ and must be adjusted manually!\n" "Data widths: For port '%s': (%s) - resolved as (%s) | " "For port '%s': (%s) - resolved as (%s).\n" "Use generics '%s' of the respective modules."), pout.code_name, pin.code_name, pout_mod, pin_mod, pout.code_name, pout_dw, pout_rdw, pin.code_name, pin_dw, pin_rdw, all_gens, ) # Special case for "slv_reg_interface" connections to as_regmgr if (pin.parent.type == "slv_reg_interface") and (pout.parent.type == "slv_reg_interface"): if pin_mod.entity_name == "as_regmgr": pin.data_width = copy(pout.data_width) return True if pout_mod.entity_name == "as_regmgr": pout.data_width = copy(pin.data_width) return True return False
def __instantiate_module__(self, module: AsModule) -> Sequence[str]: """Generate VHDL code as a list of strings to instantiate 'module'. Handles generic assignment and port mapping.""" out = [ "\n -- Instantiate module {}:\n".format(module.name), " {} : entity {}\n".format( "as_main_impl" if module.name == "as_main" else module.name, module.entity_name, ), ] gen_str = [] if module.generics: gen_str.append(" generic map(\n") for tgen in module.generics: # If the generic was set by the user in the generator, # use that value if isinstance(tgen.value, Generic): ret = tgen.value.code_name else: ret = self.__get_printable_generic_value__(tgen) # Skip generics that use the default value if ret == tgen.default_value: continue gen_str.append(" {} => {},\n".format(tgen.code_name, ret)) if len(gen_str) > 1: # Replace the comma in the last generic mapping with a brace ')' gen_str[-1] = ")".join(gen_str[-1].rsplit(",", maxsplit=1)) out.extend(gen_str) out.append(" port map(\n") full_port_list = module.get_full_port_list(include_signals=False) # For every port of this module: for port in full_port_list: # Target of the port map target = None # Determine the format for this ports port map line if full_port_list.index(port) < len(full_port_list) - 1: templ_str = " {} => {},\n" else: templ_str = " {} => {});\n" # Port mapping target is the port's glue signal if port.glue_signal and isinstance(port.glue_signal, GlueSignal): glue = port.glue_signal target = glue.code_name # If this glue signal should be included in the signal list if glue.is_signal: # Add the glue signal to the signal list # Assemble vhdl signal declaration string glue_signal_str = " signal {} : {};\n".format( glue.code_name, as_help.get_printable_datatype(glue) ) # Make sure the same glue signal is not declared twice if glue_signal_str not in self.signal_list: self.signal_list.append(glue_signal_str) else: # If no glue signal present: # Port mapping target is one of the connected ports, # depending on port direction target = ( port.incoming if port.get_direction_normalized() == "in" else port.outgoing[0] if port.outgoing else None ) # If the target is a Port object: use the code_name as target if isinstance(target, Port): target = target.code_name # For strings: use the string as is (eg. for static values) elif isinstance(target, str): target = target else: target = None if not target: # If the target is 'None': get neutral value if port.get_direction_normalized() == "in": target = port.get_neutral_value() else: target = "open" # And warn the user of an unconnected port if port.optional: LOG.debug( ( "Optional port '%s' of module '%s' was " "left unconnected, automatically set to [%s]!" ), port.code_name, AsModule.get_parent_module(port).name, target, ) else: LOG.info( ( "Port '%s' of module '%s' was left " "unconnected, automatically set to [%s]!" ), port.code_name, AsModule.get_parent_module(port).name, target, ) # Insert the values in the format string and add to the return list out.append(templ_str.format(port.code_name, target)) return out