diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_api.c b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_api.c index 8ab75ad0c..099047c33 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_api.c +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_api.c @@ -10,6 +10,7 @@ #include #include #include +#include /* Include vpr structs*/ #include "util.h" @@ -34,6 +35,7 @@ #include "verilog_api.h" #include "fpga_bitstream.h" +#include "fpga_x2p_globals.h" #include "fpga_x2p_api.h" /* Top-level API of FPGA-SPICE */ @@ -50,8 +52,27 @@ void vpr_fpga_x2p_tool_suites(t_vpr_setup vpr_setup, /* Build multiplexer graphs */ MuxLibrary mux_lib = build_device_mux_library(num_rr_nodes, rr_node, switch_inf, Arch.spice->circuit_lib, &vpr_setup.RoutingArch); + /* TODO: Build global routing architecture modules */ + /* Create a vector of switch infs. TODO: this should be replaced switch objects!!! */ + std::vector rr_switches; + for (short i = 0; i < vpr_setup.RoutingArch.num_switch; ++i) { + rr_switches.push_back(switch_inf[i]); + } + + /* TODO: This should be done outside this function!!! */ + vtr::Point device_size(nx + 2, ny + 2); + std::vector> grids; + /* Organize a vector (matrix) of grids to feed the top-level module generation */ + grids.resize(device_size.x()); + for (size_t ix = 0; ix < device_size.x(); ++ix) { + grids[ix].resize(device_size.y()); + for (size_t iy = 0; iy < device_size.y(); ++iy) { + grids[ix][iy] = grid[ix][iy]; + } + } + /* Build module graphs */ - ModuleManager module_manager = build_device_module_graph(vpr_setup, Arch, mux_lib); + ModuleManager module_manager = build_device_module_graph(vpr_setup, Arch, mux_lib, grids, rr_switches, device_rr_gsb); /* Xifan TANG: SPICE Modeling, SPICE Netlist Output */ if (TRUE == vpr_setup.FPGA_SPICE_Opts.SpiceOpts.do_spice) { diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_types.h b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_types.h index 796707d78..982377120 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_types.h +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_types.h @@ -1,6 +1,7 @@ #ifndef FPGA_X2P_TYPES_H #define FPGA_X2P_TYPES_H +#include "vpr_types.h" #include "route_common.h" /* Define the basic data structures used for FPGA-SPICE */ 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 ad9ac0098..f6b8c23cd 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 @@ -2,6 +2,7 @@ * This file includes the main function to build module graphs * for the FPGA fabric *******************************************************************/ +#include #include #include @@ -17,6 +18,7 @@ #include "build_wire_modules.h" #include "build_memory_modules.h" #include "build_grid_modules.h" +#include "build_routing_modules.h" #include "build_module_graph.h" /******************************************************************** @@ -25,7 +27,10 @@ *******************************************************************/ ModuleManager build_device_module_graph(const t_vpr_setup& vpr_setup, const t_arch& arch, - const MuxLibrary& mux_lib) { + const MuxLibrary& mux_lib, + const std::vector>& grids, + const std::vector& rr_switches, + const DeviceRRGSB& L_device_rr_gsb) { /* Check if the routing architecture we support*/ if (UNI_DIRECTIONAL != vpr_setup.RoutingArch.directionality) { vpr_printf(TIO_MESSAGE_ERROR, @@ -104,7 +109,17 @@ ModuleManager build_device_module_graph(const t_vpr_setup& vpr_setup, build_grid_modules(module_manager, arch.spice->circuit_lib, mux_lib, arch.sram_inf.verilog_sram_inf_orgz->type, sram_model); - /* TODO: Build global routing architecture modules */ + if (TRUE == vpr_setup.FPGA_SPICE_Opts.compact_routing_hierarchy) { + build_unique_routing_modules(module_manager, L_device_rr_gsb, arch.spice->circuit_lib, + arch.sram_inf.verilog_sram_inf_orgz->type, sram_model, grids, + vpr_setup.RoutingArch, rr_switches); + } else { + VTR_ASSERT(FALSE == vpr_setup.FPGA_SPICE_Opts.compact_routing_hierarchy); + build_flatten_routing_modules(module_manager, L_device_rr_gsb, arch.spice->circuit_lib, + arch.sram_inf.verilog_sram_inf_orgz->type, sram_model, grids, + vpr_setup.RoutingArch, rr_switches); + } + /* TODO: Build FPGA fabric top-level module */ diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_module_graph.h b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_module_graph.h index 3e3ad5806..2cea75913 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_module_graph.h +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_module_graph.h @@ -1,12 +1,17 @@ #ifndef BUILD_MODULE_GRAPH_H #define BUILD_MODULE_GRAPH_H +#include #include "vpr_types.h" +#include "rr_blocks.h" #include "mux_library.h" #include "module_manager.h" ModuleManager build_device_module_graph(const t_vpr_setup& vpr_setup, const t_arch& arch, - const MuxLibrary& mux_lib); + const MuxLibrary& mux_lib, + const std::vector>& grids, + const std::vector& rr_switches, + const DeviceRRGSB& L_device_rr_gsb); #endif diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_module_graph_utils.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_module_graph_utils.cpp index 6b1df195a..f58296e11 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_module_graph_utils.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_module_graph_utils.cpp @@ -5,8 +5,33 @@ #include #include "vtr_assert.h" +#include "fpga_x2p_naming.h" +#include "fpga_x2p_pbtypes_utils.h" #include "build_module_graph_utils.h" +/********************************************************************* + * Generate the port name for a Grid + * This is a wrapper function for generate_port_name() + * which can automatically decode the port name by the pin side and height + *********************************************************************/ +std::string generate_grid_side_port_name(const std::vector>& grids, + const vtr::Point& coordinate, + const e_side& side, + const size_t& pin_id) { + /* Output the pins on the side*/ + size_t height = find_grid_pin_height(grids, coordinate, pin_id); + if (1 != grids[coordinate.x()][coordinate.y()].type->pinloc[height][side][pin_id]) { + Side side_manager(side); + vpr_printf(TIO_MESSAGE_ERROR, + "(File:%s, [LINE%d])Fail to generate a grid pin (x=%lu, y=%lu, height=%lu, side=%s, index=%d)\n", + __FILE__, __LINE__, + coordinate.x(), coordinate.y(), height, side_manager.c_str(), pin_id); + exit(1); + } + return generate_grid_port_name(coordinate, height, side, pin_id, true); +} + + /******************************************************************** * Find input port of a buffer/inverter module ********************************************************************/ diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_module_graph_utils.h b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_module_graph_utils.h index e5b3de2ca..d4816c8e0 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_module_graph_utils.h +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_module_graph_utils.h @@ -4,9 +4,20 @@ #ifndef BUILD_MODULE_GRAPH_UTILS_H #define BUILD_MODULE_GRAPH_UTILS_H +#include +#include +#include "spice_types.h" +#include "sides.h" +#include "vtr_geometry.h" +#include "vpr_types.h" #include "module_manager.h" #include "circuit_library.h" +std::string generate_grid_side_port_name(const std::vector>& grids, + const vtr::Point& coordinate, + const e_side& side, + const size_t& pin_id); + ModulePortId find_inverter_buffer_module_port(const ModuleManager& module_manager, const ModuleId& module_id, const CircuitLibrary& circuit_lib, diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_routing_modules.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_routing_modules.cpp new file mode 100644 index 000000000..61ee8fd29 --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_routing_modules.cpp @@ -0,0 +1,1166 @@ +/******************************************************************** + * This file includes functions that are used to build modules + * for global routing architecture of a FPGA fabric + * Covering: + * 1. Connection blocks + * 2. Switch blocks + *******************************************************************/ +#include + +#include "vtr_assert.h" +#include "vtr_geometry.h" +#include "sides.h" +#include "util.h" +#include "device_coordinator.h" + +#include "fpga_x2p_types.h" +#include "fpga_x2p_naming.h" + +#include "fpga_x2p_utils.h" +#include "module_manager_utils.h" +#include "build_module_graph_utils.h" + +#include "build_routing_modules.h" +#include "verilog_global.h" + +/********************************************************************* + * Generate a port for a routing track of a swtich block + ********************************************************************/ +static +ModulePortId find_switch_block_module_chan_port(const ModuleManager& module_manager, + const ModuleId& sb_module, + const RRGSB& rr_gsb, + const e_side& chan_side, + t_rr_node* cur_rr_node, + const PORTS& cur_rr_node_direction) { + /* Get the index in sb_info of cur_rr_node */ + int index = rr_gsb.get_node_index(cur_rr_node, chan_side, cur_rr_node_direction); + /* Make sure this node is included in this sb_info */ + VTR_ASSERT((-1 != index)&&(NUM_SIDES != chan_side)); + + DeviceCoordinator chan_rr_node_coordinator = rr_gsb.get_side_block_coordinator(chan_side); + + vtr::Point chan_port_coord(chan_rr_node_coordinator.get_x(), chan_rr_node_coordinator.get_y()); + std::string chan_port_name = generate_routing_track_port_name(rr_gsb.get_chan_node(chan_side, index)->type, + chan_port_coord, index, + rr_gsb.get_chan_node_direction(chan_side, index)); + + /* Must find a valid port id in the Switch Block module */ + ModulePortId chan_port_id = module_manager.find_module_port(sb_module, chan_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(sb_module, chan_port_id)); + return chan_port_id; +} + +/********************************************************************* + * Generate an input port for routing multiplexer inside the switch block + * In addition to give the Routing Resource node of the input + * Users should provide the side of input, which is different case by case: + * 1. When the input is a pin of a CLB/Logic Block, the input_side should + * be the side of the node on its grid! + * For example, the input pin is on the top side of a switch block + * but on the right side of a switch block + * +--------+ + * | | + * | Grid |---+ + * | | | + * +--------+ v input_pin + * +----------------+ + * | Switch Block | + * +----------------+ + * 2. When the input is a routing track, the input_side should be + * the side of the node locating on the switch block + ********************************************************************/ +static +ModulePortId find_switch_block_module_input_port(const ModuleManager& module_manager, + const ModuleId& sb_module, + const RRGSB& rr_gsb, + const std::vector>& grids, + const e_side& input_side, + t_rr_node* input_rr_node) { + /* Deposit an invalid value */ + ModulePortId input_port_id = ModulePortId::INVALID(); + /* Generate the input port object */ + switch (input_rr_node->type) { + /* case SOURCE: */ + case OPIN: { + /* Find the coordinator (grid_x and grid_y) for the input port */ + vtr::Point input_port_coord(input_rr_node->xlow, input_rr_node->ylow); + std::string input_port_name = generate_grid_side_port_name(grids, + input_port_coord, + input_side, + input_rr_node->ptc_num); + /* Must find a valid port id in the Switch Block module */ + input_port_id = module_manager.find_module_port(sb_module, input_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(sb_module, input_port_id)); + break; + } + case CHANX: + case CHANY: { + input_port_id = find_switch_block_module_chan_port(module_manager, sb_module, + rr_gsb, input_side, input_rr_node, IN_PORT); + break; + } + default: /* SOURCE, IPIN, SINK are invalid*/ + vpr_printf(TIO_MESSAGE_ERROR, + "(File:%s, [LINE%d])Invalid rr_node type! Should be [OPIN|CHANX|CHANY].\n", + __FILE__, __LINE__); + exit(1); + } + + return input_port_id; +} + +/********************************************************************* + * Generate a list of input ports for routing multiplexer inside the switch block + ********************************************************************/ +static +std::vector find_switch_block_module_input_ports(const ModuleManager& module_manager, + const ModuleId& sb_module, + const RRGSB& rr_gsb, + const std::vector>& grids, + const std::vector& input_rr_nodes) { + std::vector input_ports; + + for (auto input_rr_node : input_rr_nodes) { + enum e_side input_pin_side = NUM_SIDES; + switch (input_rr_node->type) { + case OPIN: + input_pin_side = rr_gsb.get_opin_node_grid_side(input_rr_node); + break; + case CHANX: + case CHANY: { + /* The input could be at any side of the switch block, find it */ + int index = -1; + rr_gsb.get_node_side_and_index(input_rr_node, IN_PORT, &input_pin_side, &index); + VTR_ASSERT(NUM_SIDES != input_pin_side); + break; + } + default: /* SOURCE, IPIN, SINK are invalid*/ + vpr_printf(TIO_MESSAGE_ERROR, + "(File:%s, [LINE%d])Invalid rr_node type! Should be [OPIN|CHANX|CHANY].\n", + __FILE__, __LINE__); + exit(1); + } + input_ports.push_back(find_switch_block_module_input_port(module_manager, sb_module, rr_gsb, grids, input_pin_side, input_rr_node)); + } + + return input_ports; +} + + +/********************************************************************* + * Generate a short interconneciton in switch box + * There are two cases should be noticed. + * 1. The actual fan-in of cur_rr_node is 0. In this case, + the cur_rr_node need to be short connected to itself + which is on the opposite side of this switch block + * 2. The actual fan-in of cur_rr_node is 0. In this case, + * The cur_rr_node need to connected to the drive_rr_node + ********************************************************************/ +static +void build_switch_block_module_short_interc(ModuleManager& module_manager, + const ModuleId& sb_module, + const RRGSB& rr_gsb, + const e_side& chan_side, + t_rr_node* cur_rr_node, + t_rr_node* drive_rr_node, + const std::vector>& grids, + const std::map& input_port_to_module_nets) { + /* Find the name of output port */ + ModulePortId output_port_id = find_switch_block_module_chan_port(module_manager, sb_module, rr_gsb, chan_side, cur_rr_node, OUT_PORT); + enum e_side input_pin_side = chan_side; + + /* Generate the input port object */ + switch (drive_rr_node->type) { + case OPIN: + input_pin_side = rr_gsb.get_opin_node_grid_side(drive_rr_node); + break; + case CHANX: + case CHANY: { + /* This should be an input in the data structure of RRGSB */ + if (cur_rr_node == drive_rr_node) { + /* To be strict, the input should locate on the opposite side. + * Use the else part if this may change in some architecture. + */ + Side side_manager(chan_side); + input_pin_side = side_manager.get_opposite(); + } else { + /* The input could be at any side of the switch block, find it */ + int index = -1; + rr_gsb.get_node_side_and_index(drive_rr_node, IN_PORT, &input_pin_side, &index); + } + break; + } + default: /* SOURCE, IPIN, SINK are invalid*/ + vpr_printf(TIO_MESSAGE_ERROR, + "(File:%s, [LINE%d])Invalid rr_node type! Should be [OPIN|CHANX|CHANY].\n", + __FILE__, __LINE__); + exit(1); + } + /* Find the name of input port */ + ModulePortId input_port_id = find_switch_block_module_input_port(module_manager, sb_module, rr_gsb, grids, input_pin_side, drive_rr_node); + + /* The input port and output port must match in size */ + BasicPort input_port = module_manager.module_port(sb_module, input_port_id); + BasicPort output_port = module_manager.module_port(sb_module, output_port_id); + VTR_ASSERT(input_port.get_width() == output_port.get_width()); + + /* Create a module net for this short-wire connection */ + for (size_t pin_id = 0; pin_id < input_port.pins().size(); ++pin_id) { + ModuleNetId net = input_port_to_module_nets.at(input_port_id); + /* Skip Configuring the net source, it is done before */ + /* Configure the net sink */ + module_manager.add_module_net_sink(sb_module, net, sb_module, 0, output_port_id, output_port.pins()[pin_id]); + } +} + +/********************************************************************* + * Build a instance of a routing multiplexer as well as + * associated memory modules for a connection inside a switch block + ********************************************************************/ +static +void build_switch_block_mux_module(ModuleManager& module_manager, + const ModuleId& sb_module, + const RRGSB& rr_gsb, + const CircuitLibrary& circuit_lib, + const std::vector>& grids, + const std::vector& rr_switches, + const e_side& chan_side, + t_rr_node* cur_rr_node, + const std::vector& drive_rr_nodes, + const size_t& switch_index, + const std::map& input_port_to_module_nets, + std::vector& memory_modules, + std::vector& memory_instances) { + /* Check current rr_node is CHANX or CHANY*/ + VTR_ASSERT((CHANX == cur_rr_node->type)||(CHANY == cur_rr_node->type)); + + /* Get the circuit model id of the routing multiplexer */ + CircuitModelId mux_model = rr_switches[switch_index].circuit_model; + + /* Find the input size of the implementation of a routing multiplexer */ + size_t datapath_mux_size = drive_rr_nodes.size(); + + /* Find the module name of the multiplexer and try to find it in the module manager */ + std::string mux_module_name = generate_mux_subckt_name(circuit_lib, mux_model, datapath_mux_size, std::string("")); + ModuleId mux_module = module_manager.find_module(mux_module_name); + VTR_ASSERT (true == module_manager.valid_module_id(mux_module)); + + /* Get the MUX instance id from the module manager */ + size_t mux_instance_id = module_manager.num_instance(sb_module, mux_module); + /* Instanciate the MUX Module */ + module_manager.add_child_module(sb_module, mux_module); + + /* Generate input ports that are wired to the input bus of the routing multiplexer */ + std::vector sb_input_port_ids = find_switch_block_module_input_ports(module_manager, sb_module, rr_gsb, grids, drive_rr_nodes); + + /* Link input bus port to Switch Block inputs */ + std::vector mux_model_input_ports = circuit_lib.model_ports_by_type(mux_model, SPICE_MODEL_PORT_INPUT, true); + VTR_ASSERT(1 == mux_model_input_ports.size()); + /* Find the module port id of the input port */ + ModulePortId mux_input_port_id = module_manager.find_module_port(mux_module, circuit_lib.port_lib_name(mux_model_input_ports[0])); + VTR_ASSERT(true == module_manager.valid_module_port_id(mux_module, mux_input_port_id)); + BasicPort mux_input_port = module_manager.module_port(mux_module, mux_input_port_id); + + /* Check port size should match */ + VTR_ASSERT(mux_input_port.get_width() == sb_input_port_ids.size()); + for (size_t pin_id = 0; pin_id < sb_input_port_ids.size(); ++pin_id) { + /* Use the exising net */ + ModuleNetId net = input_port_to_module_nets.at(sb_input_port_ids[pin_id]); + /* Configure the net source */ + module_manager.add_module_net_source(sb_module, net, sb_module, 0, sb_input_port_ids[pin_id], 0); + /* Configure the net sink */ + module_manager.add_module_net_sink(sb_module, net, mux_module, mux_instance_id, mux_input_port_id, mux_input_port.pins()[pin_id]); + } + + /* Link output port to Switch Block outputs */ + std::vector mux_model_output_ports = circuit_lib.model_ports_by_type(mux_model, SPICE_MODEL_PORT_OUTPUT, true); + VTR_ASSERT(1 == mux_model_output_ports.size()); + /* Use the port name convention in the circuit library */ + ModulePortId mux_output_port_id = module_manager.find_module_port(mux_module, circuit_lib.port_lib_name(mux_model_output_ports[0])); + VTR_ASSERT(true == module_manager.valid_module_port_id(mux_module, mux_output_port_id)); + BasicPort mux_output_port = module_manager.module_port(mux_module, mux_output_port_id); + ModulePortId sb_output_port_id = find_switch_block_module_chan_port(module_manager, sb_module, rr_gsb, chan_side, cur_rr_node, OUT_PORT); + + /* Check port size should match */ + VTR_ASSERT(1 == mux_output_port.get_width()); + for (size_t pin_id = 0; pin_id < mux_output_port.pins().size(); ++pin_id) { + ModuleNetId net = module_manager.create_module_net(sb_module); + /* Skip Configuring the net source, it is done before */ + /* Configure the net sink */ + module_manager.add_module_net_sink(sb_module, net, sb_module, 0, sb_output_port_id, 0); + } + + /* Instanciate memory modules */ + /* Find the name and module id of the memory module */ + std::string mem_module_name = generate_mux_subckt_name(circuit_lib, mux_model, datapath_mux_size, std::string(verilog_mem_posfix)); + ModuleId mem_module = module_manager.find_module(mem_module_name); + VTR_ASSERT (true == module_manager.valid_module_id(mem_module)); + + size_t mem_instance_id = module_manager.num_instance(sb_module, mem_module); + module_manager.add_child_module(sb_module, mem_module); + + /* Add nets to connect regular and mode-select SRAM ports to the SRAM port of memory module */ + add_module_nets_between_logic_and_memory_sram_bus(module_manager, sb_module, + mux_module, mux_instance_id, + mem_module, mem_instance_id, + circuit_lib, mux_model); + /* Update memory and instance list */ + memory_modules.push_back(mem_module); + memory_instances.push_back(mem_instance_id); +} + +/********************************************************************* + * Generate child modules for a interconnection inside switch block + * The interconnection could be either a wire or a routing multiplexer, + * which depends on the fan-in of the rr_nodes in the switch block + ********************************************************************/ +static +void build_switch_block_interc_modules(ModuleManager& module_manager, + const ModuleId& sb_module, + const RRGSB& rr_gsb, + const CircuitLibrary& circuit_lib, + const std::vector>& grids, + const std::vector& rr_switches, + const e_side& chan_side, + const size_t& chan_node_id, + const std::map& input_port_to_module_nets, + std::vector& memory_modules, + std::vector& memory_instances) { + std::vector drive_rr_nodes; + + /* Get the node */ + t_rr_node* cur_rr_node = rr_gsb.get_chan_node(chan_side, chan_node_id); + + /* Determine if the interc lies inside a channel wire, that is interc between segments */ + if (false == rr_gsb.is_sb_node_passing_wire(chan_side, chan_node_id)) { + for (int i = 0; i < cur_rr_node->num_drive_rr_nodes; ++i) { + drive_rr_nodes.push_back(cur_rr_node->drive_rr_nodes[i]); + } + /* Special: if there are zero-driver nodes. We skip here */ + if (0 == drive_rr_nodes.size()) { + return; + } + } + + if (0 == drive_rr_nodes.size()) { + /* Print a special direct connection*/ + build_switch_block_module_short_interc(module_manager, sb_module, + rr_gsb, chan_side, cur_rr_node, + cur_rr_node, grids, + input_port_to_module_nets); + } else if (1 == drive_rr_nodes.size()) { + /* Print a direct connection*/ + build_switch_block_module_short_interc(module_manager, sb_module, + rr_gsb, chan_side, cur_rr_node, + drive_rr_nodes[DEFAULT_SWITCH_ID], + grids, + input_port_to_module_nets); + } else if (1 < drive_rr_nodes.size()) { + /* Print the multiplexer, fan_in >= 2 */ + build_switch_block_mux_module(module_manager, + sb_module, rr_gsb, circuit_lib, + grids, rr_switches, chan_side, cur_rr_node, + drive_rr_nodes, + cur_rr_node->drive_switches[DEFAULT_SWITCH_ID], + input_port_to_module_nets, + memory_modules, memory_instances); + } /*Nothing should be done else*/ +} + + +/******************************************************************** + * Build a module for a switch block whose detailed description is + * available in a RRGSB object + * A Switch Box module consists of following ports: + * 1. Channel Y [x][y] inputs + * 2. Channel X [x+1][y] inputs + * 3. Channel Y [x][y-1] outputs + * 4. Channel X [x][y] outputs + * 5. Grid[x][y+1] Right side outputs pins + * 6. Grid[x+1][y+1] Left side output pins + * 7. Grid[x+1][y+1] Bottom side output pins + * 8. Grid[x+1][y] Top side output pins + * 9. Grid[x+1][y] Left side output pins + * 10. Grid[x][y] Right side output pins + * 11. Grid[x][y] Top side output pins + * 12. Grid[x][y+1] Bottom side output pins + * + * Location of a Switch Box in FPGA fabric: + * + * -------------- -------------- + * | | | | + * | Grid | ChanY | Grid | + * | [x][y+1] | [x][y+1] | [x+1][y+1] | + * | | | | + * -------------- -------------- + * ---------- + * ChanX | Switch | ChanX + * [x][y] | Box | [x+1][y] + * | [x][y] | + * ---------- + * -------------- -------------- + * | | | | + * | Grid | ChanY | Grid | + * | [x][y] | [x][y] | [x+1][y] | + * | | | | + * -------------- -------------- + * + * Switch Block pin location map + * + * Grid[x][y+1] ChanY[x][y+1] Grid[x+1][y+1] + * right_pins inputs/outputs left_pins + * | ^ | + * | | | + * v v v + * +-----------------------------------------------+ + * | | + * Grid[x][y+1] | | Grid[x+1][y+1] + * bottom_pins---->| |<---- bottom_pins + * | | + * ChanX[x][y] | Switch Box [x][y] | ChanX[x+1][y] + * inputs/outputs<--->| |<---> inputs/outputs + * | | + * Grid[x][y+1] | | Grid[x+1][y+1] + * top_pins---->| |<---- top_pins + * | | + * +-----------------------------------------------+ + * ^ ^ ^ + * | | | + * | v | + * Grid[x][y] ChanY[x][y] Grid[x+1][y] + * right_pins inputs/outputs left_pins + * + * + ********************************************************************/ +static +void build_switch_block_module(ModuleManager& module_manager, + const CircuitLibrary& circuit_lib, + const std::vector>& grids, + const std::vector& rr_switches, + const e_sram_orgz& sram_orgz_type, + const CircuitModelId& sram_model, + const RRGSB& rr_gsb) { + /* Create a Module of Switch Block and add to module manager */ + vtr::Point gsb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y()); + ModuleId sb_module = module_manager.add_module(generate_switch_block_module_name(gsb_coordinate)); + + /* Create a cache (fast look up) for module nets whose source are input ports */ + std::map input_port_to_module_nets; + + /* Add routing channel ports at each side of the GSB */ + for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { + Side side_manager(side); + DeviceCoordinator port_coordinator = rr_gsb.get_side_block_coordinator(side_manager.get_side()); + + for (size_t itrack = 0; itrack < rr_gsb.get_chan_width(side_manager.get_side()); ++itrack) { + vtr::Point port_coord(port_coordinator.get_x(), port_coordinator.get_y()); + std::string port_name = generate_routing_track_port_name(rr_gsb.get_chan_node(side_manager.get_side(), itrack)->type, + port_coord, itrack, + rr_gsb.get_chan_node_direction(side_manager.get_side(), itrack)); + BasicPort module_port(port_name, 1); /* Every track has a port size of 1 */ + + switch (rr_gsb.get_chan_node_direction(side_manager.get_side(), itrack)) { + case OUT_PORT: + module_manager.add_port(sb_module, module_port, ModuleManager::MODULE_OUTPUT_PORT); + break; + case IN_PORT: { + ModulePortId input_port_id = module_manager.add_port(sb_module, module_port, ModuleManager::MODULE_INPUT_PORT); + /* Cache the input net */ + ModuleNetId net = module_manager.create_module_net(sb_module); + module_manager.add_module_net_source(sb_module, net, sb_module, 0, input_port_id, 0); + input_port_to_module_nets[input_port_id] = net; + break; + } + default: + vpr_printf(TIO_MESSAGE_ERROR, + "(File: %s [LINE%d]) Invalid direction of chan[%d][%d]_track[%d]!\n", + __FILE__, __LINE__, rr_gsb.get_sb_x(), rr_gsb.get_sb_y(), itrack); + exit(1); + } + } + /* Dump OPINs of adjacent CLBs */ + for (size_t inode = 0; inode < rr_gsb.get_num_opin_nodes(side_manager.get_side()); ++inode) { + vtr::Point port_coord(rr_gsb.get_opin_node(side_manager.get_side(), inode)->xlow, + rr_gsb.get_opin_node(side_manager.get_side(), inode)->ylow); + std::string port_name = generate_grid_side_port_name(grids, port_coord, + rr_gsb.get_opin_node_grid_side(side_manager.get_side(), inode), + rr_gsb.get_opin_node(side_manager.get_side(), inode)->ptc_num); + BasicPort module_port(port_name, 1); /* Every grid output has a port size of 1 */ + /* Grid outputs are inputs of switch blocks */ + ModulePortId input_port_id = module_manager.add_port(sb_module, module_port, ModuleManager::MODULE_INPUT_PORT); + + /* Cache the input net */ + ModuleNetId net = module_manager.create_module_net(sb_module); + module_manager.add_module_net_source(sb_module, net, sb_module, 0, input_port_id, 0); + input_port_to_module_nets[input_port_id] = net; + } + } + + /* Vectors to record all the memory modules have been added + * They are used to add module nets of configuration bus + */ + std::vector memory_modules; + std::vector memory_instances; + + /* Add routing multiplexers as child modules */ + for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { + Side side_manager(side); + for (size_t itrack = 0; itrack < rr_gsb.get_chan_width(side_manager.get_side()); ++itrack) { + /* We care INC_DIRECTION tracks at this side*/ + if (OUT_PORT == rr_gsb.get_chan_node_direction(side_manager.get_side(), itrack)) { + build_switch_block_interc_modules(module_manager, + sb_module, rr_gsb, + circuit_lib, grids, rr_switches, + side_manager.get_side(), + itrack, + input_port_to_module_nets, + memory_modules, memory_instances); + } + } + } + + /* 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, sb_module); + + /* Count shared SRAM ports from the sub-modules under this Verilog module + * This is a much easier job after adding sub modules (instances), + * we just need to find all the I/O ports from the child modules and build a list of it + */ + size_t module_num_shared_config_bits = find_module_num_shared_config_bits_from_child_modules(module_manager, sb_module); + if (0 < module_num_shared_config_bits) { + add_reserved_sram_ports_to_module_manager(module_manager, sb_module, module_num_shared_config_bits); + } + + /* Count SRAM ports from the sub-modules under this Verilog module + * This is a much easier job after adding sub modules (instances), + * we just need to find all the I/O ports from the child modules and build a list of it + */ + size_t module_num_config_bits = find_module_num_config_bits_from_child_modules(module_manager, sb_module, circuit_lib, sram_model, sram_orgz_type); + if (0 < module_num_config_bits) { + add_sram_ports_to_module_manager(module_manager, sb_module, circuit_lib, sram_model, sram_orgz_type, module_num_config_bits); + } + + /* Add all the nets to connect configuration ports from memory module to primitive modules + * This is a one-shot addition that covers all the memory modules in this primitive module! + */ + if (false == memory_modules.empty()) { + add_module_nets_memory_config_bus(module_manager, sb_module, + memory_modules, memory_instances, + sram_orgz_type, circuit_lib.design_tech_type(sram_model)); + } +} + +/********************************************************************* + * Generate an input port for routing multiplexer inside the connection block + * which is the middle output of a routing track + ********************************************************************/ +static +ModulePortId find_connection_block_module_chan_port(const ModuleManager& module_manager, + const ModuleId& cb_module, + const RRGSB& rr_gsb, + const t_rr_type& cb_type, + t_rr_node* chan_rr_node) { + ModulePortId input_port_id; + /* Generate the input port object */ + switch (chan_rr_node->type) { + case CHANX: + case CHANY: { + /* Create port description for the routing track middle output */ + vtr::Point port_coord(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type)); + int chan_node_track_id = rr_gsb.get_cb_chan_node_index(cb_type, chan_rr_node); + /* Create a port description for the middle output */ + std::string input_port_name = generate_routing_track_port_name(cb_type, + port_coord, chan_node_track_id, + IN_PORT); + /* Must find a valid port id in the Switch Block module */ + input_port_id = module_manager.find_module_port(cb_module, input_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(cb_module, input_port_id)); + break; + } + default: /* OPIN, SOURCE, IPIN, SINK are invalid*/ + vpr_printf(TIO_MESSAGE_ERROR, + "(File:%s, [LINE%d])Invalid rr_node type! Should be [OPIN|CHANX|CHANY].\n", + __FILE__, __LINE__); + exit(1); + } + + return input_port_id; +} + +/********************************************************************* + * Generate a port for a routing track of a swtich block + ********************************************************************/ +static +ModulePortId find_connection_block_module_ipin_port(const ModuleManager& module_manager, + const ModuleId& cb_module, + const RRGSB& rr_gsb, + const std::vector>& grids, + t_rr_node* src_rr_node) { + + /* Ensure the src_rr_node is an input pin of a CLB */ + VTR_ASSERT(IPIN == src_rr_node->type); + /* Create port description for input pin of a CLB */ + vtr::Point port_coord(src_rr_node->xlow, src_rr_node->ylow); + /* Search all the sides of a SB, see this drive_rr_node is an INPUT of this SB */ + enum e_side cb_ipin_side = NUM_SIDES; + int cb_ipin_index = -1; + rr_gsb.get_node_side_and_index(src_rr_node, OUT_PORT, &cb_ipin_side, &cb_ipin_index); + /* We need to be sure that drive_rr_node is part of the CB */ + VTR_ASSERT((-1 != cb_ipin_index)&&(NUM_SIDES != cb_ipin_side)); + std::string port_name = generate_grid_side_port_name(grids, + port_coord, + rr_gsb.get_ipin_node_grid_side(cb_ipin_side, cb_ipin_index), + rr_gsb.get_ipin_node(cb_ipin_side, cb_ipin_index)->ptc_num); + + /* Must find a valid port id in the Switch Block module */ + ModulePortId ipin_port_id = module_manager.find_module_port(cb_module, port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(cb_module, ipin_port_id)); + return ipin_port_id; +} + +/********************************************************************* + * Generate a list of routing track middle output ports + * for routing multiplexer inside the connection block + ********************************************************************/ +static +std::vector find_connection_block_module_input_ports(const ModuleManager& module_manager, + const ModuleId& cb_module, + const RRGSB& rr_gsb, + const t_rr_type& cb_type, + const std::vector& input_rr_nodes) { + std::vector input_ports; + + for (auto input_rr_node : input_rr_nodes) { + input_ports.push_back(find_connection_block_module_chan_port(module_manager, cb_module, rr_gsb, cb_type, input_rr_node)); + } + + return input_ports; +} + +/********************************************************************* + * Print a short interconneciton in connection + ********************************************************************/ +static +void build_connection_block_module_short_interc(ModuleManager& module_manager, + const ModuleId& cb_module, + const RRGSB& rr_gsb, + const t_rr_type& cb_type, + const std::vector>& grids, + t_rr_node* src_rr_node, + const std::map& input_port_to_module_nets) { + /* Ensure we have only one 1 driver node */ + VTR_ASSERT_SAFE(1 == src_rr_node->fan_in); + + /* Find the driver node */ + t_rr_node* drive_rr_node = src_rr_node->drive_rr_nodes[0]; + + /* We have OPINs since we may have direct connections: + * These connections should be handled by other functions in the compact_netlist.c + * So we just return here for OPINs + */ + if (OPIN == drive_rr_node->type) { + return; + } + + VTR_ASSERT((CHANX == drive_rr_node->type) || (CHANY == drive_rr_node->type)); + + /* Create port description for the routing track middle output */ + ModulePortId input_port_id = find_connection_block_module_chan_port(module_manager, cb_module, rr_gsb, cb_type, drive_rr_node); + + /* Create port description for input pin of a CLB */ + ModulePortId ipin_port_id = find_connection_block_module_ipin_port(module_manager, cb_module, rr_gsb, grids, src_rr_node); + + /* The input port and output port must match in size */ + BasicPort input_port = module_manager.module_port(cb_module, input_port_id); + BasicPort ipin_port = module_manager.module_port(cb_module, ipin_port_id); + VTR_ASSERT(input_port.get_width() == ipin_port.get_width()); + + /* Create a module net for this short-wire connection */ + for (size_t pin_id = 0; pin_id < input_port.pins().size(); ++pin_id) { + ModuleNetId net = input_port_to_module_nets.at(input_port_id); + /* Skip Configuring the net source, it is done before */ + /* Configure the net sink */ + module_manager.add_module_net_sink(cb_module, net, cb_module, 0, ipin_port_id, ipin_port.pins()[pin_id]); + } +} + +/********************************************************************* + * Build a instance of a routing multiplexer as well as + * associated memory modules for a connection inside a connection block + ********************************************************************/ +static +void build_connection_block_mux_module(ModuleManager& module_manager, + const ModuleId& cb_module, + const RRGSB& rr_gsb, + const t_rr_type& cb_type, + const CircuitLibrary& circuit_lib, + const std::vector>& grids, + const std::vector& rr_switches, + t_rr_node* cur_rr_node, + const std::map& input_port_to_module_nets, + std::vector& memory_modules, + std::vector& memory_instances) { + /* Check current rr_node is an input pin of a CLB */ + VTR_ASSERT(IPIN == cur_rr_node->type); + + /* Build a vector of driver rr_nodes */ + std::vector drive_rr_nodes; + for (int inode = 0; inode < cur_rr_node->num_drive_rr_nodes; inode++) { + drive_rr_nodes.push_back(cur_rr_node->drive_rr_nodes[inode]); + } + + int switch_index = cur_rr_node->drive_switches[DEFAULT_SWITCH_ID]; + + /* Get the circuit model id of the routing multiplexer */ + CircuitModelId mux_model = rr_switches[switch_index].circuit_model; + + /* Find the input size of the implementation of a routing multiplexer */ + size_t datapath_mux_size = drive_rr_nodes.size(); + + /* Find the module name of the multiplexer and try to find it in the module manager */ + std::string mux_module_name = generate_mux_subckt_name(circuit_lib, mux_model, datapath_mux_size, std::string("")); + ModuleId mux_module = module_manager.find_module(mux_module_name); + VTR_ASSERT (true == module_manager.valid_module_id(mux_module)); + + /* Get the MUX instance id from the module manager */ + size_t mux_instance_id = module_manager.num_instance(cb_module, mux_module); + module_manager.add_child_module(cb_module, mux_module); + + /* TODO: Generate input ports that are wired to the input bus of the routing multiplexer */ + std::vector cb_input_port_ids = find_connection_block_module_input_ports(module_manager, cb_module, rr_gsb, cb_type, drive_rr_nodes); + + /* Link input bus port to Switch Block inputs */ + std::vector mux_model_input_ports = circuit_lib.model_ports_by_type(mux_model, SPICE_MODEL_PORT_INPUT, true); + VTR_ASSERT(1 == mux_model_input_ports.size()); + /* Find the module port id of the input port */ + ModulePortId mux_input_port_id = module_manager.find_module_port(mux_module, circuit_lib.port_lib_name(mux_model_input_ports[0])); + VTR_ASSERT(true == module_manager.valid_module_port_id(mux_module, mux_input_port_id)); + BasicPort mux_input_port = module_manager.module_port(mux_module, mux_input_port_id); + + /* Check port size should match */ + VTR_ASSERT(mux_input_port.get_width() == cb_input_port_ids.size()); + for (size_t pin_id = 0; pin_id < cb_input_port_ids.size(); ++pin_id) { + /* Use the exising net */ + ModuleNetId net = input_port_to_module_nets.at(cb_input_port_ids[pin_id]); + /* Configure the net source */ + module_manager.add_module_net_source(cb_module, net, cb_module, 0, cb_input_port_ids[pin_id], 0); + /* Configure the net sink */ + module_manager.add_module_net_sink(cb_module, net, mux_module, mux_instance_id, mux_input_port_id, mux_input_port.pins()[pin_id]); + } + + /* Link output port to Switch Block outputs */ + std::vector mux_model_output_ports = circuit_lib.model_ports_by_type(mux_model, SPICE_MODEL_PORT_OUTPUT, true); + VTR_ASSERT(1 == mux_model_output_ports.size()); + /* Use the port name convention in the circuit library */ + ModulePortId mux_output_port_id = module_manager.find_module_port(mux_module, circuit_lib.port_lib_name(mux_model_output_ports[0])); + VTR_ASSERT(true == module_manager.valid_module_port_id(mux_module, mux_output_port_id)); + BasicPort mux_output_port = module_manager.module_port(mux_module, mux_output_port_id); + ModulePortId cb_output_port_id = find_connection_block_module_ipin_port(module_manager, cb_module, rr_gsb, grids, cur_rr_node); + + /* Check port size should match */ + VTR_ASSERT(1 == mux_output_port.get_width()); + for (size_t pin_id = 0; pin_id < mux_output_port.pins().size(); ++pin_id) { + ModuleNetId net = module_manager.create_module_net(cb_module); + /* Skip Configuring the net source, it is done before */ + /* Configure the net sink */ + module_manager.add_module_net_sink(cb_module, net, cb_module, 0, cb_output_port_id, 0); + } + + /* Instanciate memory modules */ + /* Find the name and module id of the memory module */ + std::string mem_module_name = generate_mux_subckt_name(circuit_lib, mux_model, datapath_mux_size, std::string(verilog_mem_posfix)); + ModuleId mem_module = module_manager.find_module(mem_module_name); + VTR_ASSERT (true == module_manager.valid_module_id(mem_module)); + + size_t mem_instance_id = module_manager.num_instance(cb_module, mem_module); + module_manager.add_child_module(cb_module, mem_module); + + /* Add nets to connect regular and mode-select SRAM ports to the SRAM port of memory module */ + add_module_nets_between_logic_and_memory_sram_bus(module_manager, cb_module, + mux_module, mux_instance_id, + mem_module, mem_instance_id, + circuit_lib, mux_model); + /* Update memory and instance list */ + memory_modules.push_back(mem_module); + memory_instances.push_back(mem_instance_id); +} + +/******************************************************************** + * Print internal connections of a connection block + * For a IPIN node that is driven by only 1 fan-in, + * a short wire will be created + * For a IPIN node that is driven by more than two fan-ins, + * a routing multiplexer will be instanciated + ********************************************************************/ +static +void build_connection_block_interc_modules(ModuleManager& module_manager, + const ModuleId& cb_module, + const RRGSB& rr_gsb, + const t_rr_type& cb_type, + const CircuitLibrary& circuit_lib, + const std::vector>& grids, + const std::vector& rr_switches, + t_rr_node* src_rr_node, + const std::map& input_port_to_module_nets, + std::vector& memory_modules, + std::vector& memory_instances) { + if (1 > src_rr_node->fan_in) { + return; /* This port has no driver, skip it */ + } else if (1 == src_rr_node->fan_in) { + /* Print a direct connection */ + build_connection_block_module_short_interc(module_manager, cb_module, rr_gsb, cb_type, grids, src_rr_node, input_port_to_module_nets); + + } else if (1 < src_rr_node->fan_in) { + /* Print the multiplexer, fan_in >= 2 */ + build_connection_block_mux_module(module_manager, + cb_module, rr_gsb, cb_type, + circuit_lib, grids, rr_switches, + src_rr_node, + input_port_to_module_nets, + memory_modules, memory_instances); + } /*Nothing should be done else*/ +} + +/******************************************************************** + * Generate a module of a connection Box (Type: [CHANX|CHANY]) + * Actually it is very similiar to switch box but + * the difference is connection boxes connect Grid INPUT Pins to channels + * NOTE: direct connection between CLBs should NOT be included inside this + * module! They should be added in the top-level module as their connection + * is not limited to adjacent CLBs!!! + * + * Location of a X- and Y-direction Connection Block in FPGA fabric + * +------------+ +-------------+ + * | |------>| | + * | CLB |<------| Y-direction | + * | | ... | Connection | + * | |------>| Block | + * +------------+ +-------------+ + * | ^ ... | | ^ ... | + * v | v v | v + * +-------------------+ +-------------+ + * --->| |--->| | + * <---| X-direction |<---| Switch | + * ...| Connection block |... | Block | + * --->| |--->| | + * +-------------------+ +-------------+ + * + * Internal structure: + * This is an example of a X-direction connection block + * Note that middle output ports are shorted wire from inputs of routing tracks, + * which are also the inputs of routing multiplexer of the connection block + * + * CLB Input Pins + * (IPINs) + * ^ ^ ^ + * | | ... | + * +--------------------------+ + * | ^ ^ ^ | + * | | | ... | | + * | +--------------------+ | + * | | routing | | + * | | multiplexers | | + * | +--------------------+ | + * | middle outputs | + * | of routing channel | + * | ^ ^ ^ ^ ^ ^ ^ ^ | + * | | | | | ... | | | | | + * in[0] -->|------------------------->|---> out[0] + * out[1] <--|<-------------------------|<--- in[1] + * | ... | + * in[W-2] -->|------------------------->|---> out[W-2] + * out[W-1] <--|<-------------------------|<--- in[W-1] + * +--------------------------+ + * + * W: routing channel width + * + ********************************************************************/ +static +void build_connection_block_module(ModuleManager& module_manager, + const CircuitLibrary& circuit_lib, + const std::vector>& grids, + const std::vector& rr_switches, + const e_sram_orgz& sram_orgz_type, + const CircuitModelId& sram_model, + const RRGSB& rr_gsb, + const t_rr_type& cb_type) { + /* Create the netlist */ + vtr::Point gsb_coordinate(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type)); + + /* Create a Verilog Module based on the circuit model, and add to module manager */ + ModuleId cb_module = module_manager.add_module(generate_connection_block_module_name(cb_type, gsb_coordinate)); + + /* Add the input and output ports of routing tracks in the channel + * Routing tracks pass through the connection blocks + */ + for (size_t itrack = 0; itrack < rr_gsb.get_cb_chan_width(cb_type); ++itrack) { + vtr::Point port_coord(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type)); + std::string port_name = generate_routing_track_port_name(cb_type, + port_coord, itrack, + IN_PORT); + BasicPort module_port(port_name, 1); /* Every track has a port size of 1 */ + module_manager.add_port(cb_module, module_port, ModuleManager::MODULE_INPUT_PORT); + } + for (size_t itrack = 0; itrack < rr_gsb.get_cb_chan_width(cb_type); ++itrack) { + vtr::Point port_coord(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type)); + std::string port_name = generate_routing_track_port_name(cb_type, + port_coord, itrack, + OUT_PORT); + BasicPort module_port(port_name, 1); /* Every track has a port size of 1 */ + module_manager.add_port(cb_module, module_port, ModuleManager::MODULE_OUTPUT_PORT); + } + + /* Add the input pins of grids, which are output ports of the connection block */ + std::vector cb_ipin_sides = rr_gsb.get_cb_ipin_sides(cb_type); + for (size_t iside = 0; iside < cb_ipin_sides.size(); ++iside) { + enum e_side cb_ipin_side = cb_ipin_sides[iside]; + for (size_t inode = 0; inode < rr_gsb.get_num_ipin_nodes(cb_ipin_side); ++inode) { + t_rr_node* ipin_node = rr_gsb.get_ipin_node(cb_ipin_side, inode); + vtr::Point port_coord(ipin_node->xlow, ipin_node->ylow); + std::string port_name = generate_grid_side_port_name(grids, + port_coord, + rr_gsb.get_ipin_node_grid_side(cb_ipin_side, inode), + ipin_node->ptc_num); + BasicPort module_port(port_name, 1); /* Every grid output has a port size of 1 */ + /* Grid outputs are inputs of switch blocks */ + module_manager.add_port(cb_module, module_port, ModuleManager::MODULE_OUTPUT_PORT); + } + } + + /* Create a cache (fast look up) for module nets whose source are input ports */ + std::map input_port_to_module_nets; + + /* TODO: Generate short-wire connection for each routing track : + * Each input port is short-wired to its output port and middle output port + * + * in[i] ----------> out[i] + * | + * +-----> mid_out[i] + */ + for (size_t itrack = 0; itrack < rr_gsb.get_cb_chan_width(cb_type); ++itrack) { + vtr::Point port_coord(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type)); + /* Create a port description for the input */ + std::string input_port_name = generate_routing_track_port_name(cb_type, + port_coord, itrack, + IN_PORT); + ModulePortId input_port_id = module_manager.find_module_port(cb_module, input_port_name); + BasicPort input_port = module_manager.module_port(cb_module, input_port_id); + + /* Create a port description for the output */ + std::string output_port_name = generate_routing_track_port_name(cb_type, + port_coord, itrack, + OUT_PORT); + ModulePortId output_port_id = module_manager.find_module_port(cb_module, output_port_name); + BasicPort output_port = module_manager.module_port(cb_module, output_port_id); + + /* Ensure port size matching */ + VTR_ASSERT(1 == input_port.get_width()); + VTR_ASSERT(input_port.get_width() == output_port.get_width()); + + /* Create short-wires: input port ---> output port + * Do short-wires: input port ---> middle output port + */ + for (size_t pin_id = 0; pin_id < input_port.pins().size(); ++pin_id) { + ModuleNetId net = module_manager.create_module_net(cb_module); + module_manager.add_module_net_source(cb_module, net, cb_module, 0, input_port_id, input_port.pins()[pin_id]); + module_manager.add_module_net_sink(cb_module, net, cb_module, 0, output_port_id, output_port.pins()[pin_id]); + /* Cache the module net */ + input_port_to_module_nets[input_port_id] = net; + } + } + + /* Vectors to record all the memory modules have been added + * They are used to add module nets of configuration bus + */ + std::vector memory_modules; + std::vector memory_instances; + + /* TODO: Add sub modules of routing multiplexers or direct interconnect*/ + for (size_t iside = 0; iside < cb_ipin_sides.size(); ++iside) { + enum e_side cb_ipin_side = cb_ipin_sides[iside]; + for (size_t inode = 0; inode < rr_gsb.get_num_ipin_nodes(cb_ipin_side); ++inode) { + build_connection_block_interc_modules(module_manager, + cb_module, rr_gsb, cb_type, + circuit_lib, grids, rr_switches, + rr_gsb.get_ipin_node(cb_ipin_side, inode), + input_port_to_module_nets, + memory_modules, memory_instances); + } + } + + /* 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, cb_module); + + /* Count shared SRAM ports from the sub-modules under this Verilog module + * This is a much easier job after adding sub modules (instances), + * we just need to find all the I/O ports from the child modules and build a list of it + */ + size_t module_num_shared_config_bits = find_module_num_shared_config_bits_from_child_modules(module_manager, cb_module); + if (0 < module_num_shared_config_bits) { + add_reserved_sram_ports_to_module_manager(module_manager, cb_module, module_num_shared_config_bits); + } + + /* Count SRAM ports from the sub-modules under this Verilog module + * This is a much easier job after adding sub modules (instances), + * we just need to find all the I/O ports from the child modules and build a list of it + */ + size_t module_num_config_bits = find_module_num_config_bits_from_child_modules(module_manager, cb_module, circuit_lib, sram_model, sram_orgz_type); + if (0 < module_num_config_bits) { + add_sram_ports_to_module_manager(module_manager, cb_module, circuit_lib, sram_model, sram_orgz_type, module_num_config_bits); + } + + /* Add all the nets to connect configuration ports from memory module to primitive modules + * This is a one-shot addition that covers all the memory modules in this primitive module! + */ + if (false == memory_modules.empty()) { + add_module_nets_memory_config_bus(module_manager, cb_module, + memory_modules, memory_instances, + sram_orgz_type, circuit_lib.design_tech_type(sram_model)); + } +} + + +/******************************************************************** + * Iterate over all the connection blocks in a device + * and build a module for each of them + *******************************************************************/ +static +void build_flatten_connection_block_modules(ModuleManager& module_manager, + const DeviceRRGSB& L_device_rr_gsb, + const CircuitLibrary& circuit_lib, + const std::vector>& grids, + const std::vector& rr_switches, + const e_sram_orgz& sram_orgz_type, + const CircuitModelId& sram_model, + const t_rr_type& cb_type) { + /* Build unique X-direction connection block modules */ + DeviceCoordinator cb_range = L_device_rr_gsb.get_gsb_range(); + + for (size_t ix = 0; ix < cb_range.get_x(); ++ix) { + for (size_t iy = 0; iy < cb_range.get_y(); ++iy) { + /* Check if the connection block exists in the device! + * Some of them do NOT exist due to heterogeneous blocks (height > 1) + * We will skip those modules + */ + const RRGSB& rr_gsb = L_device_rr_gsb.get_gsb(ix, iy); + if ( (TRUE != is_cb_exist(CHANX, rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type))) + || (true != rr_gsb.is_cb_exist(cb_type))) { + continue; + } + build_connection_block_module(module_manager, + circuit_lib, + grids, rr_switches, + sram_orgz_type, sram_model, + rr_gsb, cb_type); + } + } +} + +/******************************************************************** + * A top-level function of this file + * Build all the modules for global routing architecture of a FPGA fabric + * in a flatten way: + * Each connection block and switch block will be generated as a unique module + * Covering: + * 1. Connection blocks + * 2. Switch blocks + *******************************************************************/ +void build_flatten_routing_modules(ModuleManager& module_manager, + const DeviceRRGSB& L_device_rr_gsb, + const CircuitLibrary& circuit_lib, + const e_sram_orgz& sram_orgz_type, + const CircuitModelId& sram_model, + const std::vector>& grids, + const t_det_routing_arch& routing_arch, + const std::vector& rr_switches) { + /* We only support uni-directional routing architecture now */ + VTR_ASSERT (UNI_DIRECTIONAL == routing_arch.directionality); + + /* TODO: deprecate DeviceCoordinator, use vtr::Point only! */ + DeviceCoordinator sb_range = L_device_rr_gsb.get_gsb_range(); + + /* Build unique switch block modules */ + for (size_t ix = 0; ix < sb_range.get_x(); ++ix) { + for (size_t iy = 0; iy < sb_range.get_y(); ++iy) { + const RRGSB& rr_gsb = L_device_rr_gsb.get_gsb(ix, iy); + build_switch_block_module(module_manager, circuit_lib, + grids, rr_switches, + sram_orgz_type, sram_model, + rr_gsb); + } + } + + build_flatten_connection_block_modules(module_manager, L_device_rr_gsb, + circuit_lib, + grids, rr_switches, + sram_orgz_type, sram_model, + CHANX); + + build_flatten_connection_block_modules(module_manager, L_device_rr_gsb, + circuit_lib, + grids, rr_switches, + sram_orgz_type, sram_model, + CHANY); + +} + +/******************************************************************** + * A top-level function of this file + * Build all the unique modules for global routing architecture of a FPGA fabric + * This function will use unique module list built in device_rr_gsb, + * to build only unique modules (in terms of graph connections) of + * 1. Connection blocks + * 2. Switch blocks + * + * Note: this function SHOULD be called only when + * the option compact_routing_hierarchy is turned on!!! + *******************************************************************/ +void build_unique_routing_modules(ModuleManager& module_manager, + const DeviceRRGSB& L_device_rr_gsb, + const CircuitLibrary& circuit_lib, + const e_sram_orgz& sram_orgz_type, + const CircuitModelId& sram_model, + const std::vector>& grids, + const t_det_routing_arch& routing_arch, + const std::vector& rr_switches) { + /* We only support uni-directional routing architecture now */ + VTR_ASSERT (UNI_DIRECTIONAL == routing_arch.directionality); + + /* Build unique switch block modules */ + for (size_t isb = 0; isb < L_device_rr_gsb.get_num_sb_unique_module(); ++isb) { + const RRGSB& unique_mirror = L_device_rr_gsb.get_sb_unique_module(isb); + build_switch_block_module(module_manager, circuit_lib, + grids, rr_switches, + sram_orgz_type, sram_model, + unique_mirror); + } + + /* Build unique X-direction connection block modules */ + for (size_t icb = 0; icb < L_device_rr_gsb.get_num_cb_unique_module(CHANX); ++icb) { + const RRGSB& unique_mirror = L_device_rr_gsb.get_cb_unique_module(CHANX, icb); + + build_connection_block_module(module_manager, + circuit_lib, + grids, rr_switches, + sram_orgz_type, sram_model, + unique_mirror, CHANX); + } + + /* Build unique X-direction connection block modules */ + for (size_t icb = 0; icb < L_device_rr_gsb.get_num_cb_unique_module(CHANY); ++icb) { + const RRGSB& unique_mirror = L_device_rr_gsb.get_cb_unique_module(CHANY, icb); + + build_connection_block_module(module_manager, + circuit_lib, + grids, rr_switches, + sram_orgz_type, sram_model, + unique_mirror, CHANY); + } +} diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_routing_modules.h b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_routing_modules.h new file mode 100644 index 000000000..8f00aad2b --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_routing_modules.h @@ -0,0 +1,33 @@ +/******************************************************************** + * Header file for build_routing_modules.cpp + *******************************************************************/ +#ifndef BUILD_ROUTING_MODULES_H +#define BUILD_ROUTING_MODULES_H + +#include "spice_types.h" +#include "vpr_types.h" +#include "rr_blocks.h" +#include "mux_library.h" +#include "circuit_library.h" +#include "module_manager.h" + +void build_flatten_routing_modules(ModuleManager& module_manager, + const DeviceRRGSB& L_device_rr_gsb, + const CircuitLibrary& circuit_lib, + const e_sram_orgz& sram_orgz_type, + const CircuitModelId& sram_model, + const std::vector>& grids, + const t_det_routing_arch& routing_arch, + const std::vector& rr_switches); + +void build_unique_routing_modules(ModuleManager& module_manager, + const DeviceRRGSB& L_device_rr_gsb, + const CircuitLibrary& circuit_lib, + const e_sram_orgz& sram_orgz_type, + const CircuitModelId& sram_model, + const std::vector>& grids, + const t_det_routing_arch& routing_arch, + const std::vector& rr_switches); + + +#endif diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_api.c b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_api.c index fae787353..c4f2805a5 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_api.c +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_api.c @@ -13,6 +13,7 @@ #include /* Include vpr structs*/ +#include "vtr_assert.h" #include "vtr_geometry.h" #include "util.h" #include "physical_types.h" @@ -280,11 +281,25 @@ void vpr_fpga_verilog(ModuleManager& module_manager, vpr_setup.FPGA_SPICE_Opts.SynVerilogOpts); /* Dump routing resources: switch blocks, connection blocks and channel tracks */ - print_verilog_routing_resources(module_manager, mux_lib, sram_verilog_orgz_info, + print_verilog_routing_resources(module_manager, sram_verilog_orgz_info, src_dir_path, rr_dir_path, Arch, vpr_setup.RoutingArch, num_rr_nodes, rr_node, rr_node_indices, rr_indexed_data, vpr_setup.FPGA_SPICE_Opts); + if (TRUE == vpr_setup.FPGA_SPICE_Opts.compact_routing_hierarchy) { + print_verilog_unique_routing_modules(module_manager, device_rr_gsb, + vpr_setup.RoutingArch, + std::string(src_dir_path), std::string(rr_dir_path), + TRUE == vpr_setup.FPGA_SPICE_Opts.SynVerilogOpts.dump_explicit_verilog); + } else { + VTR_ASSERT(FALSE == vpr_setup.FPGA_SPICE_Opts.compact_routing_hierarchy); + print_verilog_flatten_routing_modules(module_manager, device_rr_gsb, + vpr_setup.RoutingArch, + std::string(src_dir_path), std::string(rr_dir_path), + TRUE == vpr_setup.FPGA_SPICE_Opts.SynVerilogOpts.dump_explicit_verilog); + } + + /* Dump logic blocks * Branches to go: * 1. a compact output diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_routing.c b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_routing.c index 13dc670f0..d7943f805 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_routing.c +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_routing.c @@ -49,100 +49,9 @@ #include "verilog_global.h" #include "verilog_utils.h" #include "verilog_writer_utils.h" +#include "verilog_module_writer.h" #include "verilog_routing.h" -/******************************************************************** - * Print local wires that are used for SRAM configuration - * This function is supposed to be used by Verilog generation - * of connection blocks - * It will count the number of connection blocks, which is the - * port width for local wires when Configuration chain is used - ********************************************************************/ -static -void print_verilog_connection_block_local_sram_wires(std::fstream& fp, - const RRGSB& rr_gsb, - const t_rr_type& cb_type, - const CircuitLibrary& circuit_lib, - const CircuitModelId& sram_model, - const e_sram_orgz& sram_orgz_type, - const size_t& port_size) { - size_t local_port_size = port_size; - if (SPICE_SRAM_SCAN_CHAIN == sram_orgz_type) { - /* Plus 1 for the wire size to connect to the tail of the configuration chain */ - local_port_size = find_connection_block_number_of_muxes(rr_gsb, cb_type) + 1; - } - print_verilog_local_sram_wires(fp, circuit_lib, sram_model, sram_orgz_type, local_port_size); -} - - -/******************************************************************** - * Print local wires that are used for SRAM configuration - * This function is supposed to be used by Verilog generation - * of switch blocks - * It will count the number of switch blocks, which is the - * port width for local wires when Configuration chain is used - ********************************************************************/ -static -void print_verilog_switch_block_local_sram_wires(std::fstream& fp, - const RRGSB& rr_gsb, - const CircuitLibrary& circuit_lib, - const CircuitModelId& sram_model, - const e_sram_orgz& sram_orgz_type, - const size_t& port_size) { - size_t local_port_size = port_size; - if (SPICE_SRAM_SCAN_CHAIN == sram_orgz_type) { - /* Plus 1 for the wire size to connect to the tail of the configuration chain */ - local_port_size = find_switch_block_number_of_muxes(rr_gsb) + 1; - } - print_verilog_local_sram_wires(fp, circuit_lib, sram_model, sram_orgz_type, local_port_size); -} - -/******************************************************************** - * Check if the MSB of a configuration bus of a connection block - * matches the expected value - * Exception: - * 1. Configuration bus for configuration chain will follow - * the number of multiplexers in the connection block - ********************************************************************/ -static -bool check_connection_block_mem_config_bus(const e_sram_orgz& sram_orgz_type, - const RRGSB& rr_gsb, - const t_rr_type& cb_type, - const BasicPort& config_bus, - const size_t& expected_msb) { - size_t local_expected_msb = expected_msb; - if (SPICE_SRAM_SCAN_CHAIN == sram_orgz_type) { - /* Note the size of local wires is number of routing multiplexers + 1 - * Wire MSB is the number of routing multiplexers in the configuration chain - */ - local_expected_msb = find_connection_block_number_of_muxes(rr_gsb, cb_type); - } - return check_mem_config_bus(sram_orgz_type, config_bus, local_expected_msb); -} - - -/******************************************************************** - * Check if the MSB of a configuration bus of a switch block - * matches the expected value - * Exception: - * 1. Configuration bus for configuration chain will follow - * the number of multiplexers in the switch block - ********************************************************************/ -static -bool check_switch_block_mem_config_bus(const e_sram_orgz& sram_orgz_type, - const RRGSB& rr_gsb, - const BasicPort& config_bus, - const size_t& expected_msb) { - size_t local_expected_msb = expected_msb; - if (SPICE_SRAM_SCAN_CHAIN == sram_orgz_type) { - /* Note the size of local wires is number of routing multiplexers + 1 - * Wire MSB is the number of routing multiplexers in the configuration chain - */ - local_expected_msb = find_switch_block_number_of_muxes(rr_gsb); - } - return check_mem_config_bus(sram_orgz_type, config_bus, local_expected_msb); -} - /********************************************************************* * Generate the Verilog module for a routing channel * Routing track wire, which is 1-input and dual output @@ -340,216 +249,6 @@ void print_verilog_routing_unique_chan_subckt(ModuleManager& module_manager, return; } -/********************************************************************* - * Generate the Verilog module for a routing channel - * Routing track wire, which is 1-input and dual output - * This type of wires are used in the global routing architecture. - * One of the output is wired to another Switch block multiplexer, - * while the mid-output is wired to a Connection block multiplexer. - * - * | CLB | - * +------------+ - * ^ - * | - * +------------------------------+ - * | Connection block multiplexer | - * +------------------------------+ - * ^ - * | mid-output +-------------- - * +--------------------+ | - * input --->| Routing track wire |--------->| Switch Block - * +--------------------+ output | - * +-------------- - * - * IMPORTANT: This function is designed for outputting non-unique Verilog modules - * of routing channels - * - * TODO: This function should be adapted to the RRGraph object - *********************************************************************/ -static -void print_verilog_routing_chan_subckt(ModuleManager& module_manager, - const std::string& verilog_dir, - const std::string& subckt_dir, - const vtr::Point& chan_coordinate, - const t_rr_type& chan_type, - int LL_num_rr_nodes, t_rr_node* LL_rr_node, - t_ivec*** LL_rr_node_indices) { - int chan_width = 0; - t_rr_node** chan_rr_nodes = NULL; - - std::string fname_prefix; - - /* TODO: use a constexpr String arrary to replace this switch cases? */ - /* Find the prefix for the Verilog file name */ - switch (chan_type) { - case CHANX: - fname_prefix = std::string(chanx_verilog_file_name_prefix); - break; - case CHANY: - fname_prefix = std::string(chany_verilog_file_name_prefix); - break; - default: - vpr_printf(TIO_MESSAGE_ERROR, - "(File:%s, [LINE%d])Invalid Channel type! Should be CHANX or CHANY.\n", - __FILE__, __LINE__); - exit(1); - } - - std::string verilog_fname(subckt_dir + generate_routing_block_netlist_name(fname_prefix, chan_coordinate, std::string(verilog_netlist_file_postfix))); - /* TODO: remove the bak file when the file is ready */ - verilog_fname += ".bak"; - - /* Create the file stream */ - std::fstream fp; - fp.open(verilog_fname, std::fstream::out | std::fstream::trunc); - - check_file_handler(fp); - - print_verilog_file_header(fp, "Verilog modules for routing channel in X- and Y-direction"); - - /* Print preprocessing flags */ - print_verilog_include_defines_preproc_file(fp, verilog_dir); - - /* Create a Verilog Module based on the circuit model, and add to module manager */ - ModuleId module_id = module_manager.add_module(generate_routing_channel_module_name(chan_type, chan_coordinate)); - - /* Collect rr_nodes for Tracks for chanx[ix][iy] */ - chan_rr_nodes = get_chan_rr_nodes(&chan_width, chan_type, chan_coordinate.x(), chan_coordinate.y(), - LL_num_rr_nodes, LL_rr_node, LL_rr_node_indices); - - /* Add ports to the module */ - /* For the LEFT side of a X-direction routing channel - * or the BOTTOM bottom side of a Y-direction routing channel - * Routing Resource Nodes in INC_DIRECTION are inputs of the module - * - * For the RIGHT side of a X-direction routing channel - * or the TOP bottom side of a Y-direction routing channel - * Routing Resource Nodes in INC_DIRECTION are outputs of the module - * - * An example of X-direction routing channel consisting of W routing nodes: - * +--------------------------+ - * nodeA(INC_DIRECTION)--->| in[0] out[0] |---> nodeA(INC_DIRECTION) - * nodeB(DEC_DIRECTION)<---| out[1] in[1] |<--- nodeB(DEC_DIRECTION) - * ... ... ... ... - * nodeX(INC_DIRECTION)--->| in[W-1] out[W-1] |---> nodeX(INC_DIRECTION) - * +--------------------------+ - * - * An example of Y-direction routing channel consisting of W routing nodes: - * - * nodeA nodeB nodeX - * (INC_DIRECTION) (DEC_DIRECTION) (DEC_DIRECTION) - * ^ | ... | - * | v v - * +------------------------------ ... -------+ - * | out[0] in[1] in[X] | - * | | - * | | - * | in[0] out[1] ... out[X] | - * +------------------------------ ... -------+ - * ^ | | - * | v v - * nodeA nodeB nodeX - * (INC_DIRECTION) (DEC_DIRECTION) (DEC_DIRECTION) - */ - /* Add ports at LEFT/BOTTOM side of the module */ - for (size_t itrack = 0; itrack < size_t(chan_width); ++itrack) { - switch (chan_rr_nodes[itrack]->direction) { - case INC_DIRECTION: { - /* TODO: naming should be more flexible !!! */ - BasicPort input_port(std::string("in" + std::to_string(itrack)), 1); - module_manager.add_port(module_id, input_port, ModuleManager::MODULE_INPUT_PORT); - break; - } - case DEC_DIRECTION: { - /* TODO: naming should be more flexible !!! */ - BasicPort output_port(std::string("out" + std::to_string(itrack)), 1); - module_manager.add_port(module_id, output_port, ModuleManager::MODULE_OUTPUT_PORT); - break; - } - case BI_DIRECTION: - default: - vpr_printf(TIO_MESSAGE_ERROR, - "(File: %s [LINE%d]) Invalid direction of rr_node %s[%lu][%lu]_in/out[%lu]!\n", - __FILE__, __LINE__, - convert_chan_type_to_string(chan_type), - chan_coordinate.x(), chan_coordinate.y(), itrack); - exit(1); - } - } - /* Add ports at RIGHT/TOP side of the module */ - for (size_t itrack = 0; itrack < size_t(chan_width); ++itrack) { - switch (chan_rr_nodes[itrack]->direction) { - case INC_DIRECTION: { - /* TODO: naming should be more flexible !!! */ - BasicPort output_port(std::string("out" + std::to_string(itrack)), 1); - module_manager.add_port(module_id, output_port, ModuleManager::MODULE_OUTPUT_PORT); - break; - } - case DEC_DIRECTION: { - /* TODO: naming should be more flexible !!! */ - BasicPort input_port(std::string("in" + std::to_string(itrack)), 1); - module_manager.add_port(module_id, input_port, ModuleManager::MODULE_INPUT_PORT); - break; - } - case BI_DIRECTION: - default: - vpr_printf(TIO_MESSAGE_ERROR, - "(File: %s [LINE%d]) Invalid direction of rr_node %s[%lu][%lu]_in/out[%lu]!\n", - __FILE__, __LINE__, - convert_chan_type_to_string(chan_type), - chan_coordinate.x(), chan_coordinate.y(), itrack); - exit(1); - } - } - /* Add middle-point output for connection box inputs */ - for (size_t itrack = 0; itrack < size_t(chan_width); ++itrack) { - /* TODO: naming should be more flexible !!! */ - BasicPort mid_output_port(std::string("mid_out" + std::to_string(itrack)), 1); - module_manager.add_port(module_id, mid_output_port, ModuleManager::MODULE_OUTPUT_PORT); - } - - /* dump module definition + ports */ - print_verilog_module_declaration(fp, module_manager, module_id); - /* Finish dumping ports */ - - /* Print short-wire connection: - * - * in[i] ----------> out[i] - * | - * +-----> mid_out[i] - */ - for (size_t itrack = 0; itrack < size_t(chan_width); ++itrack) { - /* short connecting inputs and outputs: - * length of metal wire and parasitics are handled by semi-custom flow - */ - BasicPort input_port(std::string("in" + std::to_string(itrack)), 1); - BasicPort output_port(std::string("out" + std::to_string(itrack)), 1); - BasicPort mid_output_port(std::string("mid_out" + std::to_string(itrack)), 1); - print_verilog_wire_connection(fp, output_port, input_port, false); - print_verilog_wire_connection(fp, mid_output_port, input_port, false); - } - - /* Put an end to the Verilog module */ - print_verilog_module_end(fp, module_manager.module_name(module_id)); - - /* Add an empty line as a splitter */ - fp << std::endl; - - /* Close file handler */ - fp.close(); - - /* Add fname to the linked list */ - /* Uncomment this when it is ready - routing_verilog_subckt_file_path_head = add_one_subckt_file_name_to_llist(routing_verilog_subckt_file_path_head, verilog_fname.c_str()); - */ - - /* Free */ - my_free(chan_rr_nodes); - - return; -} - - static void dump_verilog_routing_chan_subckt(char* verilog_dir, char* subckt_dir, @@ -2227,724 +1926,7 @@ void update_routing_connection_box_conf_bits(t_sram_orgz_info* cur_sram_orgz_inf return; } - -/********************************************************************* - * Generate a port for a routing track of a swtich block - ********************************************************************/ -static -BasicPort generate_verilog_connection_box_ipin_port(const RRGSB& rr_gsb, - t_rr_node* src_rr_node) { - - /* Ensure the src_rr_node is an input pin of a CLB */ - VTR_ASSERT(IPIN == src_rr_node->type); - /* Create port description for input pin of a CLB */ - vtr::Point port_coord(src_rr_node->xlow, src_rr_node->ylow); - /* Search all the sides of a SB, see this drive_rr_node is an INPUT of this SB */ - enum e_side cb_ipin_side = NUM_SIDES; - int cb_ipin_index = -1; - rr_gsb.get_node_side_and_index(src_rr_node, OUT_PORT, &cb_ipin_side, &cb_ipin_index); - /* We need to be sure that drive_rr_node is part of the CB */ - VTR_ASSERT((-1 != cb_ipin_index)&&(NUM_SIDES != cb_ipin_side)); - std::string port_name = generate_grid_side_port_name(port_coord, - rr_gsb.get_ipin_node_grid_side(cb_ipin_side, cb_ipin_index), - rr_gsb.get_ipin_node(cb_ipin_side, cb_ipin_index)->ptc_num); - return BasicPort(port_name, 1); /* Every grid output has a port size of 1 */ -} - -/********************************************************************* - * Generate a port for a routing track of a swtich block - ********************************************************************/ -static -BasicPort generate_verilog_unique_switch_box_chan_port(const RRGSB& rr_sb, - const e_side& chan_side, - t_rr_node* cur_rr_node, - const PORTS& cur_rr_node_direction) { - /* Get the index in sb_info of cur_rr_node */ - int index = rr_sb.get_node_index(cur_rr_node, chan_side, cur_rr_node_direction); - /* Make sure this node is included in this sb_info */ - VTR_ASSERT((-1 != index)&&(NUM_SIDES != chan_side)); - - DeviceCoordinator chan_rr_node_coordinator = rr_sb.get_side_block_coordinator(chan_side); - - vtr::Point chan_port_coord(chan_rr_node_coordinator.get_x(), chan_rr_node_coordinator.get_y()); - std::string chan_port_name = generate_routing_track_port_name(rr_sb.get_chan_node(chan_side, index)->type, - chan_port_coord, index, - rr_sb.get_chan_node_direction(chan_side, index)); - return BasicPort(chan_port_name, 1); /* Every track has a port size of 1 */ -} - -/********************************************************************* - * Generate an input port for routing multiplexer inside the connection block - * which is the middle output of a routing track - ********************************************************************/ -static -BasicPort generate_connection_block_chan_port(const RRGSB& rr_gsb, - const t_rr_type& cb_type, - t_rr_node* chan_rr_node) { - BasicPort input_port; - /* Generate the input port object */ - switch (chan_rr_node->type) { - case CHANX: - case CHANY: { - /* Create port description for the routing track middle output */ - vtr::Point middle_output_port_coord(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type)); - int chan_node_track_id = rr_gsb.get_cb_chan_node_index(cb_type, chan_rr_node); - /* Create a port description for the middle output */ - std::string middle_output_port_name = generate_routing_track_middle_output_port_name(cb_type, middle_output_port_coord, chan_node_track_id); - input_port.set_name(middle_output_port_name); - input_port.set_width(1); - break; - } - default: /* OPIN, SOURCE, IPIN, SINK are invalid*/ - vpr_printf(TIO_MESSAGE_ERROR, - "(File:%s, [LINE%d])Invalid rr_node type! Should be [OPIN|CHANX|CHANY].\n", - __FILE__, __LINE__); - exit(1); - } - - return input_port; -} - - -/********************************************************************* - * Generate an input port for routing multiplexer inside the switch block - * In addition to give the Routing Resource node of the input - * Users should provide the side of input, which is different case by case: - * 1. When the input is a pin of a CLB/Logic Block, the input_side should - * be the side of the node on its grid! - * For example, the input pin is on the top side of a switch block - * but on the right side of a switch block - * +--------+ - * | | - * | Grid |---+ - * | | | - * +--------+ v input_pin - * +----------------+ - * | Switch Block | - * +----------------+ - * 2. When the input is a routing track, the input_side should be - * the side of the node locating on the switch block - ********************************************************************/ -static -BasicPort generate_switch_block_input_port(const RRGSB& rr_sb, - const e_side& input_side, - t_rr_node* input_rr_node) { - BasicPort input_port; - /* Generate the input port object */ - switch (input_rr_node->type) { - /* case SOURCE: */ - case OPIN: { - /* Find the coordinator (grid_x and grid_y) for the input port */ - vtr::Point input_port_coord(input_rr_node->xlow, input_rr_node->ylow); - std::string input_port_name = generate_grid_side_port_name(input_port_coord, - input_side, - input_rr_node->ptc_num); - input_port.set_name(input_port_name); - input_port.set_width(1); /* Every grid output has a port size of 1 */ - break; - } - case CHANX: - case CHANY: { - input_port = generate_verilog_unique_switch_box_chan_port(rr_sb, input_side, input_rr_node, IN_PORT); - break; - } - default: /* SOURCE, IPIN, SINK are invalid*/ - vpr_printf(TIO_MESSAGE_ERROR, - "(File:%s, [LINE%d])Invalid rr_node type! Should be [OPIN|CHANX|CHANY].\n", - __FILE__, __LINE__); - exit(1); - } - - return input_port; -} - -/********************************************************************* - * Generate a list of routing track middle output ports - * for routing multiplexer inside the connection block - ********************************************************************/ -static -std::vector generate_connection_block_mux_input_ports(const RRGSB& rr_gsb, - const t_rr_type& cb_type, - const std::vector& input_rr_nodes) { - std::vector input_ports; - - for (auto input_rr_node : input_rr_nodes) { - input_ports.push_back(generate_connection_block_chan_port(rr_gsb, cb_type, input_rr_node)); - } - - return input_ports; -} - - -/********************************************************************* - * Generate a list of input ports for routing multiplexer inside the switch block - ********************************************************************/ -static -std::vector generate_switch_block_input_ports(const RRGSB& rr_sb, - const std::vector& input_rr_nodes) { - std::vector input_ports; - - for (auto input_rr_node : input_rr_nodes) { - enum e_side input_pin_side = NUM_SIDES; - switch (input_rr_node->type) { - case OPIN: - input_pin_side = rr_sb.get_opin_node_grid_side(input_rr_node); - break; - case CHANX: - case CHANY: { - /* The input could be at any side of the switch block, find it */ - int index = -1; - rr_sb.get_node_side_and_index(input_rr_node, IN_PORT, &input_pin_side, &index); - VTR_ASSERT(NUM_SIDES != input_pin_side); - break; - } - default: /* SOURCE, IPIN, SINK are invalid*/ - vpr_printf(TIO_MESSAGE_ERROR, - "(File:%s, [LINE%d])Invalid rr_node type! Should be [OPIN|CHANX|CHANY].\n", - __FILE__, __LINE__); - exit(1); - } - input_ports.push_back(generate_switch_block_input_port(rr_sb, input_pin_side, input_rr_node)); - } - - return input_ports; -} - -/********************************************************************* - * Print a short interconneciton in switch box - * There are two cases should be noticed. - * 1. The actual fan-in of cur_rr_node is 0. In this case, - the cur_rr_node need to be short connected to itself which is on the opposite side of this switch - * 2. The actual fan-in of cur_rr_node is 0. In this case, - * The cur_rr_node need to connected to the drive_rr_node - ********************************************************************/ -static -void print_verilog_unique_switch_box_short_interc(std::fstream& fp, - const RRGSB& rr_sb, - const e_side& chan_side, - t_rr_node* cur_rr_node, - t_rr_node* drive_rr_node) { - /* Check the file handler*/ - check_file_handler(fp); - - /* Find the name of output port */ - BasicPort output_port = generate_verilog_unique_switch_box_chan_port(rr_sb, chan_side, cur_rr_node, OUT_PORT); - enum e_side input_pin_side = chan_side; - - /* Generate the input port object */ - switch (drive_rr_node->type) { - case OPIN: - input_pin_side = rr_sb.get_opin_node_grid_side(drive_rr_node); - break; - case CHANX: - case CHANY: { - /* This should be an input in the data structure of RRGSB */ - if (cur_rr_node == drive_rr_node) { - /* To be strict, the input should locate on the opposite side. - * Use the else part if this may change in some architecture. - */ - Side side_manager(chan_side); - input_pin_side = side_manager.get_opposite(); - } else { - /* The input could be at any side of the switch block, find it */ - int index = -1; - rr_sb.get_node_side_and_index(drive_rr_node, IN_PORT, &input_pin_side, &index); - } - break; - } - default: /* SOURCE, IPIN, SINK are invalid*/ - vpr_printf(TIO_MESSAGE_ERROR, - "(File:%s, [LINE%d])Invalid rr_node type! Should be [OPIN|CHANX|CHANY].\n", - __FILE__, __LINE__); - exit(1); - } - /* Find the name of input port */ - BasicPort input_port = generate_switch_block_input_port(rr_sb, input_pin_side, drive_rr_node); - - /* Print the wire connection in Verilog format */ - print_verilog_comment(fp, std::string("----- Short connection " + output_port.get_name() + " -----")); - print_verilog_wire_connection(fp, output_port, input_port, false); - fp << std::endl; -} - -/********************************************************************* - * Print a Verilog instance of a routing multiplexer as well as - * associated memory modules for a connection inside a switch block - ********************************************************************/ -static -void print_verilog_unique_switch_box_mux(ModuleManager& module_manager, - std::fstream& fp, - t_sram_orgz_info* cur_sram_orgz_info, - BasicPort& config_bus, - BasicPort& fm_config_bus, - const ModuleId& sb_module, - const RRGSB& rr_sb, - const CircuitLibrary& circuit_lib, - const MuxLibrary& mux_lib, - const std::vector& rr_switches, - const e_side& chan_side, - t_rr_node* cur_rr_node, - const std::vector& drive_rr_nodes, - const size_t& switch_index, - const bool& use_explicit_mapping) { - /* Check the file handler*/ - check_file_handler(fp); - - /* Check */ - /* Check current rr_node is CHANX or CHANY*/ - VTR_ASSERT((CHANX == cur_rr_node->type)||(CHANY == cur_rr_node->type)); - - /* Get the circuit model id of the routing multiplexer */ - CircuitModelId mux_model = rr_switches[switch_index].circuit_model; - - /* Find the input size of the implementation of a routing multiplexer */ - size_t datapath_mux_size = drive_rr_nodes.size(); - - /* Get the multiplexing graph from the Mux Library */ - MuxId mux_id = mux_lib.mux_graph(mux_model, datapath_mux_size); - const MuxGraph& mux_graph = mux_lib.mux_graph(mux_id); - - /* Find the module name of the multiplexer and try to find it in the module manager */ - std::string mux_module_name = generate_mux_subckt_name(circuit_lib, mux_model, datapath_mux_size, std::string("")); - ModuleId mux_module = module_manager.find_module(mux_module_name); - VTR_ASSERT (true == module_manager.valid_module_id(mux_module)); - - /* Get the MUX instance id from the module manager */ - size_t mux_instance_id = module_manager.num_instance(sb_module, mux_module); - - /* Print the input bus for the inputs of a multiplexer - * We use the datapath input size (mux_size) to name the bus - * just to following the naming convention when the tool is built - * The bus port size should be the input size of multiplexer implementation - */ - BasicPort inbus_port; - inbus_port.set_name(generate_mux_input_bus_port_name(circuit_lib, mux_model, datapath_mux_size, mux_instance_id)); - inbus_port.set_width(datapath_mux_size); - - /* Generate input ports that are wired to the input bus of the routing multiplexer */ - std::vector mux_input_ports = generate_switch_block_input_ports(rr_sb, drive_rr_nodes); - /* Connect input ports to bus */ - print_verilog_comment(fp, std::string("----- BEGIN A local bus wire for multiplexer inputs -----")); - fp << generate_verilog_local_wire(inbus_port, mux_input_ports) << std::endl; - print_verilog_comment(fp, std::string("----- END A local bus wire for multiplexer inputs -----")); - fp << std::endl; - - /* Find the number of reserved configuration bits for the routing multiplexer */ - size_t mux_num_reserved_config_bits = find_mux_num_reserved_config_bits(circuit_lib, mux_model, mux_graph); - - /* Find the number of configuration bits for the routing multiplexer */ - size_t mux_num_config_bits = find_mux_num_config_bits(circuit_lib, mux_model, mux_graph, cur_sram_orgz_info->type); - - /* Print the configuration bus for the routing multiplexers */ - print_verilog_comment(fp, std::string("----- BEGIN Local wires to group configuration ports -----")); - print_verilog_mux_config_bus(fp, circuit_lib, mux_model, cur_sram_orgz_info->type, - datapath_mux_size, mux_instance_id, - mux_num_reserved_config_bits, mux_num_config_bits); - print_verilog_comment(fp, std::string("----- END Local wires to group configuration ports -----")); - fp << std::endl; - - /* Dump ports visible only during formal verification */ - print_verilog_comment(fp, std::string("----- BEGIN Local wires used in only formal verification purpose -----")); - print_verilog_preprocessing_flag(fp, std::string(verilog_formal_verification_preproc_flag)); - /* Print the SRAM configuration ports for formal verification */ - /* Update config bus for formal verification, - * shift with number of configuration bit of the MUX - */ - fm_config_bus.set_width(fm_config_bus.get_msb() + 1, fm_config_bus.get_msb() + mux_num_config_bits); - - /* Align with the port width of formal verification port of SB module */ - print_verilog_formal_verification_mux_sram_ports_wiring(fp, circuit_lib, mux_model, - datapath_mux_size, mux_instance_id, - mux_num_config_bits, fm_config_bus); - print_verilog_endif(fp); - print_verilog_comment(fp, std::string("----- END Local wires used in only formal verification purpose -----")); - fp << std::endl; - - /* Instanciate the MUX Module */ - /* Create port-to-port map */ - std::map mux_port2port_name_map; - - /* Link input bus port to Switch Block inputs */ - std::vector mux_model_input_ports = circuit_lib.model_ports_by_type(mux_model, SPICE_MODEL_PORT_INPUT, true); - VTR_ASSERT(1 == mux_model_input_ports.size()); - /* Use the port name convention in the circuit library */ - mux_port2port_name_map[circuit_lib.port_lib_name(mux_model_input_ports[0])] = inbus_port; - - /* Link output port to Switch Block outputs */ - std::vector mux_model_output_ports = circuit_lib.model_ports_by_type(mux_model, SPICE_MODEL_PORT_OUTPUT, true); - VTR_ASSERT(1 == mux_model_output_ports.size()); - /* Use the port name convention in the circuit library */ - mux_port2port_name_map[circuit_lib.port_lib_name(mux_model_output_ports[0])] = generate_verilog_unique_switch_box_chan_port(rr_sb, chan_side, cur_rr_node, OUT_PORT); - - /* Link SRAM port to different configuraton port for the routing multiplexer - * Different design technology requires different configuration bus! - */ - std::vector mux_model_sram_ports = circuit_lib.model_ports_by_type(mux_model, SPICE_MODEL_PORT_SRAM, true); - VTR_ASSERT( 1 == mux_model_sram_ports.size() ); - /* For the regular SRAM port, module port use the same name */ - std::string mux_module_sram_port_name = circuit_lib.port_lib_name(mux_model_sram_ports[0]); - BasicPort mux_config_port(generate_mux_sram_port_name(circuit_lib, mux_model, datapath_mux_size, mux_instance_id, SPICE_MODEL_PORT_INPUT), - mux_num_config_bits); - mux_port2port_name_map[mux_module_sram_port_name] = mux_config_port; - - /* For the inverted SRAM port */ - std::string mux_module_sram_inv_port_name = circuit_lib.port_lib_name(mux_model_sram_ports[0]) + std::string("_inv"); - BasicPort mux_config_inv_port(generate_mux_sram_port_name(circuit_lib, mux_model, datapath_mux_size, mux_instance_id, SPICE_MODEL_PORT_OUTPUT), - mux_num_config_bits); - mux_port2port_name_map[mux_module_sram_inv_port_name] = mux_config_inv_port; - - /* Print an instance of the MUX Module */ - print_verilog_comment(fp, std::string("----- BEGIN Instanciation of a routing multiplexer -----")); - print_verilog_module_instance(fp, module_manager, sb_module, mux_module, mux_port2port_name_map, use_explicit_mapping); - print_verilog_comment(fp, std::string("----- END Instanciation of a routing multiplexer -----")); - 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(sb_module, mux_module); - - /* Instanciate memory modules */ - /* Find the name and module id of the memory module */ - std::string mem_module_name = generate_mux_subckt_name(circuit_lib, mux_model, datapath_mux_size, std::string(verilog_mem_posfix)); - ModuleId mem_module = module_manager.find_module(mem_module_name); - VTR_ASSERT (true == module_manager.valid_module_id(mem_module)); - - /* Create port-to-port map */ - std::map mem_port2port_name_map; - - /* TODO: Make the port2port map generation more generic!!! */ - /* Link the SRAM ports of the routing multiplexer to the memory module */ - std::vector mem_output_ports; - mem_output_ports.push_back(mux_config_port); - mem_output_ports.push_back(mux_config_inv_port); - mem_port2port_name_map = generate_mem_module_port2port_map(config_bus, - mem_output_ports, - circuit_lib.design_tech_type(mux_model), - cur_sram_orgz_info->type); - /* Update the config bus for the module */ - update_mem_module_config_bus(cur_sram_orgz_info->type, - circuit_lib.design_tech_type(mux_model), - mux_num_config_bits, - config_bus); - - /* Print an instance of the memory module associated with the routing multiplexer */ - print_verilog_comment(fp, std::string("----- BEGIN Instanciation of memory cells for a routing multiplexer -----")); - print_verilog_module_instance(fp, module_manager, sb_module, mem_module, mem_port2port_name_map, use_explicit_mapping); - print_verilog_comment(fp, std::string("----- END Instanciation of memory cells for a routing multiplexer -----")); - 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(sb_module, mem_module); - - /* Create the path of the input of multiplexer in the hierarchy - * TODO: this MUST be deprecated later because module manager is created to handle these problems!!! - */ - std::string mux_input_hie_path = std::string(rr_sb.gen_sb_verilog_instance_name()) + std::string("/") - + mux_module_name + std::string("_") - + std::to_string(mux_instance_id) + std::string("_/in"); - cur_rr_node->name_mux = my_strdup(mux_input_hie_path.c_str()); -} - - -/********************************************************************* - * Print the Verilog modules for a interconnection inside switch block - * The interconnection could be either a wire or a routing multiplexer, - * which depends on the fan-in of the rr_nodes in the switch block - ********************************************************************/ -static -void print_verilog_unique_switch_box_interc(ModuleManager& module_manager, - std::fstream& fp, - t_sram_orgz_info* cur_sram_orgz_info, - BasicPort& config_bus, - BasicPort& fm_config_bus, - const ModuleId& sb_module, - const RRGSB& rr_sb, - const CircuitLibrary& circuit_lib, - const MuxLibrary& mux_lib, - const std::vector& rr_switches, - const e_side& chan_side, - const size_t& chan_node_id, - const bool& use_explicit_mapping) { - std::vector drive_rr_nodes; - - /* Get the node */ - t_rr_node* cur_rr_node = rr_sb.get_chan_node(chan_side, chan_node_id); - - /* Determine if the interc lies inside a channel wire, that is interc between segments */ - if (false == rr_sb.is_sb_node_passing_wire(chan_side, chan_node_id)) { - for (int i = 0; i < cur_rr_node->num_drive_rr_nodes; ++i) { - drive_rr_nodes.push_back(cur_rr_node->drive_rr_nodes[i]); - } - /* Special: if there are zero-driver nodes. We skip here */ - if (0 == drive_rr_nodes.size()) { - return; - } - } - - if (0 == drive_rr_nodes.size()) { - /* Print a special direct connection*/ - print_verilog_unique_switch_box_short_interc(fp, rr_sb, chan_side, cur_rr_node, - cur_rr_node); - } else if (1 == drive_rr_nodes.size()) { - /* Print a direct connection*/ - print_verilog_unique_switch_box_short_interc(fp, rr_sb, chan_side, cur_rr_node, - drive_rr_nodes[DEFAULT_SWITCH_ID]); - } else if (1 < drive_rr_nodes.size()) { - /* Print the multiplexer, fan_in >= 2 */ - print_verilog_unique_switch_box_mux(module_manager, fp, cur_sram_orgz_info, - config_bus, fm_config_bus, - sb_module, rr_sb, circuit_lib, mux_lib, - rr_switches, chan_side, cur_rr_node, - drive_rr_nodes, - cur_rr_node->drive_switches[DEFAULT_SWITCH_ID], - use_explicit_mapping); - } /*Nothing should be done else*/ -} - -/********************************************************************* - * Generate the Verilog module for a Switch Box. - * A Switch Box module consists of following ports: - * 1. Channel Y [x][y] inputs - * 2. Channel X [x+1][y] inputs - * 3. Channel Y [x][y-1] outputs - * 4. Channel X [x][y] outputs - * 5. Grid[x][y+1] Right side outputs pins - * 6. Grid[x+1][y+1] Left side output pins - * 7. Grid[x+1][y+1] Bottom side output pins - * 8. Grid[x+1][y] Top side output pins - * 9. Grid[x+1][y] Left side output pins - * 10. Grid[x][y] Right side output pins - * 11. Grid[x][y] Top side output pins - * 12. Grid[x][y+1] Bottom side output pins - * - * Location of a Switch Box in FPGA fabric: - * - * -------------- -------------- - * | | | | - * | Grid | ChanY | Grid | - * | [x][y+1] | [x][y+1] | [x+1][y+1] | - * | | | | - * -------------- -------------- - * ---------- - * ChanX | Switch | ChanX - * [x][y] | Box | [x+1][y] - * | [x][y] | - * ---------- - * -------------- -------------- - * | | | | - * | Grid | ChanY | Grid | - * | [x][y] | [x][y] | [x+1][y] | - * | | | | - * -------------- -------------- - * - * Switch Block pin location map - * - * Grid[x][y+1] ChanY[x][y+1] Grid[x+1][y+1] - * right_pins inputs/outputs left_pins - * | ^ | - * | | | - * v v v - * +-----------------------------------------------+ - * | | - * Grid[x][y+1] | | Grid[x+1][y+1] - * bottom_pins---->| |<---- bottom_pins - * | | - * ChanX[x][y] | Switch Box [x][y] | ChanX[x+1][y] - * inputs/outputs<--->| |<---> inputs/outputs - * | | - * Grid[x][y+1] | | Grid[x+1][y+1] - * top_pins---->| |<---- top_pins - * | | - * +-----------------------------------------------+ - * ^ ^ ^ - * | | | - * | v | - * Grid[x][y] ChanY[x][y] Grid[x+1][y] - * right_pins inputs/outputs left_pins - * - * - ********************************************************************/ -static -void print_verilog_routing_switch_box_unique_module(ModuleManager& module_manager, - const CircuitLibrary& circuit_lib, - const MuxLibrary& mux_lib, - const std::vector& rr_switches, - t_sram_orgz_info* cur_sram_orgz_info, - const std::string& verilog_dir, - const std::string& subckt_dir, - const RRGSB& rr_sb, - const bool& is_explicit_mapping) { - /* TODO: move this part to another function where we count the conf bits for all the switch blocks !!!*/ - /* Count the number of configuration bits to be consumed by this Switch block */ - int num_conf_bits = find_switch_block_num_conf_bits(cur_sram_orgz_info, circuit_lib, mux_lib, rr_switches, rr_sb); - /* Count the number of reserved configuration bits to be consumed by this Switch block */ - int num_reserved_conf_bits = find_switch_block_num_shared_conf_bits(cur_sram_orgz_info, circuit_lib, mux_lib, rr_switches, rr_sb); - /* Estimate the sram_verilog_model->cnt */ - int cur_num_sram = get_sram_orgz_info_num_mem_bit(cur_sram_orgz_info); - RRGSB rr_gsb = rr_sb; /* IMPORTANT: this copy will be removed when the config ports are initialized when created!!! */ - rr_gsb.set_sb_num_reserved_conf_bits(size_t(num_reserved_conf_bits)); - rr_gsb.set_sb_conf_bits_lsb(size_t(cur_num_sram)); - rr_gsb.set_sb_conf_bits_msb(size_t(cur_num_sram + num_conf_bits - 1)); - /* Create the netlist */ - vtr::Point gsb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y()); - std::string verilog_fname(subckt_dir + generate_routing_block_netlist_name(sb_verilog_file_name_prefix, gsb_coordinate, std::string(verilog_netlist_file_postfix))); - /* TODO: remove the bak file when the file is ready */ - verilog_fname += ".bak"; - - /* Create the file stream */ - std::fstream fp; - fp.open(verilog_fname, std::fstream::out | std::fstream::trunc); - - check_file_handler(fp); - - print_verilog_file_header(fp, std::string("Verilog modules for Unique Switch Blocks[" + std::to_string(rr_gsb.get_sb_x()) + "]["+ std::to_string(rr_gsb.get_sb_y()) + "]")); - - /* Print preprocessing flags */ - print_verilog_include_defines_preproc_file(fp, verilog_dir); - - /* Create a Verilog Module based on the circuit model, and add to module manager */ - ModuleId module_id = module_manager.add_module(generate_switch_block_module_name(gsb_coordinate)); - - /* Add ports to the module */ - /* Global ports: - * In the circuit_library, find all the circuit models that may be included in the Switch Block - * Collect the global ports from the circuit_models and merge with the same name - */ - std::vector global_ports = find_switch_block_global_ports(rr_gsb, circuit_lib, rr_switches); - for (const auto& port : global_ports) { - BasicPort module_port(circuit_lib.port_lib_name(port), circuit_lib.port_size(port)); - module_manager.add_port(module_id, module_port, ModuleManager::MODULE_GLOBAL_PORT); - } - /* Add routing channel ports at each side of the GSB */ - for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { - Side side_manager(side); - DeviceCoordinator port_coordinator = rr_gsb.get_side_block_coordinator(side_manager.get_side()); - - for (size_t itrack = 0; itrack < rr_gsb.get_chan_width(side_manager.get_side()); ++itrack) { - vtr::Point port_coord(port_coordinator.get_x(), port_coordinator.get_y()); - std::string port_name = generate_routing_track_port_name(rr_gsb.get_chan_node(side_manager.get_side(), itrack)->type, - port_coord, itrack, - rr_gsb.get_chan_node_direction(side_manager.get_side(), itrack)); - BasicPort module_port(port_name, 1); /* Every track has a port size of 1 */ - - switch (rr_gsb.get_chan_node_direction(side_manager.get_side(), itrack)) { - case OUT_PORT: - module_manager.add_port(module_id, module_port, ModuleManager::MODULE_OUTPUT_PORT); - break; - case IN_PORT: - module_manager.add_port(module_id, module_port, ModuleManager::MODULE_INPUT_PORT); - break; - default: - vpr_printf(TIO_MESSAGE_ERROR, - "(File: %s [LINE%d]) Invalid direction of chan[%d][%d]_track[%d]!\n", - __FILE__, __LINE__, rr_gsb.get_sb_x(), rr_gsb.get_sb_y(), itrack); - exit(1); - } - } - /* Dump OPINs of adjacent CLBs */ - for (size_t inode = 0; inode < rr_gsb.get_num_opin_nodes(side_manager.get_side()); ++inode) { - vtr::Point port_coord(rr_gsb.get_opin_node(side_manager.get_side(), inode)->xlow, - rr_gsb.get_opin_node(side_manager.get_side(), inode)->ylow); - std::string port_name = generate_grid_side_port_name(port_coord, - rr_gsb.get_opin_node_grid_side(side_manager.get_side(), inode), - rr_gsb.get_opin_node(side_manager.get_side(), inode)->ptc_num); - BasicPort module_port(port_name, 1); /* Every grid output has a port size of 1 */ - /* Grid outputs are inputs of switch blocks */ - module_manager.add_port(module_id, module_port, ModuleManager::MODULE_INPUT_PORT); - } - } - - /* Add configuration ports */ - /* Reserved sram ports */ - if (0 < rr_gsb.get_sb_num_reserved_conf_bits()) { - /* Check: this SRAM organization type must be memory-bank ! */ - VTR_ASSERT( SPICE_SRAM_MEMORY_BANK == cur_sram_orgz_info->type ); - /* Generate a list of ports */ - add_reserved_sram_ports_to_module_manager(module_manager, module_id, - rr_gsb.get_sb_num_reserved_conf_bits()); - } - - /* TODO: this should be added to the cur_sram_orgz_info !!! */ - t_spice_model* mem_model = NULL; - get_sram_orgz_info_mem_model(cur_sram_orgz_info, & mem_model); - CircuitModelId sram_model = circuit_lib.model(mem_model->name); - VTR_ASSERT(CircuitModelId::INVALID() != sram_model); - - /* Normal sram ports */ - if (0 < rr_gsb.get_sb_num_conf_bits()) { - add_sram_ports_to_module_manager(module_manager, module_id, - circuit_lib, sram_model, cur_sram_orgz_info->type, - rr_gsb.get_sb_num_conf_bits()); - /* Add ports only visible during formal verification to the module */ - add_formal_verification_sram_ports_to_module_manager(module_manager, module_id, circuit_lib, sram_model, - std::string(verilog_formal_verification_preproc_flag), - rr_gsb.get_sb_num_conf_bits()); - } - - /* Print module definition + ports */ - print_verilog_module_declaration(fp, module_manager, module_id); - /* Finish printing ports */ - - print_verilog_comment(fp, std::string("---- BEGIN local wires for SRAM data ports ----")); - /* Local wires for memory configurations */ - print_verilog_switch_block_local_sram_wires(fp, rr_gsb, circuit_lib, sram_model, cur_sram_orgz_info->type, - rr_gsb.get_sb_num_conf_bits()); - print_verilog_comment(fp, std::string("---- END local wires for SRAM data ports ----")); - - /* Create a counter for the configuration bus */ - BasicPort config_bus; - /* Counter start from 0 */ - config_bus.set_width(0, 0); - - /* Create a counter for the configuration bus used for formal verification */ - BasicPort fm_config_bus; - /* fm_config_bus has an invalid width here. It is designed to be easy to rotate */ - fm_config_bus.set_width(0, -1); - - /* TODO: Print routing multiplexers */ - for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { - Side side_manager(side); - print_verilog_comment(fp, std::string("----- " + side_manager.to_string() + " side Routing Multiplexers -----")); - for (size_t itrack = 0; itrack < rr_gsb.get_chan_width(side_manager.get_side()); ++itrack) { - /* We care INC_DIRECTION tracks at this side*/ - if (OUT_PORT == rr_gsb.get_chan_node_direction(side_manager.get_side(), itrack)) { - print_verilog_unique_switch_box_interc(module_manager, fp, cur_sram_orgz_info, - config_bus, fm_config_bus, - module_id, rr_sb, - circuit_lib, mux_lib, rr_switches, - side_manager.get_side(), - itrack, is_explicit_mapping); - } - } - } - - /* Add check code for config_bus. - * The MSB should match the number of configuration bits!!! - */ - VTR_ASSERT(true == check_switch_block_mem_config_bus(cur_sram_orgz_info->type, - rr_gsb, config_bus, - rr_gsb.get_sb_num_conf_bits())); - VTR_ASSERT(fm_config_bus.get_msb() == rr_gsb.get_sb_num_conf_bits() - 1); - - /* Put an end to the Verilog module */ - print_verilog_module_end(fp, module_manager.module_name(module_id)); - - /* Add an empty line as a splitter */ - fp << std::endl; - - /* Close file handler */ - fp.close(); - - /* Add fname to the linked list */ - /* - routing_verilog_subckt_file_path_head = add_one_subckt_file_name_to_llist(routing_verilog_subckt_file_path_head, verilog_fname.c_str()); - */ - - return; -} - /* Task: Print the subckt of a Switch Box. * A Switch Box subckt consists of following ports: * 1. Channel Y [x][y] inputs @@ -3440,46 +2422,6 @@ int count_verilog_connection_box_one_side_reserved_conf_bits(t_sram_orgz_info* c return num_reserved_conf_bits; } -/********************************************************************* - * Print a short interconneciton in connection - ********************************************************************/ -static -void print_verilog_connection_box_short_interc(std::fstream& fp, - const RRGSB& rr_gsb, - const t_rr_type& cb_type, - t_rr_node* src_rr_node) { - /* Check the file handler*/ - check_file_handler(fp); - - /* Ensure we have only one 1 driver node */ - VTR_ASSERT_SAFE(1 == src_rr_node->fan_in); - - /* Find the driver node */ - t_rr_node* drive_rr_node = src_rr_node->drive_rr_nodes[0]; - - /* We have OPINs since we may have direct connections: - * These connections should be handled by other functions in the compact_netlist.c - * So we just return here for OPINs - */ - if (OPIN == drive_rr_node->type) { - return; - } - - VTR_ASSERT((CHANX == drive_rr_node->type) || (CHANY == drive_rr_node->type)); - - /* Create port description for the routing track middle output */ - BasicPort middle_output_port = generate_connection_block_chan_port(rr_gsb, cb_type, drive_rr_node); - - /* Create port description for input pin of a CLB */ - BasicPort input_port = generate_verilog_connection_box_ipin_port(rr_gsb, src_rr_node); - - /* Print the wire connection */ - print_verilog_wire_connection(fp, input_port, middle_output_port, false); - - return; -} - - /* SRC rr_node is the IPIN of a grid.*/ static void dump_verilog_connection_box_short_interc(FILE* fp, @@ -3630,191 +2572,6 @@ void dump_verilog_connection_box_short_interc(FILE* fp, return; } -/********************************************************************* - * Print a Verilog instance of a routing multiplexer as well as - * associated memory modules for a connection inside a connection block - ********************************************************************/ -static -void print_verilog_connection_box_mux(ModuleManager& module_manager, - std::fstream& fp, - t_sram_orgz_info* cur_sram_orgz_info, - BasicPort& config_bus, - BasicPort& fm_config_bus, - const ModuleId& cb_module, - const RRGSB& rr_gsb, - const t_rr_type& cb_type, - const CircuitLibrary& circuit_lib, - const MuxLibrary& mux_lib, - const std::vector& rr_switches, - t_rr_node* cur_rr_node, - const bool& use_explicit_mapping) { - /* Check the file handler*/ - check_file_handler(fp); - - /* Check */ - /* Check current rr_node is an input pin of a CLB */ - VTR_ASSERT(IPIN == cur_rr_node->type); - - /* Build a vector of driver rr_nodes */ - std::vector drive_rr_nodes; - for (int inode = 0; inode < cur_rr_node->num_drive_rr_nodes; inode++) { - drive_rr_nodes.push_back(cur_rr_node->drive_rr_nodes[inode]); - } - - int switch_index = cur_rr_node->drive_switches[DEFAULT_SWITCH_ID]; - - /* Get the circuit model id of the routing multiplexer */ - CircuitModelId mux_model = rr_switches[switch_index].circuit_model; - - /* Find the input size of the implementation of a routing multiplexer */ - size_t datapath_mux_size = drive_rr_nodes.size(); - - /* Get the multiplexing graph from the Mux Library */ - MuxId mux_id = mux_lib.mux_graph(mux_model, datapath_mux_size); - const MuxGraph& mux_graph = mux_lib.mux_graph(mux_id); - - /* Find the module name of the multiplexer and try to find it in the module manager */ - std::string mux_module_name = generate_mux_subckt_name(circuit_lib, mux_model, datapath_mux_size, std::string("")); - ModuleId mux_module = module_manager.find_module(mux_module_name); - VTR_ASSERT (true == module_manager.valid_module_id(mux_module)); - - /* Get the MUX instance id from the module manager */ - size_t mux_instance_id = module_manager.num_instance(cb_module, mux_module); - - /* Print the input bus for the inputs of a multiplexer - * We use the datapath input size (mux_size) to name the bus - * just to following the naming convention when the tool is built - * The bus port size should be the input size of multiplexer implementation - */ - BasicPort inbus_port; - inbus_port.set_name(generate_mux_input_bus_port_name(circuit_lib, mux_model, datapath_mux_size, mux_instance_id)); - inbus_port.set_width(datapath_mux_size); - - /* TODO: Generate input ports that are wired to the input bus of the routing multiplexer */ - std::vector mux_input_ports = generate_connection_block_mux_input_ports(rr_gsb, cb_type, drive_rr_nodes); - /* Connect input ports to bus */ - print_verilog_comment(fp, std::string("----- BEGIN A local bus wire for multiplexer inputs -----")); - fp << generate_verilog_local_wire(inbus_port, mux_input_ports) << std::endl; - print_verilog_comment(fp, std::string("----- END A local bus wire for multiplexer inputs -----")); - fp << std::endl; - - /* Find the number of reserved configuration bits for the routing multiplexer */ - size_t mux_num_reserved_config_bits = find_mux_num_reserved_config_bits(circuit_lib, mux_model, mux_graph); - - /* Find the number of configuration bits for the routing multiplexer */ - size_t mux_num_config_bits = find_mux_num_config_bits(circuit_lib, mux_model, mux_graph, cur_sram_orgz_info->type); - - /* Print the configuration bus for the routing multiplexers */ - print_verilog_comment(fp, std::string("----- BEGIN Local wires to group configuration ports -----")); - print_verilog_mux_config_bus(fp, circuit_lib, mux_model, cur_sram_orgz_info->type, - datapath_mux_size, mux_instance_id, - mux_num_reserved_config_bits, mux_num_config_bits); - print_verilog_comment(fp, std::string("----- END Local wires to group configuration ports -----")); - fp << std::endl; - - /* Dump ports visible only during formal verification */ - print_verilog_comment(fp, std::string("----- BEGIN Local wires used in only formal verification purpose -----")); - print_verilog_preprocessing_flag(fp, std::string(verilog_formal_verification_preproc_flag)); - /* Print the SRAM configuration ports for formal verification */ - /* Update config bus for formal verification, - * shift with number of configuration bit of the MUX - */ - fm_config_bus.set_width(fm_config_bus.get_msb() + 1, fm_config_bus.get_msb() + mux_num_config_bits); - /* Align with the port width of formal verification port of SB module */ - print_verilog_formal_verification_mux_sram_ports_wiring(fp, circuit_lib, mux_model, - datapath_mux_size, mux_instance_id, - mux_num_config_bits, fm_config_bus); - print_verilog_endif(fp); - print_verilog_comment(fp, std::string("----- END Local wires used in only formal verification purpose -----")); - fp << std::endl; - - /* Instanciate the MUX Module */ - /* Create port-to-port map */ - std::map mux_port2port_name_map; - - /* Link input bus port to routing track middle outputs */ - std::vector mux_model_input_ports = circuit_lib.model_ports_by_type(mux_model, SPICE_MODEL_PORT_INPUT, true); - VTR_ASSERT(1 == mux_model_input_ports.size()); - /* Use the port name convention in the circuit library */ - mux_port2port_name_map[circuit_lib.port_lib_name(mux_model_input_ports[0])] = inbus_port; - - /* Link output port to Connection Block output: src_rr_node */ - std::vector mux_model_output_ports = circuit_lib.model_ports_by_type(mux_model, SPICE_MODEL_PORT_OUTPUT, true); - VTR_ASSERT(1 == mux_model_output_ports.size()); - /* Use the port name convention in the circuit library */ - mux_port2port_name_map[circuit_lib.port_lib_name(mux_model_output_ports[0])] = generate_verilog_connection_box_ipin_port(rr_gsb, cur_rr_node); - - /* Link SRAM port to different configuraton port for the routing multiplexer - * Different design technology requires different configuration bus! - */ - std::vector mux_model_sram_ports = circuit_lib.model_ports_by_type(mux_model, SPICE_MODEL_PORT_SRAM, true); - VTR_ASSERT( 1 == mux_model_sram_ports.size() ); - /* For the regular SRAM port, module port use the same name */ - std::string mux_module_sram_port_name = circuit_lib.port_lib_name(mux_model_sram_ports[0]); - BasicPort mux_config_port(generate_mux_sram_port_name(circuit_lib, mux_model, datapath_mux_size, mux_instance_id, SPICE_MODEL_PORT_INPUT), - mux_num_config_bits); - mux_port2port_name_map[mux_module_sram_port_name] = mux_config_port; - - /* For the inverted SRAM port */ - std::string mux_module_sram_inv_port_name = circuit_lib.port_lib_name(mux_model_sram_ports[0]) + std::string("_inv"); - BasicPort mux_config_inv_port(generate_mux_sram_port_name(circuit_lib, mux_model, datapath_mux_size, mux_instance_id, SPICE_MODEL_PORT_OUTPUT), - mux_num_config_bits); - mux_port2port_name_map[mux_module_sram_inv_port_name] = mux_config_inv_port; - - /* Print an instance of the MUX Module */ - print_verilog_comment(fp, std::string("----- BEGIN Instanciation of a routing multiplexer -----")); - print_verilog_module_instance(fp, module_manager, cb_module, mux_module, mux_port2port_name_map, use_explicit_mapping); - print_verilog_comment(fp, std::string("----- END Instanciation of a routing multiplexer -----")); - 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(cb_module, mux_module); - - /* Instanciate memory modules */ - /* Find the name and module id of the memory module */ - std::string mem_module_name = generate_mux_subckt_name(circuit_lib, mux_model, datapath_mux_size, std::string(verilog_mem_posfix)); - ModuleId mem_module = module_manager.find_module(mem_module_name); - VTR_ASSERT (true == module_manager.valid_module_id(mem_module)); - - /* Create port-to-port map */ - std::map mem_port2port_name_map; - - /* TODO: Make the port2port map generation more generic!!! */ - /* Link the SRAM ports of the routing multiplexer to the memory module */ - std::vector mem_output_ports; - mem_output_ports.push_back(mux_config_port); - mem_output_ports.push_back(mux_config_inv_port); - mem_port2port_name_map = generate_mem_module_port2port_map(config_bus, - mem_output_ports, - circuit_lib.design_tech_type(mux_model), - cur_sram_orgz_info->type); - /* Update the config bus for the module */ - update_mem_module_config_bus(cur_sram_orgz_info->type, - circuit_lib.design_tech_type(mux_model), - mux_num_config_bits, - config_bus); - - /* Print an instance of the memory module associated with the routing multiplexer */ - print_verilog_comment(fp, std::string("----- BEGIN Instanciation of memory cells for a routing multiplexer -----")); - print_verilog_module_instance(fp, module_manager, cb_module, mem_module, mem_port2port_name_map, use_explicit_mapping); - print_verilog_comment(fp, std::string("----- END Instanciation of memory cells for a routing multiplexer -----")); - 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(cb_module, mem_module); - - /* Create the path of the input of multiplexer in the hierarchy - * TODO: this MUST be deprecated later because module manager is created to handle these problems!!! - */ - std::string mux_input_hie_path = std::string(rr_gsb.gen_cb_verilog_instance_name(cb_type)) + std::string("/") - + mux_module_name + std::string("_") - + std::to_string(mux_instance_id) + std::string("_/in"); - cur_rr_node->name_mux = my_strdup(mux_input_hie_path.c_str()); -} - - static void dump_verilog_connection_box_mux(t_sram_orgz_info* cur_sram_orgz_info, FILE* fp, @@ -4307,45 +3064,6 @@ void dump_verilog_connection_box_mux(t_sram_orgz_info* cur_sram_orgz_info, return; } -/******************************************************************** - * Print internal connections of a connection block - * For a IPIN node that is driven by only 1 fan-in, - * a short wire will be created - * For a IPIN node that is driven by more than two fan-ins, - * a routing multiplexer will be instanciated - ********************************************************************/ -static -void print_verilog_connection_box_interc(ModuleManager& module_manager, - std::fstream& fp, - t_sram_orgz_info* cur_sram_orgz_info, - BasicPort& config_bus, - BasicPort& fm_config_bus, - const ModuleId& cb_module, - const RRGSB& rr_gsb, - const t_rr_type& cb_type, - const CircuitLibrary& circuit_lib, - const MuxLibrary& mux_lib, - const std::vector& rr_switches, - t_rr_node* src_rr_node, - const bool& use_explicit_mapping) { - if (1 > src_rr_node->fan_in) { - return; /* This port has no driver, skip it */ - } else if (1 == src_rr_node->fan_in) { - /* Print a direct connection */ - print_verilog_connection_box_short_interc(fp, rr_gsb, cb_type, src_rr_node); - - } else if (1 < src_rr_node->fan_in) { - /* Print the multiplexer, fan_in >= 2 */ - print_verilog_connection_box_mux(module_manager, fp, cur_sram_orgz_info, - config_bus, fm_config_bus, - cb_module, rr_gsb, cb_type, - circuit_lib, mux_lib, rr_switches, - src_rr_node, use_explicit_mapping); - } /*Nothing should be done else*/ - - return; -} - static void dump_verilog_connection_box_interc(t_sram_orgz_info* cur_sram_orgz_info, FILE* fp, @@ -4493,294 +3211,6 @@ int count_verilog_connection_box_reserved_conf_bits(t_sram_orgz_info* cur_sram_o return num_reserved_conf_bits; } -/******************************************************************** - * Print the sub-circuit of a connection Box (Type: [CHANX|CHANY]) - * Actually it is very similiar to switch box but - * the difference is connection boxes connect Grid INPUT Pins to channels - * NOTE: direct connection between CLBs should NOT be included inside this - * module! They should be added in the top-level module as their connection - * is not limited to adjacent CLBs!!! - * - * Location of a X- and Y-direction Connection Block in FPGA fabric - * +------------+ +-------------+ - * | |------>| | - * | CLB |<------| Y-direction | - * | | ... | Connection | - * | |------>| Block | - * +------------+ +-------------+ - * | ^ ... | | ^ ... | - * v | v v | v - * +-------------------+ +-------------+ - * --->| |--->| | - * <---| X-direction |<---| Switch | - * ...| Connection block |... | Block | - * --->| |--->| | - * +-------------------+ +-------------+ - * - * Internal structure: - * This is an example of a X-direction connection block - * Note that middle output ports are shorted wire from inputs of routing tracks, - * which are also the inputs of routing multiplexer of the connection block - * - * CLB Input Pins - * (IPINs) - * ^ ^ ^ - * | | ... | - * +--------------------------+ - * | ^ ^ ^ | - * | | | ... | | - * | +--------------------+ | - * | | routing | | - * | | multiplexers | | - * | +--------------------+ | - * | middle outputs | - * | of routing channel | - * | ^ ^ ^ ^ ^ ^ ^ ^ | - * | | | | | ... | | | | | - * in[0] -->|------------------------->|---> out[0] - * out[1] <--|<-------------------------|<--- in[1] - * | ... | - * in[W-2] -->|------------------------->|---> out[W-2] - * out[W-1] <--|<-------------------------|<--- in[W-1] - * +--------------------------+ - * - * W: routing channel width - * - ********************************************************************/ -static -void print_verilog_routing_connection_box_unique_module(ModuleManager& module_manager, - const CircuitLibrary& circuit_lib, - const MuxLibrary& mux_lib, - const std::vector& rr_switches, - t_sram_orgz_info* cur_sram_orgz_info, - const std::string& verilog_dir, - const std::string& subckt_dir, - const RRGSB& rr_cb, - const t_rr_type& cb_type, - const bool& use_explicit_mapping) { - RRGSB rr_gsb = rr_cb; /* IMPORTANT: this copy will be removed when the config ports are initialized when created!!! */ - - /* TODO: These should be done when initializing the tool */ - /* Count the number of configuration bits to be consumed by this Switch block */ - int num_conf_bits = (int)find_connection_block_num_conf_bits(cur_sram_orgz_info, circuit_lib, mux_lib, rr_switches, rr_gsb, cb_type); - /* Count the number of reserved configuration bits to be consumed by this Switch block */ - int num_reserved_conf_bits = (int)find_connection_block_num_shared_conf_bits(cur_sram_orgz_info, circuit_lib, mux_lib, rr_switches, rr_gsb, cb_type); - /* Estimate the sram_verilog_model->cnt */ - int cur_num_sram = get_sram_orgz_info_num_mem_bit(cur_sram_orgz_info); - /* Record index */ - rr_gsb.set_cb_num_reserved_conf_bits(cb_type, num_reserved_conf_bits); - rr_gsb.set_cb_conf_bits_lsb(cb_type, cur_num_sram); - rr_gsb.set_cb_conf_bits_msb(cb_type, cur_num_sram + num_conf_bits - 1); - - /* Create the netlist */ - vtr::Point gsb_coordinate(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type)); - std::string verilog_fname(subckt_dir + generate_connection_block_netlist_name(cb_type, gsb_coordinate, std::string(verilog_netlist_file_postfix))); - /* TODO: remove the bak file when the file is ready */ - verilog_fname += ".bak"; - - /* Create the file stream */ - std::fstream fp; - fp.open(verilog_fname, std::fstream::out | std::fstream::trunc); - - check_file_handler(fp); - - print_verilog_file_header(fp, std::string("Verilog modules for Unique Connection Blocks[" + std::to_string(rr_gsb.get_cb_x(cb_type)) + "]["+ std::to_string(rr_gsb.get_cb_y(cb_type)) + "]")); - - /* Print preprocessing flags */ - print_verilog_include_defines_preproc_file(fp, verilog_dir); - - /* Create a Verilog Module based on the circuit model, and add to module manager */ - ModuleId module_id = module_manager.add_module(generate_connection_block_module_name(cb_type, gsb_coordinate)); - - /* Add ports to the module */ - /* Global ports: - * In the circuit_library, find all the circuit models that may be included in the Connection Block - * Collect the global ports from the circuit_models and merge with the same name - */ - std::vector global_ports = find_connection_block_global_ports(rr_gsb, cb_type, circuit_lib, rr_switches); - for (const auto& port : global_ports) { - BasicPort module_port(circuit_lib.port_lib_name(port), circuit_lib.port_size(port)); - module_manager.add_port(module_id, module_port, ModuleManager::MODULE_GLOBAL_PORT); - } - - /* Add the input and output ports of routing tracks in the channel - * Routing tracks pass through the connection blocks - */ - for (size_t itrack = 0; itrack < rr_gsb.get_cb_chan_width(cb_type); ++itrack) { - vtr::Point port_coord(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type)); - std::string port_name = generate_routing_track_port_name(cb_type, - port_coord, itrack, - IN_PORT); - BasicPort module_port(port_name, 1); /* Every track has a port size of 1 */ - module_manager.add_port(module_id, module_port, ModuleManager::MODULE_INPUT_PORT); - } - for (size_t itrack = 0; itrack < rr_gsb.get_cb_chan_width(cb_type); ++itrack) { - vtr::Point port_coord(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type)); - std::string port_name = generate_routing_track_port_name(cb_type, - port_coord, itrack, - OUT_PORT); - BasicPort module_port(port_name, 1); /* Every track has a port size of 1 */ - module_manager.add_port(module_id, module_port, ModuleManager::MODULE_OUTPUT_PORT); - } - - /* Add the input pins of grids, which are output ports of the connection block */ - std::vector cb_ipin_sides = rr_gsb.get_cb_ipin_sides(cb_type); - for (size_t iside = 0; iside < cb_ipin_sides.size(); ++iside) { - enum e_side cb_ipin_side = cb_ipin_sides[iside]; - for (size_t inode = 0; inode < rr_gsb.get_num_ipin_nodes(cb_ipin_side); ++inode) { - t_rr_node* ipin_node = rr_gsb.get_ipin_node(cb_ipin_side, inode); - vtr::Point port_coord(ipin_node->xlow, ipin_node->ylow); - std::string port_name = generate_grid_side_port_name(port_coord, - rr_gsb.get_ipin_node_grid_side(cb_ipin_side, inode), - ipin_node->ptc_num); - BasicPort module_port(port_name, 1); /* Every grid output has a port size of 1 */ - /* Grid outputs are inputs of switch blocks */ - module_manager.add_port(module_id, module_port, ModuleManager::MODULE_OUTPUT_PORT); - } - } - - /* Add configuration ports */ - /* Reserved sram ports */ - if (0 < rr_gsb.get_cb_num_reserved_conf_bits(cb_type)) { - /* Check: this SRAM organization type must be memory-bank ! */ - VTR_ASSERT( SPICE_SRAM_MEMORY_BANK == cur_sram_orgz_info->type ); - /* Generate a list of ports */ - add_reserved_sram_ports_to_module_manager(module_manager, module_id, - rr_gsb.get_cb_num_reserved_conf_bits(cb_type)); - } - - /* TODO: this should be added to the cur_sram_orgz_info !!! */ - t_spice_model* mem_model = NULL; - get_sram_orgz_info_mem_model(cur_sram_orgz_info, & mem_model); - CircuitModelId sram_model = circuit_lib.model(mem_model->name); - VTR_ASSERT(CircuitModelId::INVALID() != sram_model); - - /* Normal sram ports */ - if (0 < rr_gsb.get_cb_num_conf_bits(cb_type)) { - add_sram_ports_to_module_manager(module_manager, module_id, - circuit_lib, sram_model, cur_sram_orgz_info->type, - rr_gsb.get_cb_num_conf_bits(cb_type)); - /* Add ports only visible during formal verification to the module */ - add_formal_verification_sram_ports_to_module_manager(module_manager, module_id, circuit_lib, sram_model, - std::string(verilog_formal_verification_preproc_flag), - rr_gsb.get_cb_num_conf_bits(cb_type)); - } - - /* Print module definition + ports */ - print_verilog_module_declaration(fp, module_manager, module_id); - /* Finish printing ports */ - - /* Print an empty line a splitter */ - fp << std::endl; - - /* Print local wires, which are middle outputs of routing tracks */ - print_verilog_comment(fp, std::string("---- BEGIN local wires for middle output ports of routing tracks ----")); - for (size_t itrack = 0; itrack < rr_gsb.get_cb_chan_width(cb_type); ++itrack) { - vtr::Point port_coord(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type)); - /* Create a port description for the middle output */ - std::string port_name = generate_routing_track_middle_output_port_name(cb_type, - port_coord, itrack); - BasicPort middle_output_port(port_name, 1); - fp << generate_verilog_port(VERILOG_PORT_WIRE, middle_output_port) << ";" << std::endl; - } - print_verilog_comment(fp, std::string("---- END local wires for middle output ports of routing tracks ----")); - /* Print an empty line a splitter */ - fp << std::endl; - - /* Print short-wire connection for each routing track : - * Each input port is short-wired to its output port and middle output port - * - * in[i] ----------> out[i] - * | - * +-----> mid_out[i] - */ - print_verilog_comment(fp, std::string("---- BEGIN wire connection between inputs, outputs and middle outputs of routing tracks ----")); - for (size_t itrack = 0; itrack < rr_gsb.get_cb_chan_width(cb_type); ++itrack) { - vtr::Point port_coord(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type)); - /* Create a port description for the input */ - std::string input_port_name = generate_routing_track_port_name(cb_type, - port_coord, itrack, - IN_PORT); - BasicPort input_port(input_port_name, 1); /* Every track has a port size of 1 */ - - /* Create a port description for the output */ - std::string output_port_name = generate_routing_track_port_name(cb_type, - port_coord, itrack, - OUT_PORT); - BasicPort output_port(output_port_name, 1); /* Every track has a port size of 1 */ - - /* Create a port description for the middle output */ - std::string middle_output_port_name = generate_routing_track_middle_output_port_name(cb_type, port_coord, itrack); - BasicPort middle_output_port(middle_output_port_name, 1); - - /* Print short-wires: input port ---> output port */ - print_verilog_wire_connection(fp, output_port, input_port, false); - /* Print short-wires: input port ---> middle output port */ - print_verilog_wire_connection(fp, middle_output_port, input_port, false); - } - print_verilog_comment(fp, std::string("---- END wire connection between inputs, outputs and middle outputs of routing tracks ----")); - - /* Print an empty line a splitter */ - fp << std::endl; - - print_verilog_comment(fp, std::string("---- BEGIN local wires for SRAM data ports ----")); - /* Print local wires for memory configurations */ - print_verilog_connection_block_local_sram_wires(fp, rr_gsb, cb_type, circuit_lib, sram_model, cur_sram_orgz_info->type, - rr_gsb.get_cb_num_conf_bits(cb_type)); - print_verilog_comment(fp, std::string("---- END local wires for SRAM data ports ----")); - - /* Print an empty line a splitter */ - fp << std::endl; - - /* Create a counter for the configuration bus */ - BasicPort config_bus; - /* Counter start from 0 */ - config_bus.set_width(0, 0); - - /* Create a counter for the configuration bus used for formal verification */ - BasicPort fm_config_bus; - /* fm_config_bus has an invalid width here. It is designed to be easy to rotate */ - fm_config_bus.set_width(0, -1); - - /* TODO: Print routing multiplexers or direct interconnect*/ - for (size_t iside = 0; iside < cb_ipin_sides.size(); ++iside) { - enum e_side cb_ipin_side = cb_ipin_sides[iside]; - for (size_t inode = 0; inode < rr_gsb.get_num_ipin_nodes(cb_ipin_side); ++inode) { - print_verilog_connection_box_interc(module_manager, fp, cur_sram_orgz_info, - config_bus, fm_config_bus, - module_id, rr_gsb, cb_type, - circuit_lib, mux_lib, rr_switches, - rr_gsb.get_ipin_node(cb_ipin_side, inode), - use_explicit_mapping); - } - } - - /* Add check code for config_bus. - * The MSB should match the number of configuration bits!!! - */ - VTR_ASSERT(true == check_connection_block_mem_config_bus(cur_sram_orgz_info->type, - rr_gsb, cb_type, config_bus, - rr_gsb.get_cb_num_conf_bits(cb_type))); - VTR_ASSERT(fm_config_bus.get_msb() == rr_gsb.get_cb_num_conf_bits(cb_type) - 1); - - - /* Put an end to the Verilog module */ - print_verilog_module_end(fp, module_manager.module_name(module_id)); - - /* Add an empty line as a splitter */ - fp << std::endl; - - /* Close file handler */ - fp.close(); - - /* Add fname to the linked list */ - /* - routing_verilog_subckt_file_path_head = add_one_subckt_file_name_to_llist(routing_verilog_subckt_file_path_head, fname); - */ -} - - - /* Print connection boxes * Print the sub-circuit of a connection Box (Type: [CHANX|CHANY]) * Actually it is very similiar to switch box but @@ -5184,29 +3614,6 @@ void dump_verilog_routing_connection_box_subckt(t_sram_orgz_info* cur_sram_orgz_ return; } -/********************************************************************* - * Generate the port name for a Grid - * This is a wrapper function for generate_port_name() - * which can automatically decode the port name by the pin side and height - * - * TODO: This function is dependent on the global variable: grid - * This should be replaced by a local variable!!! - *********************************************************************/ -std::string generate_grid_side_port_name(const vtr::Point& coordinate, - const e_side& side, - const size_t& pin_id) { - /* Output the pins on the side*/ - int height = get_grid_pin_height(coordinate.x(), coordinate.y(), (int)pin_id); - if (1 != grid[coordinate.x()][coordinate.y()].type->pinloc[height][side][pin_id]) { - vpr_printf(TIO_MESSAGE_ERROR, - "(File:%s, [LINE%d])Fail to generate a grid pin (x=%lu, y=%lu, height=%lu, side=%s, index=%d)\n", - __FILE__, __LINE__, - coordinate.x(), coordinate.y(), height, convert_side_index_to_string(side), pin_id); - exit(1); - } - return generate_grid_port_name(coordinate, (size_t)height, side, pin_id, true); -} - /********************************************************************* * Top-level function: * Build the Verilog modules for global routing architecture @@ -5224,7 +3631,6 @@ std::string generate_grid_side_port_name(const vtr::Point& coordinate, * unique modules in terms of internal logics *********************************************************************/ void print_verilog_routing_resources(ModuleManager& module_manager, - const MuxLibrary& mux_lib, t_sram_orgz_info* cur_sram_orgz_info, char* verilog_dir, char* subckt_dir, @@ -5292,9 +3698,6 @@ void print_verilog_routing_resources(ModuleManager& module_manager, LL_num_rr_nodes, LL_rr_node, LL_rr_node_indices, LL_rr_indexed_data, arch.num_segments); - vtr::Point chan_coordinate((size_t)ix, (size_t)iy); - print_verilog_routing_chan_subckt(module_manager, std::string(verilog_dir), std::string(subckt_dir), chan_coordinate, CHANX, - LL_num_rr_nodes, LL_rr_node, LL_rr_node_indices); } } /* Y - channels [1...ny][0..nx]*/ @@ -5305,9 +3708,6 @@ void print_verilog_routing_resources(ModuleManager& module_manager, LL_num_rr_nodes, LL_rr_node, LL_rr_node_indices, LL_rr_indexed_data, arch.num_segments); - vtr::Point chan_coordinate((size_t)ix, (size_t)iy); - print_verilog_routing_chan_subckt(module_manager, std::string(verilog_dir), std::string(subckt_dir), chan_coordinate, CHANY, - LL_num_rr_nodes, LL_rr_node, LL_rr_node_indices); } } } @@ -5322,11 +3722,6 @@ void print_verilog_routing_resources(ModuleManager& module_manager, const RRGSB& unique_mirror = device_rr_gsb.get_sb_unique_module(isb); dump_verilog_routing_switch_box_unique_subckt(cur_sram_orgz_info, verilog_dir, subckt_dir, unique_mirror, explicit_port_mapping); - print_verilog_routing_switch_box_unique_module(module_manager, arch.spice->circuit_lib, mux_lib, - rr_switches, - cur_sram_orgz_info, std::string(verilog_dir), - std::string(subckt_dir), unique_mirror, - explicit_port_mapping); } /* Restore sram_orgz_info to the base */ @@ -5369,14 +3764,6 @@ void print_verilog_routing_resources(ModuleManager& module_manager, verilog_dir, subckt_dir, unique_mirror, CHANX, explicit_port_mapping); - print_verilog_routing_connection_box_unique_module(module_manager, - arch.spice->circuit_lib, mux_lib, - rr_switches, - cur_sram_orgz_info, - std::string(verilog_dir), - std::string(subckt_dir), - unique_mirror, CHANX, - explicit_port_mapping); } /* Y - channels [1...ny][0..nx]*/ @@ -5386,14 +3773,6 @@ void print_verilog_routing_resources(ModuleManager& module_manager, verilog_dir, subckt_dir, unique_mirror, CHANY, explicit_port_mapping); - print_verilog_routing_connection_box_unique_module(module_manager, - arch.spice->circuit_lib, mux_lib, - rr_switches, - cur_sram_orgz_info, - std::string(verilog_dir), - std::string(subckt_dir), - unique_mirror, CHANY, - explicit_port_mapping); } /* Restore sram_orgz_info to the base */ @@ -5454,3 +3833,332 @@ void print_verilog_routing_resources(ModuleManager& module_manager, return; } + + +/******************************************************************** + * Print the sub-circuit of a connection Box (Type: [CHANX|CHANY]) + * Actually it is very similiar to switch box but + * the difference is connection boxes connect Grid INPUT Pins to channels + * NOTE: direct connection between CLBs should NOT be included inside this + * module! They should be added in the top-level module as their connection + * is not limited to adjacent CLBs!!! + * + * Location of a X- and Y-direction Connection Block in FPGA fabric + * +------------+ +-------------+ + * | |------>| | + * | CLB |<------| Y-direction | + * | | ... | Connection | + * | |------>| Block | + * +------------+ +-------------+ + * | ^ ... | | ^ ... | + * v | v v | v + * +-------------------+ +-------------+ + * --->| |--->| | + * <---| X-direction |<---| Switch | + * ...| Connection block |... | Block | + * --->| |--->| | + * +-------------------+ +-------------+ + * + * Internal structure: + * This is an example of a X-direction connection block + * Note that middle output ports are shorted wire from inputs of routing tracks, + * which are also the inputs of routing multiplexer of the connection block + * + * CLB Input Pins + * (IPINs) + * ^ ^ ^ + * | | ... | + * +--------------------------+ + * | ^ ^ ^ | + * | | | ... | | + * | +--------------------+ | + * | | routing | | + * | | multiplexers | | + * | +--------------------+ | + * | middle outputs | + * | of routing channel | + * | ^ ^ ^ ^ ^ ^ ^ ^ | + * | | | | | ... | | | | | + * in[0] -->|------------------------->|---> out[0] + * out[1] <--|<-------------------------|<--- in[1] + * | ... | + * in[W-2] -->|------------------------->|---> out[W-2] + * out[W-1] <--|<-------------------------|<--- in[W-1] + * +--------------------------+ + * + * W: routing channel width + * + ********************************************************************/ +static +void print_verilog_routing_connection_box_unique_module(ModuleManager& module_manager, + const std::string& verilog_dir, + const std::string& subckt_dir, + const RRGSB& rr_gsb, + const t_rr_type& cb_type, + const bool& use_explicit_port_map) { + /* Create the netlist */ + vtr::Point gsb_coordinate(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type)); + std::string verilog_fname(subckt_dir + generate_connection_block_netlist_name(cb_type, gsb_coordinate, std::string(verilog_netlist_file_postfix))); + /* TODO: remove the bak file when the file is ready */ + verilog_fname += ".bak"; + + /* Create the file stream */ + std::fstream fp; + fp.open(verilog_fname, std::fstream::out | std::fstream::trunc); + + check_file_handler(fp); + + print_verilog_file_header(fp, std::string("Verilog modules for Unique Connection Blocks[" + std::to_string(rr_gsb.get_cb_x(cb_type)) + "]["+ std::to_string(rr_gsb.get_cb_y(cb_type)) + "]")); + + /* Print preprocessing flags */ + print_verilog_include_defines_preproc_file(fp, verilog_dir); + + /* Create a Verilog Module based on the circuit model, and add to module manager */ + ModuleId cb_module = module_manager.find_module(generate_connection_block_module_name(cb_type, gsb_coordinate)); + VTR_ASSERT(true == module_manager.valid_module_id(cb_module)); + + /* Write the verilog module */ + write_verilog_module_to_file(fp, module_manager, cb_module, use_explicit_port_map); + + /* Add an empty line as a splitter */ + fp << std::endl; + + /* Close file handler */ + fp.close(); + + /* Add fname to the linked list */ + /* + routing_verilog_subckt_file_path_head = add_one_subckt_file_name_to_llist(routing_verilog_subckt_file_path_head, fname); + */ +} + +/********************************************************************* + * Generate the Verilog module for a Switch Box. + * A Switch Box module consists of following ports: + * 1. Channel Y [x][y] inputs + * 2. Channel X [x+1][y] inputs + * 3. Channel Y [x][y-1] outputs + * 4. Channel X [x][y] outputs + * 5. Grid[x][y+1] Right side outputs pins + * 6. Grid[x+1][y+1] Left side output pins + * 7. Grid[x+1][y+1] Bottom side output pins + * 8. Grid[x+1][y] Top side output pins + * 9. Grid[x+1][y] Left side output pins + * 10. Grid[x][y] Right side output pins + * 11. Grid[x][y] Top side output pins + * 12. Grid[x][y+1] Bottom side output pins + * + * Location of a Switch Box in FPGA fabric: + * + * -------------- -------------- + * | | | | + * | Grid | ChanY | Grid | + * | [x][y+1] | [x][y+1] | [x+1][y+1] | + * | | | | + * -------------- -------------- + * ---------- + * ChanX | Switch | ChanX + * [x][y] | Box | [x+1][y] + * | [x][y] | + * ---------- + * -------------- -------------- + * | | | | + * | Grid | ChanY | Grid | + * | [x][y] | [x][y] | [x+1][y] | + * | | | | + * -------------- -------------- + * + * Switch Block pin location map + * + * Grid[x][y+1] ChanY[x][y+1] Grid[x+1][y+1] + * right_pins inputs/outputs left_pins + * | ^ | + * | | | + * v v v + * +-----------------------------------------------+ + * | | + * Grid[x][y+1] | | Grid[x+1][y+1] + * bottom_pins---->| |<---- bottom_pins + * | | + * ChanX[x][y] | Switch Box [x][y] | ChanX[x+1][y] + * inputs/outputs<--->| |<---> inputs/outputs + * | | + * Grid[x][y+1] | | Grid[x+1][y+1] + * top_pins---->| |<---- top_pins + * | | + * +-----------------------------------------------+ + * ^ ^ ^ + * | | | + * | v | + * Grid[x][y] ChanY[x][y] Grid[x+1][y] + * right_pins inputs/outputs left_pins + * + * + ********************************************************************/ +static +void print_verilog_routing_switch_box_unique_module(ModuleManager& module_manager, + const std::string& verilog_dir, + const std::string& subckt_dir, + const RRGSB& rr_gsb, + const bool& use_explicit_port_map) { + /* Create the netlist */ + vtr::Point gsb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y()); + std::string verilog_fname(subckt_dir + generate_routing_block_netlist_name(sb_verilog_file_name_prefix, gsb_coordinate, std::string(verilog_netlist_file_postfix))); + /* TODO: remove the bak file when the file is ready */ + verilog_fname += ".bak"; + + /* Create the file stream */ + std::fstream fp; + fp.open(verilog_fname, std::fstream::out | std::fstream::trunc); + + check_file_handler(fp); + + print_verilog_file_header(fp, std::string("Verilog modules for Unique Switch Blocks[" + std::to_string(rr_gsb.get_sb_x()) + "]["+ std::to_string(rr_gsb.get_sb_y()) + "]")); + + /* Print preprocessing flags */ + print_verilog_include_defines_preproc_file(fp, verilog_dir); + + /* Create a Verilog Module based on the circuit model, and add to module manager */ + ModuleId sb_module = module_manager.find_module(generate_switch_block_module_name(gsb_coordinate)); + VTR_ASSERT(true == module_manager.valid_module_id(sb_module)); + + /* Write the verilog module */ + write_verilog_module_to_file(fp, module_manager, sb_module, use_explicit_port_map); + + /* Close file handler */ + fp.close(); + + /* Add fname to the linked list */ + /* + routing_verilog_subckt_file_path_head = add_one_subckt_file_name_to_llist(routing_verilog_subckt_file_path_head, verilog_fname.c_str()); + */ + + return; +} + + +/******************************************************************** + * Iterate over all the connection blocks in a device + * and build a module for each of them + *******************************************************************/ +static +void print_verilog_flatten_connection_block_modules(ModuleManager& module_manager, + const DeviceRRGSB& L_device_rr_gsb, + const std::string& verilog_dir, + const std::string& subckt_dir, + const t_rr_type& cb_type, + const bool& use_explicit_port_map) { + /* Build unique X-direction connection block modules */ + DeviceCoordinator cb_range = L_device_rr_gsb.get_gsb_range(); + + for (size_t ix = 0; ix < cb_range.get_x(); ++ix) { + for (size_t iy = 0; iy < cb_range.get_y(); ++iy) { + /* Check if the connection block exists in the device! + * Some of them do NOT exist due to heterogeneous blocks (height > 1) + * We will skip those modules + */ + const RRGSB& rr_gsb = L_device_rr_gsb.get_gsb(ix, iy); + if ( (TRUE != is_cb_exist(CHANX, rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type))) + || (true != rr_gsb.is_cb_exist(cb_type))) { + continue; + } + print_verilog_routing_connection_box_unique_module(module_manager, + verilog_dir, + subckt_dir, + rr_gsb, cb_type, + use_explicit_port_map); + } + } +} + +/******************************************************************** + * A top-level function of this file + * Print all the modules for global routing architecture of a FPGA fabric + * in Verilog format in a flatten way: + * Each connection block and switch block will be generated as a unique module + * Covering: + * 1. Connection blocks + * 2. Switch blocks + *******************************************************************/ +void print_verilog_flatten_routing_modules(ModuleManager& module_manager, + const DeviceRRGSB& L_device_rr_gsb, + const t_det_routing_arch& routing_arch, + const std::string& verilog_dir, + const std::string& subckt_dir, + const bool& use_explicit_port_map) { + /* We only support uni-directional routing architecture now */ + VTR_ASSERT (UNI_DIRECTIONAL == routing_arch.directionality); + + /* TODO: deprecate DeviceCoordinator, use vtr::Point only! */ + DeviceCoordinator sb_range = L_device_rr_gsb.get_gsb_range(); + + /* Build unique switch block modules */ + for (size_t ix = 0; ix < sb_range.get_x(); ++ix) { + for (size_t iy = 0; iy < sb_range.get_y(); ++iy) { + const RRGSB& rr_gsb = L_device_rr_gsb.get_gsb(ix, iy); + print_verilog_routing_switch_box_unique_module(module_manager, + verilog_dir, + subckt_dir, + rr_gsb, + use_explicit_port_map); + } + } + + print_verilog_flatten_connection_block_modules(module_manager, L_device_rr_gsb, verilog_dir, subckt_dir, CHANX, use_explicit_port_map); + + print_verilog_flatten_connection_block_modules(module_manager, L_device_rr_gsb, verilog_dir, subckt_dir, CHANY, use_explicit_port_map); + +} + + +/******************************************************************** + * A top-level function of this file + * Print all the unique modules for global routing architecture of a FPGA fabric + * in Verilog format, including: + * 1. Connection blocks + * 2. Switch blocks + * + * Note: this function SHOULD be called only when + * the option compact_routing_hierarchy is turned on!!! + *******************************************************************/ +void print_verilog_unique_routing_modules(ModuleManager& module_manager, + const DeviceRRGSB& L_device_rr_gsb, + const t_det_routing_arch& routing_arch, + const std::string& verilog_dir, + const std::string& subckt_dir, + const bool& use_explicit_port_map) { + /* We only support uni-directional routing architecture now */ + VTR_ASSERT (UNI_DIRECTIONAL == routing_arch.directionality); + + /* Build unique switch block modules */ + for (size_t isb = 0; isb < L_device_rr_gsb.get_num_sb_unique_module(); ++isb) { + const RRGSB& unique_mirror = L_device_rr_gsb.get_sb_unique_module(isb); + print_verilog_routing_switch_box_unique_module(module_manager, + verilog_dir, + subckt_dir, + unique_mirror, + use_explicit_port_map); + } + + /* Build unique X-direction connection block modules */ + for (size_t icb = 0; icb < L_device_rr_gsb.get_num_cb_unique_module(CHANX); ++icb) { + const RRGSB& unique_mirror = L_device_rr_gsb.get_cb_unique_module(CHANX, icb); + + print_verilog_routing_connection_box_unique_module(module_manager, + verilog_dir, + subckt_dir, + unique_mirror, CHANX, + use_explicit_port_map); + } + + /* Build unique X-direction connection block modules */ + for (size_t icb = 0; icb < L_device_rr_gsb.get_num_cb_unique_module(CHANY); ++icb) { + const RRGSB& unique_mirror = L_device_rr_gsb.get_cb_unique_module(CHANY, icb); + + print_verilog_routing_connection_box_unique_module(module_manager, + verilog_dir, + subckt_dir, + unique_mirror, CHANY, + use_explicit_port_map); + } +} diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_routing.h b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_routing.h index 8cb8b8ecf..23132fcdc 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_routing.h +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_routing.h @@ -142,13 +142,7 @@ void dump_verilog_routing_connection_box_subckt(t_sram_orgz_info* cur_sram_orgz_ boolean compact_routing_hierarchy, bool is_explicit_mapping); - -std::string generate_grid_side_port_name(const vtr::Point& coordinate, - const e_side& side, - const size_t& pin_id); - void print_verilog_routing_resources(ModuleManager& module_manager, - const MuxLibrary& mux_lib, t_sram_orgz_info* cur_sram_orgz_info, char* verilog_dir, char* subckt_dir, @@ -159,4 +153,18 @@ void print_verilog_routing_resources(ModuleManager& module_manager, t_rr_indexed_data* LL_rr_indexed_data, const t_fpga_spice_opts& FPGA_SPICE_Opts); +void print_verilog_flatten_routing_modules(ModuleManager& module_manager, + const DeviceRRGSB& L_device_rr_gsb, + const t_det_routing_arch& routing_arch, + const std::string& verilog_dir, + const std::string& subckt_dir, + const bool& use_explicit_port_map); + +void print_verilog_unique_routing_modules(ModuleManager& module_manager, + const DeviceRRGSB& L_device_rr_gsb, + const t_det_routing_arch& routing_arch, + const std::string& verilog_dir, + const std::string& subckt_dir, + const bool& use_explicit_port_map); + #endif diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_top_module.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_top_module.cpp index 136cef804..9939c406f 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_top_module.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_top_module.cpp @@ -19,7 +19,7 @@ #include "build_top_module_directs.h" #include "verilog_global.h" -#include "verilog_routing.h" +#include "build_module_graph_utils.h" #include "verilog_writer_utils.h" #include "verilog_module_writer.h" #include "verilog_top_module.h" @@ -401,7 +401,7 @@ void add_top_module_nets_connect_grids_and_sb(ModuleManager& module_manager, /* Collect sink-related information */ vtr::Point sink_sb_port_coord(module_sb.get_opin_node(side_manager.get_side(), inode)->xlow, module_sb.get_opin_node(side_manager.get_side(), inode)->ylow); - std::string sink_sb_port_name = generate_grid_side_port_name(sink_sb_port_coord, + std::string sink_sb_port_name = generate_grid_side_port_name(grids, sink_sb_port_coord, module_sb.get_opin_node_grid_side(side_manager.get_side(), inode), src_grid_pin_index); ModulePortId sink_sb_port_id = module_manager.find_module_port(sink_sb_module, sink_sb_port_name); @@ -526,7 +526,7 @@ void add_top_module_nets_connect_grids_and_cb(ModuleManager& module_manager, /* Collect source-related information */ t_rr_node* module_ipin_node = module_cb.get_ipin_node(cb_ipin_side, inode); vtr::Point cb_src_port_coord(module_ipin_node->xlow, module_ipin_node->ylow); - std::string src_cb_port_name = generate_grid_side_port_name(cb_src_port_coord, + std::string src_cb_port_name = generate_grid_side_port_name(grids, cb_src_port_coord, module_cb.get_ipin_node_grid_side(cb_ipin_side, inode), module_ipin_node->ptc_num); ModulePortId src_cb_port_id = module_manager.find_module_port(src_cb_module, src_cb_port_name);