From bc9d95408ec2d813483b92a1a652ed3582f46f7e Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 5 Sep 2019 16:09:28 -0600 Subject: [PATCH] bug fixed and refactored intermediate buffer addition --- vpr7_x2p/libarchfpga/SRC/circuit_library.cpp | 55 ++- vpr7_x2p/libarchfpga/SRC/circuit_library.h | 3 +- vpr7_x2p/vpr/SRC/device/mux_graph.cpp | 8 + vpr7_x2p/vpr/SRC/device/mux_graph.h | 1 + vpr7_x2p/vpr/SRC/device/mux_utils.cpp | 55 ++- vpr7_x2p/vpr/SRC/device/mux_utils.h | 8 +- .../vpr/SRC/fpga_x2p/verilog/verilog_mux.cpp | 384 ++++++++++++++++-- .../SRC/fpga_x2p/verilog/verilog_submodules.c | 64 ++- .../fpga_x2p/verilog/verilog_writer_utils.cpp | 6 + 9 files changed, 504 insertions(+), 80 deletions(-) diff --git a/vpr7_x2p/libarchfpga/SRC/circuit_library.cpp b/vpr7_x2p/libarchfpga/SRC/circuit_library.cpp index d2aca01c3..ecdd11c65 100644 --- a/vpr7_x2p/libarchfpga/SRC/circuit_library.cpp +++ b/vpr7_x2p/libarchfpga/SRC/circuit_library.cpp @@ -175,7 +175,12 @@ bool CircuitLibrary::is_lut_intermediate_buffered(const CircuitModelId& model_id VTR_ASSERT(valid_model_id(model_id)); /* validate the circuit model type is LUT */ VTR_ASSERT(SPICE_MODEL_LUT == model_type(model_id)); - return buffer_existence_[model_id][LUT_INTER_BUFFER]; + /* LUT inter buffer may not always exist */ + if (LUT_INTER_BUFFER < buffer_existence_[model_id].size()) { + return buffer_existence_[model_id][LUT_INTER_BUFFER]; + } else { + return false; + } } /* Return a flag showing if a LUT circuit model uses fracturable structure */ @@ -187,6 +192,38 @@ bool CircuitLibrary::is_lut_fracturable(const CircuitModelId& model_id) const { return lut_is_fracturable_[model_id]; } +/* Return the circuit model of intermediate buffers + * that are inserted inside LUT multiplexing structures + */ +CircuitModelId CircuitLibrary::lut_intermediate_buffer_model(const CircuitModelId& model_id) const { + /* validate the model_id */ + VTR_ASSERT(valid_model_id(model_id)); + /* validate the circuit model type is BUF */ + VTR_ASSERT(SPICE_MODEL_LUT == model_type(model_id)); + /* if we have an intermediate buffer, we return something, otherwise return an empty map */ + if (true == is_lut_intermediate_buffered(model_id)) { + return buffer_model_ids_[model_id][LUT_INTER_BUFFER]; + } else { + return CircuitModelId::INVALID(); + } +} + +/* Return the location map of intermediate buffers + * that are inserted inside LUT multiplexing structures + */ +std::string CircuitLibrary::lut_intermediate_buffer_location_map(const CircuitModelId& model_id) const { + /* validate the model_id */ + VTR_ASSERT(valid_model_id(model_id)); + /* validate the circuit model type is BUF */ + VTR_ASSERT(SPICE_MODEL_LUT == model_type(model_id)); + /* if we have an intermediate buffer, we return something, otherwise return an empty map */ + if (true == is_lut_intermediate_buffered(model_id)) { + return buffer_location_maps_[model_id][LUT_INTER_BUFFER]; + } else { + return std::string(); + } +} + /* Find the id of pass-gate circuit model * Two cases to be considered: * 1. this is a pass-gate circuit model, just find the data and return @@ -298,22 +335,6 @@ size_t CircuitLibrary::buffer_num_levels(const CircuitModelId& model_id) const { return buffer_num_levels_[model_id]; } -/* Return the location map of intermediate buffers - * that are inserted inside LUT multiplexing structures - */ -std::string CircuitLibrary::lut_intermediate_buffer_location_map(const CircuitModelId& model_id) const { - /* validate the model_id */ - VTR_ASSERT(valid_model_id(model_id)); - /* validate the circuit model type is BUF */ - VTR_ASSERT(SPICE_MODEL_LUT == model_type(model_id)); - /* if we have an intermediate buffer, we return something, otherwise return an empty map */ - if (true == is_lut_intermediate_buffered(model_id)) { - return buffer_location_maps_[model_id][LUT_INTER_BUFFER]; - } else { - return std::string(); - } -} - /* Return the number of levels of delay types for a circuit model */ size_t CircuitLibrary::num_delay_info(const CircuitModelId& model_id) const { /* validate the model_id */ diff --git a/vpr7_x2p/libarchfpga/SRC/circuit_library.h b/vpr7_x2p/libarchfpga/SRC/circuit_library.h index a2f5eab95..351717cf1 100644 --- a/vpr7_x2p/libarchfpga/SRC/circuit_library.h +++ b/vpr7_x2p/libarchfpga/SRC/circuit_library.h @@ -226,6 +226,8 @@ class CircuitLibrary { /* LUT-related information */ bool is_lut_intermediate_buffered(const CircuitModelId& model_id) const; bool is_lut_fracturable(const CircuitModelId& model_id) const; + CircuitModelId lut_intermediate_buffer_model(const CircuitModelId& model_id) const; + std::string lut_intermediate_buffer_location_map(const CircuitModelId& model_id) const; /* Pass-gate-logic information */ CircuitModelId pass_gate_logic_model(const CircuitModelId& model_id) const; enum e_spice_model_pass_gate_logic_type pass_gate_logic_type(const CircuitModelId& model_id) const; @@ -239,7 +241,6 @@ class CircuitLibrary { /* Buffer information */ enum e_spice_model_buffer_type buffer_type(const CircuitModelId& model_id) const; size_t buffer_num_levels(const CircuitModelId& model_id) const; - std::string lut_intermediate_buffer_location_map(const CircuitModelId& model_id) const; /* Delay information */ size_t num_delay_info(const CircuitModelId& model_id) const; public: /* Public Accessors: Basic data query on cirucit models' Circuit Ports*/ diff --git a/vpr7_x2p/vpr/SRC/device/mux_graph.cpp b/vpr7_x2p/vpr/SRC/device/mux_graph.cpp index 6656edca3..294c6741f 100644 --- a/vpr7_x2p/vpr/SRC/device/mux_graph.cpp +++ b/vpr7_x2p/vpr/SRC/device/mux_graph.cpp @@ -144,6 +144,13 @@ size_t MuxGraph::num_levels() const { return node_lookup_.size() - 1; } +/* Find the actual number of levels in the MUX graph */ +size_t MuxGraph::num_node_levels() const { + /* need to check if the graph is valid or not */ + VTR_ASSERT_SAFE(valid_mux_graph()); + return node_lookup_.size(); +} + /* Find the number of configuration memories in the MUX graph */ size_t MuxGraph::num_memory_bits() const { /* need to check if the graph is valid or not */ @@ -566,6 +573,7 @@ void MuxGraph::build_multilevel_mux_graph(const size_t& mux_size, /* Number of outputs is definite, add and configure */ MuxNodeId output_node = add_node(MUX_OUTPUT_NODE); node_levels_[output_node] = num_levels; + node_ids_at_level_[output_node] = 0; node_output_ids_[output_node] = MuxOutputId(0); /* Update node lookup */ node_lookup[num_levels].push_back(output_node); diff --git a/vpr7_x2p/vpr/SRC/device/mux_graph.h b/vpr7_x2p/vpr/SRC/device/mux_graph.h index 7e1e7d128..70f5a525e 100644 --- a/vpr7_x2p/vpr/SRC/device/mux_graph.h +++ b/vpr7_x2p/vpr/SRC/device/mux_graph.h @@ -73,6 +73,7 @@ class MuxGraph { std::vector find_edges(const MuxNodeId& from_node, const MuxNodeId& to_node) const; /* Find the number of levels in the MUX graph */ size_t num_levels() const; + size_t num_node_levels() const; /* Find the number of SRAMs in the MUX graph */ size_t num_memory_bits() const; /* Find the number of nodes at a given level in the MUX graph */ diff --git a/vpr7_x2p/vpr/SRC/device/mux_utils.cpp b/vpr7_x2p/vpr/SRC/device/mux_utils.cpp index c15e3081f..3fafb1998 100644 --- a/vpr7_x2p/vpr/SRC/device/mux_utils.cpp +++ b/vpr7_x2p/vpr/SRC/device/mux_utils.cpp @@ -151,31 +151,52 @@ size_t find_multilevel_mux_branch_num_inputs(const size_t& mux_size, } /************************************************** - * Find if there is an intermediate buffer - * locating at the multiplexing structure of a LUT + * Build a location map for intermediate buffers + * that may appear at the multiplexing structure of a LUT + * Here is a tricky thing: + * By default, the first and last stage should not exist any intermediate buffers + * For example: + * There are 5 stages in a 4-stage multiplexer is available for buffering + * but only 3 stages [1,2,3] are intermedate buffers + * and these are users' specification + * + * +-------+ +-------+ +-------+ +-------+ + * location | stage | location | stage | location | stage | location | stage | location + * [0] | [0] | [1] | [1] | [2] | [2] | [3] | [3] | [5] + * +-------+ +-------+ +-------+ +-------+ + * + * We will check if the length of location map matches the number of + * multiplexer levels. And then complete a location map + * for the given multiplexers *************************************************/ -bool require_intermediate_buffer_at_mux_level(const CircuitLibrary& circuit_lib, - const CircuitModelId& circuit_model, - const size_t& node_level) { - std::string intermediate_buffer_location_map; +std::vector build_mux_intermediate_buffer_location_map(const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model, + const size_t& num_mux_levels) { + /* Deposite a default location map */ + std::vector location_map(num_mux_levels, false); + std::string location_map_str; /* ONLY for LUTs: intermediate buffers may exist if specified */ if (SPICE_MODEL_LUT == circuit_lib.model_type(circuit_model)) { - intermediate_buffer_location_map = circuit_lib.lut_intermediate_buffer_location_map(circuit_model); + location_map_str = circuit_lib.lut_intermediate_buffer_location_map(circuit_model); } /* If no location map is specified, we can return here */ - if (intermediate_buffer_location_map.empty()) { - return false; + if (location_map_str.empty()) { + return location_map; } - /* We have a location map. Make sure we are in the range */ - if (node_level >= intermediate_buffer_location_map.length()) { - return false; + + /* Check if the user-defined location map matches the number of mux levels*/ + VTR_ASSERT(num_mux_levels - 2 == location_map_str.length()); + + /* Apply the location_map string to the intermediate stages of multiplexers */ + for (size_t i = 0; i < location_map_str.length(); ++i) { + /* '1' indicates that an intermediate buffer is needed at the location */ + if ('1' == location_map_str[i]) { + location_map[i + 1] = true; + } } - /* '1' indicates that the location is needed */ - if ('1' == intermediate_buffer_location_map[node_level]) { - return true; - } - return false; + + return location_map; } /************************************************** diff --git a/vpr7_x2p/vpr/SRC/device/mux_utils.h b/vpr7_x2p/vpr/SRC/device/mux_utils.h index b4a20ebe4..c61db23c8 100644 --- a/vpr7_x2p/vpr/SRC/device/mux_utils.h +++ b/vpr7_x2p/vpr/SRC/device/mux_utils.h @@ -6,6 +6,8 @@ #ifndef MUX_UTILS_H #define MUX_UTILS_H +#include + #include "linkedlist.h" #include "circuit_library.h" #include "mux_library.h" @@ -29,9 +31,9 @@ size_t find_treelike_mux_num_levels(const size_t& mux_size); size_t find_multilevel_mux_branch_num_inputs(const size_t& mux_size, const size_t& mux_level); -bool require_intermediate_buffer_at_mux_level(const CircuitLibrary& circuit_lib, - const CircuitModelId& circuit_model, - const size_t& node_level); +std::vector build_mux_intermediate_buffer_location_map(const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model, + const size_t& num_mux_levels); MuxLibrary convert_mux_arch_to_library(const CircuitLibrary& circuit_lib, t_llist* muxes_head); 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 de027c0b9..e5899b978 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_mux.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_mux.cpp @@ -695,20 +695,222 @@ void generate_verilog_mux_branch_module(ModuleManager& module_manager, } /******************************************************************** - * Generate the internal logic (multiplexing structure) for - * a multiplexer or LUT in Verilog codes + * 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 the branch circuits - * generated before or standard cells MUX2 + * 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_multiplexing_structure(ModuleManager& module_manager, - const CircuitLibrary& circuit_lib, - std::fstream& fp, - const ModuleId& module_id, - const CircuitModelId& circuit_model, - const MuxGraph& mux_graph) { +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_verilog_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_verilog_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 -----")); + + /* 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_verilog_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_verilog_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]); + 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); + /* 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()); + + /* 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); + + /* Create a port-to-port map */ + std::map buffer_port2port_name_map; + + /* Build the link between buffer_input_port[0] and output_node_pre_buffer + * Build the link between buffer_output_port[0] and output_node_bufferred + */ + { /* Create a code block to accommodate the local variables */ + BasicPort buffer_instance_input_port(generate_verilog_mux_node_name(output_node_level, false), output_node_index_at_level, output_node_index_at_level); + std::string module_input_port_name = circuit_lib.port_lib_name(buffer_model_input_ports[0]); + buffer_port2port_name_map[module_input_port_name] = buffer_instance_input_port; + BasicPort buffer_instance_output_port(generate_verilog_mux_node_name(output_node_level, true), output_node_index_at_level, output_node_index_at_level); + std::string module_output_port_name = circuit_lib.port_lib_name(buffer_model_output_ports[0]); + buffer_port2port_name_map[module_output_port_name] = buffer_instance_output_port; + } + + /* Output an instance of the module */ + print_verilog_module_instance(fp, module_manager, module_id, buffer_module_id, buffer_port2port_name_map, circuit_lib.dump_explicit_port_map(buffer_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, buffer_module_id); + + print_verilog_comment(fp, std::string("---- END Instanciation of a intermediate buffer modules -----")); + } + + print_verilog_comment(fp, std::string("---- END Internal Logic of a CMOS MUX module based on Standard Cells -----")); +} + +/******************************************************************** + * 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); @@ -730,6 +932,11 @@ void generate_verilog_cmos_mux_module_multiplexing_structure(ModuleManager& modu } 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) { @@ -737,7 +944,7 @@ void generate_verilog_cmos_mux_module_multiplexing_structure(ModuleManager& modu BasicPort internal_wire_port(generate_verilog_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 == require_intermediate_buffer_at_mux_level(circuit_lib, circuit_model, level)) { + if (false == inter_buffer_location_map[level]) { continue; } BasicPort internal_wire_buffered_port(generate_verilog_mux_node_name(level, true), mux_graph.num_nodes_at_level(level)); @@ -745,22 +952,14 @@ void generate_verilog_cmos_mux_module_multiplexing_structure(ModuleManager& modu } print_verilog_comment(fp, std::string("---- END Internal wires of a CMOS MUX module -----")); - print_verilog_comment(fp, std::string("---- BEGIN Instanciation of a branch CMOS MUX modules -----")); + print_verilog_comment(fp, std::string("---- BEGIN Instanciation of a branch CMOS MUX module -----")); /* 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(); - /* 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 = generate_verilog_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); /* Get the node level and index in the current level */ size_t output_node_level = mux_graph.node_level(node); @@ -788,19 +987,25 @@ void generate_verilog_cmos_mux_module_multiplexing_structure(ModuleManager& modu } } + /* Instanciate the branch module which is a tgate-based module + */ + std::string branch_module_name= generate_verilog_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! */ - /* TODO: for clean representation, need to merge the node names in [a:b] format, if possible!!! - * All the input node names organized in bus - */ + /* 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_verilog_mux_node_name(input_node_level, require_intermediate_buffer_at_mux_level(circuit_lib, circuit_model, input_node_level)), input_node_index_at_level, input_node_index_at_level); + BasicPort branch_node_input_port(generate_verilog_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); } /* Try to combine the ports */ @@ -828,13 +1033,10 @@ void generate_verilog_cmos_mux_module_multiplexing_structure(ModuleManager& modu } /* Link nodes to input ports for the branch module */ - /* TODO: the naming could be more flexible? */ 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); - /* Double check: Port width should match the number of input nodes */ - VTR_ASSERT(module_input_port.get_width() == instance_input_port.get_width()); port2port_name_map[module_input_port.get_name()] = instance_input_port; /* Link nodes to output ports for the branch module */ @@ -843,9 +1045,7 @@ void generate_verilog_cmos_mux_module_multiplexing_structure(ModuleManager& modu 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); - /* Double check: Port width should match the number of output nodes */ - VTR_ASSERT(module_output_port.get_width() == instance_output_port.get_width()); - port2port_name_map[module_output_port.get_name()] = module_output_port; + 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; @@ -857,10 +1057,10 @@ void generate_verilog_cmos_mux_module_multiplexing_structure(ModuleManager& modu /* Try to combine the ports */ std::vector combined_branch_node_mem_ports = combine_verilog_ports(branch_node_mem_ports); /* If we have more than 1 port in the combined ports , - * - * output a local wire */ + * output a local wire + */ VTR_ASSERT(0 < combined_branch_node_mem_ports.size()); - /* Create the port info for the input */ + /* Create the port info for the mem */ BasicPort instance_mem_port; if (1 == combined_branch_node_mem_ports.size()) { instance_mem_port = combined_branch_node_mem_ports[0]; @@ -885,9 +1085,47 @@ void generate_verilog_cmos_mux_module_multiplexing_structure(ModuleManager& modu 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); - /* Double check: Port width should match the number of input nodes */ - VTR_ASSERT(module_mem_port.get_width() == instance_mem_port.get_width()); 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)); + branch_node_mem_inv_ports.push_back(branch_node_mem_inv_port); + } + /* Try to combine the ports */ + std::vector combined_branch_node_mem_inv_ports = combine_verilog_ports(branch_node_mem_inv_ports); + /* If we have more than 1 port in the combined ports , + * output a local wire + */ + VTR_ASSERT(0 < combined_branch_node_mem_inv_ports.size()); + BasicPort instance_mem_inv_port; + if (1 == combined_branch_node_mem_inv_ports.size()) { + instance_mem_inv_port = combined_branch_node_mem_inv_ports[0]; + } else { + /* TODO: the naming could be more flexible? */ + instance_mem_inv_port.set_name(generate_verilog_mux_node_name(output_node_level, false) + "_mem"); + /* Deposite a [0:0] port */ + instance_mem_inv_port.set_width(1); + /* TODO: combine the ports could be a function? */ + for (const auto& port : combined_branch_node_mem_inv_ports) { + instance_mem_inv_port.combine(port); + } + /* Print a local wire for the merged ports */ + fp << "\t" << generate_verilog_port(VERILOG_PORT_WIRE, instance_mem_inv_port); + fp << " = " << generate_verilog_ports(combined_branch_node_mem_inv_ports); + fp << ";" << std::endl; + } + + /* 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)); @@ -896,9 +1134,56 @@ void generate_verilog_cmos_mux_module_multiplexing_structure(ModuleManager& modu */ module_manager.add_child_module(module_id, branch_module_id); - /* TODO: Now we need to add intermediate buffers by instanciating the modules */ + print_verilog_comment(fp, std::string("---- END Instanciation of a branch CMOS MUX module -----")); + + 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); + /* 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()); + + /* 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); + + /* Create a port-to-port map */ + std::map buffer_port2port_name_map; + + /* Build the link between buffer_input_port[0] and output_node_pre_buffer + * Build the link between buffer_output_port[0] and output_node_bufferred + */ + { /* Create a code block to accommodate the local variables */ + BasicPort buffer_instance_input_port(generate_verilog_mux_node_name(output_node_level, false), output_node_index_at_level, output_node_index_at_level); + std::string module_input_port_name = circuit_lib.port_lib_name(buffer_model_input_ports[0]); + buffer_port2port_name_map[module_input_port_name] = buffer_instance_input_port; + BasicPort buffer_instance_output_port(generate_verilog_mux_node_name(output_node_level, true), output_node_index_at_level, output_node_index_at_level); + std::string module_output_port_name = circuit_lib.port_lib_name(buffer_model_output_ports[0]); + buffer_port2port_name_map[module_output_port_name] = buffer_instance_output_port; + } + + /* Output an instance of the module */ + print_verilog_module_instance(fp, module_manager, module_id, buffer_module_id, buffer_port2port_name_map, circuit_lib.dump_explicit_port_map(buffer_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, buffer_module_id); + + print_verilog_comment(fp, std::string("---- END Instanciation of a intermediate buffer modules -----")); + } - print_verilog_comment(fp, std::string("---- END Instanciation of a branch CMOS MUX modules -----")); + + print_verilog_comment(fp, std::string("---- END Internal Logic of a CMOS MUX module based on Pass-transistor/Transmission-gates -----")); } /********************************************************************* @@ -1008,8 +1293,27 @@ void generate_verilog_cmos_mux_module(ModuleManager& module_manager, print_verilog_module_declaration(fp, module_manager, module_id); /* TODO: Print the internal logic in Verilog codes */ - /* TODO: Print the Multiplexing structure in Verilog codes */ - generate_verilog_cmos_mux_module_multiplexing_structure(module_manager, circuit_lib, fp, module_id, circuit_model, mux_graph); + /* TODO: Print the Multiplexing structure in Verilog codes + * TODO: branch the module port name by 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); + } + /* TODO: Print the input buffers in Verilog codes */ /* TODO: Print the output buffers in Verilog codes */ diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_submodules.c b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_submodules.c index 7bd065cbe..cea48799d 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_submodules.c +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_submodules.c @@ -3471,9 +3471,62 @@ void dump_verilog_submodule_templates(t_sram_orgz_info* cur_sram_orgz_info, return; } -/* Dump verilog files of submodules to be used in FPGA components : +/********************************************************************* + * Register all the user-defined modules in the module manager + * Walk through the circuit library and add user-defined circuit models + * to the module_manager + ********************************************************************/ +static +void add_user_defined_verilog_modules(ModuleManager& module_manager, + const CircuitLibrary& circuit_lib) { + /* Module port depends on the model port attributes: + * Any model ports whose is_global_port() is true => MODULE_GLOBAL_PORT + * Inout model port: SPICE_MODEL_PORT_INOUT => MODULE_INOUT_PORT + * Input model port: SPICE_MODEL_PORT_INPUT/SRAM/BL/WL/BLB/WLB => MODULE_INPUT_PORT + * Output model port: SPICE_MODEL_PORT_OUTPUT => MODULE_OUTPUT_PORT + * Clock model port: SPICE_MODEL_PORT_CLOCK => MODULE_CLOCK_PORT + */ + std::map port_type2type_map; + port_type2type_map[SPICE_MODEL_PORT_INOUT] = ModuleManager::MODULE_INOUT_PORT; + port_type2type_map[SPICE_MODEL_PORT_INPUT] = ModuleManager::MODULE_INPUT_PORT; + port_type2type_map[SPICE_MODEL_PORT_SRAM] = ModuleManager::MODULE_INPUT_PORT; + port_type2type_map[SPICE_MODEL_PORT_BL] = ModuleManager::MODULE_INPUT_PORT; + port_type2type_map[SPICE_MODEL_PORT_WL] = ModuleManager::MODULE_INPUT_PORT; + port_type2type_map[SPICE_MODEL_PORT_BLB] = ModuleManager::MODULE_INPUT_PORT; + port_type2type_map[SPICE_MODEL_PORT_WLB] = ModuleManager::MODULE_INPUT_PORT; + port_type2type_map[SPICE_MODEL_PORT_OUTPUT] = ModuleManager::MODULE_OUTPUT_PORT; + port_type2type_map[SPICE_MODEL_PORT_CLOCK] = ModuleManager::MODULE_CLOCK_PORT; + + /* Iterate over verilog modules */ + for (const auto& model : circuit_lib.models()) { + /* We only care about user-defined models */ + if (true == circuit_lib.model_verilog_netlist(model).empty()) { + continue; + } + /* Reach here, the model requires a user-defined Verilog netlist, + * Register it in the module_manager + */ + ModuleId module_id = module_manager.add_module(circuit_lib.model_name(model)); + /* Iterate over the ports of circuit model, and add them to module_manager */ + for (const auto& model_port : circuit_lib.model_ports(model)) { + /* Create port information */ + BasicPort module_port(circuit_lib.port_lib_name(model_port), circuit_lib.port_size(model_port)); + + /* Deposite a module port type */ + ModuleManager::e_module_port_type module_port_type = port_type2type_map[circuit_lib.port_type(model_port)]; + /* Force a global port type */ + if (true == circuit_lib.port_is_global(model_port)) { + module_port_type = ModuleManager::MODULE_GLOBAL_PORT; + } + module_manager.add_port(module_id, module_port, module_port_type); + } + } +} + +/********************************************************************* + * Dump verilog files of submodules to be used in FPGA components : * 1. MUXes - */ + ********************************************************************/ void dump_verilog_submodules(ModuleManager& module_manager, const MuxLibrary& mux_lib, t_sram_orgz_info* cur_sram_orgz_info, @@ -3483,6 +3536,13 @@ void dump_verilog_submodules(ModuleManager& module_manager, t_det_routing_arch* routing_arch, t_syn_verilog_opts fpga_verilog_opts) { + /* TODO: 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 + */ + vpr_printf(TIO_MESSAGE_INFO, "Registering user-defined modules...\n"); + add_user_defined_verilog_modules(module_manager, Arch.spice->circuit_lib); + vpr_printf(TIO_MESSAGE_INFO, "Generating essential modules...\n"); print_verilog_submodule_essentials(module_manager, std::string(verilog_dir), diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_writer_utils.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_writer_utils.cpp index 623f016d5..01e515e3c 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_writer_utils.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_writer_utils.cpp @@ -156,6 +156,12 @@ void print_verilog_module_instance(std::fstream& fp, check_file_handler(fp); + /* Check: all the key ports in the port2port_name_map does exist in the child module */ + for (const auto& kv : port2port_name_map) { + ModulePortId module_port_id = module_manager.find_module_port(child_module_id, kv.first); + VTR_ASSERT(ModulePortId::INVALID() != module_port_id); + } + /* Print module name */ fp << "\t" << module_manager.module_name(child_module_id) << " "; /* Print instance name, _ */