diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_mem_utils.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_mem_utils.cpp new file mode 100644 index 000000000..f95f99a88 --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_mem_utils.cpp @@ -0,0 +1,304 @@ +/********************************************************************* + * This file includes functions that are used for + * generating ports for memory modules + *********************************************************************/ +#include "vtr_assert.h" +#include "util.h" +#include "fpga_x2p_naming.h" +#include "fpga_x2p_mem_utils.h" + +/********************************************************************* + * Create a port-to-port map for a CMOS memory module + * + * Configuration Chain + * ------------------- + * + * config_bus (head) config_bus (tail) + * | ^ + * v | + * +-------------------------------------+ + * | CMOS-based Memory Module | + * +-------------------------------------+ + * | | + * v v + * sram_out sram_outb + * + * + * Memory bank + * ----------- + * + * config_bus (BL) config_bus (WL) + * | | + * v v + * +-------------------------------------+ + * | CMOS-based Memory Module | + * +-------------------------------------+ + * | | + * v v + * sram_out sram_outb + * + **********************************************************************/ +static +std::map generate_cmos_mem_module_port2port_map(const ModuleManager& module_manager, + const ModuleId& mem_module, + const BasicPort& config_bus, + const std::vector& mem_output_bus_ports, + const e_sram_orgz& sram_orgz_type) { + std::map port2port_name_map; + + switch (sram_orgz_type) { + case SPICE_SRAM_STANDALONE: + /* Nothing to do */ + break; + case SPICE_SRAM_SCAN_CHAIN: { + /* Link the head port of the memory module: + * the LSB of config bus port is the head port index + */ + std::vector config_bus_ports; + config_bus_ports.push_back(BasicPort(generate_local_config_bus_port_name(), config_bus.get_msb(), config_bus.get_msb() + 1)); + BasicPort head_port(config_bus_ports[0].get_name(), config_bus_ports[0].get_lsb(), config_bus_ports[0].get_lsb()); + port2port_name_map[generate_configuration_chain_head_name()] = head_port; + + /* Link the tail port of the memory module: + * the MSB of config bus port is the tail port index + */ + BasicPort tail_port(config_bus_ports[0].get_name(), config_bus_ports[0].get_msb(), config_bus_ports[0].get_msb()); + port2port_name_map[generate_configuration_chain_tail_name()] = tail_port; + + /* Link the SRAM output ports of the memory module */ + VTR_ASSERT( 2 == mem_output_bus_ports.size() ); + port2port_name_map[generate_configuration_chain_data_out_name()] = mem_output_bus_ports[0]; + port2port_name_map[generate_configuration_chain_inverted_data_out_name()] = mem_output_bus_ports[1]; + break; + } + case SPICE_SRAM_MEMORY_BANK: + /* TODO: */ + break; + default: + vpr_printf(TIO_MESSAGE_ERROR, + "(File:%s,[LINE%d])Invalid type of SRAM organization!\n", + __FILE__, __LINE__); + exit(1); + } + + return port2port_name_map; +} + +/********************************************************************* + * Create a port-to-port map for a ReRAM-based memory module + * Memory bank + * ----------- + * + * config_bus (BL) config_bus (WL) + * | | + * v v + * +-------------------------------------+ + * | ReRAM-based Memory Module | + * +-------------------------------------+ + * | | + * v v + * Mem_out Mem_outb + **********************************************************************/ +static +std::map generate_rram_mem_module_port2port_map(const ModuleManager& module_manager, + const ModuleId& mem_module, + const BasicPort& config_bus, + const std::vector& mem_output_bus_ports, + const e_sram_orgz& sram_orgz_type) { + std::map port2port_name_map; + + switch (sram_orgz_type) { + case SPICE_SRAM_STANDALONE: + /* Not supported */ + break; + case SPICE_SRAM_SCAN_CHAIN: { + /* Link the head port of the memory module: + * the LSB of config bus port is the head port index + */ + std::vector config_bus_ports; + config_bus_ports.push_back(BasicPort(generate_local_config_bus_port_name(), config_bus.get_msb(), config_bus.get_msb() + 1)); + BasicPort head_port(config_bus_ports[0].get_name(), config_bus_ports[0].get_lsb(), config_bus_ports[0].get_lsb()); + port2port_name_map[generate_configuration_chain_head_name()] = head_port; + + /* Link the tail port of the memory module: + * the MSB of config bus port is the tail port index + */ + BasicPort tail_port(config_bus_ports[0].get_name(), config_bus_ports[0].get_msb(), config_bus_ports[0].get_msb()); + port2port_name_map[generate_configuration_chain_tail_name()] = tail_port; + + /* Link the SRAM output ports of the memory module */ + VTR_ASSERT( 2 == mem_output_bus_ports.size() ); + port2port_name_map[generate_configuration_chain_data_out_name()] = mem_output_bus_ports[0]; + port2port_name_map[generate_configuration_chain_inverted_data_out_name()] = mem_output_bus_ports[1]; + break; + } + case SPICE_SRAM_MEMORY_BANK: + /* TODO: link BL/WL/Reserved Ports to the inputs of a memory module */ + break; + default: + vpr_printf(TIO_MESSAGE_ERROR, + "(File:%s,[LINE%d])Invalid type of SRAM organization!\n", + __FILE__, __LINE__); + exit(1); + } + + return port2port_name_map; +} + +/********************************************************************* + * Create a port-to-port map for a memory module + * The content of the port-to-port map will depend not only + * the design technology of the memory cells but also the + * configuration styles of FPGA fabric. + * Here we will branch on the design technology + **********************************************************************/ +std::map generate_mem_module_port2port_map(const ModuleManager& module_manager, + const ModuleId& mem_module, + const BasicPort& config_bus, + const std::vector& mem_output_bus_ports, + const e_spice_model_design_tech& mem_design_tech, + const e_sram_orgz& sram_orgz_type) { + std::map port2port_name_map; + + switch (mem_design_tech) { + case SPICE_MODEL_DESIGN_CMOS: + port2port_name_map = generate_cmos_mem_module_port2port_map(module_manager, mem_module, config_bus, mem_output_bus_ports, sram_orgz_type); + break; + case SPICE_MODEL_DESIGN_RRAM: + port2port_name_map = generate_rram_mem_module_port2port_map(module_manager, mem_module, config_bus, mem_output_bus_ports, sram_orgz_type); + break; + default: + vpr_printf(TIO_MESSAGE_ERROR, + "(File:%s,[LINE%d])Invalid type of memory design technology !\n", + __FILE__, __LINE__); + exit(1); + } + + return port2port_name_map; +} + +/********************************************************************* + * Update the LSB and MSB of a configuration bus based on the number of + * memory bits of a CMOS memory module. + **********************************************************************/ +static +void update_cmos_mem_module_config_bus(const e_sram_orgz& sram_orgz_type, + const size_t& num_config_bits, + BasicPort& config_bus) { + switch (sram_orgz_type) { + case SPICE_SRAM_STANDALONE: + /* Not supported */ + break; + case SPICE_SRAM_SCAN_CHAIN: + /* Scan-chain of a memory module only has a head and a tail. + * LSB and MSB of configuration bus will be shifted to the next head. + */ + VTR_ASSERT(true == config_bus.rotate(1)); + break; + case SPICE_SRAM_MEMORY_BANK: + /* In this case, a memory module has a number of BL/WL and BLB/WLB (possibly). + * LSB and MSB of configuration bus will be shifted by the number of BL/WL/BLB/WLB. + */ + VTR_ASSERT(true == config_bus.rotate(num_config_bits)); + break; + default: + vpr_printf(TIO_MESSAGE_ERROR, + "(File:%s,[LINE%d])Invalid type of SRAM organization!\n", + __FILE__, __LINE__); + exit(1); + } +} + +/********************************************************************* + * Update the LSB and MSB of a configuration bus based on the number of + * memory bits of a ReRAM memory module. + **********************************************************************/ +static +void update_rram_mem_module_config_bus(const e_sram_orgz& sram_orgz_type, + const size_t& num_config_bits, + BasicPort& config_bus) { + switch (sram_orgz_type) { + case SPICE_SRAM_STANDALONE: + /* Not supported */ + break; + case SPICE_SRAM_SCAN_CHAIN: + /* Scan-chain of a memory module only has a head and a tail. + * LSB and MSB of configuration bus will be shifted to the next head. + * TODO: this may be changed later!!! + */ + VTR_ASSERT(true == config_bus.rotate(1)); + break; + case SPICE_SRAM_MEMORY_BANK: + /* In this case, a memory module contains unique BL/WL or BLB/WLB, + * which are not shared with other modules + * TODO: this may be changed later!!! + */ + VTR_ASSERT(true == config_bus.rotate(1)); + break; + default: + vpr_printf(TIO_MESSAGE_ERROR, + "(File:%s,[LINE%d])Invalid type of SRAM organization!\n", + __FILE__, __LINE__); + exit(1); + } +} + +/********************************************************************* + * Update the LSB and MSB of a configuration bus based on the number of + * memory bits of a module. + * Note that this function is designed to do such simple job, in purpose of + * being independent from adding ports or printing ports. + * As such, this function can be re-used in bitstream generation + * when Verilog generation is not needed. + * DO NOT update the configuration bus in the function of adding/printing ports + **********************************************************************/ +void update_mem_module_config_bus(const e_sram_orgz& sram_orgz_type, + const e_spice_model_design_tech& mem_design_tech, + const size_t& num_config_bits, + BasicPort& config_bus) { + switch (mem_design_tech) { + case SPICE_MODEL_DESIGN_CMOS: + update_cmos_mem_module_config_bus(sram_orgz_type, num_config_bits, config_bus); + break; + case SPICE_MODEL_DESIGN_RRAM: + update_rram_mem_module_config_bus(sram_orgz_type, num_config_bits, config_bus); + break; + default: + vpr_printf(TIO_MESSAGE_ERROR, + "(File:%s,[LINE%d])Invalid type of memory design technology !\n", + __FILE__, __LINE__); + exit(1); + } +} + +/******************************************************************** + * Check if the MSB of a configuration bus of a switch block + * matches the expected value + ********************************************************************/ +bool check_mem_config_bus(const e_sram_orgz& sram_orgz_type, + const BasicPort& config_bus, + const size_t& local_expected_msb) { + switch (sram_orgz_type) { + case SPICE_SRAM_STANDALONE: + /* Not supported */ + return false; + break; + case SPICE_SRAM_SCAN_CHAIN: + /* TODO: comment on why + */ + return (local_expected_msb == config_bus.get_msb()); + break; + case SPICE_SRAM_MEMORY_BANK: + /* TODO: comment on why + */ + return (local_expected_msb == config_bus.get_msb()); + break; + default: + vpr_printf(TIO_MESSAGE_ERROR, + "(File:%s,[LINE%d])Invalid type of SRAM organization!\n", + __FILE__, __LINE__); + exit(1); + } + /* Reach here, it means something goes wrong, return a false value */ + return false; +} diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_mem_utils.h b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_mem_utils.h new file mode 100644 index 000000000..831aca5a8 --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_mem_utils.h @@ -0,0 +1,29 @@ +/******************************************************************** + * Header file for fpga_x2p_mem_utils.cpp + **********************************************************************/ +#ifndef FPGA_X2P_MEM_UTILS_H +#define FPGA_X2P_MEM_UTILS_H + +/* Header files are included for the data types appear in the function declaration below */ +#include +#include "device_port.h" +#include "spice_types.h" +#include "module_manager.h" + +std::map generate_mem_module_port2port_map(const ModuleManager& module_manager, + const ModuleId& mem_module, + const BasicPort& config_bus, + const std::vector& mem_output_bus_ports, + const e_spice_model_design_tech& mem_design_tech, + const e_sram_orgz& sram_orgz_type); + +void update_mem_module_config_bus(const e_sram_orgz& sram_orgz_type, + const e_spice_model_design_tech& mem_design_tech, + const size_t& num_config_bits, + BasicPort& config_bus); + +bool check_mem_config_bus(const e_sram_orgz& sram_orgz_type, + const BasicPort& config_bus, + const size_t& local_expected_msb); + +#endif diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_naming.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_naming.cpp index d39d4961a..597329292 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_naming.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_naming.cpp @@ -168,6 +168,30 @@ std::string generate_routing_block_netlist_name(const std::string& prefix, return std::string( prefix + std::to_string(coordinate.x()) + std::string("_") + std::to_string(coordinate.y()) + postfix ); } +/********************************************************************* + * Generate the netlist name for a connection block with a given coordinate + *********************************************************************/ +std::string generate_connection_block_netlist_name(const t_rr_type& cb_type, + const vtr::Point& coordinate, + const std::string& postfix) { + std::string prefix("cb"); + switch (cb_type) { + case CHANX: + prefix += std::string("x_"); + break; + case CHANY: + prefix += std::string("y_"); + break; + default: + vpr_printf(TIO_MESSAGE_ERROR, + "(File: %s [LINE%d]) Invalid type of connection block!\n", + __FILE__, __LINE__); + exit(1); + } + + return generate_routing_block_netlist_name(prefix, coordinate, postfix); +} + /********************************************************************* * Generate the module name for a unique routing channel *********************************************************************/ @@ -242,6 +266,33 @@ std::string generate_routing_track_port_name(const t_rr_type& chan_type, return port_name; } +/********************************************************************* + * Generate the middle output port name for a routing track + * with a given coordinate + *********************************************************************/ +std::string generate_routing_track_middle_output_port_name(const t_rr_type& chan_type, + const vtr::Point& coordinate, + const size_t& track_id) { + /* Channel must be either CHANX or CHANY */ + VTR_ASSERT( (CHANX == chan_type) || (CHANY == chan_type) ); + + /* Create a map between chan_type and module_prefix */ + std::map module_prefix_map; + /* TODO: use a constexpr string to replace the fixed name? */ + module_prefix_map[CHANX] = std::string("chanx"); + module_prefix_map[CHANY] = std::string("chany"); + + std::string port_name = module_prefix_map[chan_type]; + port_name += std::string("_" + std::to_string(coordinate.x()) + std::string("__") + std::to_string(coordinate.y()) + std::string("__")); + + port_name += std::string("midout_"); + + /* Add the track id to the port name */ + port_name += std::to_string(track_id) + std::string("_"); + + return port_name; +} + /********************************************************************* * Generate the module name for a switch block with a given coordinate *********************************************************************/ @@ -249,6 +300,29 @@ std::string generate_switch_block_module_name(const vtr::Point& coordina return std::string( "sb_" + std::to_string(coordinate.x()) + std::string("__") + std::to_string(coordinate.y()) + std::string("_") ); } +/********************************************************************* + * Generate the module name for a connection block with a given coordinate + *********************************************************************/ +std::string generate_connection_block_module_name(const t_rr_type& cb_type, + const vtr::Point& coordinate) { + std::string prefix("cb"); + switch (cb_type) { + case CHANX: + prefix += std::string("x_"); + break; + case CHANY: + prefix += std::string("y_"); + break; + default: + vpr_printf(TIO_MESSAGE_ERROR, + "(File: %s [LINE%d]) Invalid type of connection block!\n", + __FILE__, __LINE__); + exit(1); + } + + return std::string( prefix + std::to_string(coordinate.x()) + std::string("__") + std::to_string(coordinate.y()) + std::string("_") ); +} + /********************************************************************* * Generate the port name for a Grid * TODO: add more comments about why we need different names for diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_naming.h b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_naming.h index 6d7f9c88c..bd7ced7b8 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_naming.h +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_naming.h @@ -48,6 +48,10 @@ std::string generate_routing_block_netlist_name(const std::string& prefix, const vtr::Point& block_id, const std::string& postfix); +std::string generate_connection_block_netlist_name(const t_rr_type& cb_type, + const vtr::Point& coordinate, + const std::string& postfix); + std::string generate_routing_channel_module_name(const t_rr_type& chan_type, const size_t& block_id); @@ -59,8 +63,15 @@ std::string generate_routing_track_port_name(const t_rr_type& chan_type, const size_t& track_id, const PORTS& port_direction); +std::string generate_routing_track_middle_output_port_name(const t_rr_type& chan_type, + const vtr::Point& coordinate, + const size_t& track_id); + std::string generate_switch_block_module_name(const vtr::Point& coordinate); +std::string generate_connection_block_module_name(const t_rr_type& cb_type, + const vtr::Point& coordinate); + std::string generate_grid_port_name(const vtr::Point& coordinate, const size_t& height, const e_side& side, diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager_utils.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager_utils.cpp index e442b0223..6b7c9bdc9 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager_utils.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager_utils.cpp @@ -210,150 +210,3 @@ void add_sram_ports_to_module_manager(ModuleManager& module_manager, } } -/********************************************************************* - * Create a port-to-port map for a CMOS memory module - * - * Configuration Chain - * ------------------- - * - * config_bus (head) config_bus (tail) - * | ^ - * v | - * +-------------------------------------+ - * | CMOS-based Memory Module | - * +-------------------------------------+ - * | | - * v v - * sram_out sram_outb - * - * - * Memory bank - * ----------- - * - * config_bus (BL) config_bus (WL) - * | | - * v v - * +-------------------------------------+ - * | CMOS-based Memory Module | - * +-------------------------------------+ - * | | - * v v - * sram_out sram_outb - * - **********************************************************************/ -static -std::map generate_cmos_mem_module_port2port_map(const ModuleManager& module_manager, - const ModuleId& mem_module, - const std::vector& config_bus_ports, - const std::vector& mem_output_bus_ports, - const e_sram_orgz& sram_orgz_type) { - std::map port2port_name_map; - - switch (sram_orgz_type) { - case SPICE_SRAM_STANDALONE: - /* Nothing to do */ - break; - case SPICE_SRAM_SCAN_CHAIN: { - /* Link the head port of the memory module: - * the LSB of config bus port is the head port index - */ - VTR_ASSERT( 1 == config_bus_ports.size() ); - BasicPort head_port(config_bus_ports[0].get_name(), config_bus_ports[0].get_lsb(), config_bus_ports[0].get_lsb()); - port2port_name_map[generate_configuration_chain_head_name()] = head_port; - - /* Link the tail port of the memory module: - * the MSB of config bus port is the tail port index - */ - BasicPort tail_port(config_bus_ports[0].get_name(), config_bus_ports[0].get_msb(), config_bus_ports[0].get_msb()); - port2port_name_map[generate_configuration_chain_tail_name()] = tail_port; - - /* Link the SRAM output ports of the memory module */ - VTR_ASSERT( 2 == mem_output_bus_ports.size() ); - port2port_name_map[generate_configuration_chain_data_out_name()] = mem_output_bus_ports[0]; - port2port_name_map[generate_configuration_chain_inverted_data_out_name()] = mem_output_bus_ports[1]; - break; - } - case SPICE_SRAM_MEMORY_BANK: - break; - default: - vpr_printf(TIO_MESSAGE_ERROR, - "(File:%s,[LINE%d])Invalid type of SRAM organization!\n", - __FILE__, __LINE__); - exit(1); - } - - return port2port_name_map; -} - -/********************************************************************* - * Create a port-to-port map for a ReRAM-based memory module - * Memory bank - * ----------- - * - * config_bus (BL) config_bus (WL) - * | | - * v v - * +-------------------------------------+ - * | ReRAM-based Memory Module | - * +-------------------------------------+ - * | | - * v v - * Mem_out Mem_outb - **********************************************************************/ -static -std::map generate_rram_mem_module_port2port_map(const ModuleManager& module_manager, - const ModuleId& mem_module, - const e_sram_orgz& sram_orgz_type) { - std::map port2port_name_map; - - switch (sram_orgz_type) { - case SPICE_SRAM_STANDALONE: - /* Not supported */ - break; - case SPICE_SRAM_SCAN_CHAIN: - /* TODO: to be supported */ - break; - case SPICE_SRAM_MEMORY_BANK: - /* TODO: link BL/WL/Reserved Ports to the inputs of a memory module */ - break; - default: - vpr_printf(TIO_MESSAGE_ERROR, - "(File:%s,[LINE%d])Invalid type of SRAM organization!\n", - __FILE__, __LINE__); - exit(1); - } - - return port2port_name_map; -} - -/********************************************************************* - * Create a port-to-port map for a memory module - * The content of the port-to-port map will depend not only - * the design technology of the memory cells but also the - * configuration styles of FPGA fabric. - * Here we will branch on the design technology - **********************************************************************/ -std::map generate_mem_module_port2port_map(const ModuleManager& module_manager, - const ModuleId& mem_module, - const std::vector& config_bus_ports, - const std::vector& mem_output_bus_ports, - const e_spice_model_design_tech& mem_design_tech, - const e_sram_orgz& sram_orgz_type) { - std::map port2port_name_map; - - switch (mem_design_tech) { - case SPICE_MODEL_DESIGN_CMOS: - port2port_name_map = generate_cmos_mem_module_port2port_map(module_manager, mem_module, config_bus_ports, mem_output_bus_ports, sram_orgz_type); - break; - case SPICE_MODEL_DESIGN_RRAM: - port2port_name_map = generate_rram_mem_module_port2port_map(module_manager, mem_module, sram_orgz_type); - break; - default: - vpr_printf(TIO_MESSAGE_ERROR, - "(File:%s,[LINE%d])Invalid type of memory design technology !\n", - __FILE__, __LINE__); - exit(1); - } - - return port2port_name_map; -} diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager_utils.h b/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager_utils.h index bee3e7ff8..17d395cdb 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager_utils.h +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager_utils.h @@ -38,12 +38,5 @@ void add_sram_ports_to_module_manager(ModuleManager& module_manager, const e_sram_orgz sram_orgz_type, const size_t& port_size); -std::map generate_mem_module_port2port_map(const ModuleManager& module_manager, - const ModuleId& mem_module, - const std::vector& config_bus_ports, - const std::vector& mem_output_bus_ports, - const e_spice_model_design_tech& mem_design_tech, - const e_sram_orgz& sram_orgz_type); - #endif diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/rr_blocks_utils.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/base/rr_blocks_utils.cpp index 197da9c8b..9184e29d6 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/base/rr_blocks_utils.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/rr_blocks_utils.cpp @@ -8,6 +8,64 @@ #include "vpr_types.h" #include "fpga_x2p_types.h" #include "rr_blocks_utils.h" + +/********************************************************************* + * This function will find the global ports required by a Connection Block + * module. It will find all the circuit models in the circuit library + * that may be included in the connection block + * Collect the global ports from the circuit_models and merge with the same name + ********************************************************************/ +std::vector find_connection_block_global_ports(const RRGSB& rr_gsb, + const t_rr_type& cb_type, + const CircuitLibrary& circuit_lib, + const std::vector& switch_lib) { + std::vector sub_models; + /* Walk through the OUTPUT nodes at each side of a GSB, + * get the switch id of incoming edges + * and get the circuit model linked to the switch id + */ + 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) { + /* Find the size of routing multiplexers driving this IPIN node */ + int mux_size = rr_gsb.get_ipin_node(cb_ipin_side, inode)->fan_in; + /* Bypass fan_in == 1 or 0, they are not considered as routing multiplexers */ + if (2 > mux_size) { + continue; + } + /* Find the driver switch of the node */ + short driver_switch = rr_gsb.get_ipin_node(cb_ipin_side, inode)->drive_switches[DEFAULT_SWITCH_ID]; + /* Find the circuit model id of the driver switch */ + VTR_ASSERT( (size_t)driver_switch < switch_lib.size() ); + /* Get the model, and try to add to the sub_model list */ + CircuitModelId switch_circuit_model = switch_lib[driver_switch].circuit_model; + /* Make sure it is a valid id */ + VTR_ASSERT( CircuitModelId::INVALID() != switch_circuit_model ); + /* Get the model, and try to add to the sub_model list */ + if (sub_models.end() == std::find(sub_models.begin(), sub_models.end(), switch_circuit_model)) { + /* Not yet in the list, add it */ + sub_models.push_back(switch_circuit_model); + } + } + } + + std::vector global_ports; + /* Iterate over the model list, and add the global ports*/ + for (const auto& model : sub_models) { + std::vector temp_global_ports = circuit_lib.model_global_ports(model, true); + /* Add the temp_global_ports to the list to be returned, make sure we do not have any duplicated ports */ + for (const auto& port_candidate : temp_global_ports) { + if (global_ports.end() == std::find(global_ports.begin(), global_ports.end(), port_candidate)) { + /* Not yet in the list, add it */ + global_ports.push_back(port_candidate); + } + } + } + + return global_ports; +} + /********************************************************************* * This function will find the global ports required by a Switch Block @@ -61,6 +119,32 @@ std::vector find_switch_block_global_ports(const RRGSB& rr_gsb, return global_ports; } +/********************************************************************* + * This function will find the number of multiplexers required by + * a connection Block module. + ********************************************************************/ +size_t find_connection_block_number_of_muxes(const RRGSB& rr_gsb, + const t_rr_type& cb_type) { + size_t num_muxes = 0; + + 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) { + /* Find the size of routing multiplexers driving this IPIN node */ + int mux_size = rr_gsb.get_ipin_node(cb_ipin_side, inode)->fan_in; + /* Bypass fan_in == 1 or 0, they are not considered as routing multiplexers */ + if (2 > mux_size) { + continue; + } + /* This means we need a multiplexer, update the counter */ + num_muxes++; + } + } + + return num_muxes; +} + /********************************************************************* * This function will find the number of multiplexers required by * a Switch Block module. diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/rr_blocks_utils.h b/vpr7_x2p/vpr/SRC/fpga_x2p/base/rr_blocks_utils.h index d49a55539..0ad9113fb 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/base/rr_blocks_utils.h +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/rr_blocks_utils.h @@ -10,10 +10,18 @@ #include "circuit_library.h" #include "rr_blocks.h" +std::vector find_connection_block_global_ports(const RRGSB& rr_gsb, + const t_rr_type& cb_type, + const CircuitLibrary& circuit_lib, + const std::vector& switch_lib); + std::vector find_switch_block_global_ports(const RRGSB& rr_gsb, const CircuitLibrary& circuit_lib, const std::vector& switch_lib); +size_t find_connection_block_number_of_muxes(const RRGSB& rr_gsb, + const t_rr_type& cb_type); + size_t find_switch_block_number_of_muxes(const RRGSB& rr_gsb); #endif 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 ff68546a3..4ba51216f 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_routing.c +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_routing.c @@ -43,6 +43,7 @@ #include "mux_utils.h" #include "module_manager.h" #include "module_manager_utils.h" +#include "fpga_x2p_mem_utils.h" /* Include Verilog support headers*/ #include "verilog_global.h" @@ -50,6 +51,30 @@ #include "verilog_writer_utils.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 @@ -62,7 +87,7 @@ 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 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) { @@ -72,6 +97,28 @@ void print_verilog_switch_block_local_sram_wires(std::fstream& fp, 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 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 @@ -2157,6 +2204,29 @@ 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 ********************************************************************/ @@ -2179,6 +2249,39 @@ BasicPort generate_verilog_unique_switch_box_chan_port(const RRGSB& rr_sb, 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 @@ -2231,6 +2334,24 @@ BasicPort generate_switch_block_input_port(const RRGSB& rr_sb, 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 ********************************************************************/ @@ -2330,6 +2451,7 @@ 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, const ModuleId& sb_module, const RRGSB& rr_sb, const CircuitLibrary& circuit_lib, @@ -2460,18 +2582,22 @@ void print_verilog_unique_switch_box_mux(ModuleManager& module_manager, std::map mem_port2port_name_map; /* TODO: Make the port2port map generation more generic!!! */ - std::vector config_ports; - config_ports.push_back(BasicPort(generate_local_config_bus_port_name(), mux_instance_id - 1, mux_instance_id)); + /* 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(module_manager, mem_module, - config_ports, + 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 MUX Module */ + /* 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 -----")); @@ -2500,6 +2626,7 @@ 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, const ModuleId& sb_module, const RRGSB& rr_sb, const CircuitLibrary& circuit_lib, @@ -2534,7 +2661,7 @@ void print_verilog_unique_switch_box_interc(ModuleManager& module_manager, 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, + print_verilog_unique_switch_box_mux(module_manager, fp, cur_sram_orgz_info, config_bus, sb_module, rr_sb, circuit_lib, mux_lib, rr_switches, chan_side, cur_rr_node, drive_rr_nodes, @@ -2734,6 +2861,11 @@ void print_verilog_routing_switch_box_unique_module(ModuleManager& module_manage 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); + /* TODO: Print routing multiplexers */ for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { Side side_manager(side); @@ -2741,7 +2873,8 @@ void print_verilog_routing_switch_box_unique_module(ModuleManager& module_manage 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, module_id, rr_sb, + print_verilog_unique_switch_box_interc(module_manager, fp, cur_sram_orgz_info, config_bus, + module_id, rr_sb, circuit_lib, mux_lib, rr_switches, side_manager.get_side(), itrack, is_explicit_mapping); @@ -2749,6 +2882,11 @@ void print_verilog_routing_switch_box_unique_module(ModuleManager& module_manage } } + /* TODO: 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())); + /* Put an end to the Verilog module */ print_verilog_module_end(fp, module_manager.module_name(module_id)); @@ -3261,6 +3399,46 @@ 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, @@ -3411,6 +3589,186 @@ 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, + 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 */ + /* TODO: 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); + 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(module_manager, mem_module, + 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, @@ -3903,6 +4261,43 @@ 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, + 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, + 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, @@ -4050,6 +4445,279 @@ 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 = count_verilog_connection_box_conf_bits(cur_sram_orgz_info, rr_gsb, cb_type); + /* Count the number of reserved configuration bits to be consumed by this Switch block */ + int num_reserved_conf_bits = count_verilog_connection_box_reserved_conf_bits(cur_sram_orgz_info, 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); + + /* 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, module_id, rr_gsb, cb_type, + circuit_lib, mux_lib, rr_switches, + rr_gsb.get_ipin_node(cb_ipin_side, inode), + use_explicit_mapping); + } + } + + /* 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 @@ -4632,6 +5300,15 @@ void print_verilog_routing_resources(ModuleManager& module_manager, dump_verilog_routing_connection_box_unique_module(cur_sram_orgz_info, 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]*/ @@ -4640,6 +5317,15 @@ void print_verilog_routing_resources(ModuleManager& module_manager, dump_verilog_routing_connection_box_unique_module(cur_sram_orgz_info, 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 */