implementing mux Verilog generation. Bugs detected, fixing ongoing
This commit is contained in:
parent
fde9c8b4ec
commit
e623c19055
|
@ -298,6 +298,22 @@ 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 */
|
||||
|
|
|
@ -239,6 +239,7 @@ 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*/
|
||||
|
|
|
@ -43,6 +43,20 @@ MuxGraph::node_range MuxGraph::nodes() const {
|
|||
return vtr::make_range(node_ids_.begin(), node_ids_.end());
|
||||
}
|
||||
|
||||
/* Find the non-input nodes */
|
||||
std::vector<MuxNodeId> MuxGraph::non_input_nodes() const {
|
||||
std::vector<MuxNodeId> node_list;
|
||||
for (const auto& node : nodes()) {
|
||||
/* Bypass any nodes which are not OUTPUT and INTERNAL */
|
||||
if (MUX_INPUT_NODE == node_types_[node]) {
|
||||
continue;
|
||||
}
|
||||
/* Reach here, this is either an OUTPUT or INTERNAL node */
|
||||
node_list.push_back(node);
|
||||
}
|
||||
return node_list;
|
||||
}
|
||||
|
||||
MuxGraph::edge_range MuxGraph::edges() const {
|
||||
return vtr::make_range(edge_ids_.begin(), edge_ids_.end());
|
||||
}
|
||||
|
@ -137,6 +151,47 @@ size_t MuxGraph::num_memory_bits() const {
|
|||
return mem_ids_.size();
|
||||
}
|
||||
|
||||
/* Find the number of nodes at a given level in the MUX graph */
|
||||
size_t MuxGraph::num_nodes_at_level(const size_t& level) const {
|
||||
/* validate the level numbers */
|
||||
VTR_ASSERT_SAFE(valid_level(level));
|
||||
VTR_ASSERT_SAFE(valid_mux_graph());
|
||||
|
||||
size_t num_nodes = 0;
|
||||
for (size_t node_type = 0; node_type < size_t(NUM_MUX_NODE_TYPES); ++node_type) {
|
||||
num_nodes += node_lookup_[level][node_type].size();
|
||||
}
|
||||
return num_nodes;
|
||||
}
|
||||
|
||||
/* Find the level of a node */
|
||||
size_t MuxGraph::node_level(const MuxNodeId& node) const {
|
||||
/* validate the node */
|
||||
VTR_ASSERT(valid_node_id(node));
|
||||
return node_levels_[node];
|
||||
}
|
||||
|
||||
/* Find the index of a node at its level */
|
||||
size_t MuxGraph::node_index_at_level(const MuxNodeId& node) const {
|
||||
/* validate the node */
|
||||
VTR_ASSERT(valid_node_id(node));
|
||||
return node_ids_at_level_[node];
|
||||
}
|
||||
|
||||
/* Find the input edges for a node */
|
||||
std::vector<MuxEdgeId> MuxGraph::node_in_edges(const MuxNodeId& node) const {
|
||||
/* validate the node */
|
||||
VTR_ASSERT(valid_node_id(node));
|
||||
return node_in_edges_[node];
|
||||
}
|
||||
|
||||
/* Find the input nodes for a edge */
|
||||
std::vector<MuxNodeId> MuxGraph::edge_src_nodes(const MuxEdgeId& edge) const {
|
||||
/* validate the edge */
|
||||
VTR_ASSERT(valid_edge_id(edge));
|
||||
return edge_src_nodes_[edge];
|
||||
}
|
||||
|
||||
/* Find the mem that control the edge */
|
||||
MuxMemId MuxGraph::find_edge_mem(const MuxEdgeId& edge) const {
|
||||
/* validate the edge */
|
||||
|
@ -846,6 +901,10 @@ bool MuxGraph::valid_output_id(const MuxOutputId& output_id) const {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool MuxGraph::valid_level(const size_t& level) const {
|
||||
return level < num_levels();
|
||||
}
|
||||
|
||||
bool MuxGraph::valid_node_lookup() const {
|
||||
return node_lookup_.empty();
|
||||
}
|
||||
|
|
|
@ -58,6 +58,8 @@ class MuxGraph {
|
|||
MuxGraph();
|
||||
public: /* Public accessors: Aggregates */
|
||||
node_range nodes() const;
|
||||
/* Find the non-input nodes */
|
||||
std::vector<MuxNodeId> non_input_nodes() const;
|
||||
edge_range edges() const;
|
||||
mem_range memories() const;
|
||||
public: /* Public accessors: Data query */
|
||||
|
@ -73,6 +75,16 @@ class MuxGraph {
|
|||
size_t num_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 */
|
||||
size_t num_nodes_at_level(const size_t& level) const;
|
||||
/* Find the level of a node */
|
||||
size_t node_level(const MuxNodeId& node) const;
|
||||
/* Find the index of a node at its level */
|
||||
size_t node_index_at_level(const MuxNodeId& node) const;
|
||||
/* Find the input edges for a node */
|
||||
std::vector<MuxEdgeId> node_in_edges(const MuxNodeId& node) const;
|
||||
/* Find the input nodes for a edge */
|
||||
std::vector<MuxNodeId> edge_src_nodes(const MuxEdgeId& edge) const;
|
||||
/* Find the mem that control the edge */
|
||||
MuxMemId find_edge_mem(const MuxEdgeId& edge) const;
|
||||
/* Identify if the edge is controlled by the inverted output of a mem */
|
||||
|
@ -122,6 +134,7 @@ class MuxGraph {
|
|||
bool valid_mem_id(const MuxMemId& mem) const;
|
||||
bool valid_input_id(const MuxInputId& input_id) const;
|
||||
bool valid_output_id(const MuxOutputId& output_id) const;
|
||||
bool valid_level(const size_t& level) const;
|
||||
/* validate/invalidate node lookup */
|
||||
bool valid_node_lookup() const;
|
||||
void invalidate_node_lookup();
|
||||
|
|
|
@ -150,6 +150,34 @@ size_t find_multilevel_mux_branch_num_inputs(const size_t& mux_size,
|
|||
return num_input_per_unit;
|
||||
}
|
||||
|
||||
/**************************************************
|
||||
* Find if there is an intermediate buffer
|
||||
* locating at the multiplexing structure of a LUT
|
||||
*************************************************/
|
||||
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;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
/* If no location map is specified, we can return here */
|
||||
if (intermediate_buffer_location_map.empty()) {
|
||||
return false;
|
||||
}
|
||||
/* We have a location map. Make sure we are in the range */
|
||||
if (node_level >= intermediate_buffer_location_map.length()) {
|
||||
return false;
|
||||
}
|
||||
/* '1' indicates that the location is needed */
|
||||
if ('1' == intermediate_buffer_location_map[node_level]) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**************************************************
|
||||
* Convert a linked list of MUX architecture to MuxLibrary
|
||||
* TODO: this function will be deleted when MUXLibrary fully
|
||||
|
|
|
@ -29,6 +29,10 @@ 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);
|
||||
|
||||
MuxLibrary convert_mux_arch_to_library(const CircuitLibrary& circuit_lib, t_llist* muxes_head);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -7,6 +7,27 @@
|
|||
|
||||
#include "fpga_x2p_naming.h"
|
||||
|
||||
/************************************************
|
||||
* Generate the node name for a multiplexing structure
|
||||
* Case 1 : If there is an intermediate buffer followed by,
|
||||
* the node name will be mux_l<node_level>_in_buf
|
||||
* Case 1 : If there is NO intermediate buffer followed by,
|
||||
* the node name will be mux_l<node_level>_in
|
||||
***********************************************/
|
||||
std::string generate_verilog_mux_node_name(const size_t& node_level,
|
||||
const bool& add_buffer_postfix) {
|
||||
/* Generate the basic node_name */
|
||||
std::string node_name = "mux_l" + std::to_string(node_level) + "_in";
|
||||
|
||||
/* Add a postfix upon requests */
|
||||
if (true == add_buffer_postfix) {
|
||||
/* '1' indicates that the location is needed */
|
||||
node_name += "_buf";
|
||||
}
|
||||
|
||||
return node_name;
|
||||
}
|
||||
|
||||
/************************************************
|
||||
* Generate the module name for a multiplexer in Verilog format
|
||||
* Different circuit model requires different names:
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
|
||||
#include "circuit_library.h"
|
||||
|
||||
std::string generate_verilog_mux_node_name(const size_t& node_level,
|
||||
const bool& add_buffer_postfix);
|
||||
|
||||
std::string generate_verilog_mux_subckt_name(const CircuitLibrary& circuit_lib,
|
||||
const CircuitModelId& circuit_model,
|
||||
const size_t& mux_size,
|
||||
|
|
|
@ -50,6 +50,29 @@ std::vector<BasicPort> ModuleManager::module_ports_by_type(const ModuleId& modul
|
|||
return ports;
|
||||
}
|
||||
|
||||
/* Find a port of a module by a given name */
|
||||
ModulePortId ModuleManager::find_module_port(const ModuleId& module_id, const std::string& port_name) const {
|
||||
/* Validate the module id */
|
||||
VTR_ASSERT(valid_module_id(module_id));
|
||||
|
||||
/* Iterate over the ports of the module */
|
||||
for (const auto& port : port_ids_[module_id]) {
|
||||
if (0 == port_name.compare(ports_[module_id][port].get_name())) {
|
||||
/* Find it, return the id */
|
||||
return port;
|
||||
}
|
||||
}
|
||||
/* Not found, return an invalid id */
|
||||
return ModulePortId::INVALID();
|
||||
}
|
||||
|
||||
/* Find the Port information with a given port id */
|
||||
BasicPort ModuleManager::module_port(const ModuleId& module_id, const ModulePortId& port_id) const {
|
||||
/* Validate the module and port id */
|
||||
VTR_ASSERT(valid_module_port_id(module_id, port_id));
|
||||
return ports_[module_id][port_id];
|
||||
}
|
||||
|
||||
/* Find the module id by a given name, return invalid if not found */
|
||||
ModuleId ModuleManager::find_module(const std::string& name) const {
|
||||
if (name_id_map_.find(name) != name_id_map_.end()) {
|
||||
|
|
|
@ -37,6 +37,11 @@ class ModuleManager {
|
|||
std::string module_name(const ModuleId& module_id) const;
|
||||
std::string module_port_type_str(const enum e_module_port_type& port_type) const;
|
||||
std::vector<BasicPort> module_ports_by_type(const ModuleId& module_id, const enum e_module_port_type& port_type) const;
|
||||
/* Find a port of a module by a given name */
|
||||
ModulePortId find_module_port(const ModuleId& module_id, const std::string& port_name) const;
|
||||
/* Find the Port information with a given port id */
|
||||
BasicPort module_port(const ModuleId& module_id, const ModulePortId& port_id) const;
|
||||
/* Find a module by a given name */
|
||||
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;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* and the full multiplexer
|
||||
**********************************************/
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
#include "util.h"
|
||||
#include "vtr_assert.h"
|
||||
|
@ -74,7 +75,7 @@ void generate_verilog_cmos_mux_branch_body_structural(ModuleManager& module_mana
|
|||
if (0 == edges.size()) {
|
||||
continue;
|
||||
}
|
||||
/* TODO: Output a tgate use a module manager */
|
||||
/* Output a tgate use a module manager */
|
||||
/* Create a port-to-port name map */
|
||||
std::map<std::string, BasicPort> port2port_name_map;
|
||||
/* input port */
|
||||
|
@ -693,6 +694,213 @@ void generate_verilog_mux_branch_module(ModuleManager& module_manager,
|
|||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Generate the 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
|
||||
* 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) {
|
||||
/* 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());
|
||||
|
||||
/* TODO: these are duplicated codes, find a way to simplify it!!!
|
||||
* Get the regular (non-mode-select) sram ports from the mux
|
||||
*/
|
||||
std::vector<CircuitPortId> 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());
|
||||
|
||||
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 == require_intermediate_buffer_at_mux_level(circuit_lib, circuit_model, 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 -----"));
|
||||
|
||||
print_verilog_comment(fp, std::string("---- BEGIN Instanciation of a branch CMOS MUX modules -----"));
|
||||
/* Iterate over all the internal nodes and output nodes in the mux graph */
|
||||
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:
|
||||
* 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);
|
||||
size_t output_node_index_at_level = mux_graph.node_index_at_level(node);
|
||||
|
||||
/* Get the nodes which drive the root_node */
|
||||
std::vector<MuxNodeId> 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<MuxMemId> 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);
|
||||
}
|
||||
}
|
||||
|
||||
/* Create a port-to-port map */
|
||||
std::map<std::string, BasicPort> 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
|
||||
*/
|
||||
std::vector<BasicPort> 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);
|
||||
branch_node_input_ports.push_back(branch_node_input_port);
|
||||
}
|
||||
/* Try to combine the ports */
|
||||
std::vector<BasicPort> combined_branch_node_input_ports = combine_verilog_ports(branch_node_input_ports);
|
||||
/* If we have more than 1 port in the combined ports ,
|
||||
*
|
||||
* output a local wire */
|
||||
VTR_ASSERT(0 < combined_branch_node_input_ports.size());
|
||||
/* Create the port info for the input */
|
||||
BasicPort instance_input_port;
|
||||
if (1 == combined_branch_node_input_ports.size()) {
|
||||
instance_input_port = combined_branch_node_input_ports[0];
|
||||
} else {
|
||||
/* TODO: the naming could be more flexible? */
|
||||
instance_input_port.set_name(generate_verilog_mux_node_name(output_node_level, false) + "_in");
|
||||
/* Deposite a [0:0] port */
|
||||
instance_input_port.set_width(1);
|
||||
for (const auto& port : combined_branch_node_input_ports) {
|
||||
instance_input_port.combine(port);
|
||||
}
|
||||
/* Print a local wire for the merged ports */
|
||||
fp << "\t" << generate_verilog_port(VERILOG_PORT_WIRE, instance_input_port);
|
||||
fp << " = " << generate_verilog_ports(combined_branch_node_input_ports);
|
||||
fp << ";" << std::endl;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
BasicPort instance_output_port(generate_verilog_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);
|
||||
/* 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;
|
||||
|
||||
/* All the mem node names organized in bus */
|
||||
std::vector<BasicPort> 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));
|
||||
branch_node_mem_ports.push_back(branch_node_mem_port);
|
||||
}
|
||||
/* Try to combine the ports */
|
||||
std::vector<BasicPort> 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 */
|
||||
VTR_ASSERT(0 < combined_branch_node_mem_ports.size());
|
||||
/* Create the port info for the input */
|
||||
BasicPort instance_mem_port;
|
||||
if (1 == combined_branch_node_mem_ports.size()) {
|
||||
instance_mem_port = combined_branch_node_mem_ports[0];
|
||||
} else {
|
||||
/* TODO: the naming could be more flexible? */
|
||||
instance_mem_port.set_name(generate_verilog_mux_node_name(output_node_level, false) + "_mem");
|
||||
/* Deposite a [0:0] port */
|
||||
instance_mem_port.set_width(1);
|
||||
/* TODO: combine the ports could be a function? */
|
||||
for (const auto& port : combined_branch_node_mem_ports) {
|
||||
instance_mem_port.combine(port);
|
||||
}
|
||||
/* Print a local wire for the merged ports */
|
||||
fp << "\t" << generate_verilog_port(VERILOG_PORT_WIRE, instance_mem_port);
|
||||
fp << " = " << generate_verilog_ports(combined_branch_node_mem_ports);
|
||||
fp << ";" << std::endl;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
/* 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;
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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 modules -----"));
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Generate Verilog codes modeling a CMOS multiplexer with the given size
|
||||
* The Verilog module will consist of three parts:
|
||||
|
@ -731,7 +939,6 @@ void generate_verilog_cmos_mux_module(ModuleManager& module_manager,
|
|||
/* 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());
|
||||
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))
|
||||
|
@ -802,6 +1009,7 @@ void generate_verilog_cmos_mux_module(ModuleManager& module_manager,
|
|||
|
||||
/* 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 input buffers in Verilog codes */
|
||||
/* TODO: Print the output buffers in Verilog codes */
|
||||
|
||||
|
|
|
@ -128,6 +128,25 @@ void print_verilog_module_declaration(std::fstream& fp,
|
|||
|
||||
/************************************************
|
||||
* Print an instance for a Verilog module
|
||||
* This function will output the port map
|
||||
* by referring to a port-to-port mapping:
|
||||
* <module_port_name> -> <instance_port_name>
|
||||
* The key of the port-to-port mapping is the
|
||||
* port name of the module:
|
||||
* The value of the port-to-port mapping is the
|
||||
* port information of the instance
|
||||
* With link between module and instance, the function
|
||||
* can output a Verilog instance easily, supporting
|
||||
* both explicit port mapping:
|
||||
* .<module_port_name>(<instance_port_name>)
|
||||
* and inexplicit port mapping
|
||||
* <instance_port_name>
|
||||
*
|
||||
* Note that, it is not necessary that
|
||||
* the port-to-port mapping covers all the module ports.
|
||||
* Any instance/module port which are not specified in the
|
||||
* port-to-port mapping will be output by the module
|
||||
* port name.
|
||||
***********************************************/
|
||||
void print_verilog_module_instance(std::fstream& fp,
|
||||
const ModuleManager& module_manager,
|
||||
|
@ -168,6 +187,11 @@ void print_verilog_module_instance(std::fstream& fp,
|
|||
/* Try to find the instanced port name in the name map */
|
||||
if (port2port_name_map.find(port.get_name()) != port2port_name_map.end()) {
|
||||
/* Found it, we assign the port name */
|
||||
/* TODO: make sure the port width matches! */
|
||||
ModulePortId module_port_id = module_manager.find_module_port(child_module_id, port.get_name());
|
||||
/* Get the port from module */
|
||||
BasicPort module_port = module_manager.module_port(child_module_id, module_port_id);
|
||||
VTR_ASSERT(module_port.get_width() == port2port_name_map.at(port.get_name()).get_width());
|
||||
fp << generate_verilog_port(kv.second, port2port_name_map.at(port.get_name()));
|
||||
} else {
|
||||
/* Not found, we give the default port name */
|
||||
|
@ -197,7 +221,9 @@ void print_verilog_module_end(std::fstream& fp,
|
|||
fp << std::endl;
|
||||
}
|
||||
|
||||
/* Generate a string of a Verilog port */
|
||||
/************************************************
|
||||
* Generate a string of a Verilog port
|
||||
***********************************************/
|
||||
std::string generate_verilog_port(const enum e_dump_verilog_port_type& verilog_port_type,
|
||||
const BasicPort& port_info) {
|
||||
std::string verilog_line;
|
||||
|
@ -224,4 +250,86 @@ std::string generate_verilog_port(const enum e_dump_verilog_port_type& verilog_p
|
|||
return verilog_line;
|
||||
}
|
||||
|
||||
/************************************************
|
||||
* This function takes a list of ports and
|
||||
* combine the port string by comparing the name
|
||||
* and width of ports.
|
||||
* For example, two ports A and B share the same name is
|
||||
* mergable as long as A's MSB + 1 == B's LSB
|
||||
* Note that the port sequence really matters!
|
||||
* This function will NOT change the sequence
|
||||
* of ports in the list port_info
|
||||
***********************************************/
|
||||
std::vector<BasicPort> combine_verilog_ports(const std::vector<BasicPort>& ports) {
|
||||
std::vector<BasicPort> merged_ports;
|
||||
|
||||
/* Directly return if there are no ports */
|
||||
if (0 == ports.size()) {
|
||||
return merged_ports;
|
||||
}
|
||||
/* Push the first port to the merged ports */
|
||||
merged_ports.push_back(ports[0]);
|
||||
|
||||
/* Iterate over ports */
|
||||
for (const auto& port : ports) {
|
||||
/* Bypass the first port, it is already in the list */
|
||||
if (&port == &ports[0]) {
|
||||
continue;
|
||||
}
|
||||
/* Identify if the port name can be potentially merged: if the port name is already in the merged port list, it may be merged */
|
||||
for (auto& merged_port : merged_ports) {
|
||||
if (0 != port.get_name().compare(merged_port.get_name())) {
|
||||
/* Unable to merge, add the port to merged port list */
|
||||
merged_ports.push_back(port);
|
||||
/* Go to next */
|
||||
break;
|
||||
}
|
||||
/* May be merged, check LSB of port and MSB of merged_port */
|
||||
if (merged_port.get_msb() + 1 != port.get_lsb()) {
|
||||
/* Unable to merge, add the port to merged port list */
|
||||
merged_ports.push_back(port);
|
||||
/* Go to next */
|
||||
break;
|
||||
}
|
||||
/* Reach here, we should merge the ports,
|
||||
* LSB of merged_port remains the same,
|
||||
* MSB of merged_port will be updated
|
||||
* to the MSB of port
|
||||
*/
|
||||
merged_port.set_msb(port.get_msb());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return merged_ports;
|
||||
}
|
||||
|
||||
/************************************************
|
||||
* Generate the string of a list of verilog ports
|
||||
***********************************************/
|
||||
std::string generate_verilog_ports(const std::vector<BasicPort>& merged_ports) {
|
||||
|
||||
/* Output the string of ports:
|
||||
* If there is only one port in the merged_port list
|
||||
* we only output the port.
|
||||
* If there are more than one port in the merged port list, we output an concatenated port:
|
||||
* {<port1>, <port2>, ... <last_port>}
|
||||
*/
|
||||
VTR_ASSERT(0 < merged_ports.size());
|
||||
if ( 1 == merged_ports.size()) {
|
||||
/* Use connection type of verilog port */
|
||||
return generate_verilog_port(VERILOG_PORT_CONKT, merged_ports[0]);
|
||||
}
|
||||
|
||||
std::string verilog_line = "{";
|
||||
for (const auto& port : merged_ports) {
|
||||
/* The first port does not need a comma */
|
||||
if (&port != &merged_ports[0]) {
|
||||
verilog_line += ", ";
|
||||
}
|
||||
verilog_line += generate_verilog_port(VERILOG_PORT_CONKT, merged_ports[0]);
|
||||
}
|
||||
verilog_line += "}";
|
||||
|
||||
return verilog_line;
|
||||
}
|
||||
|
|
|
@ -40,4 +40,8 @@ void print_verilog_module_end(std::fstream& fp,
|
|||
std::string generate_verilog_port(const enum e_dump_verilog_port_type& dump_port_type,
|
||||
const BasicPort& port_info);
|
||||
|
||||
std::vector<BasicPort> combine_verilog_ports(const std::vector<BasicPort>& ports);
|
||||
|
||||
std::string generate_verilog_ports(const std::vector<BasicPort>& merged_ports);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue