From b2f57ecf81f01639b53ae83aacf42ffe2ad3be38 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 21 Oct 2019 00:00:30 -0600 Subject: [PATCH] plug in MUX module graph generation, still local encoders contain dangling net, bug fixing --- vpr7_x2p/vpr/SRC/device/mux_graph.cpp | 41 +- vpr7_x2p/vpr/SRC/device/mux_graph.h | 4 + .../vpr/SRC/fpga_x2p/base/fpga_x2p_naming.cpp | 34 + .../vpr/SRC/fpga_x2p/base/fpga_x2p_naming.h | 8 + .../vpr/SRC/fpga_x2p/base/module_manager.cpp | 94 +- .../vpr/SRC/fpga_x2p/base/module_manager.h | 11 + .../SRC/fpga_x2p/base/module_manager_fwd.h | 2 + .../build_essential_modules.cpp | 30 + .../module_builder/build_essential_modules.h | 2 + .../module_builder/build_module_graph.cpp | 10 +- .../module_builder/build_mux_modules.cpp | 1464 +++++++++++++++++ .../module_builder/build_mux_modules.h | 19 + .../verilog/verilog_module_writer.cpp | 38 +- .../vpr/SRC/fpga_x2p/verilog/verilog_mux.cpp | 824 +--------- 14 files changed, 1792 insertions(+), 789 deletions(-) create mode 100644 vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_mux_modules.cpp create mode 100644 vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_mux_modules.h diff --git a/vpr7_x2p/vpr/SRC/device/mux_graph.cpp b/vpr7_x2p/vpr/SRC/device/mux_graph.cpp index 4ce432657..b453ffef8 100644 --- a/vpr7_x2p/vpr/SRC/device/mux_graph.cpp +++ b/vpr7_x2p/vpr/SRC/device/mux_graph.cpp @@ -45,15 +45,24 @@ MuxGraph::node_range MuxGraph::nodes() const { /* Find the non-input nodes */ std::vector MuxGraph::non_input_nodes() const { + /* Must be an valid graph */ + VTR_ASSERT_SAFE(valid_mux_graph()); std::vector node_list; - for (const auto& node : nodes()) { - /* Bypass any nodes which are not OUTPUT and INTERNAL */ - if (MUX_INPUT_NODE == node_types_[node]) { - continue; + + /* Build the node list, level by level */ + for (size_t level = 0; level < num_node_levels(); ++level) { + for (size_t node_type = 0; node_type < size_t(NUM_MUX_NODE_TYPES); ++node_type) { + /* Bypass any nodes which are not OUTPUT and INTERNAL */ + if (size_t(MUX_INPUT_NODE) == node_type) { + continue; + } + /* Reach here, this is either an OUTPUT or INTERNAL node */ + for (auto node : node_lookup_[level][node_type]) { + node_list.push_back(node); + } } - /* Reach here, this is either an OUTPUT or INTERNAL node */ - node_list.push_back(node); } + return node_list; } @@ -436,15 +445,29 @@ MuxInputId MuxGraph::input_id(const MuxNodeId& node_id) const { return node_input_ids_[node_id]; } -/* Get the input id of a given node */ +/* Identify if the node is an input of the MUX */ +bool MuxGraph::is_node_input(const MuxNodeId& node_id) const { + /* Validate node id */ + VTR_ASSERT(true == valid_node_id(node_id)); + return (MUX_INPUT_NODE == node_types_[node_id]); +} + +/* Get the output id of a given node */ MuxOutputId MuxGraph::output_id(const MuxNodeId& node_id) const { /* Validate node id */ VTR_ASSERT(valid_node_id(node_id)); - /* Must be an input */ + /* Must be an output */ VTR_ASSERT(MUX_OUTPUT_NODE == node_types_[node_id]); return node_output_ids_[node_id]; } +/* Identify if the node is an output of the MUX */ +bool MuxGraph::is_node_output(const MuxNodeId& node_id) const { + /* Validate node id */ + VTR_ASSERT(true == valid_node_id(node_id)); + return (MUX_OUTPUT_NODE == node_types_[node_id]); +} + /* Get the node id of a given input */ MuxNodeId MuxGraph::node_id(const MuxInputId& input_id) const { /* Use the node_lookup to accelerate the search */ @@ -1045,7 +1068,7 @@ bool MuxGraph::valid_output_id(const MuxOutputId& output_id) const { } bool MuxGraph::valid_level(const size_t& level) const { - return level < num_levels(); + return level < num_node_levels(); } bool MuxGraph::valid_node_lookup() const { diff --git a/vpr7_x2p/vpr/SRC/device/mux_graph.h b/vpr7_x2p/vpr/SRC/device/mux_graph.h index ed1f4773d..6774389f0 100644 --- a/vpr7_x2p/vpr/SRC/device/mux_graph.h +++ b/vpr7_x2p/vpr/SRC/device/mux_graph.h @@ -109,8 +109,12 @@ class MuxGraph { MuxNodeId node_id(const size_t& node_level, const size_t& node_index_at_level) const; /* Get the input id of a given node */ MuxInputId input_id(const MuxNodeId& node_id) const; + /* Identify if the node is an input of the MUX */ + bool is_node_input(const MuxNodeId& node_id) const; /* Get the output id of a given node */ MuxOutputId output_id(const MuxNodeId& node_id) const; + /* Identify if the node is an output of the MUX */ + bool is_node_output(const MuxNodeId& node_id) const; /* Decode memory bits based on an input id */ std::vector decode_memory_bits(const MuxInputId& input_id) const; private: /* Private mutators : basic operations */ diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_naming.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_naming.cpp index 082e333a0..acd88162e 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_naming.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_naming.cpp @@ -33,6 +33,19 @@ std::string generate_mux_node_name(const size_t& node_level, return node_name; } + /************************************************ + * Generate the instance name for a branch circuit in multiplexing structure + * Case 1 : If there is an intermediate buffer followed by, + * the node name will be mux_l_in_buf + * Case 1 : If there is NO intermediate buffer followed by, + * the node name will be mux_l_in + ***********************************************/ +std::string generate_mux_branch_instance_name(const size_t& node_level, + const size_t& node_index_at_level, + const bool& add_buffer_postfix) { + return std::string(generate_mux_node_name(node_level, add_buffer_postfix) + "_" + std::to_string(node_index_at_level) + "_"); +} + /************************************************ * Generate the module name for a multiplexer in Verilog format * Different circuit model requires different names: @@ -936,3 +949,24 @@ std::string generate_fpga_top_module_name() { std::string generate_fpga_top_netlist_name(const std::string& postfix) { return std::string("fpga_top" + postfix); } + +/********************************************************************* + * Generate the module name for a constant generator + * either VDD or GND, depending on the input argument + ********************************************************************/ +std::string generate_const_value_module_name(const size_t& const_val) { + if (0 == const_val) { + return std::string("gnd"); + } + + VTR_ASSERT (1 == const_val); + return std::string("vdd"); +} + +/********************************************************************* + * Generate the output port name for a constant generator module + * either VDD or GND, depending on the input argument + ********************************************************************/ +std::string generate_const_value_module_output_port_name(const size_t& const_val) { + return generate_const_value_module_name(const_val); +} diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_naming.h b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_naming.h index 00aa66b0d..6ab310f5c 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_naming.h +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_naming.h @@ -16,6 +16,10 @@ std::string generate_mux_node_name(const size_t& node_level, const bool& add_buffer_postfix); +std::string generate_mux_branch_instance_name(const size_t& node_level, + const size_t& node_index_at_level, + const bool& add_buffer_postfix); + std::string generate_mux_subckt_name(const CircuitLibrary& circuit_lib, const CircuitModelId& circuit_model, const size_t& mux_size, @@ -165,4 +169,8 @@ std::string generate_fpga_top_module_name(); std::string generate_fpga_top_netlist_name(const std::string& postfix); +std::string generate_const_value_module_name(const size_t& const_val); + +std::string generate_const_value_module_output_port_name(const size_t& const_val); + #endif diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager.cpp index 46a9b7d74..3eab7112a 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager.cpp @@ -175,18 +175,51 @@ ModuleId ModuleManager::find_module(const std::string& name) const { /* Find the number of instances of a child module in the parent module */ size_t ModuleManager::num_instance(const ModuleId& parent_module, const ModuleId& child_module) const { - /* validate both module ids */ - VTR_ASSERT(valid_module_id(parent_module)); - VTR_ASSERT(valid_module_id(child_module)); - /* Try to find the child_module in the children list of parent_module*/ - for (size_t i = 0; i < children_[parent_module].size(); ++i) { - if (child_module == children_[parent_module][i]) { - /* Found, return the number of instances */ - return num_child_instances_[parent_module][i]; + size_t child_index = find_child_module_index_in_parent_module(parent_module, child_module); + if (size_t(-1) == child_index) { + /* Not found, return a zero */ + return 0; + } + + return num_child_instances_[parent_module][child_index]; +} + +/* Find the instance name of a child module */ +std::string ModuleManager::instance_name(const ModuleId& parent_module, const ModuleId& child_module, + const size_t& instance_id) const { + /* Validate the id of both parent and child modules */ + VTR_ASSERT ( valid_module_id(parent_module) ); + VTR_ASSERT ( valid_module_id(child_module) ); + + /* Find the index of child module in the child list of parent module */ + size_t child_index = find_child_module_index_in_parent_module(parent_module, child_module); + VTR_ASSERT (child_index < children_[parent_module].size()); + /* Ensure that instance id is valid */ + VTR_ASSERT (instance_id < num_instance(parent_module, child_module)); + return child_instance_names_[parent_module][child_index][instance_id]; +} + +/* Find the instance id of a given instance name */ +size_t ModuleManager::instance_id(const ModuleId& parent_module, const ModuleId& child_module, + const std::string& instance_name) const { + /* Validate the id of both parent and child modules */ + VTR_ASSERT ( valid_module_id(parent_module) ); + VTR_ASSERT ( valid_module_id(child_module) ); + + /* Find the index of child module in the child list of parent module */ + size_t child_index = find_child_module_index_in_parent_module(parent_module, child_module); + VTR_ASSERT (child_index < children_[parent_module].size()); + + /* Search the instance name list and try to find a match */ + for (size_t name_id = 0; name_id < child_instance_names_[parent_module][child_index].size(); ++name_id) { + const std::string& name = child_instance_names_[parent_module][child_index][name_id]; + if (0 == name.compare(instance_name)) { + return name_id; } } - /* Not found, return a zero */ - return 0; + + /* Not found, return an invalid name */ + return size_t(-1); } /* Find if a port is a wire connection */ @@ -309,6 +342,24 @@ vtr::vector ModuleManager::net_sink_pins(const ModuleId return net_sink_pin_ids_[module][net]; } +/****************************************************************************** + * Private Accessors + ******************************************************************************/ +size_t ModuleManager::find_child_module_index_in_parent_module(const ModuleId& parent_module, const ModuleId& child_module) const { + /* validate both module ids */ + VTR_ASSERT(valid_module_id(parent_module)); + VTR_ASSERT(valid_module_id(child_module)); + /* Try to find the child_module in the children list of parent_module*/ + for (size_t i = 0; i < children_[parent_module].size(); ++i) { + if (child_module == children_[parent_module][i]) { + /* Found, return the number of instances */ + return i; + } + } + /* Not found: return an valid value */ + return size_t(-1); +} + /****************************************************************************** * Public Mutators ******************************************************************************/ @@ -329,6 +380,7 @@ ModuleId ModuleManager::add_module(const std::string& name) { parents_.emplace_back(); children_.emplace_back(); num_child_instances_.emplace_back(); + child_instance_names_.emplace_back(); port_ids_.emplace_back(); ports_.emplace_back(); @@ -442,9 +494,13 @@ void ModuleManager::add_child_module(const ModuleId& parent_module, const Module /* Update the child module of parent module */ children_[parent_module].push_back(child_module); num_child_instances_[parent_module].push_back(1); /* By default give one */ + /* Update the instance name list */ + child_instance_names_[parent_module].emplace_back(); + child_instance_names_[parent_module].back().emplace_back(); } else { /* Increase the counter of instances */ num_child_instances_[parent_module][child_it - children_[parent_module].begin()]++; + child_instance_names_[parent_module][child_it - children_[parent_module].begin()].emplace_back(); } /* Update fast look-up for nets */ @@ -456,6 +512,24 @@ void ModuleManager::add_child_module(const ModuleId& parent_module, const Module } } +/* Set the instance name of a child module */ +void ModuleManager::set_child_instance_name(const ModuleId& parent_module, + const ModuleId& child_module, + const size_t& instance_id, + const std::string& instance_name) { + /* Validate the id of both parent and child modules */ + VTR_ASSERT ( valid_module_id(parent_module) ); + VTR_ASSERT ( valid_module_id(child_module) ); + /* Ensure that the instance id is in range */ + VTR_ASSERT ( instance_id < num_instance(parent_module, child_module)); + /* Try to find the child_module in the children list of parent_module*/ + size_t child_index = find_child_module_index_in_parent_module(parent_module, child_module); + /* We must find something! */ + VTR_ASSERT(size_t(-1) != child_index); + /* Set the name */ + child_instance_names_[parent_module][child_index][instance_id] = instance_name; +} + /* Add a net to the connection graph of the module */ ModuleNetId ModuleManager::create_module_net(const ModuleId& module) { /* Validate the module id */ diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager.h b/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager.h index 4c1222e0f..5001205ba 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager.h +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager.h @@ -80,6 +80,12 @@ class ModuleManager { ModuleId find_module(const std::string& name) const; /* Find the number of instances of a child module in the parent module */ size_t num_instance(const ModuleId& parent_module, const ModuleId& child_module) const; + /* Find the instance name of a child module */ + std::string instance_name(const ModuleId& parent_module, const ModuleId& child_module, + const size_t& instance_id) const; + /* Find the instance id of a given instance name */ + size_t instance_id(const ModuleId& parent_module, const ModuleId& child_module, + const std::string& instance_name) const; /* Find if a port is a wire connection */ bool port_is_wire(const ModuleId& module, const ModulePortId& port) const; /* Find if a port is register */ @@ -109,6 +115,8 @@ class ModuleManager { vtr::vector net_sink_ports(const ModuleId& module, const ModuleNetId& net) const; /* Find the sink pin indices of a net */ vtr::vector net_sink_pins(const ModuleId& module, const ModuleNetId& net) const; + private: /* Private accessors */ + size_t find_child_module_index_in_parent_module(const ModuleId& parent_module, const ModuleId& child_module) const; public: /* Public mutators */ /* Add a module */ ModuleId add_module(const std::string& name); @@ -125,6 +133,8 @@ class ModuleManager { void set_port_preproc_flag(const ModuleId& module, const ModulePortId& port, const std::string& preproc_flag); /* Add a child module to a parent module */ void add_child_module(const ModuleId& parent_module, const ModuleId& child_module); + /* Set the instance name of a child module */ + void set_child_instance_name(const ModuleId& parent_module, const ModuleId& child_module, const size_t& instance_id, const std::string& instance_name); /* Add a net to the connection graph of the module */ ModuleNetId create_module_net(const ModuleId& module); /* Set the name of net */ @@ -153,6 +163,7 @@ class ModuleManager { vtr::vector> parents_; /* Parent modules that include the module */ vtr::vector> children_; /* Child modules that this module contain */ vtr::vector> num_child_instances_; /* Number of children instance in each child module */ + vtr::vector>> child_instance_names_; /* Number of children instance in each child module */ /* Port-level data */ vtr::vector> port_ids_; /* List of ports for each Module */ diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager_fwd.h b/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager_fwd.h index bffc2df6a..6f85339bc 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager_fwd.h +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager_fwd.h @@ -10,6 +10,7 @@ /* Strong Ids for ModuleManager */ struct module_id_tag; +struct instance_id_tag; /* TODO: use instance id in module_manager */ struct module_port_id_tag; struct module_pin_id_tag; struct module_net_id_tag; @@ -17,6 +18,7 @@ struct module_net_src_id_tag; struct module_net_sink_id_tag; typedef vtr::StrongId ModuleId; +typedef vtr::StrongId InstanceId; typedef vtr::StrongId ModulePortId; typedef vtr::StrongId ModulePinId; typedef vtr::StrongId ModuleNetId; diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_essential_modules.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_essential_modules.cpp index 006584d36..ccd94a001 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_essential_modules.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_essential_modules.cpp @@ -217,3 +217,33 @@ void build_user_defined_modules(ModuleManager& module_manager, } } +/********************************************************************* + * This function will build a constant generator modules + * and add it to the module manager + * It could be either + * 1. VDD or 2. GND + * Each module will have only one output port + ********************************************************************/ +static +void build_constant_generator_module(ModuleManager& module_manager, + const size_t& const_value) { + ModuleId const_module = module_manager.add_module(generate_const_value_module_name(const_value)); + /* Add one output port */ + BasicPort const_output_port(generate_const_value_module_output_port_name(const_value), 1); + module_manager.add_port(const_module, const_output_port, ModuleManager::MODULE_OUTPUT_PORT); +} + +/********************************************************************* + * This function will add two constant generator modules + * to the module manager + * 1. VDD + * 2. GND + ********************************************************************/ +void build_constant_generator_modules(ModuleManager& module_manager) { + + /* VDD */ + build_constant_generator_module(module_manager, 1); + + /* GND */ + build_constant_generator_module(module_manager, 0); +} diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_essential_modules.h b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_essential_modules.h index d91c529fc..34200f68b 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_essential_modules.h +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_essential_modules.h @@ -11,4 +11,6 @@ void build_user_defined_modules(ModuleManager& module_manager, const CircuitLibrary& circuit_lib, const std::vector& routing_segments); +void build_constant_generator_modules(ModuleManager& module_manager); + #endif diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_module_graph.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_module_graph.cpp index 39cfdd453..c97f031ba 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_module_graph.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_module_graph.cpp @@ -12,6 +12,7 @@ #include "build_essential_modules.h" #include "build_decoder_modules.h" +#include "build_mux_modules.h" #include "build_module_graph.h" /******************************************************************** @@ -66,9 +67,13 @@ ModuleManager build_device_module_graph(const t_vpr_setup& vpr_setup, for (int i = 0; i < arch.num_segments; ++i) { L_segment_vec.push_back(arch.Segments[i]); } + + /* Add constant generator modules: VDD and GND */ + build_constant_generator_modules(module_manager); + /* Register all the user-defined modules in the module manager * This should be done prior to other steps in this function, - * because they will be instanciated by other primitive modules + * because they will be instanciated by other primitive modules */ build_user_defined_modules(module_manager, arch.spice->circuit_lib, L_segment_vec); @@ -78,7 +83,8 @@ ModuleManager build_device_module_graph(const t_vpr_setup& vpr_setup, /* Build local encoders for multiplexers, this MUST be called before multiplexer building */ build_mux_local_decoder_modules(module_manager, mux_lib, arch.spice->circuit_lib); - /* TODO: Build multiplexer modules */ + /* Build multiplexer modules */ + build_mux_modules(module_manager, mux_lib, arch.spice->circuit_lib); /* TODO: Build LUT modules */ diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_mux_modules.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_mux_modules.cpp new file mode 100644 index 000000000..5385da9ae --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_mux_modules.cpp @@ -0,0 +1,1464 @@ +/*********************************************** + * 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 + +#include "util.h" +#include "vtr_assert.h" + +/* Device-level header files */ +#include "mux_graph.h" +#include "module_manager.h" +#include "physical_types.h" +#include "vpr_types.h" +#include "mux_utils.h" +#include "circuit_library_utils.h" +#include "decoder_library_utils.h" +#include "module_manager_utils.h" + +/* FPGA-X2P context header files */ +#include "spice_types.h" +/* TODO: we should have a header file for naming constexpr only */ +#include "verilog_global.h" +#include "fpga_x2p_naming.h" +#include "fpga_x2p_utils.h" + +/* FPGA-Verilog context header files */ +#include "build_mux_modules.h" + +/********************************************************************* + * 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, SPICE_MODEL_PORT_INPUT, true); + std::vector tgate_output_ports = circuit_lib.model_ports_by_type(tgate_model, SPICE_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_lib_name(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_lib_name(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_lib_name(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_lib_name(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 (SPICE_MODEL_GATE == circuit_lib.model_type(tgate_model)) { + VTR_ASSERT(SPICE_MODEL_GATE_MUX2 == circuit_lib.gate_type(tgate_model)); + return; + } + + std::vector tgate_global_ports = circuit_lib.model_global_ports_by_type(tgate_model, SPICE_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, SPICE_MODEL_PORT_INPUT, true); + /* Get the output ports from the mux */ + std::vector mux_output_ports = circuit_lib.model_ports_by_type(mux_model, SPICE_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, SPICE_MODEL_PORT_BLB, true); + std::vector mux_wl_ports = circuit_lib.model_ports_by_type(mux_model, SPICE_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, SPICE_MODEL_PORT_INPUT, true, true); + for (const auto& port : prog_enable_ports) { + /* Configure each global port */ + BasicPort global_port(circuit_lib.port_lib_name(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_lib_name(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_lib_name(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_lib_name(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_lib_name(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 size_t& mux_size, + const MuxGraph& mux_graph) { + std::string module_name = generate_mux_branch_subckt_name(circuit_lib, mux_model, mux_size, mux_graph.num_inputs(), verilog_mux_basis_posfix); + + /* Multiplexers built with different technology is in different organization */ + switch (circuit_lib.design_tech_type(mux_model)) { + case SPICE_MODEL_DESIGN_CMOS: + build_cmos_mux_branch_module(module_manager, circuit_lib, mux_model, module_name, mux_graph); + break; + case SPICE_MODEL_DESIGN_RRAM: + build_rram_mux_branch_module(module_manager, circuit_lib, mux_model, module_name, mux_graph); + break; + default: + vpr_printf(TIO_MESSAGE_ERROR, + "(FILE:%s,LINE[%d]) Invalid design technology of multiplexer (name: %s)\n", + __FILE__, __LINE__, 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, SPICE_MODEL_PORT_INPUT, true); + std::vector std_cell_output_ports = circuit_lib.model_ports_by_type(std_cell_model, SPICE_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_lib_name(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_lib_name(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_lib_name(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); + /* Get the moduleId for the buffer module */ + ModuleId buffer_module_id = module_manager.find_module(circuit_lib.model_name(buffer_model)); + /* We must have one */ + VTR_ASSERT(ModuleId::INVALID() != buffer_module_id); + + /* Find the instance id */ + size_t buffer_instance_id = module_manager.num_instance(mux_module, buffer_module_id); + /* Add the module to mux_module */ + module_manager.add_child_module(mux_module, buffer_module_id); + /* Set a name for the instance */ + std::string buffer_instance_name = generate_mux_branch_instance_name(output_node_level, output_node_index_at_level, true); + module_manager.set_child_instance_name(mux_module, buffer_module_id, buffer_instance_id, buffer_instance_name); + + /* Add module nets to wire to the buffer module */ + /* To match the context, Buffer should have only 2 non-global ports: 1 input port and 1 output port */ + std::vector buffer_model_input_ports = circuit_lib.model_ports_by_type(buffer_model, SPICE_MODEL_PORT_INPUT, true); + std::vector buffer_model_output_ports = circuit_lib.model_ports_by_type(buffer_model, SPICE_MODEL_PORT_OUTPUT, true); + VTR_ASSERT(1 == buffer_model_input_ports.size()); + VTR_ASSERT(1 == buffer_model_output_ports.size()); + + /* Find the input and output module ports */ + ModulePortId buffer_input_port_id = module_manager.find_module_port(buffer_module_id, circuit_lib.port_lib_name(buffer_model_input_ports[0])); + ModulePortId buffer_output_port_id = module_manager.find_module_port(buffer_module_id, circuit_lib.port_lib_name(buffer_model_output_ports[0])); + + /* Port size should be 1 ! */ + VTR_ASSERT(1 == module_manager.module_port(buffer_module_id, buffer_input_port_id).get_width()); + VTR_ASSERT(1 == module_manager.module_port(buffer_module_id, buffer_output_port_id).get_width()); + + /* Connect the module net from branch output to buffer input */ + module_manager.add_module_net_sink(mux_module, branch_net, buffer_module_id, buffer_instance_id, buffer_input_port_id, module_manager.module_port(buffer_module_id, buffer_input_port_id).get_lsb()); + + /* Create a module net which sources from buffer output */ + ModuleNetId buffer_net = module_manager.create_module_net(mux_module); + module_manager.add_module_net_source(mux_module, buffer_net, buffer_module_id, buffer_instance_id, buffer_output_port_id, module_manager.module_port(buffer_module_id, buffer_output_port_id).get_lsb()); + + /* 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) { + /* Find the actual mux size */ + size_t mux_size = find_mux_num_datapath_inputs(circuit_lib, circuit_model, mux_graph.num_inputs()); + + /* 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(); + + /* Instanciate the branch module which is a tgate-based module + */ + std::string branch_module_name= generate_mux_branch_subckt_name(circuit_lib, circuit_model, mux_size, branch_size, verilog_mux_basis_posfix); + /* 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 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); + } + } + + /* 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); + /* Get the moduleId for the buffer module */ + ModuleId buffer_module_id = module_manager.find_module(circuit_lib.model_name(buffer_model)); + /* We must have one */ + VTR_ASSERT(ModuleId::INVALID() != buffer_module_id); + + /* Find the instance id */ + size_t buffer_instance_id = module_manager.num_instance(mux_module, buffer_module_id); + /* Add the module to mux_module */ + module_manager.add_child_module(mux_module, buffer_module_id); + /* Set a name for the instance */ + std::string buffer_instance_name = generate_mux_branch_instance_name(output_node_level, output_node_index_at_level, true); + module_manager.set_child_instance_name(mux_module, buffer_module_id, buffer_instance_id, buffer_instance_name); + + /* Add module nets to wire to the buffer module */ + /* To match the context, Buffer should have only 2 non-global ports: 1 input port and 1 output port */ + std::vector buffer_model_input_ports = circuit_lib.model_ports_by_type(buffer_model, SPICE_MODEL_PORT_INPUT, true); + std::vector buffer_model_output_ports = circuit_lib.model_ports_by_type(buffer_model, SPICE_MODEL_PORT_OUTPUT, true); + VTR_ASSERT(1 == buffer_model_input_ports.size()); + VTR_ASSERT(1 == buffer_model_output_ports.size()); + + /* Find the input and output module ports */ + ModulePortId buffer_input_port_id = module_manager.find_module_port(buffer_module_id, circuit_lib.port_lib_name(buffer_model_input_ports[0])); + ModulePortId buffer_output_port_id = module_manager.find_module_port(buffer_module_id, circuit_lib.port_lib_name(buffer_model_output_ports[0])); + + /* Port size should be 1 ! */ + VTR_ASSERT(1 == module_manager.module_port(buffer_module_id, buffer_input_port_id).get_width()); + VTR_ASSERT(1 == module_manager.module_port(buffer_module_id, buffer_output_port_id).get_width()); + + /* Connect the module net from branch output to buffer input */ + module_manager.add_module_net_sink(mux_module, branch_net, buffer_module_id, buffer_instance_id, buffer_input_port_id, module_manager.module_port(buffer_module_id, buffer_input_port_id).get_lsb()); + + /* Create a module net which sources from buffer output */ + ModuleNetId buffer_net = module_manager.create_module_net(mux_module); + module_manager.add_module_net_source(mux_module, buffer_net, buffer_module_id, buffer_instance_id, buffer_output_port_id, module_manager.module_port(buffer_module_id, buffer_output_port_id).get_lsb()); + + /* 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 */ + std::vector mux_input_ports = circuit_lib.model_ports_by_type(mux_model, SPICE_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_lib_name(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); + /* Get the moduleId for the buffer module */ + ModuleId buffer_module_id = module_manager.find_module(circuit_lib.model_name(buffer_model)); + /* We must have one */ + VTR_ASSERT(ModuleId::INVALID() != buffer_module_id); + + /* Find the instance id */ + size_t buffer_instance_id = module_manager.num_instance(mux_module, buffer_module_id); + /* Add the module to mux_module */ + module_manager.add_child_module(mux_module, buffer_module_id); + + /* Add module nets to wire to the buffer module */ + /* To match the context, Buffer should have only 2 non-global ports: 1 input port and 1 output port */ + std::vector buffer_model_input_ports = circuit_lib.model_ports_by_type(buffer_model, SPICE_MODEL_PORT_INPUT, true); + std::vector buffer_model_output_ports = circuit_lib.model_ports_by_type(buffer_model, SPICE_MODEL_PORT_OUTPUT, true); + VTR_ASSERT(1 == buffer_model_input_ports.size()); + VTR_ASSERT(1 == buffer_model_output_ports.size()); + + /* Find the input and output module ports */ + ModulePortId buffer_input_port_id = module_manager.find_module_port(buffer_module_id, circuit_lib.port_lib_name(buffer_model_input_ports[0])); + ModulePortId buffer_output_port_id = module_manager.find_module_port(buffer_module_id, circuit_lib.port_lib_name(buffer_model_output_ports[0])); + + /* Port size should be 1 ! */ + VTR_ASSERT(1 == module_manager.module_port(buffer_module_id, buffer_input_port_id).get_width()); + VTR_ASSERT(1 == module_manager.module_port(buffer_module_id, buffer_output_port_id).get_width()); + + /* 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)); + module_manager.add_module_net_sink(mux_module, buffer_net, buffer_module_id, buffer_instance_id, buffer_input_port_id, module_manager.module_port(buffer_module_id, buffer_input_port_id).get_lsb()); + + /* Create a module net which sources from buffer output */ + ModuleNetId input_net = module_manager.create_module_net(mux_module); + module_manager.add_module_net_source(mux_module, input_net, buffer_module_id, buffer_instance_id, buffer_output_port_id, module_manager.module_port(buffer_module_id, buffer_output_port_id).get_lsb()); + 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 */ + std::vector mux_output_ports = circuit_lib.model_ports_by_type(mux_model, SPICE_MODEL_PORT_OUTPUT, true); + + /* 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_lib_name(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_masks(output_port).empty()) { + output_node_index_at_level = circuit_lib.port_lut_output_masks(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); + /* Get the moduleId for the buffer module */ + ModuleId buffer_module_id = module_manager.find_module(circuit_lib.model_name(buffer_model)); + /* We must have one */ + VTR_ASSERT(ModuleId::INVALID() != buffer_module_id); + + /* Find the instance id */ + size_t buffer_instance_id = module_manager.num_instance(mux_module, buffer_module_id); + /* Add the module to mux_module */ + module_manager.add_child_module(mux_module, buffer_module_id); + + /* Add module nets to wire to the buffer module */ + /* To match the context, Buffer should have only 2 non-global ports: 1 input port and 1 output port */ + std::vector buffer_model_input_ports = circuit_lib.model_ports_by_type(buffer_model, SPICE_MODEL_PORT_INPUT, true); + std::vector buffer_model_output_ports = circuit_lib.model_ports_by_type(buffer_model, SPICE_MODEL_PORT_OUTPUT, true); + VTR_ASSERT(1 == buffer_model_input_ports.size()); + VTR_ASSERT(1 == buffer_model_output_ports.size()); + + /* Find the input and output module ports */ + ModulePortId buffer_input_port_id = module_manager.find_module_port(buffer_module_id, circuit_lib.port_lib_name(buffer_model_input_ports[0])); + ModulePortId buffer_output_port_id = module_manager.find_module_port(buffer_module_id, circuit_lib.port_lib_name(buffer_model_output_ports[0])); + + /* Port size should be 1 ! */ + VTR_ASSERT(1 == module_manager.module_port(buffer_module_id, buffer_input_port_id).get_width()); + VTR_ASSERT(1 == module_manager.module_port(buffer_module_id, buffer_output_port_id).get_width()); + + /* Connect the module net from buffer output to MUX output */ + ModuleNetId buffer_net = module_manager.create_module_net(mux_module); + module_manager.add_module_net_source(mux_module, buffer_net, buffer_module_id, buffer_instance_id, buffer_output_port_id, module_manager.module_port(buffer_module_id, buffer_output_port_id).get_lsb()); + module_manager.add_module_net_sink(mux_module, buffer_net, mux_module, 0, module_output_port_id, pin); + + /* Create a module net which sinks at buffer input */ + ModuleNetId output_net = module_manager.create_module_net(mux_module); + module_manager.add_module_net_sink(mux_module, output_net, buffer_module_id, buffer_instance_id, buffer_input_port_id, module_manager.module_port(buffer_module_id, buffer_input_port_id).get_lsb()); + mux_output_nets[output_index] = output_net; + } + } + + return mux_output_nets; +} + +/********************************************************************* + * 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, SPICE_MODEL_PORT_INPUT, true, true); + /* Get the input ports from the mux */ + std::vector mux_input_ports = circuit_lib.model_ports_by_type(mux_model, SPICE_MODEL_PORT_INPUT, true); + /* Get the output ports from the mux */ + std::vector mux_output_ports = circuit_lib.model_ports_by_type(mux_model, SPICE_MODEL_PORT_OUTPUT, true); + /* 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 ((SPICE_MODEL_MUX == circuit_lib.model_type(mux_model)) + || ((SPICE_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( (SPICE_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); + /* 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_lib_name(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_lib_name(port), num_outputs); + if (SPICE_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_lib_name(port), num_mems); + module_manager.add_port(mux_module, mem_port, ModuleManager::MODULE_INPUT_PORT); + BasicPort mem_inv_port(std::string(circuit_lib.port_lib_name(port) + "_inv"), 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; + /* 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_lib_name(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_lib_name(port) + "_inv")); + 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()); + } else { + /* 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_lib_name(mux_sram_ports[0])); + BasicPort lvl_addr_port(circuit_lib.port_lib_name(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); + + 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 = module_manager.create_module_net(mux_module); + module_manager.add_module_net_source(mux_module, net, decoder_module, decoder_instance, decoder_module_data_port_id, pin); + module_manager.set_net_name(mux_module, net, std::string(decoder_module_data_port.get_name() + "_" + std::to_string(pin) + "_")); + /* Add the module nets to mux_mem_nets cache */ + mux_mem_nets[MuxMemId(mem_net_cnt)] = net; + 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 = module_manager.create_module_net(mux_module); + module_manager.add_module_net_source(mux_module, net, decoder_module, decoder_instance, decoder_module_data_inv_port_id, pin); + module_manager.set_net_name(mux_module, net, std::string(decoder_module_data_inv_port.get_name() + "_" + std::to_string(pin) + "_")); + /* Add the module nets to mux_mem_inv_nets cache */ + mux_mem_inv_nets[MuxMemId(mem_inv_net_cnt)] = net; + 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()); + } + + /* 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 (SPICE_MODEL_GATE == circuit_lib.model_type(tgate_model)) { + VTR_ASSERT(SPICE_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(SPICE_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 (SPICE_MODEL_LUT == circuit_lib.model_type(circuit_model)) { + /* RRAM LUT is not supported now... */ + vpr_printf(TIO_MESSAGE_ERROR, + "(File:%s,[LINE%d])RRAM-based LUT is not supported (Circuit model: %s)!\n", + __FILE__, __LINE__, 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, SPICE_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, SPICE_MODEL_PORT_INPUT, true); + /* Get the output ports from the mux */ + std::vector mux_output_ports = circuit_lib.model_ports_by_type(circuit_model, SPICE_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, SPICE_MODEL_PORT_BLB, true); + std::vector mux_wl_ports = circuit_lib.model_ports_by_type(circuit_model, SPICE_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); + /* Add module ports */ + /* Add each global port */ + for (const auto& port : mux_global_ports) { + /* Configure each global port */ + BasicPort global_port(circuit_lib.port_lib_name(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_lib_name(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_lib_name(port), num_outputs); + if (SPICE_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_lib_name(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_lib_name(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 SPICE_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 SPICE_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: + vpr_printf(TIO_MESSAGE_ERROR, + "(FILE:%s,LINE[%d]) Invalid design technology of multiplexer (name: %s)\n", + __FILE__, __LINE__, 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) { + + /* 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, + find_mux_num_datapath_inputs(circuit_lib, mux_circuit_model, mux_graph.num_inputs()), + 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); + } +} + diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_mux_modules.h b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_mux_modules.h new file mode 100644 index 000000000..7b65b56b9 --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_mux_modules.h @@ -0,0 +1,19 @@ +/*********************************************** + * Header file for verilog_mux.cpp + **********************************************/ + +#ifndef BUILD_MUX_MODULES_H +#define BUILD_MUX_MODULES_H + +/* Include other header files which are dependency on the function declared below */ +#include "spice_types.h" +#include "circuit_library.h" +#include "mux_graph.h" +#include "mux_library.h" +#include "module_manager.h" + +void build_mux_modules(ModuleManager& module_manager, + const MuxLibrary& mux_lib, + const CircuitLibrary& circuit_lib); + +#endif diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_module_writer.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_module_writer.cpp index 908bc8555..4ba620dbd 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_module_writer.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_module_writer.cpp @@ -62,11 +62,30 @@ BasicPort generate_verilog_port_for_module_net(const ModuleManager& module_manag } /* Reach here, this is a local wire */ - std::string net_name; + if (false == module_manager.net_name(module_id, module_net).empty()) { + net_name = module_manager.net_name(module_id, module_net); + printf("net_name:%s\n", net_name.c_str()); + } + /* Each net must only one 1 source */ + if (1 != module_manager.net_source_modules(module_id, module_net).size()) { + for (auto src_module : module_manager.net_source_modules(module_id, module_net)) { + printf("net_source_module: %s\n", + module_manager.module_name(src_module).c_str()); + } + for (auto sink_module : module_manager.net_sink_modules(module_id, module_net)) { + printf("net_sink_module: %s\n", + module_manager.module_name(sink_module).c_str()); + for (auto sink_port : module_manager.net_sink_ports(module_id, module_net)) { + printf("\tnet_sink_port: %s\n", + module_manager.module_port(sink_module, sink_port).get_name().c_str()); + } + } + VTR_ASSERT(1 == module_manager.net_source_modules(module_id, module_net).size()); + } /* Get the source module */ ModuleId net_src_module = module_manager.net_source_modules(module_id, module_net)[ModuleNetSrcId(0)]; @@ -102,6 +121,12 @@ std::vector find_verilog_module_local_wires(const ModuleManager& modu /* Local wires come from the child modules */ for (ModuleNetId module_net : module_manager.module_nets(module_id)) { + /* Bypass dangling nets */ + if ( (0 == module_manager.net_source_modules(module_id, module_net).size()) + && (0 == module_manager.net_source_modules(module_id, module_net).size()) ) { + continue; + } + /* We only care local wires */ if (false == module_net_is_local_wire(module_manager, module_id, module_net)) { continue; @@ -260,8 +285,15 @@ void write_verilog_instance_to_file(std::fstream& fp, /* Print module name */ fp << "\t" << module_manager.module_name(child_module) << " "; - /* Print instance name, _ */ - fp << module_manager.module_name(child_module) << "_" << instance_id << "_" << " (" << std::endl; + /* Print instance name: + * if we have an instance name, use it; + * if not, we use a default name _ + */ + if (true == module_manager.instance_name(parent_module, child_module, instance_id).empty()) { + fp << module_manager.module_name(child_module) << "_" << instance_id << "_" << " (" << std::endl; + } else { + fp << module_manager.instance_name(parent_module, child_module, instance_id) << " (" << std::endl; + } /* Print each port with/without explicit port map */ /* port type2type mapping */ diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_mux.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_mux.cpp index 0f4b10579..232d7fdf4 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_mux.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_mux.cpp @@ -28,86 +28,9 @@ /* FPGA-Verilog context header files */ #include "verilog_global.h" #include "verilog_writer_utils.h" +#include "verilog_module_writer.h" #include "verilog_mux.h" -/********************************************************************* - * Generate structural Verilog codes (consist of transmission-gates or - * pass-transistor) modeling an branch circuit - * for a multiplexer with the given size - *********************************************************************/ -static -void generate_verilog_cmos_mux_branch_body_structural(ModuleManager& module_manager, - const CircuitLibrary& circuit_lib, - std::fstream& fp, - const CircuitModelId& tgate_model, - const ModuleId& module_id, - const BasicPort& input_port, - const BasicPort& output_port, - const BasicPort& mem_port, - const BasicPort& mem_inv_port, - const MuxGraph& mux_graph) { - /* Make sure we have a valid file handler*/ - check_file_handler(fp); - - /* 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); - - /* TODO: move to check_circuit_library? Get model ports of tgate */ - std::vector tgate_input_ports = circuit_lib.model_ports_by_type(tgate_model, SPICE_MODEL_PORT_INPUT, true); - std::vector tgate_output_ports = circuit_lib.model_ports_by_type(tgate_model, SPICE_MODEL_PORT_OUTPUT, true); - VTR_ASSERT(3 == tgate_input_ports.size()); - VTR_ASSERT(1 == tgate_output_ports.size()); - - /* Verilog Behavior description for a MUX */ - print_verilog_comment(fp, std::string("---- Structure-level description -----")); - - /* Output the netlist following the connections in mux_graph */ - /* Iterate over the inputs */ - for (const auto& mux_input : mux_graph.inputs()) { - BasicPort cur_input_port(input_port.get_name(), size_t(mux_graph.input_id(mux_input)), size_t(mux_graph.input_id(mux_input))); - /* Iterate over the outputs */ - for (const auto& mux_output : mux_graph.outputs()) { - BasicPort cur_output_port(output_port.get_name(), size_t(mux_graph.output_id(mux_output)), size_t(mux_graph.output_id(mux_output))); - /* 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; - } - /* Output a tgate use a module manager */ - /* Create a port-to-port name map */ - std::map port2port_name_map; - /* input port */ - port2port_name_map[circuit_lib.port_lib_name(tgate_input_ports[0])] = cur_input_port; - /* output port */ - port2port_name_map[circuit_lib.port_lib_name(tgate_output_ports[0])] = cur_output_port; - /* Find the mem_id controlling the edge */ - MuxMemId mux_mem = mux_graph.find_edge_mem(edges[0]); - BasicPort cur_mem_port(mem_port.get_name(), size_t(mux_mem), size_t(mux_mem)); - BasicPort cur_mem_inv_port(mem_inv_port.get_name(), size_t(mux_mem), size_t(mux_mem)); - /* mem port */ - 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 */ - port2port_name_map[circuit_lib.port_lib_name(tgate_input_ports[1])] = cur_mem_port; - port2port_name_map[circuit_lib.port_lib_name(tgate_input_ports[2])] = cur_mem_inv_port; - } else { - /* wire mem_inv to mem of module, wire mem to mem_inv of module */ - port2port_name_map[circuit_lib.port_lib_name(tgate_input_ports[1])] = cur_mem_inv_port; - port2port_name_map[circuit_lib.port_lib_name(tgate_input_ports[2])] = cur_mem_port; - } - /* Output an instance of the module */ - print_verilog_module_instance(fp, module_manager, module_id, tgate_module_id, port2port_name_map, circuit_lib.dump_explicit_port_map(tgate_model)); - /* IMPORTANT: this update MUST be called after the instance outputting!!!! - * update the module manager with the relationship between the parent and child modules - */ - module_manager.add_child_module(module_id, tgate_module_id); - } - } -} - /********************************************************************* * Generate behavior-level Verilog codes modeling an branch circuit * for a multiplexer with the given size @@ -186,15 +109,14 @@ void generate_verilog_cmos_mux_branch_body_behavioral(std::fstream& fp, * Support structural and behavioral Verilog codes *********************************************************************/ static -void generate_verilog_cmos_mux_branch_module(ModuleManager& module_manager, - const CircuitLibrary& circuit_lib, - std::fstream& fp, - const CircuitModelId& circuit_model, - const std::string& module_name, - const MuxGraph& mux_graph, - const bool& use_structural_verilog) { +void print_verilog_cmos_mux_branch_module_behavioral(ModuleManager& module_manager, + const CircuitLibrary& circuit_lib, + std::fstream& fp, + 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(circuit_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 (SPICE_MODEL_GATE == circuit_lib.model_type(tgate_model)) { @@ -202,8 +124,6 @@ void generate_verilog_cmos_mux_branch_module(ModuleManager& module_manager, return; } - std::vector tgate_global_ports = circuit_lib.model_global_ports_by_type(tgate_model, SPICE_MODEL_PORT_INPUT, true, true); - /* Make sure we have a valid file handler*/ check_file_handler(fp); @@ -222,51 +142,27 @@ void generate_verilog_cmos_mux_branch_module(ModuleManager& module_manager, VTR_ASSERT(1 == mux_graph.num_levels()); /* 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); - /* Add module ports */ - /* Add each global port */ - for (const auto& port : tgate_global_ports) { - /* Configure each global port */ - BasicPort global_port(circuit_lib.port_lib_name(port), circuit_lib.port_size(port)); - module_manager.add_port(module_id, global_port, ModuleManager::MODULE_GLOBAL_PORT); - } - /* Add each input port */ + ModuleId mux_module = module_manager.find_module(module_name); + VTR_ASSERT(true == module_manager.valid_module_id(mux_module)); + /* Find module ports */ + /* Find each input port */ BasicPort input_port("in", num_inputs); - module_manager.add_port(module_id, input_port, ModuleManager::MODULE_INPUT_PORT); - /* Add each output port */ + /* Find each output port */ BasicPort output_port("out", num_outputs); - module_manager.add_port(module_id, output_port, ModuleManager::MODULE_OUTPUT_PORT); - /* Add each memory port */ + /* Find each memory port */ BasicPort mem_port("mem", num_mems); - module_manager.add_port(module_id, mem_port, ModuleManager::MODULE_INPUT_PORT); - BasicPort mem_inv_port("mem_inv", num_mems); - module_manager.add_port(module_id, mem_inv_port, ModuleManager::MODULE_INPUT_PORT); /* dump module definition + ports */ - print_verilog_module_declaration(fp, module_manager, module_id); + print_verilog_module_declaration(fp, module_manager, mux_module); - /* Print the internal logic in either structural or behavioral Verilog codes */ - if (true == use_structural_verilog) { - generate_verilog_cmos_mux_branch_body_structural(module_manager, circuit_lib, fp, tgate_model, module_id, input_port, output_port, mem_port, mem_inv_port, mux_graph); - } else { - VTR_ASSERT_SAFE(false == use_structural_verilog); - /* Get the default value of SRAM ports */ - std::vector sram_ports = circuit_lib.model_ports_by_type(circuit_model, SPICE_MODEL_PORT_SRAM, true); - std::vector non_mode_select_sram_ports; - /* We should have only have 1 sram port except those are mode_bits */ - for (const auto& port : sram_ports) { - if (true == circuit_lib.port_is_mode_select(port)) { - continue; - } - non_mode_select_sram_ports.push_back(port); - } - VTR_ASSERT(1 == non_mode_select_sram_ports.size()); - std::string mem_default_val = std::to_string(circuit_lib.port_default_value(non_mode_select_sram_ports[0])); - /* Mem string must be only 1-bit! */ - VTR_ASSERT(1 == mem_default_val.length()); - generate_verilog_cmos_mux_branch_body_behavioral(fp, input_port, output_port, mem_port, mux_graph, mem_default_val[0]); - } + /* Print the internal logic in behavioral Verilog codes */ + /* Get the default value of SRAM ports */ + std::vector regular_sram_ports = find_circuit_regular_sram_ports(circuit_lib, mux_model); + VTR_ASSERT(1 == regular_sram_ports.size()); + std::string mem_default_val = std::to_string(circuit_lib.port_default_value(regular_sram_ports[0])); + /* Mem string must be only 1-bit! */ + VTR_ASSERT(1 == mem_default_val.length()); + generate_verilog_cmos_mux_branch_body_behavioral(fp, input_port, output_port, mem_port, mux_graph, mem_default_val[0]); /* Put an end to the Verilog module */ print_verilog_module_end(fp, module_name); @@ -638,35 +534,22 @@ void generate_verilog_rram_mux_branch_module(ModuleManager& module_manager, 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); + ModuleId module_id = module_manager.find_module(module_name); + VTR_ASSERT(true == module_manager.valid_module_id(module_id)); - /* Add module ports */ - /* Add each global programming enable/disable ports */ - std::vector prog_enable_ports = circuit_lib.model_global_ports_by_type(circuit_model, SPICE_MODEL_PORT_INPUT, true, true); - for (const auto& port : prog_enable_ports) { - /* Configure each global port */ - BasicPort global_port(circuit_lib.port_lib_name(port), circuit_lib.port_size(port)); - module_manager.add_port(module_id, global_port, ModuleManager::MODULE_GLOBAL_PORT); - } - - /* Add each input port */ + /* Find each input port */ BasicPort input_port(circuit_lib.port_lib_name(mux_input_ports[0]), num_inputs); - module_manager.add_port(module_id, input_port, ModuleManager::MODULE_INPUT_PORT); - /* Add each output port */ + /* Find each output port */ BasicPort output_port(circuit_lib.port_lib_name(mux_output_ports[0]), num_outputs); - module_manager.add_port(module_id, output_port, ModuleManager::MODULE_OUTPUT_PORT); - /* Add RRAM programming ports, + /* Find 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_lib_name(mux_blb_ports[0]), num_mems + 1); - module_manager.add_port(module_id, blb_port, ModuleManager::MODULE_INPUT_PORT); BasicPort wl_port(circuit_lib.port_lib_name(mux_wl_ports[0]), num_mems + 1); - module_manager.add_port(module_id, wl_port, ModuleManager::MODULE_INPUT_PORT); /* dump module definition + ports */ print_verilog_module_declaration(fp, module_manager, module_id); @@ -690,442 +573,38 @@ static void generate_verilog_mux_branch_module(ModuleManager& module_manager, const CircuitLibrary& circuit_lib, std::fstream& fp, - const CircuitModelId& circuit_model, + const CircuitModelId& mux_model, const size_t& mux_size, const MuxGraph& mux_graph) { - std::string module_name = generate_mux_branch_subckt_name(circuit_lib, circuit_model, mux_size, mux_graph.num_inputs(), verilog_mux_basis_posfix); + std::string module_name = generate_mux_branch_subckt_name(circuit_lib, mux_model, mux_size, mux_graph.num_inputs(), verilog_mux_basis_posfix); /* Multiplexers built with different technology is in different organization */ - switch (circuit_lib.design_tech_type(circuit_model)) { + switch (circuit_lib.design_tech_type(mux_model)) { case SPICE_MODEL_DESIGN_CMOS: - generate_verilog_cmos_mux_branch_module(module_manager, circuit_lib, fp, circuit_model, module_name, mux_graph, - circuit_lib.dump_structural_verilog(circuit_model)); + if (true == circuit_lib.dump_structural_verilog(mux_model)) { + /* Structural verilog can be easily generated by module writer */ + ModuleId mux_module = module_manager.find_module(module_name); + VTR_ASSERT(true == module_manager.valid_module_id(mux_module)); + write_verilog_module_to_file(fp, module_manager, mux_module, circuit_lib.dump_explicit_port_map(mux_model)); + /* Add an empty line as a splitter */ + fp << std::endl; + } else { + /* Behavioral verilog requires customized generation */ + print_verilog_cmos_mux_branch_module_behavioral(module_manager, circuit_lib, fp, mux_model, module_name, mux_graph); + } break; case SPICE_MODEL_DESIGN_RRAM: - generate_verilog_rram_mux_branch_module(module_manager, circuit_lib, fp, circuit_model, module_name, mux_graph, - circuit_lib.dump_structural_verilog(circuit_model)); + generate_verilog_rram_mux_branch_module(module_manager, circuit_lib, fp, mux_model, module_name, mux_graph, + circuit_lib.dump_structural_verilog(mux_model)); break; default: vpr_printf(TIO_MESSAGE_ERROR, "(FILE:%s,LINE[%d]) Invalid design technology of multiplexer (name: %s)\n", - __FILE__, __LINE__, circuit_lib.model_name(circuit_model).c_str()); + __FILE__, __LINE__, 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 generate_verilog_cmos_mux_module_mux2_multiplexing_structure(ModuleManager& module_manager, - const CircuitLibrary& circuit_lib, - std::fstream& fp, - const ModuleId& module_id, - const CircuitModelId& circuit_model, - const CircuitModelId& std_cell_model, - const MuxGraph& mux_graph) { - /* Make sure we have a valid file handler*/ - check_file_handler(fp); - - /* TODO: these are duplicated codes, find a way to simplify it!!! - * Get the regular (non-mode-select) sram ports from the mux - */ - std::vector mux_regular_sram_ports; - for (const auto& port : circuit_lib.model_ports_by_type(circuit_model, SPICE_MODEL_PORT_SRAM, true)) { - /* Multiplexing structure does not mode_sram_ports, they are handled in LUT modules - * Here we just bypass it. - */ - if (true == circuit_lib.port_is_mode_select(port)) { - continue; - } - mux_regular_sram_ports.push_back(port); - } - 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, SPICE_MODEL_PORT_INPUT, true); - std::vector std_cell_output_ports = circuit_lib.model_ports_by_type(std_cell_model, SPICE_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()); - - /* 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()); - - print_verilog_comment(fp, std::string("---- BEGIN Internal Logic of a CMOS MUX module based on Standard Cells -----")); - - print_verilog_comment(fp, std::string("---- BEGIN Internal wires of a CMOS MUX module -----")); - /* Print local wires which are the nodes in the mux graph */ - for (size_t level = 0; level < mux_graph.num_levels(); ++level) { - /* Print the internal wires located at this level */ - BasicPort internal_wire_port(generate_mux_node_name(level, false), mux_graph.num_nodes_at_level(level)); - fp << "\t" << generate_verilog_port(VERILOG_PORT_WIRE, internal_wire_port) << ";" << std::endl; - /* Identify if an intermediate buffer is needed */ - if (false == inter_buffer_location_map[level]) { - continue; - } - BasicPort internal_wire_buffered_port(generate_mux_node_name(level, true), mux_graph.num_nodes_at_level(level)); - fp << "\t" << generate_verilog_port(VERILOG_PORT_WIRE, internal_wire_buffered_port) << std::endl; - } - print_verilog_comment(fp, std::string("---- END Internal wires of a CMOS MUX module -----")); - fp << std::endl; - - /* Iterate over all the internal nodes and output nodes in the mux graph */ - for (const auto& node : mux_graph.non_input_nodes()) { - print_verilog_comment(fp, std::string("---- BEGIN Instanciation of a branch CMOS MUX modules -----")); - /* 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 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); - - /* 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); - - /* 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 standard cell MUX2 - * We follow a fixed port map: - * TODO: the port map could be more flexible? - * input_port[0] of MUX2 standard cell is wired to input_node[0] - * input_port[1] of MUX2 standard cell is wired to input_node[1] - * output_port[0] of MUX2 standard cell is wired to output_node[0] - * input_port[2] of MUX2 standard cell is wired to mem_node[0] - */ - std::string branch_module_name= circuit_lib.model_name(std_cell_model); - /* 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); - - /* Create a port-to-port map */ - std::map port2port_name_map; - - /* 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 (const auto& input_node : input_nodes) { - /* Generate the port info of each input node */ - size_t input_node_level = mux_graph.node_level(input_node); - size_t input_node_index_at_level = mux_graph.node_index_at_level(input_node); - BasicPort instance_input_port(generate_mux_node_name(input_node_level, inter_buffer_location_map[input_node_level]), input_node_index_at_level, input_node_index_at_level); - - /* Link nodes to input ports for the branch module */ - std::string module_input_port_name = circuit_lib.port_lib_name(std_cell_input_ports[&input_node - &input_nodes[0]]); - port2port_name_map[module_input_port_name] = instance_input_port; - } - - /* Build the link between output_node[0] and std_cell_output_port[0] */ - { /* Create a code block to accommodate the local variables */ - BasicPort instance_output_port(generate_mux_node_name(output_node_level, false), output_node_index_at_level, output_node_index_at_level); - std::string module_output_port_name = circuit_lib.port_lib_name(std_cell_output_ports[0]); - port2port_name_map[module_output_port_name] = instance_output_port; - } - - /* To match the standard cell MUX2: We should have only 1 mem_node */ - VTR_ASSERT(1 == mems.size()); - /* Build the link between mem_node[0] and std_cell_intput_port[2] */ - for (const auto& mem : mems) { - /* Generate the port info of each mem node */ - BasicPort instance_mem_port(circuit_lib.port_lib_name(mux_regular_sram_ports[0]), size_t(mem), size_t(mem)); - std::string module_mem_port_name = circuit_lib.port_lib_name(std_cell_input_ports[2]); - /* If use local decoders, we should use another name for the mem port */ - if (true == circuit_lib.mux_use_local_encoder(circuit_model)) { - instance_mem_port.set_name(generate_mux_local_decoder_data_port_name()); - } - port2port_name_map[module_mem_port_name] = instance_mem_port; - } - - /* Output an instance of the module */ - print_verilog_module_instance(fp, module_manager, module_id, branch_module_id, port2port_name_map, circuit_lib.dump_explicit_port_map(std_cell_model)); - /* IMPORTANT: this update MUST be called after the instance outputting!!!! - * update the module manager with the relationship between the parent and child modules - */ - module_manager.add_child_module(module_id, branch_module_id); - - print_verilog_comment(fp, std::string("---- END Instanciation of a branch CMOS MUX modules -----")); - - if (false == inter_buffer_location_map[output_node_level]) { - continue; /* No need for intermediate buffers */ - } - - print_verilog_comment(fp, std::string("---- BEGIN Instanciation of a intermediate buffer modules -----")); - - /* 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); - - BasicPort buffer_instance_input_port(generate_mux_node_name(output_node_level, false), output_node_index_at_level, output_node_index_at_level); - BasicPort buffer_instance_output_port(generate_mux_node_name(output_node_level, true), output_node_index_at_level, output_node_index_at_level); - - print_verilog_buffer_instance(fp, module_manager, circuit_lib, module_id, buffer_model, buffer_instance_input_port, buffer_instance_output_port); - - print_verilog_comment(fp, std::string("---- END Instanciation of a intermediate buffer modules -----")); - fp << std::endl; - } - - print_verilog_comment(fp, std::string("---- END Internal Logic of a CMOS MUX module based on Standard Cells -----")); - fp << std::endl; -} - -/******************************************************************** - * 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 generate_verilog_cmos_mux_module_tgate_multiplexing_structure(ModuleManager& module_manager, - const CircuitLibrary& circuit_lib, - std::fstream& fp, - const ModuleId& module_id, - const CircuitModelId& circuit_model, - const MuxGraph& mux_graph) { - /* Make sure we have a valid file handler*/ - check_file_handler(fp); - - /* Find the actual mux size */ - size_t mux_size = find_mux_num_datapath_inputs(circuit_lib, circuit_model, mux_graph.num_inputs()); - - /* 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()); - - /* 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()); - - print_verilog_comment(fp, std::string("---- BEGIN Internal Logic of a CMOS MUX module based on Pass-transistor/Transmission-gates -----")); - - print_verilog_comment(fp, std::string("---- BEGIN Internal wires of a CMOS MUX module -----")); - /* Print local wires which are the nodes in the mux graph */ - for (size_t level = 0; level < mux_graph.num_levels(); ++level) { - /* Print the internal wires located at this level */ - BasicPort internal_wire_port(generate_mux_node_name(level, false), mux_graph.num_nodes_at_level(level)); - fp << "\t" << generate_verilog_port(VERILOG_PORT_WIRE, internal_wire_port) << ";" << std::endl; - /* Identify if an intermediate buffer is needed */ - if (false == inter_buffer_location_map[level]) { - continue; - } - BasicPort internal_wire_buffered_port(generate_mux_node_name(level, true), mux_graph.num_nodes_at_level(level)); - fp << "\t" << generate_verilog_port(VERILOG_PORT_WIRE, internal_wire_buffered_port) << std::endl; - } - print_verilog_comment(fp, std::string("---- END Internal wires of a CMOS MUX module -----")); - fp << std::endl; - - /* Iterate over all the internal nodes and output nodes in the mux graph */ - for (const auto& node : mux_graph.non_input_nodes()) { - print_verilog_comment(fp, std::string("---- BEGIN Instanciation of a branch CMOS MUX module -----")); - /* 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 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); - - /* 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); - - /* 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, mux_size, branch_size, verilog_mux_basis_posfix); - /* 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); - - /* Create a port-to-port map */ - std::map port2port_name_map; - /* TODO: the branch module name should NOT be hard-coded. Use the port lib_name given by users! */ - - /* All the input node names organized in bus */ - std::vector branch_node_input_ports; - for (const auto& input_node : input_nodes) { - /* Generate the port info of each input node */ - size_t input_node_level = mux_graph.node_level(input_node); - size_t input_node_index_at_level = mux_graph.node_index_at_level(input_node); - BasicPort branch_node_input_port(generate_mux_node_name(input_node_level, inter_buffer_location_map[input_node_level]), input_node_index_at_level, input_node_index_at_level); - branch_node_input_ports.push_back(branch_node_input_port); - } - - /* Create the port info for the input */ - /* TODO: the naming could be more flexible? */ - BasicPort instance_input_port = generate_verilog_bus_port(branch_node_input_ports, std::string(generate_mux_node_name(output_node_level, false) + "_in")); - /* If we have more than 1 port in the combined instance ports , - * output a local wire */ - if (1 < combine_verilog_ports(branch_node_input_ports).size()) { - /* Print a local wire for the merged ports */ - fp << "\t" << generate_verilog_local_wire(instance_input_port, branch_node_input_ports) << std::endl; - } else { - /* Safety check */ - VTR_ASSERT(1 == combine_verilog_ports(branch_node_input_ports).size()); - } - - /* Link nodes to input ports for the branch module */ - ModulePortId module_input_port_id = module_manager.find_module_port(branch_module_id, "in"); - VTR_ASSERT(ModulePortId::INVALID() != module_input_port_id); - /* Get the port from module */ - BasicPort module_input_port = module_manager.module_port(branch_module_id, module_input_port_id); - port2port_name_map[module_input_port.get_name()] = instance_input_port; - - /* Link nodes to output ports for the branch module */ - BasicPort instance_output_port(generate_mux_node_name(output_node_level, false), output_node_index_at_level, output_node_index_at_level); - ModulePortId module_output_port_id = module_manager.find_module_port(branch_module_id, "out"); - VTR_ASSERT(ModulePortId::INVALID() != module_output_port_id); - /* Get the port from module */ - BasicPort module_output_port = module_manager.module_port(branch_module_id, module_output_port_id); - port2port_name_map[module_output_port.get_name()] = instance_output_port; - - /* All the mem node names organized in bus */ - std::vector branch_node_mem_ports; - for (const auto& mem : mems) { - /* Generate the port info of each mem node */ - BasicPort branch_node_mem_port(circuit_lib.port_lib_name(mux_regular_sram_ports[0]), size_t(mem), size_t(mem)); - /* If use local decoders, we should use another name for the mem port */ - if (true == circuit_lib.mux_use_local_encoder(circuit_model)) { - branch_node_mem_port.set_name(generate_mux_local_decoder_data_port_name()); - } - branch_node_mem_ports.push_back(branch_node_mem_port); - } - - /* Create the port info for the input */ - /* TODO: the naming could be more flexible? */ - BasicPort instance_mem_port = generate_verilog_bus_port(branch_node_mem_ports, std::string(generate_mux_node_name(output_node_level, false) + "_mem")); - /* If we have more than 1 port in the combined instance ports , - * output a local wire */ - if (1 < combine_verilog_ports(branch_node_mem_ports).size()) { - /* Print a local wire for the merged ports */ - fp << "\t" << generate_verilog_local_wire(instance_mem_port, branch_node_mem_ports) << std::endl; - } else { - /* Safety check */ - VTR_ASSERT(1 == combine_verilog_ports(branch_node_mem_ports).size()); - } - - /* Link nodes to input ports for the branch module */ - /* TODO: the naming could be more flexible? */ - ModulePortId module_mem_port_id = module_manager.find_module_port(branch_module_id, "mem"); - VTR_ASSERT(ModulePortId::INVALID() != module_mem_port_id); - /* Get the port from module */ - BasicPort module_mem_port = module_manager.module_port(branch_module_id, module_mem_port_id); - port2port_name_map[module_mem_port.get_name()] = instance_mem_port; - - /* TODO: the postfix _inv can be soft coded in the circuit library as a port_inv_postfix */ - /* Create the port info for the mem_inv */ - std::vector branch_node_mem_inv_ports; - for (const auto& mem : mems) { - /* Generate the port info of each mem node */ - BasicPort branch_node_mem_inv_port(circuit_lib.port_lib_name(mux_regular_sram_ports[0]) + "_inv", size_t(mem), size_t(mem)); - /* If use local decoders, we should use another name for the mem port */ - if (true == circuit_lib.mux_use_local_encoder(circuit_model)) { - branch_node_mem_inv_port.set_name(generate_mux_local_decoder_data_inv_port_name()); - } - branch_node_mem_inv_ports.push_back(branch_node_mem_inv_port); - } - - /* Create the port info for the input */ - /* TODO: the naming could be more flexible? */ - BasicPort instance_mem_inv_port = generate_verilog_bus_port(branch_node_mem_inv_ports, std::string(generate_mux_node_name(output_node_level, false) + "_mem_inv")); - /* If we have more than 1 port in the combined instance ports , - * output a local wire */ - if (1 < combine_verilog_ports(branch_node_mem_inv_ports).size()) { - /* Print a local wire for the merged ports */ - fp << "\t" << generate_verilog_local_wire(instance_mem_port, branch_node_mem_inv_ports) << std::endl; - } else { - /* Safety check */ - VTR_ASSERT(1 == combine_verilog_ports(branch_node_mem_inv_ports).size()); - } - - /* Link nodes to input ports for the branch module */ - /* TODO: the naming could be more flexible? */ - ModulePortId module_mem_inv_port_id = module_manager.find_module_port(branch_module_id, "mem_inv"); - VTR_ASSERT(ModulePortId::INVALID() != module_mem_inv_port_id); - /* Get the port from module */ - BasicPort module_mem_inv_port = module_manager.module_port(branch_module_id, module_mem_inv_port_id); - port2port_name_map[module_mem_inv_port.get_name()] = instance_mem_inv_port; - - /* Output an instance of the module */ - print_verilog_module_instance(fp, module_manager, module_id, branch_module_id, port2port_name_map, circuit_lib.dump_explicit_port_map(circuit_model)); - /* IMPORTANT: this update MUST be called after the instance outputting!!!! - * update the module manager with the relationship between the parent and child modules - */ - module_manager.add_child_module(module_id, branch_module_id); - - print_verilog_comment(fp, std::string("---- END Instanciation of a branch CMOS MUX module -----")); - fp << std::endl; - - if (false == inter_buffer_location_map[output_node_level]) { - continue; /* No need for intermediate buffers */ - } - - print_verilog_comment(fp, std::string("---- BEGIN Instanciation of an intermediate buffer modules -----")); - - /* 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); - - BasicPort buffer_instance_input_port(generate_mux_node_name(output_node_level, false), output_node_index_at_level, output_node_index_at_level); - BasicPort buffer_instance_output_port(generate_mux_node_name(output_node_level, true), output_node_index_at_level, output_node_index_at_level); - - print_verilog_buffer_instance(fp, module_manager, circuit_lib, module_id, buffer_model, buffer_instance_input_port, buffer_instance_output_port); - - print_verilog_comment(fp, std::string("---- END Instanciation of an intermediate buffer module -----")); - fp << std::endl; - } - - print_verilog_comment(fp, std::string("---- END Internal Logic of a CMOS MUX module based on Pass-transistor/Transmission-gates -----")); - fp << std::endl; -} - /******************************************************************** * Generate the input bufferes for a multiplexer or LUT in Verilog codes * 1. If input are required to be buffered (specified by users), @@ -1297,196 +776,6 @@ void generate_verilog_cmos_mux_module_output_buffers(ModuleManager& module_manag } } -/********************************************************************* - * Generate Verilog codes modeling a CMOS multiplexer with the given size - * The Verilog 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 generate_verilog_cmos_mux_module(ModuleManager& module_manager, - const CircuitLibrary& circuit_lib, - std::fstream& fp, - const CircuitModelId& circuit_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(circuit_model, SPICE_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, SPICE_MODEL_PORT_INPUT, true); - /* Get the output ports from the mux */ - std::vector mux_output_ports = circuit_lib.model_ports_by_type(circuit_model, SPICE_MODEL_PORT_OUTPUT, true); - /* 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, circuit_model); - - /* Make sure we have a valid file handler*/ - check_file_handler(fp); - - /* 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(); - - /* 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(circuit_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 ((SPICE_MODEL_MUX == circuit_lib.model_type(circuit_model)) - || ((SPICE_MODEL_LUT == circuit_lib.model_type(circuit_model)) - && (false == circuit_lib.is_lut_fracturable(circuit_model))) ) { - VTR_ASSERT(1 == mux_output_ports.size()); - VTR_ASSERT(1 == circuit_lib.port_size(mux_output_ports[0])); - } else { - VTR_ASSERT_SAFE( (SPICE_MODEL_LUT == circuit_lib.model_type(circuit_model)) - && (true == circuit_lib.is_lut_fracturable(circuit_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 module_id = module_manager.add_module(module_name); - VTR_ASSERT(ModuleId::INVALID() != module_id); - /* Add module ports */ - /* Add each global port */ - for (const auto& port : mux_global_ports) { - /* Configure each global port */ - BasicPort global_port(circuit_lib.port_lib_name(port), circuit_lib.port_size(port)); - module_manager.add_port(module_id, global_port, ModuleManager::MODULE_GLOBAL_PORT); - } - /* 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_lib_name(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_lib_name(port), num_outputs); - if (SPICE_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); - } - - size_t sram_port_cnt = 0; - for (const auto& port : mux_sram_ports) { - BasicPort mem_port(circuit_lib.port_lib_name(port), num_mems); - module_manager.add_port(module_id, mem_port, ModuleManager::MODULE_INPUT_PORT); - BasicPort mem_inv_port(std::string(circuit_lib.port_lib_name(port) + "_inv"), num_mems); - module_manager.add_port(module_id, mem_inv_port, ModuleManager::MODULE_INPUT_PORT); - /* Update counter */ - sram_port_cnt++; - } - VTR_ASSERT(1 == sram_port_cnt); - - /* dump module definition + ports */ - print_verilog_module_declaration(fp, module_manager, module_id); - - /* Add local decoder instance here */ - if (true == circuit_lib.mux_use_local_encoder(circuit_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()); - /* Print local wires to bridge the port of module and memory inputs - * of each MUX branch instance - */ - fp << generate_verilog_port(VERILOG_PORT_WIRE, decoder_data_port) << ";" << std::endl; - fp << generate_verilog_port(VERILOG_PORT_WIRE, decoder_data_inv_port) << ";" << std::endl; - - /* Local port to record the LSB and MSB of each level, here, we deposite (0, 0) */ - BasicPort lvl_addr_port(circuit_lib.port_lib_name(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); - 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); - /* Print the instance of local decoder */ - 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); - - /* Create a port-to-port map */ - std::map decoder_port2port_name_map; - decoder_port2port_name_map[generate_mux_local_decoder_addr_port_name()] = lvl_addr_port; - decoder_port2port_name_map[generate_mux_local_decoder_data_port_name()] = lvl_data_port; - decoder_port2port_name_map[generate_mux_local_decoder_data_inv_port_name()] = lvl_data_inv_port; - - /* Print an instance of the MUX Module */ - print_verilog_comment(fp, std::string("----- BEGIN Instanciation of a local decoder -----")); - print_verilog_module_instance(fp, module_manager, module_id, decoder_module, decoder_port2port_name_map, circuit_lib.dump_explicit_port_map(circuit_model)); - print_verilog_comment(fp, std::string("----- END Instanciation of a local decoder -----")); - fp << std::endl; - /* IMPORTANT: this update MUST be called after the instance outputting!!!! - * update the module manager with the relationship between the parent and child modules - */ - module_manager.add_child_module(module_id, decoder_module); - } - } - - /* 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(circuit_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 (SPICE_MODEL_GATE == circuit_lib.model_type(tgate_model)) { - VTR_ASSERT(SPICE_MODEL_GATE_MUX2 == circuit_lib.gate_type(tgate_model)); - generate_verilog_cmos_mux_module_mux2_multiplexing_structure(module_manager, circuit_lib, fp, module_id, circuit_model, tgate_model, mux_graph); - } else { - VTR_ASSERT(SPICE_MODEL_PASSGATE == circuit_lib.model_type(tgate_model)); - generate_verilog_cmos_mux_module_tgate_multiplexing_structure(module_manager, circuit_lib, fp, module_id, circuit_model, mux_graph); - } - - /* Print the input and output buffers in Verilog codes */ - generate_verilog_cmos_mux_module_input_buffers(module_manager, circuit_lib, fp, module_id, circuit_model, mux_graph); - generate_verilog_cmos_mux_module_output_buffers(module_manager, circuit_lib, fp, module_id, circuit_model, mux_graph); - - /* Put an end to the Verilog module */ - print_verilog_module_end(fp, module_name); -} - /******************************************************************** * Generate the 4T1R-based internal logic * (multiplexing structure) for a multiplexer in Verilog codes @@ -1880,26 +1169,31 @@ static void generate_verilog_mux_module(ModuleManager& module_manager, const CircuitLibrary& circuit_lib, std::fstream& fp, - const CircuitModelId& circuit_model, + const CircuitModelId& mux_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 module_name = generate_mux_subckt_name(circuit_lib, mux_model, + find_mux_num_datapath_inputs(circuit_lib, mux_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 SPICE_MODEL_DESIGN_CMOS: - /* SRAM-based Multiplexer Verilog module generation */ - generate_verilog_cmos_mux_module(module_manager, circuit_lib, fp, circuit_model, module_name, mux_graph); + switch (circuit_lib.design_tech_type(mux_model)) { + case SPICE_MODEL_DESIGN_CMOS: { + /* Use Verilog writer to print the module to file */ + ModuleId mux_module = module_manager.find_module(module_name); + VTR_ASSERT(true == module_manager.valid_module_id(mux_module)); + write_verilog_module_to_file(fp, module_manager, mux_module, circuit_lib.dump_explicit_port_map(mux_model)); + /* Add an empty line as a splitter */ + fp << std::endl; break; + } case SPICE_MODEL_DESIGN_RRAM: /* TODO: RRAM-based Multiplexer Verilog module generation */ - generate_verilog_rram_mux_module(module_manager, circuit_lib, fp, circuit_model, module_name, mux_graph); + generate_verilog_rram_mux_module(module_manager, circuit_lib, fp, mux_model, module_name, mux_graph); break; default: vpr_printf(TIO_MESSAGE_ERROR, "(FILE:%s,LINE[%d]) Invalid design technology of multiplexer (name: %s)\n", - __FILE__, __LINE__, circuit_lib.model_name(circuit_model).c_str()); + __FILE__, __LINE__, circuit_lib.model_name(mux_model).c_str()); exit(1); } }