/*********************************************** * This file includes functions to generate * Verilog submodules for multiplexers. * including both fundamental submodules * such as a branch in a multiplexer * and the full multiplexer **********************************************/ #include #include /* Headers from vtrutil library */ #include "build_module_graph_utils.h" #include "build_mux_modules.h" #include "circuit_library_utils.h" #include "decoder_library_utils.h" #include "module_manager.h" #include "module_manager_utils.h" #include "mux_graph.h" #include "mux_utils.h" #include "openfpga_naming.h" #include "openfpga_reserved_words.h" #include "vtr_assert.h" #include "vtr_log.h" #include "vtr_time.h" /* begin namespace openfpga */ namespace openfpga { /********************************************************************* * Generate structural Verilog codes (consist of transmission-gates or * pass-transistor) modeling an branch circuit * for a multiplexer with the given size * * +----------+ * input[0] --->| tgate[0] |-+ * +----------+ | * | * +----------+ | * input[1] --->| tgate[1] |-+--->output[0] * +----------+ | * | * ... ... | * | * +----------+ | * input[i] --->| tgate[i] |-+ * +----------+ *********************************************************************/ static void build_cmos_mux_branch_body( ModuleManager& module_manager, const CircuitLibrary& circuit_lib, const CircuitModelId& tgate_model, const ModuleId& mux_module, const ModulePortId& module_input_port, const ModulePortId& module_output_port, const ModulePortId& module_mem_port, const ModulePortId& module_mem_inv_port, const MuxGraph& mux_graph) { /* Get the module id of tgate in Module manager */ ModuleId tgate_module_id = module_manager.find_module(circuit_lib.model_name(tgate_model)); VTR_ASSERT(ModuleId::INVALID() != tgate_module_id); /* Get model ports of tgate */ std::vector tgate_input_ports = circuit_lib.model_ports_by_type(tgate_model, CIRCUIT_MODEL_PORT_INPUT, true); std::vector tgate_output_ports = circuit_lib.model_ports_by_type(tgate_model, CIRCUIT_MODEL_PORT_OUTPUT, true); VTR_ASSERT(3 == tgate_input_ports.size()); VTR_ASSERT(1 == tgate_output_ports.size()); /* Find the module ports of tgate module */ /* Input port is the data path input of the tgate, whose size must be 1 ! */ ModulePortId tgate_module_input = module_manager.find_module_port( tgate_module_id, circuit_lib.port_prefix(tgate_input_ports[0])); VTR_ASSERT(true == module_manager.valid_module_port_id(tgate_module_id, tgate_module_input)); BasicPort tgate_module_input_port = module_manager.module_port(tgate_module_id, tgate_module_input); VTR_ASSERT(1 == tgate_module_input_port.get_width()); /* Mem port is the memory of the tgate, whose size must be 1 ! */ ModulePortId tgate_module_mem = module_manager.find_module_port( tgate_module_id, circuit_lib.port_prefix(tgate_input_ports[1])); VTR_ASSERT(true == module_manager.valid_module_port_id(tgate_module_id, tgate_module_mem)); BasicPort tgate_module_mem_port = module_manager.module_port(tgate_module_id, tgate_module_mem); VTR_ASSERT(1 == tgate_module_mem_port.get_width()); /* Mem inv port is the inverted memory of the tgate, whose size must be 1 ! */ ModulePortId tgate_module_mem_inv = module_manager.find_module_port( tgate_module_id, circuit_lib.port_prefix(tgate_input_ports[2])); VTR_ASSERT(true == module_manager.valid_module_port_id(tgate_module_id, tgate_module_mem_inv)); BasicPort tgate_module_mem_inv_port = module_manager.module_port(tgate_module_id, tgate_module_mem_inv); VTR_ASSERT(1 == tgate_module_mem_inv_port.get_width()); /* Output port is the data path output of the tgate, whose size must be 1 ! */ ModulePortId tgate_module_output = module_manager.find_module_port( tgate_module_id, circuit_lib.port_prefix(tgate_output_ports[0])); VTR_ASSERT(true == module_manager.valid_module_port_id(tgate_module_id, tgate_module_output)); BasicPort tgate_module_output_port = module_manager.module_port(tgate_module_id, tgate_module_output); VTR_ASSERT(1 == tgate_module_output_port.get_width()); /* Ensure that input port size does match mux inputs */ BasicPort input_port = module_manager.module_port(mux_module, module_input_port); VTR_ASSERT(input_port.get_width() == mux_graph.num_inputs()); /* Add module nets for each mux inputs */ std::vector mux_input_nets; for (const size_t& pin : input_port.pins()) { ModuleNetId input_net = module_manager.create_module_net(mux_module); mux_input_nets.push_back(input_net); /* Configure the source for each net */ module_manager.add_module_net_source(mux_module, input_net, mux_module, 0, module_input_port, pin); } /* Ensure that output port size does match mux outputs */ BasicPort output_port = module_manager.module_port(mux_module, module_output_port); VTR_ASSERT(output_port.get_width() == mux_graph.num_outputs()); /* Add module nets for each mux outputs */ std::vector mux_output_nets; for (const size_t& pin : output_port.pins()) { ModuleNetId output_net = module_manager.create_module_net(mux_module); mux_output_nets.push_back(output_net); /* Configure the sink for each net */ module_manager.add_module_net_sink(mux_module, output_net, mux_module, 0, module_output_port, pin); } /* Ensure that mem port size does match mux outputs */ BasicPort mem_port = module_manager.module_port(mux_module, module_mem_port); VTR_ASSERT(mem_port.get_width() == mux_graph.num_memory_bits()); /* Add module nets for each mem inputs */ std::vector mux_mem_nets; for (const size_t& pin : mem_port.pins()) { ModuleNetId mem_net = module_manager.create_module_net(mux_module); mux_mem_nets.push_back(mem_net); /* Configure the source for each net */ module_manager.add_module_net_source(mux_module, mem_net, mux_module, 0, module_mem_port, pin); } /* Ensure that mem_inv port size does match mux outputs */ BasicPort mem_inv_port = module_manager.module_port(mux_module, module_mem_inv_port); VTR_ASSERT(mem_inv_port.get_width() == mux_graph.num_memory_bits()); /* Add module nets for each mem inverted inputs */ std::vector mux_mem_inv_nets; for (const size_t& pin : mem_inv_port.pins()) { ModuleNetId mem_net = module_manager.create_module_net(mux_module); mux_mem_inv_nets.push_back(mem_net); /* Configure the source for each net */ module_manager.add_module_net_source(mux_module, mem_net, mux_module, 0, module_mem_inv_port, pin); } /* Build a module following the connections in mux_graph */ /* Iterate over the inputs */ for (const auto& mux_input : mux_graph.inputs()) { /* Iterate over the outputs */ for (const auto& mux_output : mux_graph.outputs()) { /* Add the a tgate to bridge the mux input and output */ size_t tgate_instance = module_manager.num_instance(mux_module, tgate_module_id); module_manager.add_child_module(mux_module, tgate_module_id); /* Add module nets to connect the mux input and tgate input */ module_manager.add_module_net_sink( mux_module, mux_input_nets[size_t(mux_graph.input_id(mux_input))], tgate_module_id, tgate_instance, tgate_module_input, tgate_module_input_port.get_lsb()); /* if there is a connection between the input and output, a tgate will be * outputted */ std::vector edges = mux_graph.find_edges(mux_input, mux_output); /* There should be only one edge or no edge*/ VTR_ASSERT((1 == edges.size()) || (0 == edges.size())); /* No need to output tgates if there are no edges between two nodes */ if (0 == edges.size()) { continue; } /* Add module nets to connect the mux output and tgate output */ module_manager.add_module_net_source( mux_module, mux_output_nets[size_t(mux_graph.output_id(mux_output))], tgate_module_id, tgate_instance, tgate_module_output, tgate_module_output_port.get_lsb()); MuxMemId mux_mem = mux_graph.find_edge_mem(edges[0]); /* Add module nets to connect the mem input and tgate mem input */ if (false == mux_graph.is_edge_use_inv_mem(edges[0])) { /* wire mem to mem of module, and wire mem_inv to mem_inv of module */ module_manager.add_module_net_sink( mux_module, mux_mem_nets[size_t(mux_mem)], tgate_module_id, tgate_instance, tgate_module_mem, tgate_module_mem_port.get_lsb()); module_manager.add_module_net_sink( mux_module, mux_mem_inv_nets[size_t(mux_mem)], tgate_module_id, tgate_instance, tgate_module_mem_inv, tgate_module_mem_inv_port.get_lsb()); } else { /* wire mem_inv to mem of module, wire mem to mem_inv of module */ module_manager.add_module_net_sink( mux_module, mux_mem_inv_nets[size_t(mux_mem)], tgate_module_id, tgate_instance, tgate_module_mem, tgate_module_mem_port.get_lsb()); module_manager.add_module_net_sink( mux_module, mux_mem_nets[size_t(mux_mem)], tgate_module_id, tgate_instance, tgate_module_mem_inv, tgate_module_mem_inv_port.get_lsb()); } } } } /********************************************************************* * Generate Verilog codes modeling an branch circuit * for a CMOS multiplexer with the given size * Support structural and behavioral Verilog codes *********************************************************************/ static void build_cmos_mux_branch_module(ModuleManager& module_manager, const CircuitLibrary& circuit_lib, const CircuitModelId& mux_model, const std::string& module_name, const MuxGraph& mux_graph) { /* Get the tgate model */ CircuitModelId tgate_model = circuit_lib.pass_gate_logic_model(mux_model); /* Skip output if the tgate model is a MUX2, it is handled by essential-gate * generator */ if (CIRCUIT_MODEL_GATE == circuit_lib.model_type(tgate_model)) { VTR_ASSERT(CIRCUIT_MODEL_GATE_MUX2 == circuit_lib.gate_type(tgate_model)); return; } std::vector tgate_global_ports = circuit_lib.model_global_ports_by_type( tgate_model, CIRCUIT_MODEL_PORT_INPUT, true, true); /* Generate the Verilog netlist according to the mux_graph */ /* Find out the number of inputs */ size_t num_inputs = mux_graph.num_inputs(); /* Find out the number of outputs */ size_t num_outputs = mux_graph.num_outputs(); /* Find out the number of memory bits */ size_t num_mems = mux_graph.num_memory_bits(); /* Check codes to ensure the port of Verilog netlists will match */ /* MUX graph must have only 1 output */ VTR_ASSERT(1 == num_outputs); /* MUX graph must have only 1 level*/ VTR_ASSERT(1 == mux_graph.num_levels()); /* Create a Verilog Module based on the circuit model, and add to module * manager */ ModuleId mux_module = module_manager.add_module(module_name); VTR_ASSERT(true == module_manager.valid_module_id(mux_module)); /* Add module ports */ /* Add each input port */ BasicPort input_port("in", num_inputs); ModulePortId module_input_port = module_manager.add_port( mux_module, input_port, ModuleManager::MODULE_INPUT_PORT); /* Add each output port */ BasicPort output_port("out", num_outputs); ModulePortId module_output_port = module_manager.add_port( mux_module, output_port, ModuleManager::MODULE_OUTPUT_PORT); /* Add each memory port */ BasicPort mem_port("mem", num_mems); ModulePortId module_mem_port = module_manager.add_port( mux_module, mem_port, ModuleManager::MODULE_INPUT_PORT); BasicPort mem_inv_port("mem_inv", num_mems); ModulePortId module_mem_inv_port = module_manager.add_port( mux_module, mem_inv_port, ModuleManager::MODULE_INPUT_PORT); /* By default we give a structural description, * Writers can freely write the module in their styles * For instance, Verilog writer can ignore the internal structure and write in * behavioral codes */ build_cmos_mux_branch_body(module_manager, circuit_lib, tgate_model, mux_module, module_input_port, module_output_port, module_mem_port, module_mem_inv_port, mux_graph); /* Add global ports to the mux module: * This is a much easier job after adding sub modules (instances), * we just need to find all the global ports from the child modules and build * a list of it */ add_module_global_ports_from_child_modules(module_manager, mux_module); } /********************************************************************* * Generate Verilog codes modeling an branch circuit * for a RRAM-based multiplexer with the given size * Support structural and behavioral Verilog codes *********************************************************************/ static void build_rram_mux_branch_module(ModuleManager& module_manager, const CircuitLibrary& circuit_lib, const CircuitModelId& mux_model, const std::string& module_name, const MuxGraph& mux_graph) { /* Get the input ports from the mux */ std::vector mux_input_ports = circuit_lib.model_ports_by_type(mux_model, CIRCUIT_MODEL_PORT_INPUT, true); /* Get the output ports from the mux */ std::vector mux_output_ports = circuit_lib.model_ports_by_type(mux_model, CIRCUIT_MODEL_PORT_OUTPUT, true); /* Get the BL and WL ports from the mux */ std::vector mux_blb_ports = circuit_lib.model_ports_by_type(mux_model, CIRCUIT_MODEL_PORT_BLB, true); std::vector mux_wl_ports = circuit_lib.model_ports_by_type(mux_model, CIRCUIT_MODEL_PORT_WL, true); /* Generate the Verilog netlist according to the mux_graph */ /* Find out the number of inputs */ size_t num_inputs = mux_graph.num_inputs(); /* Find out the number of outputs */ size_t num_outputs = mux_graph.num_outputs(); /* Find out the number of memory bits */ size_t num_mems = mux_graph.num_memory_bits(); /* Check codes to ensure the port of Verilog netlists will match */ /* MUX graph must have only 1 output */ VTR_ASSERT(1 == num_outputs); /* MUX graph must have only 1 level*/ VTR_ASSERT(1 == mux_graph.num_levels()); /* MUX graph must have only 1 input and 1 BLB and 1 WL port */ VTR_ASSERT(1 == mux_input_ports.size()); VTR_ASSERT(1 == mux_output_ports.size()); VTR_ASSERT(1 == mux_blb_ports.size()); VTR_ASSERT(1 == mux_wl_ports.size()); /* Create a Verilog Module based on the circuit model, and add to module * manager */ ModuleId mux_module = module_manager.add_module(module_name); VTR_ASSERT(ModuleId::INVALID() != mux_module); /* Add module ports */ /* Add each global programming enable/disable ports */ std::vector prog_enable_ports = circuit_lib.model_global_ports_by_type(mux_model, CIRCUIT_MODEL_PORT_INPUT, true, true); for (const auto& port : prog_enable_ports) { /* Configure each global port */ BasicPort global_port(circuit_lib.port_prefix(port), circuit_lib.port_size(port)); module_manager.add_port(mux_module, global_port, ModuleManager::MODULE_GLOBAL_PORT); } /* Add each input port */ BasicPort input_port(circuit_lib.port_prefix(mux_input_ports[0]), num_inputs); module_manager.add_port(mux_module, input_port, ModuleManager::MODULE_INPUT_PORT); /* Add each output port */ BasicPort output_port(circuit_lib.port_prefix(mux_output_ports[0]), num_outputs); module_manager.add_port(mux_module, output_port, ModuleManager::MODULE_OUTPUT_PORT); /* Add RRAM programming ports, * RRAM MUXes require one more pair of BLB and WL * to configure the memories. See schematic for details */ BasicPort blb_port(circuit_lib.port_prefix(mux_blb_ports[0]), num_mems + 1); module_manager.add_port(mux_module, blb_port, ModuleManager::MODULE_INPUT_PORT); BasicPort wl_port(circuit_lib.port_prefix(mux_wl_ports[0]), num_mems + 1); module_manager.add_port(mux_module, wl_port, ModuleManager::MODULE_INPUT_PORT); /* Note: we do not generate the internal structure of the ReRAM-based MUX * circuit as a module graph! * This is mainly due to that the internal structure could be different * in Verilog or SPICE netlists * Leave the writers to customize this */ } /*********************************************** * Generate Verilog codes modeling an branch circuit * for a multiplexer with the given size **********************************************/ static void build_mux_branch_module(ModuleManager& module_manager, const CircuitLibrary& circuit_lib, const CircuitModelId& mux_model, const MuxGraph& mux_graph) { std::string module_name = generate_mux_branch_subckt_name( circuit_lib, mux_model, mux_graph.num_inputs(), mux_graph.num_memory_bits(), MUX_BASIS_MODULE_POSTFIX); /* Skip the module building if it is already there */ if (module_manager.valid_module_id(module_manager.find_module(module_name))) { return; } /* Multiplexers built with different technology is in different organization */ switch (circuit_lib.design_tech_type(mux_model)) { case CIRCUIT_MODEL_DESIGN_CMOS: build_cmos_mux_branch_module(module_manager, circuit_lib, mux_model, module_name, mux_graph); break; case CIRCUIT_MODEL_DESIGN_RRAM: build_rram_mux_branch_module(module_manager, circuit_lib, mux_model, module_name, mux_graph); break; default: VTR_LOGF_ERROR(__FILE__, __LINE__, "Invalid design technology of multiplexer '%s'\n", circuit_lib.model_name(mux_model).c_str()); exit(1); } } /******************************************************************** * Generate the standard-cell-based internal logic (multiplexing structure) * for a multiplexer or LUT in Verilog codes * This function will : * 1. build a multiplexing structure by instanciating standard cells MUX2 * 2. add intermediate buffers between multiplexing stages if specified. *******************************************************************/ static void build_cmos_mux_module_mux2_multiplexing_structure( ModuleManager& module_manager, const CircuitLibrary& circuit_lib, const ModuleId& mux_module, const CircuitModelId& mux_model, const CircuitModelId& std_cell_model, const vtr::vector& mux_module_input_nets, const vtr::vector& mux_module_output_nets, const vtr::vector& mux_module_mem_nets, const MuxGraph& mux_graph) { /* Get the regular (non-mode-select) sram ports from the mux */ std::vector mux_regular_sram_ports = find_circuit_regular_sram_ports(circuit_lib, mux_model); VTR_ASSERT(1 == mux_regular_sram_ports.size()); /* Find the input ports and output ports of the standard cell */ std::vector std_cell_input_ports = circuit_lib.model_ports_by_type(std_cell_model, CIRCUIT_MODEL_PORT_INPUT, true); std::vector std_cell_output_ports = circuit_lib.model_ports_by_type(std_cell_model, CIRCUIT_MODEL_PORT_OUTPUT, true); /* Quick check the requirements on port map */ VTR_ASSERT(3 == std_cell_input_ports.size()); VTR_ASSERT(1 == std_cell_output_ports.size()); /* Find module information of the standard cell MUX2 */ std::string std_cell_module_name = circuit_lib.model_name(std_cell_model); /* Get the moduleId for the submodule */ ModuleId std_cell_module_id = module_manager.find_module(std_cell_module_name); /* We must have one */ VTR_ASSERT(ModuleId::INVALID() != std_cell_module_id); /* Find the module ports of the standard cell MUX2 module */ std::vector std_cell_module_inputs; std::vector std_cell_module_input_ports; /* Input 0 port is the first data path input of the tgate, whose size must be * 1 ! */ for (size_t port_id = 0; port_id < 2; ++port_id) { std_cell_module_inputs.push_back(module_manager.find_module_port( std_cell_module_id, circuit_lib.port_prefix(std_cell_input_ports[port_id]))); VTR_ASSERT(true == module_manager.valid_module_port_id( std_cell_module_id, std_cell_module_inputs[port_id])); std_cell_module_input_ports.push_back(module_manager.module_port( std_cell_module_id, std_cell_module_inputs[port_id])); VTR_ASSERT(1 == std_cell_module_input_ports[port_id].get_width()); } /* Mem port is the memory of the standard cell MUX2, whose size must be 1 ! */ ModulePortId std_cell_module_mem = module_manager.find_module_port( std_cell_module_id, circuit_lib.port_prefix(std_cell_input_ports[2])); VTR_ASSERT(true == module_manager.valid_module_port_id(std_cell_module_id, std_cell_module_mem)); BasicPort std_cell_module_mem_port = module_manager.module_port(std_cell_module_id, std_cell_module_mem); VTR_ASSERT(1 == std_cell_module_mem_port.get_width()); /* Output port is the data path output of the standard cell MUX2, whose size * must be 1 ! */ ModulePortId std_cell_module_output = module_manager.find_module_port( std_cell_module_id, circuit_lib.port_prefix(std_cell_output_ports[0])); VTR_ASSERT(true == module_manager.valid_module_port_id( std_cell_module_id, std_cell_module_output)); BasicPort std_cell_module_output_port = module_manager.module_port(std_cell_module_id, std_cell_module_output); VTR_ASSERT(1 == std_cell_module_output_port.get_width()); /* Cache Net ids for each level of the multiplexer */ std::vector> module_nets_by_level; module_nets_by_level.resize(mux_graph.num_node_levels()); for (size_t level = 0; level < mux_graph.num_node_levels(); ++level) { /* Print the internal wires located at this level */ module_nets_by_level[level].resize(mux_graph.num_nodes_at_level(level)); } /* Build the location map of intermediate buffers */ std::vector inter_buffer_location_map = build_mux_intermediate_buffer_location_map(circuit_lib, mux_model, mux_graph.num_node_levels()); /* Add all the branch modules and intermediate buffers */ for (const auto& node : mux_graph.non_input_nodes()) { /* Get the size of branch circuit * Instanciate an branch circuit by the size (fan-in) of the node */ size_t branch_size = mux_graph.node_in_edges(node).size(); /* To match the standard cell MUX2: We should have only 2 input_nodes */ VTR_ASSERT(2 == branch_size); /* Find the instance id */ size_t std_cell_instance_id = module_manager.num_instance(mux_module, std_cell_module_id); /* Add the module to mux_module */ module_manager.add_child_module(mux_module, std_cell_module_id); /* Get the node level and index in the current level */ size_t output_node_level = mux_graph.node_level(node); size_t output_node_index_at_level = mux_graph.node_index_at_level(node); /* Set a name for the instance */ std::string std_cell_instance_name = generate_mux_branch_instance_name( output_node_level, output_node_index_at_level, false); module_manager.set_child_instance_name(mux_module, std_cell_module_id, std_cell_instance_id, std_cell_instance_name); /* Add module nets to wire to next stage modules */ ModuleNetId branch_net; if (true == mux_graph.is_node_output(node)) { /* This is an output node, we should use existing output nets */ MuxOutputId output_id = mux_graph.output_id(node); branch_net = mux_module_output_nets[output_id]; } else { VTR_ASSERT(false == mux_graph.is_node_output(node)); branch_net = module_manager.create_module_net(mux_module); } module_manager.add_module_net_source( mux_module, branch_net, std_cell_module_id, std_cell_instance_id, std_cell_module_output, std_cell_module_output_port.get_lsb()); /* Record the module net id in the cache */ module_nets_by_level[output_node_level][output_node_index_at_level] = branch_net; /* Wire the branch module memory ports to the nets of MUX memory ports */ /* Get the mems in the branch circuits */ std::vector mems; for (const auto& edge : mux_graph.node_in_edges(node)) { /* Get the mem control the edge */ MuxMemId mem = mux_graph.find_edge_mem(edge); /* Add the mem if it is not in the list */ if (mems.end() == std::find(mems.begin(), mems.end(), mem)) { mems.push_back(mem); } } /* Connect mem to mem net one by one * Note that standard cell MUX2 only needs mem but NOT mem_inv */ for (const MuxMemId& mem : mems) { module_manager.add_module_net_sink( mux_module, mux_module_mem_nets[mem], std_cell_module_id, std_cell_instance_id, std_cell_module_mem, std_cell_module_mem_port.get_lsb()); } /* Wire the branch module inputs to the nets in previous stage */ /* Get the nodes which drive the root_node */ std::vector input_nodes; for (const auto& edge : mux_graph.node_in_edges(node)) { /* Get the nodes drive the edge */ for (const auto& src_node : mux_graph.edge_src_nodes(edge)) { input_nodes.push_back(src_node); } } /* Number of inputs should match the branch_input_size!!! */ VTR_ASSERT(input_nodes.size() == branch_size); /* To match the standard cell MUX2: We should have only 2 input_nodes */ VTR_ASSERT(2 == input_nodes.size()); /* build the link between input_node[0] and std_cell_input_port[0] * build the link between input_node[1] and std_cell_input_port[1] */ for (size_t node_id = 0; node_id < input_nodes.size(); ++node_id) { /* Find the port info of each input node */ size_t input_node_level = mux_graph.node_level(input_nodes[node_id]); size_t input_node_index_at_level = mux_graph.node_index_at_level(input_nodes[node_id]); /* For inputs of mux, the net id is reserved */ if (true == mux_graph.is_node_input(input_nodes[node_id])) { /* Get node input id */ MuxInputId input_id = mux_graph.input_id(input_nodes[node_id]); module_manager.add_module_net_sink( mux_module, mux_module_input_nets[input_id], std_cell_module_id, std_cell_instance_id, std_cell_module_inputs[node_id], std_cell_module_input_ports[node_id].get_lsb()); } else { VTR_ASSERT(false == mux_graph.is_node_input(input_nodes[node_id])); /* Find the input port of standard cell */ module_manager.add_module_net_sink( mux_module, module_nets_by_level[input_node_level][input_node_index_at_level], std_cell_module_id, std_cell_instance_id, std_cell_module_inputs[node_id], std_cell_module_input_ports[node_id].get_lsb()); } } /* Identify if an intermediate buffer is needed */ if (false == inter_buffer_location_map[output_node_level]) { continue; } /* Add an intermediate buffer to mux_module if needed */ if (true == mux_graph.is_node_output(node)) { /* Output node does not need buffer addition here, it is handled outside * this function */ continue; } /* Now we need to add intermediate buffers by instanciating the modules */ CircuitModelId buffer_model = circuit_lib.lut_intermediate_buffer_model(mux_model); /* We must have a valid model id */ VTR_ASSERT(CircuitModelId::INVALID() != buffer_model); /* Create a module net which sources from buffer output */ ModuleNetId buffer_net = add_inverter_buffer_child_module_and_nets( module_manager, mux_module, circuit_lib, buffer_model, branch_net); /* Record the module net id in the cache */ module_nets_by_level[output_node_level][output_node_index_at_level] = buffer_net; } } /******************************************************************** * Generate the pass-transistor/transmission-gate -based internal logic * (multiplexing structure) for a multiplexer or LUT in Verilog codes * This function will : * 1. build a multiplexing structure by instanciating the branch circuits * generated before * 2. add intermediate buffers between multiplexing stages if specified. *******************************************************************/ static void build_cmos_mux_module_tgate_multiplexing_structure( ModuleManager& module_manager, const CircuitLibrary& circuit_lib, const ModuleId& mux_module, const CircuitModelId& circuit_model, const vtr::vector& mux_module_input_nets, const vtr::vector& mux_module_output_nets, const vtr::vector& mux_module_mem_nets, const vtr::vector& mux_module_mem_inv_nets, const MuxGraph& mux_graph) { /* Get the regular (non-mode-select) sram ports from the mux */ std::vector mux_regular_sram_ports = find_circuit_regular_sram_ports(circuit_lib, circuit_model); VTR_ASSERT(1 == mux_regular_sram_ports.size()); /* Cache Net ids for each level of the multiplexer */ std::vector> module_nets_by_level; module_nets_by_level.resize(mux_graph.num_node_levels()); for (size_t level = 0; level < mux_graph.num_node_levels(); ++level) { /* Print the internal wires located at this level */ module_nets_by_level[level].resize(mux_graph.num_nodes_at_level(level)); } /* Build the location map of intermediate buffers */ std::vector inter_buffer_location_map = build_mux_intermediate_buffer_location_map(circuit_lib, circuit_model, mux_graph.num_node_levels()); /* Add all the branch modules and intermediate buffers */ for (const auto& node : mux_graph.non_input_nodes()) { /* Get the size of branch circuit * Instanciate an branch circuit by the size (fan-in) of the node */ size_t branch_size = mux_graph.node_in_edges(node).size(); /* Get the mems in the branch circuits */ std::vector mems; for (const auto& edge : mux_graph.node_in_edges(node)) { /* Get the mem control the edge */ MuxMemId mem = mux_graph.find_edge_mem(edge); /* Add the mem if it is not in the list */ if (mems.end() == std::find(mems.begin(), mems.end(), mem)) { mems.push_back(mem); } } /* Instanciate the branch module which is a tgate-based module */ std::string branch_module_name = generate_mux_branch_subckt_name(circuit_lib, circuit_model, branch_size, mems.size(), MUX_BASIS_MODULE_POSTFIX); /* Get the moduleId for the submodule */ ModuleId branch_module_id = module_manager.find_module(branch_module_name); /* We must have one */ VTR_ASSERT(ModuleId::INVALID() != branch_module_id); /* Find the instance id */ size_t branch_instance_id = module_manager.num_instance(mux_module, branch_module_id); /* Add the module to mux_module */ module_manager.add_child_module(mux_module, branch_module_id); /* Get the node level and index in the current level */ size_t output_node_level = mux_graph.node_level(node); size_t output_node_index_at_level = mux_graph.node_index_at_level(node); /* Set a name for the instance */ std::string branch_instance_name = generate_mux_branch_instance_name( output_node_level, output_node_index_at_level, false); module_manager.set_child_instance_name( mux_module, branch_module_id, branch_instance_id, branch_instance_name); /* Get the output port id of branch module */ ModulePortId branch_module_output_port_id = module_manager.find_module_port(branch_module_id, std::string("out")); BasicPort branch_module_output_port = module_manager.module_port( branch_module_id, branch_module_output_port_id); /* Add module nets to wire to next stage modules */ ModuleNetId branch_net; if (true == mux_graph.is_node_output(node)) { /* This is an output node, we should use existing output nets */ MuxOutputId output_id = mux_graph.output_id(node); branch_net = mux_module_output_nets[output_id]; } else { VTR_ASSERT(false == mux_graph.is_node_output(node)); branch_net = module_manager.create_module_net(mux_module); } module_manager.add_module_net_source( mux_module, branch_net, branch_module_id, branch_instance_id, branch_module_output_port_id, branch_module_output_port.get_lsb()); /* Record the module net id in the cache */ module_nets_by_level[output_node_level][output_node_index_at_level] = branch_net; /* Wire the branch module memory ports to the nets of MUX memory ports */ /* Get mem/mem_inv ports of branch module */ ModulePortId branch_module_mem_port_id = module_manager.find_module_port(branch_module_id, std::string("mem")); BasicPort branch_module_mem_port = module_manager.module_port(branch_module_id, branch_module_mem_port_id); ModulePortId branch_module_mem_inv_port_id = module_manager.find_module_port(branch_module_id, std::string("mem_inv")); BasicPort branch_module_mem_inv_port = module_manager.module_port( branch_module_id, branch_module_mem_inv_port_id); /* Note that we do NOT care inverted edge-to-mem connection. * It is handled in branch module generation!!! */ /* Connect mem/mem_inv to mem/mem_inv net one by one */ for (size_t mem_id = 0; mem_id < mems.size(); ++mem_id) { module_manager.add_module_net_sink( mux_module, mux_module_mem_nets[mems[mem_id]], branch_module_id, branch_instance_id, branch_module_mem_port_id, branch_module_mem_port.pins()[mem_id]); module_manager.add_module_net_sink( mux_module, mux_module_mem_inv_nets[mems[mem_id]], branch_module_id, branch_instance_id, branch_module_mem_inv_port_id, branch_module_mem_inv_port.pins()[mem_id]); } /* Wire the branch module inputs to the nets in previous stage */ /* Get the input port id of branch module */ ModulePortId branch_module_input_port_id = module_manager.find_module_port(branch_module_id, std::string("in")); BasicPort branch_module_input_port = module_manager.module_port(branch_module_id, branch_module_input_port_id); /* Get the nodes which drive the root_node */ std::vector input_nodes; for (const auto& edge : mux_graph.node_in_edges(node)) { /* Get the nodes drive the edge */ for (const auto& src_node : mux_graph.edge_src_nodes(edge)) { input_nodes.push_back(src_node); } } /* Number of inputs should match the branch_input_size!!! */ VTR_ASSERT(input_nodes.size() == branch_size); /* build the link between input_node and branch circuit input_port[0] */ for (size_t node_id = 0; node_id < input_nodes.size(); ++node_id) { /* Find the port info of each input node */ size_t input_node_level = mux_graph.node_level(input_nodes[node_id]); size_t input_node_index_at_level = mux_graph.node_index_at_level(input_nodes[node_id]); /* For inputs of mux, the net id is reserved */ if (true == mux_graph.is_node_input(input_nodes[node_id])) { /* Get node input id */ MuxInputId input_id = mux_graph.input_id(input_nodes[node_id]); module_manager.add_module_net_sink( mux_module, mux_module_input_nets[input_id], branch_module_id, branch_instance_id, branch_module_input_port_id, branch_module_input_port.pins()[node_id]); } else { VTR_ASSERT(false == mux_graph.is_node_input(input_nodes[node_id])); module_manager.add_module_net_sink( mux_module, module_nets_by_level[input_node_level][input_node_index_at_level], branch_module_id, branch_instance_id, branch_module_input_port_id, branch_module_input_port.pins()[node_id]); } } /* Identify if an intermediate buffer is needed */ if (false == inter_buffer_location_map[output_node_level]) { continue; } /* Add an intermediate buffer to mux_module if needed */ if (true == mux_graph.is_node_output(node)) { /* Output node does not need buffer addition here, it is handled outside * this function */ continue; } /* Now we need to add intermediate buffers by instanciating the modules */ CircuitModelId buffer_model = circuit_lib.lut_intermediate_buffer_model(circuit_model); /* We must have a valid model id */ VTR_ASSERT(CircuitModelId::INVALID() != buffer_model); ModuleNetId buffer_net = add_inverter_buffer_child_module_and_nets( module_manager, mux_module, circuit_lib, buffer_model, branch_net); /* Record the module net id in the cache */ module_nets_by_level[output_node_level][output_node_index_at_level] = buffer_net; } } /********************************************************************* * This function will add nets and input buffers (if needed) * to a mux module * Module net represents the connections when there are no input buffers * mux_input_net[0] * | * v +------------ * mux_in[0] ----------->| * | * | * | * | Multiplexing * mux_input_net[i] | Structure * | | * v | * mux_in[0] ----------->| * | * * * Module net represents the connections when there are input buffers * mux_input_net[0] * | * +-----------------+ v +------------ * mux_in[0] ----->| input_buffer[0] |-----> | * +-----------------+ | * | * ... | * | Multiplexing * mux_input_net[i] | Structure * | | * +-----------------+ v | * mux_in[0] ----->| input_buffer[0] |-----> | * +-----------------+ | *********************************************************************/ static vtr::vector build_mux_module_input_buffers( ModuleManager& module_manager, const CircuitLibrary& circuit_lib, const ModuleId& mux_module, const CircuitModelId& mux_model, const MuxGraph& mux_graph) { vtr::vector mux_input_nets(mux_graph.num_inputs(), ModuleNetId::INVALID()); /* Get the input ports from the mux: * - LUT may have ports that are driven by harden logic, * which should not be included when building the mux graph */ std::vector mux_input_ports; if (CIRCUIT_MODEL_LUT == circuit_lib.model_type(mux_model)) { mux_input_ports = find_lut_circuit_model_input_port(circuit_lib, mux_model, false, false); } else { VTR_ASSERT(CIRCUIT_MODEL_MUX == circuit_lib.model_type(mux_model)); mux_input_ports = circuit_lib.model_ports_by_type( mux_model, CIRCUIT_MODEL_PORT_INPUT, true); } /* We should have only 1 input port! */ VTR_ASSERT(1 == mux_input_ports.size()); /* Get the input port from MUX module */ ModulePortId module_input_port_id = module_manager.find_module_port( mux_module, circuit_lib.port_prefix(mux_input_ports[0])); VTR_ASSERT(ModulePortId::INVALID() != module_input_port_id); /* Get the port from module */ BasicPort module_input_port = module_manager.module_port(mux_module, module_input_port_id); /* Iterate over all the inputs in the MUX graph */ for (const auto& input_node : mux_graph.inputs()) { /* Fetch fundamental information from MUX graph w.r.t. the input node */ MuxInputId input_index = mux_graph.input_id(input_node); VTR_ASSERT(MuxInputId::INVALID() != input_index); /* For last input: * Add a constant value to the last input, if this MUX needs a constant * input */ if ((MuxInputId(mux_graph.num_inputs() - 1) == mux_graph.input_id(input_node)) && (true == circuit_lib.mux_add_const_input(mux_model))) { /* Get the constant input value */ size_t const_value = circuit_lib.mux_const_input_value(mux_model); VTR_ASSERT((0 == const_value) || (1 == const_value)); /* Instanciate a VDD module (default module) * and build a net between VDD and the MUX input */ /* Get the moduleId for the buffer module */ ModuleId const_val_module_id = module_manager.find_module( generate_const_value_module_name(const_value)); /* We must have one */ VTR_ASSERT(ModuleId::INVALID() != const_val_module_id); size_t const_val_instance = module_manager.num_instance(mux_module, const_val_module_id); module_manager.add_child_module(mux_module, const_val_module_id); ModulePortId const_port_id = module_manager.find_module_port( const_val_module_id, generate_const_value_module_output_port_name(const_value)); ModuleNetId input_net = module_manager.create_module_net(mux_module); module_manager.add_module_net_source( mux_module, input_net, const_val_module_id, const_val_instance, const_port_id, 0); mux_input_nets[input_index] = input_net; continue; } /* When we do not need any buffer, create a net for the input directly */ if (false == circuit_lib.is_input_buffered(mux_model)) { ModuleNetId input_net = module_manager.create_module_net(mux_module); module_manager.add_module_net_source(mux_module, input_net, mux_module, 0, module_input_port_id, size_t(input_index)); mux_input_nets[input_index] = input_net; continue; } /* Now we need to add intermediate buffers by instanciating the modules */ CircuitModelId buffer_model = circuit_lib.input_buffer_model(mux_model); /* We must have a valid model id */ VTR_ASSERT(CircuitModelId::INVALID() != buffer_model); /* Connect the module net from branch output to buffer input */ ModuleNetId buffer_net = module_manager.create_module_net(mux_module); module_manager.add_module_net_source(mux_module, buffer_net, mux_module, 0, module_input_port_id, size_t(input_index)); /* Create a module net which sources from buffer output */ ModuleNetId input_net = add_inverter_buffer_child_module_and_nets( module_manager, mux_module, circuit_lib, buffer_model, buffer_net); mux_input_nets[input_index] = input_net; } return mux_input_nets; } /********************************************************************* * This function will add nets and input buffers (if needed) * to a mux module * Module net represents the connections when there are no output buffers * * mux_output_net[0] * ------------+ | * | v * |--------> mux_output[0] * | * | * Multiplexer | ... * Strcuture | * |--------> mux_output[i] * | ^ * | | * ------------+ mux_output_net[i] * * Module net represents the connections when there are output buffers * * mux_output_net[0] * ------------+ | * | | * | v +------------------+ * |------->| output_buffer[0] |------> mux_output[0] * | +------------------+ * | * Multiplexer | ... * Strcuture | * | +------------------+ * |------->| output_buffer[i] |------> mux_output[i] * | ^ +------------------+ * | | * | | * ------------+ mux_output_net[i] * *********************************************************************/ static vtr::vector build_mux_module_output_buffers( ModuleManager& module_manager, const CircuitLibrary& circuit_lib, const ModuleId& mux_module, const CircuitModelId& mux_model, const MuxGraph& mux_graph) { /* Create module nets for output ports */ vtr::vector mux_output_nets(mux_graph.num_outputs(), ModuleNetId::INVALID()); /* Get the output ports from the mux: * - LUT may have ports that are driven by harden logic, * which should not be included when building the mux graph * - LUT may have global output ports that are wired directly to top-level * module */ std::vector mux_output_ports; if (CIRCUIT_MODEL_LUT == circuit_lib.model_type(mux_model)) { mux_output_ports = find_lut_circuit_model_output_port(circuit_lib, mux_model, false, true); } else { VTR_ASSERT(CIRCUIT_MODEL_MUX == circuit_lib.model_type(mux_model)); mux_output_ports = circuit_lib.model_ports_by_type( mux_model, CIRCUIT_MODEL_PORT_OUTPUT, false); } /* Iterate over all the outputs in the MUX module */ for (const auto& output_port : mux_output_ports) { /* Get the output port from MUX module */ ModulePortId module_output_port_id = module_manager.find_module_port( mux_module, circuit_lib.port_prefix(output_port)); VTR_ASSERT(ModulePortId::INVALID() != module_output_port_id); /* Get the port from module */ BasicPort module_output_port = module_manager.module_port(mux_module, module_output_port_id); /* Iterate over each pin of the output port */ for (const size_t& pin : circuit_lib.pins(output_port)) { /* Fetch fundamental information from MUX graph w.r.t. the input node */ /* Deposite the last level of the graph, which is a default value */ size_t output_node_level = mux_graph.num_node_levels() - 1; /* If there is a fracturable level specified for the output, we find the * exact level */ if (size_t(-1) != circuit_lib.port_lut_frac_level(output_port)) { output_node_level = circuit_lib.port_lut_frac_level(output_port); } /* Deposite a zero, which is a default value */ size_t output_node_index_at_level = 0; /* If there are output masks, we find the node_index */ if (!circuit_lib.port_lut_output_mask(output_port).empty()) { output_node_index_at_level = circuit_lib.port_lut_output_mask(output_port).at(pin); } /* Double check the node exists in the Mux Graph */ MuxNodeId node_id = mux_graph.node_id(output_node_level, output_node_index_at_level); VTR_ASSERT(MuxNodeId::INVALID() != node_id); MuxOutputId output_index = mux_graph.output_id(node_id); /* Create the port information of the module output at the given pin * range, which is the output of buffer instance */ BasicPort instance_output_port(module_output_port.get_name(), pin, pin); /* If the output is not supposed to be buffered, create a net for the * input directly */ if (false == circuit_lib.is_output_buffered(mux_model)) { ModuleNetId output_net = module_manager.create_module_net(mux_module); module_manager.add_module_net_sink(mux_module, output_net, mux_module, 0, module_output_port_id, pin); mux_output_nets[output_index] = output_net; continue; /* Finish here */ } /* Reach here, we need a buffer, create a port-to-port map and output the * buffer instance */ /* Now we need to add intermediate buffers by instanciating the modules */ CircuitModelId buffer_model = circuit_lib.output_buffer_model(mux_model); /* We must have a valid model id */ VTR_ASSERT(CircuitModelId::INVALID() != buffer_model); /* Create a module net which sinks at buffer input */ ModuleNetId input_net = module_manager.create_module_net(mux_module); ModuleNetId output_net = add_inverter_buffer_child_module_and_nets( module_manager, mux_module, circuit_lib, buffer_model, input_net); module_manager.add_module_net_sink(mux_module, output_net, mux_module, 0, module_output_port_id, pin); mux_output_nets[output_index] = input_net; } } return mux_output_nets; } /********************************************************************* * This function will * 1. Build local encoders for a MUX module (if specified) * 2. Build nets between memory ports of a MUX module and branch circuits * This happens when local encoders are not needed * * MUX module * +--------------------- * | mux_mem_nets/mux_mem_inv_nets * | | * | v +--------- * mem-+-------->| * | | Branch Module * | | * * 3. Build nets between local encoders and memory ports of a MUX module * This happens when local encoders are needed * 4. Build nets between local encoders and branch circuits * This happens when local encoders are needed * * MUX module * +--------------------- * | * | +-------+ mux_mem_nets/mux_mem_inv_nets * | | | | * mem--+------>| | v +--------- * | | Local |-------->| * | |Encoder| | Branch * | | | | Module * | | | | * | | | | * *********************************************************************/ static void build_mux_module_local_encoders_and_memory_nets( ModuleManager& module_manager, const ModuleId& mux_module, const CircuitLibrary& circuit_lib, const CircuitModelId& mux_model, const std::vector& mux_sram_ports, const MuxGraph& mux_graph, vtr::vector& mux_mem_nets, vtr::vector& mux_mem_inv_nets) { /* Create nets here, and we will configure the net source later */ for (size_t mem = 0; mem < mux_graph.num_memory_bits(); ++mem) { ModuleNetId mem_net = module_manager.create_module_net(mux_module); mux_mem_nets.push_back(mem_net); ModuleNetId mem_inv_net = module_manager.create_module_net(mux_module); mux_mem_inv_nets.push_back(mem_inv_net); } if (false == circuit_lib.mux_use_local_encoder(mux_model)) { /* Add mem and mem_inv nets here */ size_t mem_net_cnt = 0; for (const auto& port : mux_sram_ports) { ModulePortId mem_port_id = module_manager.find_module_port( mux_module, circuit_lib.port_prefix(port)); BasicPort mem_port = module_manager.module_port(mux_module, mem_port_id); for (const size_t& pin : mem_port.pins()) { MuxMemId mem_id = MuxMemId(mem_net_cnt); /* Set the module net source */ module_manager.add_module_net_source(mux_module, mux_mem_nets[mem_id], mux_module, 0, mem_port_id, pin); /* Update counter */ mem_net_cnt++; } } VTR_ASSERT(mem_net_cnt == mux_graph.num_memory_bits()); /* Add mem and mem_inv nets here */ size_t mem_inv_net_cnt = 0; for (const auto& port : mux_sram_ports) { ModulePortId mem_inv_port_id = module_manager.find_module_port( mux_module, std::string(circuit_lib.port_prefix(port) + INV_PORT_POSTFIX)); BasicPort mem_inv_port = module_manager.module_port(mux_module, mem_inv_port_id); for (const size_t& pin : mem_inv_port.pins()) { MuxMemId mem_id = MuxMemId(mem_inv_net_cnt); /* Set the module net source */ module_manager.add_module_net_source( mux_module, mux_mem_inv_nets[mem_id], mux_module, 0, mem_inv_port_id, pin); /* Update counter */ mem_inv_net_cnt++; } } VTR_ASSERT(mem_inv_net_cnt == mux_graph.num_memory_bits()); return; /* Finish here if local encoders are not required */ } /* Add local decoder instance here */ VTR_ASSERT(true == circuit_lib.mux_use_local_encoder(mux_model)); BasicPort decoder_data_port(generate_mux_local_decoder_data_port_name(), mux_graph.num_memory_bits()); BasicPort decoder_data_inv_port( generate_mux_local_decoder_data_inv_port_name(), mux_graph.num_memory_bits()); /* Local port to record the LSB and MSB of each level, here, we deposite (0, * 0) */ ModulePortId mux_module_sram_port_id = module_manager.find_module_port( mux_module, circuit_lib.port_prefix(mux_sram_ports[0])); ModulePortId mux_module_sram_inv_port_id = module_manager.find_module_port( mux_module, circuit_lib.port_prefix(mux_sram_ports[0]) + INV_PORT_POSTFIX); BasicPort lvl_addr_port(circuit_lib.port_prefix(mux_sram_ports[0]), 0); BasicPort lvl_data_port(decoder_data_port.get_name(), 0); BasicPort lvl_data_inv_port(decoder_data_inv_port.get_name(), 0); /* Counter for mem index */ size_t mem_net_cnt = 0; size_t mem_inv_net_cnt = 0; for (const auto& lvl : mux_graph.levels()) { size_t addr_size = find_mux_local_decoder_addr_size(mux_graph.num_memory_bits_at_level(lvl)); size_t data_size = mux_graph.num_memory_bits_at_level(lvl); /* Update the LSB and MSB of addr and data port for the current level */ lvl_addr_port.rotate(addr_size); lvl_data_port.rotate(data_size); lvl_data_inv_port.rotate(data_size); /* Exception: if the data size is one, we just need wires! */ if (1 == data_size) { for (size_t pin_id = 0; pin_id < lvl_addr_port.pins().size(); ++pin_id) { MuxMemId mem_id = MuxMemId(mem_net_cnt); /* Set the module net source */ module_manager.add_module_net_source( mux_module, mux_mem_nets[mem_id], mux_module, 0, mux_module_sram_port_id, lvl_addr_port.pins()[pin_id]); /* Update counter */ mem_net_cnt++; MuxMemId mem_inv_id = MuxMemId(mem_inv_net_cnt); /* Set the module net source */ module_manager.add_module_net_source( mux_module, mux_mem_inv_nets[mem_inv_id], mux_module, 0, mux_module_sram_inv_port_id, lvl_addr_port.pins()[pin_id]); /* Update counter */ mem_inv_net_cnt++; } continue; } std::string decoder_module_name = generate_mux_local_decoder_subckt_name(addr_size, data_size); ModuleId decoder_module = module_manager.find_module(decoder_module_name); VTR_ASSERT(ModuleId::INVALID() != decoder_module); size_t decoder_instance = module_manager.num_instance(mux_module, decoder_module); module_manager.add_child_module(mux_module, decoder_module); /* Add module nets to connect sram ports of MUX to address port */ ModulePortId decoder_module_addr_port_id = module_manager.find_module_port( decoder_module, generate_mux_local_decoder_addr_port_name()); BasicPort decoder_module_addr_port = module_manager.module_port(decoder_module, decoder_module_addr_port_id); VTR_ASSERT(decoder_module_addr_port.get_width() == lvl_addr_port.get_width()); /* Build pin-to-pin net connection */ for (size_t pin_id = 0; pin_id < lvl_addr_port.pins().size(); ++pin_id) { ModuleNetId net = module_manager.create_module_net(mux_module); module_manager.add_module_net_source(mux_module, net, mux_module, 0, mux_module_sram_port_id, lvl_addr_port.pins()[pin_id]); module_manager.add_module_net_sink( mux_module, net, decoder_module, decoder_instance, decoder_module_addr_port_id, decoder_module_addr_port.pins()[pin_id]); } /* Add module nets to connect data port to MUX mem ports */ ModulePortId decoder_module_data_port_id = module_manager.find_module_port( decoder_module, generate_mux_local_decoder_data_port_name()); BasicPort decoder_module_data_port = module_manager.module_port(decoder_module, decoder_module_data_port_id); /* Build pin-to-pin net connection */ for (const size_t& pin : decoder_module_data_port.pins()) { ModuleNetId net = mux_mem_nets[MuxMemId(mem_net_cnt)]; module_manager.add_module_net_source(mux_module, net, decoder_module, decoder_instance, decoder_module_data_port_id, pin); /* Add the module nets to mux_mem_nets cache */ mem_net_cnt++; } ModulePortId decoder_module_data_inv_port_id = module_manager.find_module_port( decoder_module, generate_mux_local_decoder_data_inv_port_name()); BasicPort decoder_module_data_inv_port = module_manager.module_port( decoder_module, decoder_module_data_inv_port_id); /* Build pin-to-pin net connection */ for (const size_t& pin : decoder_module_data_inv_port.pins()) { ModuleNetId net = mux_mem_inv_nets[MuxMemId(mem_inv_net_cnt)]; module_manager.add_module_net_source( mux_module, net, decoder_module, decoder_instance, decoder_module_data_inv_port_id, pin); /* Add the module nets to mux_mem_inv_nets cache */ mem_inv_net_cnt++; } } VTR_ASSERT(mem_net_cnt == mux_graph.num_memory_bits()); VTR_ASSERT(mem_inv_net_cnt == mux_graph.num_memory_bits()); } /********************************************************************* * Generate module of a CMOS multiplexer with the given size * The module will consist of three parts: * 1. instances of the branch circuits of multiplexers which are generated *before This builds up the multiplexing structure * 2. Input buffers/inverters * 3. Output buffers/inverters *********************************************************************/ static void build_cmos_mux_module(ModuleManager& module_manager, const CircuitLibrary& circuit_lib, const CircuitModelId& mux_model, const std::string& module_name, const MuxGraph& mux_graph) { /* Get the global ports required by MUX (and any submodules) */ std::vector mux_global_ports = circuit_lib.model_global_ports_by_type(mux_model, CIRCUIT_MODEL_PORT_INPUT, true, true); /* Get the input ports from the mux: * - LUT may have ports that are driven by harden logic, * which should not be included when building the mux graph */ std::vector mux_input_ports; if (CIRCUIT_MODEL_LUT == circuit_lib.model_type(mux_model)) { mux_input_ports = find_lut_circuit_model_input_port(circuit_lib, mux_model, false, false); } else { VTR_ASSERT(CIRCUIT_MODEL_MUX == circuit_lib.model_type(mux_model)); mux_input_ports = circuit_lib.model_ports_by_type( mux_model, CIRCUIT_MODEL_PORT_INPUT, true); } /* Get the output ports from the mux: * - LUT may have ports that are driven by harden logic, * which should not be included when building the mux graph * - LUT may have global output ports that are wired directly to top-level * module */ std::vector mux_output_ports; if (CIRCUIT_MODEL_LUT == circuit_lib.model_type(mux_model)) { mux_output_ports = find_lut_circuit_model_output_port(circuit_lib, mux_model, false, true); } else { VTR_ASSERT(CIRCUIT_MODEL_MUX == circuit_lib.model_type(mux_model)); mux_output_ports = circuit_lib.model_ports_by_type( mux_model, CIRCUIT_MODEL_PORT_OUTPUT, false); } /* Get the sram ports from the mux * Multiplexing structure does not mode_sram_ports, they are handled in LUT * modules Here we just bypass it. */ std::vector mux_sram_ports = find_circuit_regular_sram_ports(circuit_lib, mux_model); /* Generate the Verilog netlist according to the mux_graph */ /* Find out the number of data-path inputs */ size_t num_inputs = find_mux_num_datapath_inputs(circuit_lib, mux_model, mux_graph.num_inputs()); /* Find out the number of outputs */ size_t num_outputs = mux_graph.num_outputs(); /* Find out the number of memory bits */ size_t num_mems = mux_graph.num_memory_bits(); /* The size of of memory ports depend on * if a local encoder is used for the mux or not * Multiplexer local encoders are applied to memory bits at each stage */ if (true == circuit_lib.mux_use_local_encoder(mux_model)) { num_mems = 0; for (const auto& lvl : mux_graph.levels()) { size_t data_size = mux_graph.num_memory_bits_at_level(lvl); num_mems += find_mux_local_decoder_addr_size(data_size); } } /* Check codes to ensure the port of Verilog netlists will match */ /* MUX graph must have only 1 output */ VTR_ASSERT(1 == mux_input_ports.size()); /* A quick check on the model ports */ if ((CIRCUIT_MODEL_MUX == circuit_lib.model_type(mux_model)) || ((CIRCUIT_MODEL_LUT == circuit_lib.model_type(mux_model)) && (false == circuit_lib.is_lut_fracturable(mux_model)))) { VTR_ASSERT(1 == mux_output_ports.size()); VTR_ASSERT(1 == circuit_lib.port_size(mux_output_ports[0])); } else { VTR_ASSERT_SAFE((CIRCUIT_MODEL_LUT == circuit_lib.model_type(mux_model)) && (true == circuit_lib.is_lut_fracturable(mux_model))); for (const auto& port : mux_output_ports) { VTR_ASSERT(0 < circuit_lib.port_size(port)); } } /* Create a Verilog Module based on the circuit model, and add to module * manager */ ModuleId mux_module = module_manager.add_module(module_name); VTR_ASSERT(ModuleId::INVALID() != mux_module); /* Label module usage */ if (CIRCUIT_MODEL_MUX == circuit_lib.model_type(mux_model)) { module_manager.set_module_usage(mux_module, ModuleManager::MODULE_INTERC); } else { VTR_ASSERT_SAFE(CIRCUIT_MODEL_LUT == circuit_lib.model_type(mux_model)); module_manager.set_module_usage(mux_module, ModuleManager::MODULE_LUT); } /* Add module ports */ /* Add each input port * Treat MUX and LUT differently * 1. MUXes: we do not have a specific input/output sizes, it is inferred by * architecture * 2. LUTes: we do have specific input/output sizes, * but the inputs of MUXes are the SRAM ports of LUTs * and the SRAM ports of MUXes are the inputs of LUTs */ size_t input_port_cnt = 0; for (const auto& port : mux_input_ports) { BasicPort input_port(circuit_lib.port_prefix(port), num_inputs); module_manager.add_port(mux_module, input_port, ModuleManager::MODULE_INPUT_PORT); /* Update counter */ input_port_cnt++; } /* Double check: We should have only 1 input port generated here! */ VTR_ASSERT(1 == input_port_cnt); /* Add input buffers and update module nets for inputs */ vtr::vector mux_input_nets = build_mux_module_input_buffers(module_manager, circuit_lib, mux_module, mux_model, mux_graph); for (const auto& port : mux_output_ports) { BasicPort output_port(circuit_lib.port_prefix(port), num_outputs); if (CIRCUIT_MODEL_LUT == circuit_lib.model_type(mux_model)) { output_port.set_width(circuit_lib.port_size(port)); } module_manager.add_port(mux_module, output_port, ModuleManager::MODULE_OUTPUT_PORT); } /* TODO: Add output buffers and update module nets for outputs */ vtr::vector mux_output_nets = build_mux_module_output_buffers(module_manager, circuit_lib, mux_module, mux_model, mux_graph); size_t sram_port_cnt = 0; for (const auto& port : mux_sram_ports) { BasicPort mem_port(circuit_lib.port_prefix(port), num_mems); module_manager.add_port(mux_module, mem_port, ModuleManager::MODULE_INPUT_PORT); BasicPort mem_inv_port( std::string(circuit_lib.port_prefix(port) + INV_PORT_POSTFIX), num_mems); module_manager.add_port(mux_module, mem_inv_port, ModuleManager::MODULE_INPUT_PORT); /* Update counter */ sram_port_cnt++; } VTR_ASSERT(1 == sram_port_cnt); /* Create module nets for mem and mem_inv ports */ vtr::vector mux_mem_nets; vtr::vector mux_mem_inv_nets; build_mux_module_local_encoders_and_memory_nets( module_manager, mux_module, circuit_lib, mux_model, mux_sram_ports, mux_graph, mux_mem_nets, mux_mem_inv_nets); /* Print the internal logic in Verilog codes */ /* Print the Multiplexing structure in Verilog codes * Separated generation strategy on using standard cell MUX2 or TGATE, * 1. MUX2 has a fixed port map: input_port[0] and input_port[1] is the * data_path input * 2. Branch TGATE-based module has a fixed port name * TODO: the naming could be more flexible? */ /* Get the tgate model */ CircuitModelId tgate_model = circuit_lib.pass_gate_logic_model(mux_model); /* Instanciate the branch module: * Case 1: the branch module is a standard cell MUX2 * Case 2: the branch module is a tgate-based module */ std::string branch_module_name; if (CIRCUIT_MODEL_GATE == circuit_lib.model_type(tgate_model)) { VTR_ASSERT(CIRCUIT_MODEL_GATE_MUX2 == circuit_lib.gate_type(tgate_model)); build_cmos_mux_module_mux2_multiplexing_structure( module_manager, circuit_lib, mux_module, mux_model, tgate_model, mux_input_nets, mux_output_nets, mux_mem_nets, mux_graph); } else { VTR_ASSERT(CIRCUIT_MODEL_PASSGATE == circuit_lib.model_type(tgate_model)); build_cmos_mux_module_tgate_multiplexing_structure( module_manager, circuit_lib, mux_module, mux_model, mux_input_nets, mux_output_nets, mux_mem_nets, mux_mem_inv_nets, mux_graph); } /* Add global ports to the pb_module: * This is a much easier job after adding sub modules (instances), * we just need to find all the global ports from the child modules and build * a list of it */ add_module_global_ports_from_child_modules(module_manager, mux_module); } /********************************************************************* * Generate a module of a RRAM-based multiplexer with the given size * The module will consist of three parts: * 1. instances of the branch circuits of multiplexers which are generated *before This builds up the 4T1R-based multiplexing structure * * BLB WL * | | ... * v v * +--------+ * in[0]-->| | BLB WL * ...| Branch |-----+ | | * in -->| 0 | | v v * [N-1] +--------+ | +--------+ * ... -->| | * BLBs WLs ...| Branch | * | | ... -->| X | * v v +--------+ * +--------+ | * -->| | | * ...| Branch |----+ * -->| i | * +--------+ * * 2. Input buffers/inverters * 3. Output buffers/inverters *********************************************************************/ static void build_rram_mux_module(ModuleManager& module_manager, const CircuitLibrary& circuit_lib, const CircuitModelId& circuit_model, const std::string& module_name, const MuxGraph& mux_graph) { /* Error out for the conditions where we are not yet supported! */ if (CIRCUIT_MODEL_LUT == circuit_lib.model_type(circuit_model)) { /* RRAM LUT is not supported now... */ VTR_LOGF_ERROR(__FILE__, __LINE__, "RRAM-based LUT is not supported for circuit model '%s')!\n", circuit_lib.model_name(circuit_model).c_str()); exit(1); } /* Get the global ports required by MUX (and any submodules) */ std::vector mux_global_ports = circuit_lib.model_global_ports_by_type( circuit_model, CIRCUIT_MODEL_PORT_INPUT, true, true); /* Get the input ports from the mux */ std::vector mux_input_ports = circuit_lib.model_ports_by_type( circuit_model, CIRCUIT_MODEL_PORT_INPUT, true); /* Get the output ports from the mux */ std::vector mux_output_ports = circuit_lib.model_ports_by_type( circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, true); /* Get the BL and WL ports from the mux */ std::vector mux_blb_ports = circuit_lib.model_ports_by_type( circuit_model, CIRCUIT_MODEL_PORT_BLB, true); std::vector mux_wl_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_WL, true); /* Generate the Verilog netlist according to the mux_graph */ /* Find out the number of data-path inputs */ size_t num_inputs = find_mux_num_datapath_inputs(circuit_lib, circuit_model, mux_graph.num_inputs()); /* Find out the number of outputs */ size_t num_outputs = mux_graph.num_outputs(); /* Find out the number of memory bits */ size_t num_mems = mux_graph.num_memory_bits(); /* Check codes to ensure the port of Verilog netlists will match */ /* MUX graph must have only 1 input and 1 BLB and 1 WL port */ VTR_ASSERT(1 == mux_input_ports.size()); VTR_ASSERT(1 == mux_blb_ports.size()); VTR_ASSERT(1 == mux_wl_ports.size()); /* Create a Verilog Module based on the circuit model, and add to module * manager */ ModuleId module_id = module_manager.add_module(module_name); VTR_ASSERT(ModuleId::INVALID() != module_id); /* Label module usage */ module_manager.set_module_usage(module_id, ModuleManager::MODULE_INTERC); /* Add module ports */ /* Add each global port */ for (const auto& port : mux_global_ports) { /* Configure each global port */ BasicPort global_port(circuit_lib.port_prefix(port), circuit_lib.port_size(port)); module_manager.add_port(module_id, global_port, ModuleManager::MODULE_GLOBAL_PORT); } /* Add each input port */ size_t input_port_cnt = 0; for (const auto& port : mux_input_ports) { BasicPort input_port(circuit_lib.port_prefix(port), num_inputs); module_manager.add_port(module_id, input_port, ModuleManager::MODULE_INPUT_PORT); /* Update counter */ input_port_cnt++; } /* Double check: We should have only 1 input port generated here! */ VTR_ASSERT(1 == input_port_cnt); for (const auto& port : mux_output_ports) { BasicPort output_port(circuit_lib.port_prefix(port), num_outputs); if (CIRCUIT_MODEL_LUT == circuit_lib.model_type(circuit_model)) { output_port.set_width(circuit_lib.port_size(port)); } module_manager.add_port(module_id, output_port, ModuleManager::MODULE_OUTPUT_PORT); } /* BLB port */ for (const auto& port : mux_blb_ports) { /* IMPORTANT: RRAM-based MUX has an additional BLB pin per level * So, the actual port width of BLB should be added by the number of levels * of the MUX graph */ BasicPort blb_port(circuit_lib.port_prefix(port), num_mems + mux_graph.num_levels()); module_manager.add_port(module_id, blb_port, ModuleManager::MODULE_INPUT_PORT); } /* WL port */ for (const auto& port : mux_wl_ports) { /* IMPORTANT: RRAM-based MUX has an additional WL pin per level * So, the actual port width of WL should be added by the number of levels * of the MUX graph */ BasicPort wl_port(circuit_lib.port_prefix(port), num_mems + mux_graph.num_levels()); module_manager.add_port(module_id, wl_port, ModuleManager::MODULE_INPUT_PORT); } /* TODO: Add the input and output buffers in Verilog codes */ /* TODO: Print the internal logic in Verilog codes */ } /*********************************************** * Generate Verilog codes modeling a multiplexer * with the given graph-level description **********************************************/ static void build_mux_module(ModuleManager& module_manager, const CircuitLibrary& circuit_lib, const CircuitModelId& circuit_model, const MuxGraph& mux_graph) { std::string module_name = generate_mux_subckt_name( circuit_lib, circuit_model, find_mux_num_datapath_inputs(circuit_lib, circuit_model, mux_graph.num_inputs()), std::string("")); /* Multiplexers built with different technology is in different organization */ switch (circuit_lib.design_tech_type(circuit_model)) { case CIRCUIT_MODEL_DESIGN_CMOS: /* SRAM-based Multiplexer Verilog module generation */ build_cmos_mux_module(module_manager, circuit_lib, circuit_model, module_name, mux_graph); break; case CIRCUIT_MODEL_DESIGN_RRAM: /* TODO: RRAM-based Multiplexer Verilog module generation */ build_rram_mux_module(module_manager, circuit_lib, circuit_model, module_name, mux_graph); break; default: VTR_LOGF_ERROR(__FILE__, __LINE__, "Invalid design technology of multiplexer '%s'\n", circuit_lib.model_name(circuit_model).c_str()); exit(1); } } /*********************************************** * Generate Verilog modules for all the unique * multiplexers in the FPGA device **********************************************/ void build_mux_modules(ModuleManager& module_manager, const MuxLibrary& mux_lib, const CircuitLibrary& circuit_lib) { vtr::ScopedStartFinishTimer timer("Building multiplexer modules"); /* Generate basis sub-circuit for unique branches shared by the multiplexers */ for (auto mux : mux_lib.muxes()) { const MuxGraph& mux_graph = mux_lib.mux_graph(mux); CircuitModelId mux_circuit_model = mux_lib.mux_circuit_model(mux); /* Create a mux graph for the branch circuit */ std::vector branch_mux_graphs = mux_graph.build_mux_branch_graphs(); /* Create branch circuits, which are N:1 one-level or 2:1 tree-like MUXes */ for (auto branch_mux_graph : branch_mux_graphs) { build_mux_branch_module(module_manager, circuit_lib, mux_circuit_model, branch_mux_graph); } } /* Generate unique Verilog modules for the multiplexers */ for (auto mux : mux_lib.muxes()) { const MuxGraph& mux_graph = mux_lib.mux_graph(mux); CircuitModelId mux_circuit_model = mux_lib.mux_circuit_model(mux); /* Create MUX circuits */ build_mux_module(module_manager, circuit_lib, mux_circuit_model, mux_graph); } } } /* end namespace openfpga */