From 4b5ecc516bb9127e89ce6650fb47f43c5cd0e204 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 9 Nov 2019 10:52:15 -0700 Subject: [PATCH] refactored SDC SB constrain generation --- .../backend_assistant/pnr_sdc_writer.cpp | 389 +++++++++++++++++- .../backend_assistant/pnr_sdc_writer.h | 8 +- .../fpga_x2p/backend_assistant/sdc_api.cpp | 14 +- .../SRC/fpga_x2p/backend_assistant/sdc_api.h | 8 +- .../backend_assistant/sdc_writer_naming.h | 4 +- .../backend_assistant/sdc_writer_utils.cpp | 29 ++ .../backend_assistant/sdc_writer_utils.h | 8 + vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_api.c | 6 +- .../build_routing_module_utils.cpp | 219 ++++++++++ .../build_routing_module_utils.h | 49 +++ .../module_builder/build_routing_modules.cpp | 213 +--------- 11 files changed, 715 insertions(+), 232 deletions(-) create mode 100644 vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_routing_module_utils.cpp create mode 100644 vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_routing_module_utils.h diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/pnr_sdc_writer.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/pnr_sdc_writer.cpp index 1fb4ebeaa..a693d0c2c 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/pnr_sdc_writer.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/pnr_sdc_writer.cpp @@ -21,6 +21,8 @@ #include "fpga_x2p_naming.h" #include "fpga_x2p_utils.h" +#include "build_routing_module_utils.h" + #include "sdc_writer_naming.h" #include "sdc_writer_utils.h" #include "pnr_sdc_writer.h" @@ -305,6 +307,327 @@ void print_sdc_disable_routing_multiplexer_outputs(const std::string& sdc_dir, run_time_sec); } +/******************************************************************** + * Break combinational loops in FPGA fabric, which mainly come from + * loops of multiplexers. + * To handle this, we disable the timing at outputs of Switch blocks + * This function is designed for flatten routing hierarchy + *******************************************************************/ +static +void print_pnr_sdc_flatten_routing_disable_switch_block_outputs(const std::string& sdc_dir, + const ModuleManager& module_manager, + const DeviceRRGSB& L_device_rr_gsb) { + /* Create the file name for Verilog netlist */ + std::string sdc_fname(sdc_dir + std::string(SDC_DISABLE_SB_OUTPUTS_FILE_NAME)); + + vpr_printf(TIO_MESSAGE_INFO, + "Generating SDC for disable Switch Block outputs for P&R flow: %s ...", + sdc_fname.c_str()); + + /* Start time count */ + clock_t t_start = clock(); + + /* Create the file stream */ + std::fstream fp; + fp.open(sdc_fname, std::fstream::out | std::fstream::trunc); + + check_file_handler(fp); + + /* Generate the descriptions*/ + print_sdc_file_header(fp, std::string("Disable Switch Block outputs for PnR")); + + /* Get the range of SB array */ + DeviceCoordinator sb_range = L_device_rr_gsb.get_gsb_range(); + /* Go for each SB */ + 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); + vtr::Point gsb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y()); + std::string sb_instance_name = generate_switch_block_module_name(gsb_coordinate); + + ModuleId sb_module = module_manager.find_module(sb_instance_name); + VTR_ASSERT(true == module_manager.valid_module_id(sb_module)); + + /* Disable the outputs of the module */ + for (const BasicPort& output_port : module_manager.module_ports_by_type(sb_module, ModuleManager::MODULE_OUTPUT_PORT)) { + fp << "set_disable_timing " << sb_instance_name << "/" << output_port.get_name() << std::endl; + fp << std::endl; + } + } + } + + /* Close file handler */ + fp.close(); + + /* End time count */ + clock_t t_end = clock(); + + float run_time_sec = (float)(t_end - t_start) / CLOCKS_PER_SEC; + vpr_printf(TIO_MESSAGE_INFO, + "took %g seconds\n", + run_time_sec); +} + +/******************************************************************** + * Break combinational loops in FPGA fabric, which mainly come from + * loops of multiplexers. + * To handle this, we disable the timing at outputs of Switch blocks + * This function is designed for compact routing hierarchy + *******************************************************************/ +static +void print_pnr_sdc_compact_routing_disable_switch_block_outputs(const std::string& sdc_dir, + const ModuleManager& module_manager, + const ModuleId& top_module, + const DeviceRRGSB& L_device_rr_gsb) { + /* Create the file name for Verilog netlist */ + std::string sdc_fname(sdc_dir + std::string(SDC_DISABLE_SB_OUTPUTS_FILE_NAME)); + + vpr_printf(TIO_MESSAGE_INFO, + "Generating SDC for disable Switch Block outputs for P&R flow: %s ...", + sdc_fname.c_str()); + + /* Start time count */ + clock_t t_start = clock(); + + /* Create the file stream */ + std::fstream fp; + fp.open(sdc_fname, std::fstream::out | std::fstream::trunc); + + check_file_handler(fp); + + /* Generate the descriptions*/ + print_sdc_file_header(fp, std::string("Disable Switch Block outputs for PnR")); + + /* Build unique switch block modules */ + for (size_t isb = 0; isb < L_device_rr_gsb.get_num_sb_unique_module(); ++isb) { + const RRGSB& rr_gsb = L_device_rr_gsb.get_sb_unique_module(isb); + vtr::Point gsb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y()); + std::string sb_module_name = generate_switch_block_module_name(gsb_coordinate); + + ModuleId sb_module = module_manager.find_module(sb_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(sb_module)); + + /* Find all the instances in the top-level module */ + for (const size_t& instance_id : module_manager.child_module_instances(top_module, sb_module)) { + std::string sb_instance_name = module_manager.instance_name(top_module, sb_module, instance_id); + /* Disable the outputs of the module */ + for (const BasicPort& output_port : module_manager.module_ports_by_type(sb_module, ModuleManager::MODULE_OUTPUT_PORT)) { + fp << "set_disable_timing " << sb_instance_name << "/" << output_port.get_name() << std::endl; + fp << std::endl; + } + } + } + + /* Close file handler */ + fp.close(); + + /* End time count */ + clock_t t_end = clock(); + + float run_time_sec = (float)(t_end - t_start) / CLOCKS_PER_SEC; + vpr_printf(TIO_MESSAGE_INFO, + "took %g seconds\n", + run_time_sec); +} + +/******************************************************************** + * Find the timing constraints between the inputs and outputs of a routing + * multiplexer in a Switch Block + *******************************************************************/ +static +float find_pnr_sdc_switch_tmax(const t_switch_inf& switch_inf) { + return switch_inf.R * switch_inf.Cout + switch_inf.Tdel; +} + +/******************************************************************** + * Set timing constraints between the inputs and outputs of a routing + * multiplexer in a Switch Block + *******************************************************************/ +static +void print_pnr_sdc_constrain_sb_mux_timing(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& sb_module, + const RRGSB& rr_gsb, + const std::vector>& grids, + const std::vector& switches, + const e_side& output_node_side, + t_rr_node* output_rr_node) { + /* Validate file stream */ + check_file_handler(fp); + + VTR_ASSERT( ( CHANX == output_rr_node->type ) + || ( CHANY == output_rr_node->type )); + + /* Find the module port corresponding to the output rr_node */ + ModulePortId module_output_port = find_switch_block_module_chan_port(module_manager, + sb_module, + rr_gsb, + output_node_side, + output_rr_node, + OUT_PORT); + + /* Find the module port corresponding to the fan-in rr_nodes of the output rr_node */ + std::vector input_rr_nodes; + for (int iedge = 0; iedge < output_rr_node->num_drive_rr_nodes; iedge++) { + input_rr_nodes.push_back(output_rr_node->drive_rr_nodes[iedge]); + } + + std::vector module_input_ports = find_switch_block_module_input_ports(module_manager, + sb_module, + rr_gsb, + grids, + input_rr_nodes); + + /* Find timing constraints for each path (edge) */ + std::map switch_delays; + for (int iedge = 0; iedge < output_rr_node->num_drive_rr_nodes; iedge++) { + /* Get the switch delay */ + int switch_id = output_rr_node->drive_switches[iedge]; + switch_delays[module_input_ports[iedge]] = find_pnr_sdc_switch_tmax(switches[switch_id]); + } + + /* Find the starting points */ + for (const ModulePortId& module_input_port : module_input_ports) { + /* Constrain a path */ + print_pnr_sdc_constrain_module_port2port_timing(fp, + module_manager, sb_module, + module_input_port, module_output_port, + switch_delays[module_input_port]); + } +} + +/******************************************************************** + * Set timing constraints between the inputs and outputs of SBs, + * which are connected by routing multiplexers with the given delays + * specified in architectural XML file + * + * To enable block by block timing constraining, we generate the SDC + * file for each unique SB module + *******************************************************************/ +static +void print_pnr_sdc_constrain_sb_timing(const std::string& sdc_dir, + const ModuleManager& module_manager, + const std::vector>& grids, + const std::vector& switches, + const RRGSB& rr_gsb) { + + /* Create the file name for Verilog netlist */ + vtr::Point gsb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y()); + std::string sdc_fname(sdc_dir + generate_switch_block_module_name(gsb_coordinate) + std::string(SDC_FILE_NAME_POSTFIX)); + + /* Create the file stream */ + std::fstream fp; + fp.open(sdc_fname, std::fstream::out | std::fstream::trunc); + + /* Validate file stream */ + check_file_handler(fp); + + std::string sb_module_name = generate_switch_block_module_name(gsb_coordinate); + ModuleId sb_module = module_manager.find_module(sb_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(sb_module)); + + /* Generate the descriptions*/ + print_sdc_file_header(fp, std::string("Constrain timing of Switch Block " + sb_module_name + " outputs for PnR")); + + 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) { + t_rr_node* chan_rr_node = rr_gsb.get_chan_node(side_manager.get_side(), itrack); + /* We only care the output port and it should indicate a SB mux */ + if (OUT_PORT != rr_gsb.get_chan_node_direction(side_manager.get_side(), itrack)) { + continue; + } + /* Constrain thru wires */ + if (false != rr_gsb.is_sb_node_passing_wire(side_manager.get_side(), itrack)) { + continue; + } + /* This is a MUX, constrain all the paths from an input to an output */ + print_pnr_sdc_constrain_sb_mux_timing(fp, + module_manager, sb_module, + rr_gsb, + grids, switches, + side_manager.get_side(), + chan_rr_node); + } + } + + /* Close file handler */ + fp.close(); +} + +/******************************************************************** + * Print SDC timing constraints for Switch blocks + * This function is designed for flatten routing hierarchy + *******************************************************************/ +static +void print_pnr_sdc_flatten_routing_constrain_sb_timing(const std::string& sdc_dir, + const ModuleManager& module_manager, + const std::vector>& grids, + const std::vector& switches, + const DeviceRRGSB& L_device_rr_gsb) { + vpr_printf(TIO_MESSAGE_INFO, + "Generating SDC for constrain Switch Block timing for P&R flow..."); + + /* Start time count */ + clock_t t_start = clock(); + + /* Get the range of SB array */ + DeviceCoordinator sb_range = L_device_rr_gsb.get_gsb_range(); + /* Go for each SB */ + 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_pnr_sdc_constrain_sb_timing(sdc_dir, + module_manager, + grids, switches, + rr_gsb); + } + } + + /* End time count */ + clock_t t_end = clock(); + + float run_time_sec = (float)(t_end - t_start) / CLOCKS_PER_SEC; + vpr_printf(TIO_MESSAGE_INFO, + "took %g seconds\n", + run_time_sec); +} + +/******************************************************************** + * Break combinational loops in FPGA fabric, which mainly come from + * loops of multiplexers. + * To handle this, we disable the timing at outputs of Switch blocks + * This function is designed for compact routing hierarchy + *******************************************************************/ +static +void print_pnr_sdc_compact_routing_constrain_sb_timing(const std::string& sdc_dir, + const ModuleManager& module_manager, + const std::vector>& grids, + const std::vector& switches, + const DeviceRRGSB& L_device_rr_gsb) { + vpr_printf(TIO_MESSAGE_INFO, + "Generating SDC for constrain Switch Block timing for P&R flow..."); + + /* Start time count */ + clock_t t_start = clock(); + + for (size_t isb = 0; isb < L_device_rr_gsb.get_num_sb_unique_module(); ++isb) { + const RRGSB& rr_gsb = L_device_rr_gsb.get_sb_unique_module(isb); + print_pnr_sdc_constrain_sb_timing(sdc_dir, + module_manager, + grids, switches, + rr_gsb); + } + + /* End time count */ + clock_t t_end = clock(); + + float run_time_sec = (float)(t_end - t_start) / CLOCKS_PER_SEC; + vpr_printf(TIO_MESSAGE_INFO, + "took %g seconds\n", + run_time_sec); +} + /******************************************************************** * Top-level function to print a number of SDC files in different purpose * This function will generate files upon the options provided by users @@ -315,12 +638,16 @@ void print_sdc_disable_routing_multiplexer_outputs(const std::string& sdc_dir, *******************************************************************/ void print_pnr_sdc(const SdcOption& sdc_options, const float& critical_path_delay, + const std::vector>& grids, + const std::vector& switches, + const DeviceRRGSB& L_device_rr_gsb, + const ModuleManager& module_manager, const MuxLibrary& mux_lib, const CircuitLibrary& circuit_lib, - const ModuleManager& module_manager, - const std::vector& global_ports) { + const std::vector& global_ports, + const bool& compact_routing_hierarchy) { - /* Part 1. Constrain global ports */ + /* Constrain global ports */ if (true == sdc_options.constrain_global_port()) { print_pnr_sdc_global_ports(sdc_options.sdc_dir(), critical_path_delay, circuit_lib, global_ports); } @@ -329,24 +656,66 @@ void print_pnr_sdc(const SdcOption& sdc_options, ModuleId top_module = module_manager.find_module(top_module_name); VTR_ASSERT(true == module_manager.valid_module_id(top_module)); - /* Part 2. Output Design Constraints to disable outputs of memory cells */ + /* Output Design Constraints to disable outputs of memory cells */ if (true == sdc_options.constrain_configurable_memory_outputs()) { print_pnr_sdc_constrain_configurable_memory_outputs(sdc_options.sdc_dir(), module_manager, top_module); } - /* 2. Break loops from Multiplexer Output */ + /* Break loops from Multiplexer Output */ if (true == sdc_options.constrain_routing_multiplexer_outputs()) { print_sdc_disable_routing_multiplexer_outputs(sdc_options.sdc_dir(), mux_lib, circuit_lib, module_manager); } - /* TODO: 3. Break loops from any SB output */ + /* Break loops from any SB output */ + if (true == sdc_options.constrain_switch_block_outputs()) { + if (true == compact_routing_hierarchy) { + print_pnr_sdc_compact_routing_disable_switch_block_outputs(sdc_options.sdc_dir(), + module_manager, top_module, + L_device_rr_gsb); + } else { + VTR_ASSERT_SAFE (false == compact_routing_hierarchy); + print_pnr_sdc_flatten_routing_disable_switch_block_outputs(sdc_options.sdc_dir(), + module_manager, + L_device_rr_gsb); + } + } + + /* Output routing constraints for Switch Blocks */ + if (true == sdc_options.constrain_sb()) { + if (true == compact_routing_hierarchy) { + print_pnr_sdc_compact_routing_constrain_sb_timing(sdc_options.sdc_dir(), + module_manager, + grids, switches, + L_device_rr_gsb); + } else { + VTR_ASSERT_SAFE (false == compact_routing_hierarchy); + print_pnr_sdc_flatten_routing_constrain_sb_timing(sdc_options.sdc_dir(), + module_manager, + grids, switches, + L_device_rr_gsb); + } + } + + /* TODO: Output routing constraints for Connection Blocks */ /* - if (TRUE == sdc_opts.compact_routing_hierarchy) { - verilog_generate_sdc_break_loop_sb(fp, LL_device_rr_gsb); - } else { - verilog_generate_sdc_break_loop_sb(fp, LL_nx, LL_ny); + if (true == sdc_options.constrain_cb()) { + if (true == compact_routing_hierarchy) { + verilog_generate_sdc_constrain_cbs(sdc_opts, LL_nx, LL_ny, LL_device_rr_gsb); + } else { + VTR_ASSERT_SAFE (false == compact_routing_hierarchy); + verilog_generate_sdc_constrain_cbs(sdc_opts, + LL_nx, LL_ny); + } + } + */ + + /* TODO: Output routing constraints for Programmable blocks */ + /* + if (true == sdc_options.constrain_grid()) { + verilog_generate_sdc_constrain_pb_types(cur_sram_orgz_info, + sdc_dir); } */ } diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/pnr_sdc_writer.h b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/pnr_sdc_writer.h index 6923320a6..6b618b2df 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/pnr_sdc_writer.h +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/pnr_sdc_writer.h @@ -12,9 +12,13 @@ void print_pnr_sdc(const SdcOption& sdc_options, const float& critical_path_delay, + const std::vector>& grids, + const std::vector& switches, + const DeviceRRGSB& L_device_rr_gsb, + const ModuleManager& module_manager, const MuxLibrary& mux_lib, const CircuitLibrary& circuit_lib, - const ModuleManager& module_manager, - const std::vector& global_ports); + const std::vector& global_ports, + const bool& compact_routing_hierarchy); #endif diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_api.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_api.cpp index 99a981df8..690e456c5 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_api.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_api.cpp @@ -11,10 +11,14 @@ *******************************************************************/ void fpga_sdc_generator(const SdcOption& sdc_options, const float& critical_path_delay, + const std::vector>& grids, + const std::vector& switches, + const DeviceRRGSB& L_device_rr_gsb, + const ModuleManager& module_manager, const MuxLibrary& mux_lib, const CircuitLibrary& circuit_lib, - const ModuleManager& module_manager, - const std::vector& global_ports) { + const std::vector& global_ports, + const bool& compact_routing_hierarchy) { vpr_printf(TIO_MESSAGE_INFO, "SDC generator starts..."); @@ -22,7 +26,11 @@ void fpga_sdc_generator(const SdcOption& sdc_options, clock_t t_start = clock(); if (true == sdc_options.generate_sdc_pnr()) { - print_pnr_sdc(sdc_options, critical_path_delay, mux_lib, circuit_lib, module_manager, global_ports); + print_pnr_sdc(sdc_options, critical_path_delay, + grids, switches, L_device_rr_gsb, + module_manager, mux_lib, + circuit_lib, global_ports, + compact_routing_hierarchy); } /* End time count */ diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_api.h b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_api.h index e0f64fafd..d53dc201a 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_api.h +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_api.h @@ -9,9 +9,13 @@ void fpga_sdc_generator(const SdcOption& sdc_options, const float& critical_path_delay, + const std::vector>& grids, + const std::vector& switches, + const DeviceRRGSB& L_device_rr_gsb, + const ModuleManager& module_manager, const MuxLibrary& mux_lib, const CircuitLibrary& circuit_lib, - const ModuleManager& module_manager, - const std::vector& global_ports); + const std::vector& global_ports, + const bool& compact_routing_hierarchy); #endif diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_writer_naming.h b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_writer_naming.h index 7371835a8..e479f95d3 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_writer_naming.h +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_writer_naming.h @@ -1,11 +1,13 @@ #ifndef SDC_WRITER_NAMING_H #define SDC_WRITER_NAMING_H +constexpr char* SDC_FILE_NAME_POSTFIX = ".sdc"; + constexpr char* SDC_GLOBAL_PORTS_FILE_NAME = "global_ports.sdc"; constexpr char* SDC_BENCHMARK_ANALYSIS_FILE_NAME= "fpga_top_analysis.sdc"; constexpr char* SDC_DISABLE_CONFIG_MEM_OUTPUTS_FILE_NAME = "disable_configurable_memory_outputs.sdc"; constexpr char* SDC_DISABLE_MUX_OUTPUTS_FILE_NAME = "disable_routing_multiplexer_outputs.sdc"; +constexpr char* SDC_DISABLE_SB_OUTPUTS_FILE_NAME = "disable_sb_outputs.sdc"; constexpr char* SDC_CB_FILE_NAME = "cb.sdc"; -constexpr char* SDC_SB_FILE_NAME = "sb.sdc"; #endif diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_writer_utils.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_writer_utils.cpp index 12ef60b6b..6a74c290d 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_writer_utils.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_writer_utils.cpp @@ -3,6 +3,7 @@ *******************************************************************/ #include #include +#include #include "fpga_x2p_utils.h" @@ -50,3 +51,31 @@ std::string generate_sdc_port(const BasicPort& port) { return sdc_line; } + +/******************************************************************** + * Constrain a path between two ports of a module with a given timing value + *******************************************************************/ +void print_pnr_sdc_constrain_module_port2port_timing(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const ModulePortId& module_input_port_id, + const ModulePortId& module_output_port_id, + const float& tmax) { + /* Validate file stream */ + check_file_handler(fp); + + fp << "set_max_delay"; + + fp << " -from "; + fp << module_manager.module_name(module_id) << "/"; + fp << generate_sdc_port(module_manager.module_port(module_id, module_input_port_id)); + + fp << " -to "; + + fp << module_manager.module_name(module_id) << "/"; + fp << generate_sdc_port(module_manager.module_port(module_id, module_output_port_id)); + + fp << " " << std::setprecision(10) << tmax; + + fp << std::endl; +} diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_writer_utils.h b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_writer_utils.h index b3d916f57..d4c2ae860 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_writer_utils.h +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_writer_utils.h @@ -4,10 +4,18 @@ #include #include #include "device_port.h" +#include "module_manager.h" void print_sdc_file_header(std::fstream& fp, const std::string& usage); std::string generate_sdc_port(const BasicPort& port); +void print_pnr_sdc_constrain_module_port2port_timing(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const ModulePortId& module_input_port_id, + const ModulePortId& module_output_port_id, + const float& tmax); + #endif 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 8c1f6b5e2..a254d4824 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 @@ -159,8 +159,10 @@ void vpr_fpga_x2p_tool_suites(t_vpr_setup vpr_setup, /* TODO: the critical path delay unit should be explicit! */ fpga_sdc_generator(sdc_options, Arch.spice->spice_params.stimulate_params.vpr_crit_path_delay / 1e-9, - mux_lib, - Arch.spice->circuit_lib, module_manager, global_ports); + grids, rr_switches, device_rr_gsb, + module_manager, mux_lib, + Arch.spice->circuit_lib, global_ports, + TRUE == vpr_setup.FPGA_SPICE_Opts.compact_routing_hierarchy); } /* Xifan Tang: Bitstream Generator */ diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_routing_module_utils.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_routing_module_utils.cpp new file mode 100644 index 000000000..a81eb567f --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_routing_module_utils.cpp @@ -0,0 +1,219 @@ +/******************************************************************** + * This file includes most utilized functions that are used to build modules + * for global routing architecture of a FPGA fabric + * Covering: + * 1. Connection blocks + * 2. Switch blocks + *******************************************************************/ +#include "vtr_assert.h" +#include "vtr_geometry.h" +#include "device_coordinator.h" + +#include "fpga_x2p_naming.h" + +#include "build_routing_module_utils.h" + +/********************************************************************* + * Generate a port for a routing track of a swtich block + ********************************************************************/ +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 + ********************************************************************/ +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 + ********************************************************************/ +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 an input port for routing multiplexer inside the connection block + * which is the middle output of a routing track + ********************************************************************/ +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 + ********************************************************************/ +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 + ********************************************************************/ +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; +} diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_routing_module_utils.h b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_routing_module_utils.h new file mode 100644 index 000000000..aa423b12e --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_routing_module_utils.h @@ -0,0 +1,49 @@ +#ifndef BUILD_ROUTING_MODULE_UTILS_H +#define BUILD_ROUTING_MODULE_UTILS_H + +#include +#include "rr_blocks.h" +#include "module_manager.h" +#include "sides.h" +#include "vpr_types.h" + +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); + +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); + +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); + +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 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); + + +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); + +#endif 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 index c460f45b4..d2b5b4464 100644 --- 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 @@ -21,135 +21,11 @@ #include "fpga_x2p_utils.h" #include "module_manager_utils.h" #include "build_module_graph_utils.h" +#include "build_routing_module_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. @@ -550,93 +426,6 @@ void build_switch_block_module(ModuleManager& module_manager, } } -/********************************************************************* - * 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 ********************************************************************/