diff --git a/openfpga/src/base/openfpga_bitstream.cpp b/openfpga/src/base/openfpga_bitstream.cpp index 08fa7bf16..e732dee92 100644 --- a/openfpga/src/base/openfpga_bitstream.cpp +++ b/openfpga/src/base/openfpga_bitstream.cpp @@ -5,6 +5,9 @@ #include "vtr_time.h" #include "vtr_log.h" +/* Headers from openfpgautil library */ +#include "openfpga_digest.h" + #include "build_device_bitstream.h" #include "bitstream_writer.h" #include "build_fabric_bitstream.h" @@ -30,6 +33,11 @@ void fpga_bitstream(OpenfpgaContext& openfpga_ctx, cmd_context.option_enable(cmd, opt_verbose)); if (true == cmd_context.option_enable(cmd, opt_file)) { + std::string src_dir_path = find_path_dir_name(cmd_context.option_value(cmd, opt_file)); + + /* Create directories */ + create_dir_path(src_dir_path.c_str()); + write_arch_independent_bitstream_to_xml_file(openfpga_ctx.bitstream_manager(), cmd_context.option_value(cmd, opt_file)); } diff --git a/openfpga/src/base/openfpga_context.h b/openfpga/src/base/openfpga_context.h index 4b6cdf2d2..20e235a35 100644 --- a/openfpga/src/base/openfpga_context.h +++ b/openfpga/src/base/openfpga_context.h @@ -60,6 +60,7 @@ class OpenfpgaContext : public Context { const openfpga::BitstreamManager& bitstream_manager() const { return bitstream_manager_; } const std::vector& fabric_bitstream() const { return fabric_bitstream_; } const openfpga::IoLocationMap& io_location_map() { return io_location_map_; } + const std::unordered_map& net_activity() { return net_activity_; } public: /* Public mutators */ openfpga::Arch& mutable_arch() { return arch_; } openfpga::VprDeviceAnnotation& mutable_vpr_device_annotation() { return vpr_device_annotation_; } @@ -75,6 +76,7 @@ class OpenfpgaContext : public Context { openfpga::BitstreamManager& mutable_bitstream_manager() { return bitstream_manager_; } std::vector& mutable_fabric_bitstream() { return fabric_bitstream_; } openfpga::IoLocationMap& mutable_io_location_map() { return io_location_map_; } + std::unordered_map& mutable_net_activity() { return net_activity_; } private: /* Internal data */ /* Data structure to store information from read_openfpga_arch library */ openfpga::Arch arch_; @@ -110,6 +112,9 @@ class OpenfpgaContext : public Context { /* Bitstream database */ openfpga::BitstreamManager bitstream_manager_; std::vector fabric_bitstream_; + + /* Net activities of users' implementation */ + std::unordered_map net_activity_; /* Flow status */ openfpga::FlowManager flow_manager_; diff --git a/openfpga/src/base/openfpga_link_arch.cpp b/openfpga/src/base/openfpga_link_arch.cpp index 9dc1e38c7..fe89b7024 100644 --- a/openfpga/src/base/openfpga_link_arch.cpp +++ b/openfpga/src/base/openfpga_link_arch.cpp @@ -2,11 +2,20 @@ * This file includes functions to read an OpenFPGA architecture file * which are built on the libarchopenfpga library *******************************************************************/ +#include +#include + /* Headers from vtrutil library */ #include "vtr_time.h" #include "vtr_assert.h" #include "vtr_log.h" +/* Headers from vpr library */ +#include "timing_info.h" +#include "AnalysisDelayCalculator.h" +#include "net_delay.h" +#include "read_activity.h" + #include "vpr_device_annotation.h" #include "pb_type_utils.h" #include "annotate_pb_types.h" @@ -46,6 +55,163 @@ bool is_vpr_rr_graph_supported(const RRGraph& rr_graph) { return true; } +/******************************************************************** + * Find the number of clock cycles in simulation based on the average signal density + *******************************************************************/ +static +size_t recommend_num_sim_clock_cycle(const AtomContext& atom_ctx, + const std::unordered_map& net_activity, + const float& sim_window_size) { + size_t recmd_num_sim_clock_cycle = 0; + + float avg_density = 0.; + size_t net_cnt = 0; + + float weighted_avg_density = 0.; + size_t weighted_net_cnt = 0; + + /* get the average density of all the nets */ + for (const AtomNetId& atom_net : atom_ctx.nlist.nets()) { + /* Skip the nets without any activity annotation */ + if (0 == net_activity.count(atom_net)) { + continue; + } + + /* Only care non-zero density nets */ + if (0. == net_activity.at(atom_net).density) { + continue; + } + + avg_density += net_activity.at(atom_net).density; + net_cnt++; + + /* Consider the weight of fan-out */ + size_t net_weight; + if (0 == std::distance(atom_ctx.nlist.net_sinks(atom_net).begin(), atom_ctx.nlist.net_sinks(atom_net).end())) { + net_weight = 1; + } else { + VTR_ASSERT(0 < std::distance(atom_ctx.nlist.net_sinks(atom_net).begin(), atom_ctx.nlist.net_sinks(atom_net).end())); + net_weight = std::distance(atom_ctx.nlist.net_sinks(atom_net).begin(), atom_ctx.nlist.net_sinks(atom_net).end()); + } + weighted_avg_density += net_activity.at(atom_net).density* net_weight; + weighted_net_cnt += net_weight; + } + avg_density = avg_density / net_cnt; + weighted_avg_density = weighted_avg_density / weighted_net_cnt; + + /* Sort the net density */ + std::vector net_densities; + net_densities.reserve(net_cnt); + for (const AtomNetId& atom_net : atom_ctx.nlist.nets()) { + /* Skip the nets without any activity annotation */ + if (0 == net_activity.count(atom_net)) { + continue; + } + + /* Only care non-zero density nets */ + if (0. == net_activity.at(atom_net).density) { + continue; + } + + net_densities.push_back(net_activity.at(atom_net).density); + } + std::sort(net_densities.begin(), net_densities.end()); + /* Get the median */ + float median_density = 0.; + /* check for even case */ + if (net_cnt % 2 != 0) { + median_density = net_densities[size_t(net_cnt / 2)]; + } else { + median_density = 0.5 * (net_densities[size_t((net_cnt - 1) / 2)] + net_densities[size_t((net_cnt - 1) / 2)]); + } + + /* It may be more reasonable to use median + * But, if median density is 0, we use average density + */ + if ((0. == median_density) && (0. == avg_density)) { + recmd_num_sim_clock_cycle = 1; + VTR_LOG_WARN("All the signal density is zero!\nNumber of clock cycles in simulations are set to be %ld!\n", + recmd_num_sim_clock_cycle); + } else if (0. == avg_density) { + recmd_num_sim_clock_cycle = (int)round(1 / median_density); + } else if (0. == median_density) { + recmd_num_sim_clock_cycle = (int)round(1 / avg_density); + } else { + /* add a sim window size to balance the weight of average density and median density + * In practice, we find that there could be huge difference between avereage and median values + * For a reasonable number of simulation clock cycles, we do this window size. + */ + recmd_num_sim_clock_cycle = (int)round(1 / (sim_window_size * avg_density + (1 - sim_window_size) * median_density )); + } + + VTR_ASSERT(0 < recmd_num_sim_clock_cycle); + + VTR_LOG("Average net density: %.2f\n", avg_density); + VTR_LOG("Median net density: %.2f\n", median_density); + VTR_LOG("Average net density after weighting: %.2f\n", weighted_avg_density); + VTR_LOG("Window size set for Simulation: %.2f\n", sim_window_size); + VTR_LOG("Net density after Window size : %.2f\n", + (sim_window_size * avg_density + (1 - sim_window_size) * median_density)); + VTR_LOG("Recommend no. of clock cycles: %ld\n", recmd_num_sim_clock_cycle); + + return recmd_num_sim_clock_cycle; +} + +/******************************************************************** + * Annotate simulation setting based on VPR results + * - If the operating clock frequency is set to follow the vpr timing results, + * we will set a new operating clock frequency here + * - If the number of clock cycles in simulation is set to be automatically determined, + * we will infer the number based on the average signal density + *******************************************************************/ +static +void annotate_simulation_setting(const AtomContext& atom_ctx, + const std::unordered_map& net_activity, + SimulationSetting& sim_setting) { + + /* Find if the operating frequency is binded to vpr results */ + if (0. == sim_setting.operating_clock_frequency()) { + VTR_LOG("User specified the operating clock frequency to use VPR results\n"); + /* Run timing analysis and collect critical path delay + * This code is copied from function vpr_analysis() in vpr_api.h + * Should keep updated to latest VPR code base + * Note: + * - MUST mention in documentation that VPR should be run in timing enabled mode + */ + vtr::vector net_delay; + vtr::t_chunk net_delay_ch; + /* Load the net delays */ + net_delay = alloc_net_delay(&net_delay_ch); + load_net_delay_from_routing(net_delay); + + /* Do final timing analysis */ + auto analysis_delay_calc = std::make_shared(atom_ctx.nlist, atom_ctx.lookup, net_delay); + auto timing_info = make_setup_hold_timing_info(analysis_delay_calc); + timing_info->update(); + + /* Get critical path delay. Update simulation settings */ + float T_crit = timing_info->least_slack_critical_path().delay() * (1. + sim_setting.operating_clock_frequency_slack()); + sim_setting.set_operating_clock_frequency(1 / T_crit); + VTR_LOG("Use VPR critical path delay %g [ns] with a %g [%] slack in OpenFPGA.\n", + T_crit / 1e9, sim_setting.operating_clock_frequency_slack() * 100); + } + VTR_LOG("Will apply operating clock frequency %g [MHz] to simulations\n", + sim_setting.operating_clock_frequency() / 1e6); + + if (0. == sim_setting.num_clock_cycles()) { + /* Find the number of clock cycles to be used in simulation by average over the signal activity */ + + VTR_LOG("User specified the number of operating clock cycles to be inferred from signal activities\n"); + size_t num_clock_cycles = recommend_num_sim_clock_cycle(atom_ctx, + net_activity, + 0.5); + sim_setting.set_num_clock_cycles(num_clock_cycles); + + VTR_LOG("Will apply %lu operating clock cycles to simulations\n", + sim_setting.num_clock_cycles()); + } +} + /******************************************************************** * Top-level function to link openfpga architecture to VPR, including: * - physical pb_type @@ -59,6 +225,7 @@ void link_arch(OpenfpgaContext& openfpga_ctx, vtr::ScopedStartFinishTimer timer("Link OpenFPGA architecture to VPR architecture"); + CommandOptionId opt_activity_file = cmd.option("activity_file"); CommandOptionId opt_verbose = cmd.option("verbose"); /* Annotate pb_type graphs @@ -118,6 +285,22 @@ void link_arch(OpenfpgaContext& openfpga_ctx, g_vpr_ctx.clustering(), g_vpr_ctx.placement(), openfpga_ctx.mutable_vpr_placement_annotation()); + + /* Read activity file is manadatory in the following flow-run settings + * - When users specify that number of clock cycles + * should be inferred from FPGA implmentation + * - When FPGA-SPICE is enabled + */ + openfpga_ctx.mutable_net_activity() = read_activity(g_vpr_ctx.atom().nlist, + cmd_context.option_value(cmd, opt_activity_file).c_str()); + + /* TODO: Annotate the number of clock cycles and clock frequency by following VPR results + * We SHOULD create a new simulation setting for OpenFPGA use only + * Avoid overwrite the raw data achieved when parsing!!! + */ + annotate_simulation_setting(g_vpr_ctx.atom(), + openfpga_ctx.net_activity(), + openfpga_ctx.mutable_arch().sim_setting); } } /* end namespace openfpga */ diff --git a/openfpga/src/base/openfpga_sdc.cpp b/openfpga/src/base/openfpga_sdc.cpp new file mode 100644 index 000000000..eaa2b9941 --- /dev/null +++ b/openfpga/src/base/openfpga_sdc.cpp @@ -0,0 +1,80 @@ +/******************************************************************** + * This file includes functions to compress the hierachy of routing architecture + *******************************************************************/ +/* Headers from vtrutil library */ +#include "vtr_time.h" +#include "vtr_log.h" + +/* Headers from openfpgautil library */ +#include "openfpga_digest.h" + +#include "circuit_library_utils.h" +#include "pnr_sdc_writer.h" +#include "openfpga_sdc.h" + +/* Include global variables of VPR */ +#include "globals.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * A wrapper function to call the PnR SDC generator of FPGA-SDC + *******************************************************************/ +void write_pnr_sdc(OpenfpgaContext& openfpga_ctx, + const Command& cmd, const CommandContext& cmd_context) { + + CommandOptionId opt_output_dir = cmd.option("file"); + CommandOptionId opt_constrain_global_port = cmd.option("constrain_global_port"); + CommandOptionId opt_constrain_grid = cmd.option("constrain_grid"); + CommandOptionId opt_constrain_sb = cmd.option("constrain_sb"); + CommandOptionId opt_constrain_cb = cmd.option("constrain_cb"); + CommandOptionId opt_constrain_configurable_memory_outputs = cmd.option("constrain_configurable_memory_outputs"); + CommandOptionId opt_constrain_routing_multiplexer_outputs = cmd.option("constrain_routing_multiplexer_outputs"); + CommandOptionId opt_constrain_switch_block_outputs = cmd.option("constrain_switch_block_outputs"); + + /* This is an intermediate data structure which is designed to modularize the FPGA-SDC + * Keep it independent from any other outside data structures + */ + std::string sdc_dir_path = format_dir_path(cmd_context.option_value(cmd, opt_output_dir)); + + /* Create directories */ + create_dir_path(sdc_dir_path.c_str()); + + PnrSdcOption options(sdc_dir_path); + + options.set_constrain_global_port(cmd_context.option_enable(cmd, opt_constrain_global_port)); + options.set_constrain_grid(cmd_context.option_enable(cmd, opt_constrain_grid)); + options.set_constrain_sb(cmd_context.option_enable(cmd, opt_constrain_sb)); + options.set_constrain_cb(cmd_context.option_enable(cmd, opt_constrain_cb)); + options.set_constrain_configurable_memory_outputs(cmd_context.option_enable(cmd, opt_constrain_configurable_memory_outputs)); + options.set_constrain_routing_multiplexer_outputs(cmd_context.option_enable(cmd, opt_constrain_routing_multiplexer_outputs)); + options.set_constrain_switch_block_outputs(cmd_context.option_enable(cmd, opt_constrain_switch_block_outputs)); + + /* We first turn on default sdc option and then disable part of them by following users' options */ + if (false == options.generate_sdc_pnr()) { + options.set_generate_sdc_pnr(true); + } + + /* Collect global ports from the circuit library: + * TODO: should we place this in the OpenFPGA context? + */ + std::vector global_ports = find_circuit_library_global_ports(openfpga_ctx.arch().circuit_lib); + + /* Execute only when sdc is enabled */ + if (true == options.generate_sdc_pnr()) { + print_pnr_sdc(options, + 1./openfpga_ctx.arch().sim_setting.programming_clock_frequency(), + 1./openfpga_ctx.arch().sim_setting.operating_clock_frequency(), + g_vpr_ctx.device(), + openfpga_ctx.vpr_device_annotation(), + openfpga_ctx.device_rr_gsb(), + openfpga_ctx.module_graph(), + openfpga_ctx.mux_lib(), + openfpga_ctx.arch().circuit_lib, + global_ports, + openfpga_ctx.flow_manager().compress_routing()); + } +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/base/openfpga_sdc.h b/openfpga/src/base/openfpga_sdc.h new file mode 100644 index 000000000..4e48964f3 --- /dev/null +++ b/openfpga/src/base/openfpga_sdc.h @@ -0,0 +1,23 @@ +#ifndef OPENFPGA_SDC_H +#define OPENFPGA_SDC_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include "command.h" +#include "command_context.h" +#include "openfpga_context.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +void write_pnr_sdc(OpenfpgaContext& openfpga_ctx, + const Command& cmd, const CommandContext& cmd_context); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/base/openfpga_sdc_command.cpp b/openfpga/src/base/openfpga_sdc_command.cpp new file mode 100644 index 000000000..cf31d73b0 --- /dev/null +++ b/openfpga/src/base/openfpga_sdc_command.cpp @@ -0,0 +1,79 @@ +/******************************************************************** + * Add commands to the OpenFPGA shell interface, + * in purpose of generate SDC files + * - write_pnr_sdc : generate SDC to constrain the back-end flow for FPGA fabric + * - write_analysis_sdc: TODO: generate SDC based on users' implementations + *******************************************************************/ +#include "openfpga_sdc.h" +#include "openfpga_sdc_command.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * - Add a command to Shell environment: generate PnR SDC + * - Add associated options + * - Add command dependency + *******************************************************************/ +static +void add_openfpga_write_pnr_sdc_command(openfpga::Shell& shell, + const ShellCommandClassId& cmd_class_id, + const ShellCommandId& shell_cmd_build_fabric_id) { + Command shell_cmd("write_pnr_sdc"); + + /* Add an option '--file' in short '-f'*/ + CommandOptionId output_opt = shell_cmd.add_option("file", true, "Specify the output directory for SDC files"); + shell_cmd.set_option_short_name(output_opt, "f"); + shell_cmd.set_option_require_value(output_opt, openfpga::OPT_STRING); + + /* Add an option '--constrain_global_port' */ + shell_cmd.add_option("constrain_global_port", false, "Constrain all the global ports of FPGA fabric"); + + /* Add an option '--constrain_grid' */ + shell_cmd.add_option("constrain_grid", false, "Constrain all the grids of FPGA fabric"); + + /* Add an option '--constrain_sb' */ + shell_cmd.add_option("constrain_sb", false, "Constrain all the switch blocks of FPGA fabric"); + + /* Add an option '--constrain_cb' */ + shell_cmd.add_option("constrain_cb", false, "Constrain all the connection blocks of FPGA fabric"); + + /* Add an option '--constrain_configurable_memory_outputs' */ + shell_cmd.add_option("constrain_configurable_memory_outputs", false, "Constrain all the outputs of configurable memories of FPGA fabric"); + + /* Add an option '--constrain_routing_multiplexer_outputs' */ + shell_cmd.add_option("constrain_routing_multiplexer_outputs", false, "Constrain all the outputs of routing multiplexer of FPGA fabric"); + + /* Add an option '--constrain_switch_block_outputs' */ + shell_cmd.add_option("constrain_switch_block_outputs", false, "Constrain all the outputs of switch blocks of FPGA fabric"); + + /* Add an option '--verbose' */ + shell_cmd.add_option("verbose", false, "Enable verbose output"); + + /* Add command 'write_fabric_verilog' to the Shell */ + ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "generate SDC files to constrain the backend flow for FPGA fabric"); + shell.set_command_class(shell_cmd_id, cmd_class_id); + shell.set_command_execute_function(shell_cmd_id, write_pnr_sdc); + + /* The 'build_fabric' command should NOT be executed before 'link_openfpga_arch' */ + std::vector cmd_dependency; + cmd_dependency.push_back(shell_cmd_build_fabric_id); + shell.set_command_dependency(shell_cmd_id, cmd_dependency); +} + +void add_openfpga_sdc_commands(openfpga::Shell& shell) { + /* Get the unique id of 'build_fabric' command which is to be used in creating the dependency graph */ + const ShellCommandId& shell_cmd_build_fabric_id = shell.command(std::string("build_fabric")); + + /* Add a new class of commands */ + ShellCommandClassId openfpga_sdc_cmd_class = shell.add_command_class("FPGA-SDC"); + + /******************************** + * Command 'write_fabric_verilog' + */ + add_openfpga_write_pnr_sdc_command(shell, + openfpga_sdc_cmd_class, + shell_cmd_build_fabric_id); +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/base/openfpga_sdc_command.h b/openfpga/src/base/openfpga_sdc_command.h new file mode 100644 index 000000000..9fbeb42a5 --- /dev/null +++ b/openfpga/src/base/openfpga_sdc_command.h @@ -0,0 +1,21 @@ +#ifndef OPENFPGA_SDC_COMMAND_H +#define OPENFPGA_SDC_COMMAND_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include "shell.h" +#include "openfpga_context.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +void add_openfpga_sdc_commands(openfpga::Shell& shell); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/base/openfpga_setup_command.cpp b/openfpga/src/base/openfpga_setup_command.cpp index e84227187..e3354d046 100644 --- a/openfpga/src/base/openfpga_setup_command.cpp +++ b/openfpga/src/base/openfpga_setup_command.cpp @@ -14,9 +14,199 @@ /* begin namespace openfpga */ namespace openfpga { +/******************************************************************** + * - Add a command to Shell environment: read_openfpga_arch + * - Add associated options + * - Add command dependency + *******************************************************************/ +static +ShellCommandId add_openfpga_read_arch_command(openfpga::Shell& shell, + const ShellCommandClassId& cmd_class_id) { + Command shell_cmd("read_openfpga_arch"); + + /* Add an option '--file' in short '-f'*/ + CommandOptionId opt_arch_file = shell_cmd.add_option("file", true, "file path to the architecture XML"); + shell_cmd.set_option_short_name(opt_arch_file, "f"); + shell_cmd.set_option_require_value(opt_arch_file, openfpga::OPT_STRING); + + /* Add command 'read_openfpga_arch' to the Shell */ + ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "read OpenFPGA architecture file"); + shell.set_command_class(shell_cmd_id, cmd_class_id); + shell.set_command_execute_function(shell_cmd_id, read_arch); + + return shell_cmd_id; +} + +/******************************************************************** + * - Add a command to Shell environment: write_openfpga_arch + * - Add associated options + * - Add command dependency + *******************************************************************/ +static +ShellCommandId add_openfpga_write_arch_command(openfpga::Shell& shell, + const ShellCommandClassId& cmd_class_id, + const std::vector& dependent_cmds) { + Command shell_cmd("write_openfpga_arch"); + /* Add an option '--file' in short '-f'*/ + CommandOptionId opt_file = shell_cmd.add_option("file", true, "file path to the architecture XML"); + shell_cmd.set_option_short_name(opt_file, "f"); + shell_cmd.set_option_require_value(opt_file, openfpga::OPT_STRING); + + /* Add command 'write_openfpga_arch' to the Shell */ + ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "write OpenFPGA architecture file"); + shell.set_command_class(shell_cmd_id, cmd_class_id); + shell.set_command_const_execute_function(shell_cmd_id, write_arch); + + /* The 'write_openfpga_arch' command should NOT be executed before 'read_openfpga_arch' */ + shell.set_command_dependency(shell_cmd_id, dependent_cmds); + + return shell_cmd_id; +} + +/******************************************************************** + * - Add a command to Shell environment: link_openfpga_arch + * - Add associated options + * - Add command dependency + *******************************************************************/ +static +ShellCommandId add_openfpga_link_arch_command(openfpga::Shell& shell, + const ShellCommandClassId& cmd_class_id, + const std::vector& dependent_cmds) { + Command shell_cmd("link_openfpga_arch"); + + /* Add an option '--activity_file'*/ + CommandOptionId opt_act_file = shell_cmd.add_option("activity_file", true, "file path to the signal activity"); + shell_cmd.set_option_require_value(opt_act_file, openfpga::OPT_STRING); + + /* Add an option '--verbose' */ + shell_cmd.add_option("verbose", false, "Show verbose outputs"); + + /* Add command 'link_openfpga_arch' to the Shell */ + ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "Bind OpenFPGA architecture to VPR"); + shell.set_command_class(shell_cmd_id, cmd_class_id); + shell.set_command_execute_function(shell_cmd_id, link_arch); + + /* The 'link_openfpga_arch' command should NOT be executed before 'read_openfpga_arch' and 'vpr' */ + shell.set_command_dependency(shell_cmd_id, dependent_cmds); + + return shell_cmd_id; +} + +/******************************************************************** + * - Add a command to Shell environment: check_netlist_naming_conflict + * - Add associated options + * - Add command dependency + *******************************************************************/ +static +ShellCommandId add_openfpga_check_netlist_naming_conflict_command(openfpga::Shell& shell, + const ShellCommandClassId& cmd_class_id, + const std::vector& dependent_cmds) { + + Command shell_cmd("check_netlist_naming_conflict"); + + /* Add an option '--fix' */ + shell_cmd.add_option("fix", false, "Apply correction to any conflicts found"); + + /* Add an option '--report' */ + CommandOptionId opt_rpt = shell_cmd.add_option("report", false, "Output a report file about what any correction applied"); + shell_cmd.set_option_require_value(opt_rpt, openfpga::OPT_STRING); + + /* Add command 'check_netlist_naming_conflict' to the Shell */ + ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "Check any block/net naming in users' BLIF netlist violates the syntax of fabric generator"); + shell.set_command_class(shell_cmd_id, cmd_class_id); + shell.set_command_execute_function(shell_cmd_id, check_netlist_naming_conflict); + + /* The 'link_openfpga_arch' command should NOT be executed before 'vpr' */ + shell.set_command_dependency(shell_cmd_id, dependent_cmds); + + return shell_cmd_id; +} + +/******************************************************************** + * - Add a command to Shell environment: pb_pin_fixup + * - Add associated options + * - Add command dependency + *******************************************************************/ +static +ShellCommandId add_openfpga_pb_pin_fixup_command(openfpga::Shell& shell, + const ShellCommandClassId& cmd_class_id, + const std::vector& dependent_cmds) { + + Command shell_cmd("pb_pin_fixup"); + /* Add an option '--verbose' */ + shell_cmd.add_option("verbose", false, "Show verbose outputs"); + + /* Add command 'pb_pin_fixup' to the Shell */ + ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "Fix up the packing results due to pin swapping during routing stage"); + shell.set_command_class(shell_cmd_id, cmd_class_id); + shell.set_command_execute_function(shell_cmd_id, pb_pin_fixup); + + /* The 'pb_pin_fixup' command should NOT be executed before 'read_openfpga_arch' and 'vpr' */ + shell.set_command_dependency(shell_cmd_id, dependent_cmds); + + return shell_cmd_id; +} + +/******************************************************************** + * - Add a command to Shell environment: lut_truth_table_fixup + * - Add associated options + * - Add command dependency + *******************************************************************/ +static +ShellCommandId add_openfpga_lut_truth_table_fixup_command(openfpga::Shell& shell, + const ShellCommandClassId& cmd_class_id, + const std::vector& dependent_cmds) { + + Command shell_cmd("lut_truth_table_fixup"); + /* Add an option '--verbose' */ + shell_cmd.add_option("verbose", false, "Show verbose outputs"); + + /* Add command 'lut_truth_table_fixup' to the Shell */ + ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "Fix up the truth table of Look-Up Tables due to pin swapping during packing stage"); + shell.set_command_class(shell_cmd_id, cmd_class_id); + shell.set_command_execute_function(shell_cmd_id, lut_truth_table_fixup); + + /* The 'lut_truth_table_fixup' command should NOT be executed before 'read_openfpga_arch' and 'vpr' */ + shell.set_command_dependency(shell_cmd_id, dependent_cmds); + + return shell_cmd_id; +} + +/******************************************************************** + * - Add a command to Shell environment: build_fabric + * - Add associated options + * - Add command dependency + *******************************************************************/ +static +ShellCommandId add_openfpga_build_fabric_command(openfpga::Shell& shell, + const ShellCommandClassId& cmd_class_id, + const std::vector& dependent_cmds) { + + Command shell_cmd("build_fabric"); + + /* Add an option '--compress_routing' */ + shell_cmd.add_option("compress_routing", false, "Compress the number of unique routing modules by identifying the unique GSBs"); + + /* Add an option '--duplicate_grid_pin' */ + shell_cmd.add_option("duplicate_grid_pin", false, "Duplicate the pins on the same side of a grid"); + + /* Add an option '--verbose' */ + shell_cmd.add_option("verbose", false, "Show verbose outputs"); + + /* Add command 'compact_routing_hierarchy' to the Shell */ + ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "Build the FPGA fabric in a graph of modules"); + shell.set_command_class(shell_cmd_id, cmd_class_id); + shell.set_command_execute_function(shell_cmd_id, build_fabric); + + /* The 'build_fabric' command should NOT be executed before 'link_openfpga_arch' */ + shell.set_command_dependency(shell_cmd_id, dependent_cmds); + + return shell_cmd_id; +} + void add_openfpga_setup_commands(openfpga::Shell& shell) { /* Get the unique id of 'vpr' command which is to be used in creating the dependency graph */ - const ShellCommandId& shell_cmd_vpr_id = shell.command(std::string("vpr")); + const ShellCommandId& vpr_cmd_id = shell.command(std::string("vpr")); /* Add a new class of commands */ ShellCommandClassId openfpga_setup_cmd_class = shell.add_command_class("OpenFPGA setup"); @@ -24,119 +214,62 @@ void add_openfpga_setup_commands(openfpga::Shell& shell) { /******************************** * Command 'read_openfpga_arch' */ - Command shell_cmd_read_arch("read_openfpga_arch"); - /* Add an option '--file' in short '-f'*/ - CommandOptionId read_arch_opt_file = shell_cmd_read_arch.add_option("file", true, "file path to the architecture XML"); - shell_cmd_read_arch.set_option_short_name(read_arch_opt_file, "f"); - shell_cmd_read_arch.set_option_require_value(read_arch_opt_file, openfpga::OPT_STRING); - - /* Add command 'read_openfpga_arch' to the Shell */ - ShellCommandId shell_cmd_read_arch_id = shell.add_command(shell_cmd_read_arch, "read OpenFPGA architecture file"); - shell.set_command_class(shell_cmd_read_arch_id, openfpga_setup_cmd_class); - shell.set_command_execute_function(shell_cmd_read_arch_id, read_arch); + ShellCommandId read_arch_cmd_id = add_openfpga_read_arch_command(shell, + openfpga_setup_cmd_class); /******************************** * Command 'write_openfpga_arch' */ - Command shell_cmd_write_arch("write_openfpga_arch"); - /* Add an option '--file' in short '-f'*/ - CommandOptionId write_arch_opt_file = shell_cmd_write_arch.add_option("file", true, "file path to the architecture XML"); - shell_cmd_write_arch.set_option_short_name(write_arch_opt_file, "f"); - shell_cmd_write_arch.set_option_require_value(write_arch_opt_file, openfpga::OPT_STRING); - - /* Add command 'write_openfpga_arch' to the Shell */ - ShellCommandId shell_cmd_write_arch_id = shell.add_command(shell_cmd_write_arch, "write OpenFPGA architecture file"); - shell.set_command_class(shell_cmd_write_arch_id, openfpga_setup_cmd_class); - shell.set_command_const_execute_function(shell_cmd_write_arch_id, write_arch); - /* The 'write_openfpga_arch' command should NOT be executed before 'read_openfpga_arch' */ - shell.set_command_dependency(shell_cmd_write_arch_id, std::vector(1, shell_cmd_read_arch_id)); + std::vector write_arch_dependent_cmds(1, read_arch_cmd_id); + add_openfpga_write_arch_command(shell, + openfpga_setup_cmd_class, + write_arch_dependent_cmds); /******************************** * Command 'link_openfpga_arch' */ - Command shell_cmd_link_openfpga_arch("link_openfpga_arch"); - /* Add an option '--verbose' */ - shell_cmd_link_openfpga_arch.add_option("verbose", false, "Show verbose outputs"); - - /* Add command 'link_openfpga_arch' to the Shell */ - ShellCommandId shell_cmd_link_openfpga_arch_id = shell.add_command(shell_cmd_link_openfpga_arch, "Bind OpenFPGA architecture to VPR"); - shell.set_command_class(shell_cmd_link_openfpga_arch_id, openfpga_setup_cmd_class); - shell.set_command_execute_function(shell_cmd_link_openfpga_arch_id, link_arch); - /* The 'link_openfpga_arch' command should NOT be executed before 'read_openfpga_arch' and 'vpr' */ - std::vector cmd_dependency_link_openfpga_arch; - cmd_dependency_link_openfpga_arch.push_back(shell_cmd_read_arch_id); - cmd_dependency_link_openfpga_arch.push_back(shell_cmd_vpr_id); - shell.set_command_dependency(shell_cmd_link_openfpga_arch_id, cmd_dependency_link_openfpga_arch); - + std::vector link_arch_dependent_cmds; + link_arch_dependent_cmds.push_back(read_arch_cmd_id); + link_arch_dependent_cmds.push_back(vpr_cmd_id); + ShellCommandId link_arch_cmd_id = add_openfpga_link_arch_command(shell, + openfpga_setup_cmd_class, + link_arch_dependent_cmds); /******************************************* * Command 'check_netlist_naming_conflict' */ - Command shell_cmd_check_netlist_naming_conflict("check_netlist_naming_conflict"); - /* Add an option '--fix' */ - shell_cmd_check_netlist_naming_conflict.add_option("fix", false, "Apply correction to any conflicts found"); - /* Add an option '--report' */ - CommandOptionId check_netlist_opt_rpt = shell_cmd_check_netlist_naming_conflict.add_option("report", false, "Output a report file about what any correction applied"); - shell_cmd_check_netlist_naming_conflict.set_option_require_value(check_netlist_opt_rpt, openfpga::OPT_STRING); - - /* Add command 'check_netlist_naming_conflict' to the Shell */ - ShellCommandId shell_cmd_check_netlist_naming_conflict_id = shell.add_command(shell_cmd_check_netlist_naming_conflict, "Check any block/net naming in users' BLIF netlist violates the syntax of fabric generator"); - shell.set_command_class(shell_cmd_check_netlist_naming_conflict_id, openfpga_setup_cmd_class); - shell.set_command_execute_function(shell_cmd_check_netlist_naming_conflict_id, check_netlist_naming_conflict); - /* The 'link_openfpga_arch' command should NOT be executed before 'vpr' */ - std::vector cmd_dependency_check_netlist_naming_conflict(1, shell_cmd_vpr_id); - shell.set_command_dependency(shell_cmd_link_openfpga_arch_id, cmd_dependency_check_netlist_naming_conflict); + std::vector nlist_naming_dependent_cmds; + nlist_naming_dependent_cmds.push_back(vpr_cmd_id); + add_openfpga_check_netlist_naming_conflict_command(shell, + openfpga_setup_cmd_class, + nlist_naming_dependent_cmds); /******************************** * Command 'pb_pin_fixup' */ - Command shell_cmd_pb_pin_fixup("pb_pin_fixup"); - /* Add an option '--verbose' */ - shell_cmd_pb_pin_fixup.add_option("verbose", false, "Show verbose outputs"); - - /* Add command 'pb_pin_fixup' to the Shell */ - ShellCommandId shell_cmd_pb_pin_fixup_id = shell.add_command(shell_cmd_pb_pin_fixup, "Fix up the packing results due to pin swapping during routing stage"); - shell.set_command_class(shell_cmd_pb_pin_fixup_id, openfpga_setup_cmd_class); - shell.set_command_execute_function(shell_cmd_pb_pin_fixup_id, pb_pin_fixup); - /* The 'pb_pin_fixup' command should NOT be executed before 'read_openfpga_arch' and 'vpr' */ - std::vector cmd_dependency_pb_pin_fixup; - cmd_dependency_pb_pin_fixup.push_back(shell_cmd_read_arch_id); - cmd_dependency_pb_pin_fixup.push_back(shell_cmd_vpr_id); - shell.set_command_dependency(shell_cmd_pb_pin_fixup_id, cmd_dependency_pb_pin_fixup); + std::vector pb_pin_fixup_dependent_cmds; + pb_pin_fixup_dependent_cmds.push_back(read_arch_cmd_id); + pb_pin_fixup_dependent_cmds.push_back(vpr_cmd_id); + add_openfpga_pb_pin_fixup_command(shell, + openfpga_setup_cmd_class, + pb_pin_fixup_dependent_cmds); /******************************** * Command 'lut_truth_table_fixup' */ - Command shell_cmd_lut_truth_table_fixup("lut_truth_table_fixup"); - /* Add an option '--verbose' */ - shell_cmd_lut_truth_table_fixup.add_option("verbose", false, "Show verbose outputs"); - - /* Add command 'lut_truth_table_fixup' to the Shell */ - ShellCommandId shell_cmd_lut_truth_table_fixup_id = shell.add_command(shell_cmd_lut_truth_table_fixup, "Fix up the truth table of Look-Up Tables due to pin swapping during packing stage"); - shell.set_command_class(shell_cmd_lut_truth_table_fixup_id, openfpga_setup_cmd_class); - shell.set_command_execute_function(shell_cmd_lut_truth_table_fixup_id, lut_truth_table_fixup); - /* The 'lut_truth_table_fixup' command should NOT be executed before 'read_openfpga_arch' and 'vpr' */ - std::vector cmd_dependency_lut_truth_table_fixup; - cmd_dependency_lut_truth_table_fixup.push_back(shell_cmd_read_arch_id); - cmd_dependency_lut_truth_table_fixup.push_back(shell_cmd_vpr_id); - shell.set_command_dependency(shell_cmd_lut_truth_table_fixup_id, cmd_dependency_lut_truth_table_fixup); - + std::vector lut_tt_fixup_dependent_cmds; + lut_tt_fixup_dependent_cmds.push_back(read_arch_cmd_id); + lut_tt_fixup_dependent_cmds.push_back(vpr_cmd_id); + add_openfpga_lut_truth_table_fixup_command(shell, + openfpga_setup_cmd_class, + lut_tt_fixup_dependent_cmds); /******************************** * Command 'build_fabric' */ - Command shell_cmd_build_fabric("build_fabric"); - /* Add an option '--verbose' */ - shell_cmd_build_fabric.add_option("compress_routing", false, "Compress the number of unique routing modules by identifying the unique GSBs"); - shell_cmd_build_fabric.add_option("duplicate_grid_pin", false, "Duplicate the pins on the same side of a grid"); - shell_cmd_build_fabric.add_option("verbose", false, "Show verbose outputs"); - - /* Add command 'compact_routing_hierarchy' to the Shell */ - ShellCommandId shell_cmd_build_fabric_id = shell.add_command(shell_cmd_build_fabric, "Build the FPGA fabric in a graph of modules"); - shell.set_command_class(shell_cmd_build_fabric_id, openfpga_setup_cmd_class); - shell.set_command_execute_function(shell_cmd_build_fabric_id, build_fabric); - /* The 'build_fabric' command should NOT be executed before 'link_openfpga_arch' */ - std::vector cmd_dependency_build_fabric; - cmd_dependency_build_fabric.push_back(shell_cmd_link_openfpga_arch_id); - shell.set_command_dependency(shell_cmd_build_fabric_id, cmd_dependency_build_fabric); + std::vector build_fabric_dependent_cmds; + build_fabric_dependent_cmds.push_back(link_arch_cmd_id); + add_openfpga_build_fabric_command(shell, + openfpga_setup_cmd_class, + build_fabric_dependent_cmds); } } /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_grid_modules.cpp b/openfpga/src/fabric/build_grid_modules.cpp index 17ecc67e2..a5c346025 100644 --- a/openfpga/src/fabric/build_grid_modules.cpp +++ b/openfpga/src/fabric/build_grid_modules.cpp @@ -1103,7 +1103,6 @@ void build_grid_modules(ModuleManager& module_manager, duplicate_grid_pin, verbose); } - continue; } else { /* For CLB and heterogenenous blocks */ build_physical_tile_module(module_manager, circuit_lib, diff --git a/openfpga/src/fabric/build_top_module.cpp b/openfpga/src/fabric/build_top_module.cpp index 7af79d46c..707ff8971 100644 --- a/openfpga/src/fabric/build_top_module.cpp +++ b/openfpga/src/fabric/build_top_module.cpp @@ -172,8 +172,8 @@ vtr::Matrix add_top_module_grid_instances(ModuleManager& module_manager, */ for (int z = 0; z < grids[io_coordinate.x()][io_coordinate.y()].type->capacity; ++z) { io_location_map.set_io_index(io_coordinate.x(), io_coordinate.y(), z, io_counter); + io_counter++; } - io_counter++; } } diff --git a/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp b/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp index 253798c86..f69b57ae6 100644 --- a/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp @@ -168,7 +168,8 @@ void build_physical_block_pin_interc_bitstream(BitstreamManager& bitstream_manag for (t_pb_graph_pin* src_pb_graph_pin : pb_graph_pin_inputs(des_pb_graph_pin, cur_interc)) { const PhysicalPbId& src_pb_id = physical_pb.find_pb(src_pb_graph_pin->parent_node); /* If the src pb id is not valid, we bypass it */ - if ( (true != physical_pb.valid_pb_id(src_pb_id)) + if ( (true == physical_pb.valid_pb_id(src_pb_id)) + && (AtomNetId::INVALID() != physical_pb.pb_graph_pin_atom_net(des_pb_id, des_pb_graph_pin)) && (physical_pb.pb_graph_pin_atom_net(src_pb_id, src_pb_graph_pin) == physical_pb.pb_graph_pin_atom_net(des_pb_id, des_pb_graph_pin))) { break; } diff --git a/openfpga/src/fpga_sdc/analysis_sdc_option.cpp b/openfpga/src/fpga_sdc/analysis_sdc_option.cpp new file mode 100644 index 000000000..444e73fb4 --- /dev/null +++ b/openfpga/src/fpga_sdc/analysis_sdc_option.cpp @@ -0,0 +1,39 @@ +/******************************************************************** + * Member functions for a data structure which includes all the options for the SDC generator + ********************************************************************/ +#include "analysis_sdc_option.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Public Constructors + ********************************************************************/ +AnalysisSdcOption::AnalysisSdcOption(const std::string& sdc_dir) { + sdc_dir_ = sdc_dir; + generate_sdc_analysis_ = false; +} + +/******************************************************************** + * Public accessors + ********************************************************************/ +std::string AnalysisSdcOption::sdc_dir() const { + return sdc_dir_; +} + +bool AnalysisSdcOption::generate_sdc_analysis() const { + return generate_sdc_analysis_; +} + +/******************************************************************** + * Public mutators + ********************************************************************/ +void AnalysisSdcOption::set_sdc_dir(const std::string& sdc_dir) { + sdc_dir_ = sdc_dir; +} + +void AnalysisSdcOption::set_generate_sdc_analysis(const bool& generate_sdc_analysis) { + generate_sdc_analysis_ = generate_sdc_analysis; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_sdc/analysis_sdc_option.h b/openfpga/src/fpga_sdc/analysis_sdc_option.h new file mode 100644 index 000000000..514e5e81c --- /dev/null +++ b/openfpga/src/fpga_sdc/analysis_sdc_option.h @@ -0,0 +1,30 @@ +#ifndef ANALYSIS_SDC_OPTION_H +#define ANALYSIS_SDC_OPTION_H + +/******************************************************************** + * A data structure to include all the options for the SDC generator + * in purpose of analyzing users' implementations + ********************************************************************/ + +#include + +/* begin namespace openfpga */ +namespace openfpga { + +class AnalysisSdcOption { + public: /* Public Constructors */ + AnalysisSdcOption(const std::string& sdc_dir); + public: /* Public accessors */ + std::string sdc_dir() const; + bool generate_sdc_analysis() const; + public: /* Public mutators */ + void set_sdc_dir(const std::string& sdc_dir); + void set_generate_sdc_analysis(const bool& generate_sdc_analysis); + private: /* Internal data */ + std::string sdc_dir_; + bool generate_sdc_analysis_; +}; + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/fpga_sdc/pnr_sdc_grid_writer.cpp b/openfpga/src/fpga_sdc/pnr_sdc_grid_writer.cpp new file mode 100644 index 000000000..f8ce1c703 --- /dev/null +++ b/openfpga/src/fpga_sdc/pnr_sdc_grid_writer.cpp @@ -0,0 +1,346 @@ +/******************************************************************** + * This file includes functions that print SDC (Synopsys Design Constraint) + * files in physical design tools, i.e., Place & Route (PnR) tools + * The SDC files are used to constrain the physical design for each grid + * (CLBs, heterogeneous blocks etc.) + * + * Note that this is different from the SDC to constrain VPR Place&Route + * engine! These SDCs are designed for PnR to generate FPGA layouts!!! + *******************************************************************/ +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_log.h" +#include "vtr_assert.h" +#include "vtr_time.h" + +/* Headers from openfpgautil library */ +#include "openfpga_port.h" +#include "openfpga_digest.h" +#include "openfpga_side_manager.h" + + +#include "openfpga_interconnect_types.h" +#include "vpr_utils.h" +#include "mux_utils.h" + +#include "openfpga_reserved_words.h" +#include "openfpga_naming.h" +#include "pb_type_utils.h" +#include "pb_graph_utils.h" + +#include "sdc_writer_naming.h" +#include "sdc_writer_utils.h" +#include "pnr_sdc_grid_writer.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Print pin-to-pin timing constraints for a given interconnection + * at an output port of a pb_graph node + *******************************************************************/ +static +void print_pnr_sdc_constrain_pb_pin_interc_timing(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& parent_module, + t_pb_graph_pin* des_pb_graph_pin, + t_mode* physical_mode) { + + /* Validate file stream */ + valid_file_stream(fp); + + /* 1. identify pin interconnection type, + * 2. Identify the number of fan-in (Consider interconnection edges of only selected mode) + * 3. Print SDC timing constraints + */ + t_interconnect* cur_interc = pb_graph_pin_interc(des_pb_graph_pin, physical_mode); + size_t fan_in = pb_graph_pin_inputs(des_pb_graph_pin, cur_interc).size(); + if ((nullptr == cur_interc) || (0 == fan_in)) { + /* No interconnection matched */ + return; + } + + /* Print pin-to-pin SDC contraint here */ + /* For more than one mode defined, the direct interc has more than one input_edge , + * We need to find which edge is connected the pin we want + */ + for (int iedge = 0; iedge < des_pb_graph_pin->num_input_edges; iedge++) { + if (cur_interc != des_pb_graph_pin->input_edges[iedge]->interconnect) { + continue; + } + + /* Source pin, node, pb_type*/ + t_pb_graph_pin* src_pb_graph_pin = des_pb_graph_pin->input_edges[iedge]->input_pins[0]; + t_pb_graph_node* src_pb_graph_node = src_pb_graph_pin->parent_node; + /* Des pin, node, pb_type */ + t_pb_graph_node* des_pb_graph_node = des_pb_graph_pin->parent_node; + + /* Find the src module in module manager */ + std::string src_module_name = generate_physical_block_module_name(src_pb_graph_pin->parent_node->pb_type); + ModuleId src_module = module_manager.find_module(src_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(src_module)); + + ModulePortId src_module_port_id = module_manager.find_module_port(src_module, generate_pb_type_port_name(src_pb_graph_pin->port)); + VTR_ASSERT(true == module_manager.valid_module_port_id(src_module, src_module_port_id)); + + /* Generate the name of the des instance name + * If des module is not the parent module, it is a child module. + * We should find the instance id + */ + std::string src_instance_name = src_module_name; + if (parent_module != src_module) { + src_instance_name = module_manager.module_name(parent_module) + std::string("/"); + /* Instance id is actually the placement index */ + size_t instance_id = src_pb_graph_node->placement_index; + if (true == module_manager.instance_name(parent_module, src_module, instance_id).empty()) { + src_instance_name += src_module_name; + src_instance_name += "_"; + src_instance_name += std::to_string(instance_id); + src_instance_name += "_"; + } else { + src_instance_name += module_manager.instance_name(parent_module, src_module, instance_id); + } + } + + /* Generate src port information */ + BasicPort src_port = module_manager.module_port(src_module, src_module_port_id); + src_port.set_width(src_pb_graph_pin->pin_number, src_pb_graph_pin->pin_number); + + /* Find the des module in module manager */ + std::string des_module_name = generate_physical_block_module_name(des_pb_graph_pin->parent_node->pb_type); + ModuleId des_module = module_manager.find_module(des_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(des_module)); + ModulePortId des_module_port_id = module_manager.find_module_port(des_module, generate_pb_type_port_name(des_pb_graph_pin->port)); + VTR_ASSERT(true == module_manager.valid_module_port_id(des_module, des_module_port_id)); + + /* Generate the name of the des instance name + * If des module is not the parent module, it is a child module. + * We should find the instance id + */ + std::string des_instance_name = des_module_name; + if (parent_module != des_module) { + des_instance_name = module_manager.module_name(parent_module) + std::string("/"); + /* Instance id is actually the placement index */ + size_t instance_id = des_pb_graph_node->placement_index; + if (true == module_manager.instance_name(parent_module, des_module, instance_id).empty()) { + des_instance_name += des_module_name; + des_instance_name += "_"; + des_instance_name += std::to_string(instance_id); + des_instance_name += "_"; + } else { + des_instance_name += module_manager.instance_name(parent_module, des_module, instance_id); + } + } + + /* Generate des port information */ + BasicPort des_port = module_manager.module_port(des_module, des_module_port_id); + des_port.set_width(des_pb_graph_pin->pin_number, des_pb_graph_pin->pin_number); + + /* Print a SDC timing constraint */ + print_pnr_sdc_constrain_max_delay(fp, + src_instance_name, + generate_sdc_port(src_port), + des_instance_name, + generate_sdc_port(des_port), + des_pb_graph_pin->input_edges[iedge]->delay_max); + } +} + +/******************************************************************** + * Print port-to-port timing constraints which source from + * an output port of a pb_graph node + *******************************************************************/ +static +void print_pnr_sdc_constrain_pb_interc_timing(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& parent_module, + t_pb_graph_node* des_pb_graph_node, + const e_circuit_pb_port_type& pb_port_type, + t_mode* physical_mode) { + /* Validate file stream */ + valid_file_stream(fp); + + switch (pb_port_type) { + case CIRCUIT_PB_PORT_INPUT: { + for (int iport = 0; iport < des_pb_graph_node->num_input_ports; ++iport) { + for (int ipin = 0; ipin < des_pb_graph_node->num_input_pins[iport]; ++ipin) { + /* If this is a idle block, we set 0 to the selected edge*/ + /* Get the selected edge of current pin*/ + print_pnr_sdc_constrain_pb_pin_interc_timing(fp, + module_manager, parent_module, + &(des_pb_graph_node->input_pins[iport][ipin]), + physical_mode); + } + } + break; + } + case CIRCUIT_PB_PORT_OUTPUT: { + for (int iport = 0; iport < des_pb_graph_node->num_output_ports; ++iport) { + for (int ipin = 0; ipin < des_pb_graph_node->num_output_pins[iport]; ++ipin) { + print_pnr_sdc_constrain_pb_pin_interc_timing(fp, + module_manager, parent_module, + &(des_pb_graph_node->output_pins[iport][ipin]), + physical_mode); + } + } + break; + } + case CIRCUIT_PB_PORT_CLOCK: { + /* Do NOT constrain clock here, it should be handled by Clock Tree Synthesis */ + break; + } + default: + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid pb port type!\n"); + exit(1); + } +} + +/******************************************************************** + * This function will generate a SDC file for each pb_type, + * constraining the pin-to-pin timing between + * 1. input port of parent_pb_graph_node and input port of child_pb_graph_nodes + * 2. output port of parent_pb_graph_node and output port of child_pb_graph_nodes + * 3. output port of child_pb_graph_node and input port of child_pb_graph_nodes + *******************************************************************/ +static +void print_pnr_sdc_constrain_pb_graph_node_timing(const std::string& sdc_dir, + const ModuleManager& module_manager, + t_pb_graph_node* parent_pb_graph_node, + t_mode* physical_mode) { + + /* Get the pb_type definition related to the node */ + t_pb_type* physical_pb_type = parent_pb_graph_node->pb_type; + std::string pb_module_name = generate_physical_block_module_name(physical_pb_type); + + /* Find the pb module in module manager */ + ModuleId pb_module = module_manager.find_module(pb_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(pb_module)); + + /* Create the file name for SDC */ + std::string sdc_fname(sdc_dir + pb_module_name + std::string(SDC_FILE_NAME_POSTFIX)); + + /* Create the file stream */ + std::fstream fp; + fp.open(sdc_fname, std::fstream::out | std::fstream::trunc); + + check_file_stream(sdc_fname.c_str(), fp); + + /* Generate the descriptions*/ + print_sdc_file_header(fp, std::string("Timing constraints for Grid " + pb_module_name + " in PnR")); + + /* We check output_pins of cur_pb_graph_node and its the input_edges + * Built the interconnections between outputs of cur_pb_graph_node and outputs of child_pb_graph_node + * child_pb_graph_node.output_pins -----------------> cur_pb_graph_node.outpins + * /|\ + * | + * input_pins, edges, output_pins + */ + print_pnr_sdc_constrain_pb_interc_timing(fp, + module_manager, pb_module, + parent_pb_graph_node, + CIRCUIT_PB_PORT_OUTPUT, + physical_mode); + + /* We check input_pins of child_pb_graph_node and its the input_edges + * Built the interconnections between inputs of cur_pb_graph_node and inputs of child_pb_graph_node + * cur_pb_graph_node.input_pins -----------------> child_pb_graph_node.input_pins + * /|\ + * | + * input_pins, edges, output_pins + */ + for (int ipb = 0; ipb < physical_mode->num_pb_type_children; ++ipb) { + for (int jpb = 0; jpb < physical_mode->pb_type_children[ipb].num_pb; ++jpb) { + t_pb_graph_node* child_pb_graph_node = &(parent_pb_graph_node->child_pb_graph_nodes[physical_mode->index][ipb][jpb]); + /* For each child_pb_graph_node input pins*/ + print_pnr_sdc_constrain_pb_interc_timing(fp, + module_manager, pb_module, + child_pb_graph_node, + CIRCUIT_PB_PORT_INPUT, + physical_mode); + /* Do NOT constrain clock here, it should be handled by Clock Tree Synthesis */ + } + } + + /* Close file handler */ + fp.close(); +} + +/******************************************************************** + * Recursively print SDC timing constraints for a pb_type + * This function will generate a SDC file for each pb_type, + * constraining the pin-to-pin timing + *******************************************************************/ +static +void rec_print_pnr_sdc_constrain_pb_graph_timing(const std::string& sdc_dir, + const ModuleManager& module_manager, + const VprDeviceAnnotation& device_annotation, + t_pb_graph_node* parent_pb_graph_node) { + /* Validate pb_graph node */ + if (nullptr == parent_pb_graph_node) { + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid parent_pb_graph_node.\n"); + exit(1); + } + + /* Get the pb_type */ + t_pb_type* parent_pb_type = parent_pb_graph_node->pb_type; + + /* No need to constrain the primitive node */ + if (true == is_primitive_pb_type(parent_pb_type)) { + return; + } + + /* Note we only go through the graph through the physical modes. + * which we build the modules + */ + t_mode* physical_mode = device_annotation.physical_mode(parent_pb_type); + + /* Write a SDC file for this pb_type */ + print_pnr_sdc_constrain_pb_graph_node_timing(sdc_dir, + module_manager, + parent_pb_graph_node, + physical_mode); + + /* Go recursively to the lower level in the pb_graph + * Note that we assume a full hierarchical P&R, we will only visit pb_graph_node of unique pb_type + */ + for (int ipb = 0; ipb < physical_mode->num_pb_type_children; ++ipb) { + rec_print_pnr_sdc_constrain_pb_graph_timing(sdc_dir, module_manager, + device_annotation, + &(parent_pb_graph_node->child_pb_graph_nodes[physical_mode->index][ipb][0])); + } +} + +/******************************************************************** + * Top-level function to print timing constraints for pb_types + *******************************************************************/ +void print_pnr_sdc_constrain_grid_timing(const std::string& sdc_dir, + const DeviceContext& device_ctx, + const VprDeviceAnnotation& device_annotation, + const ModuleManager& module_manager) { + + /* Start time count */ + vtr::ScopedStartFinishTimer timer("Write SDC for constraining grid timing for P&R flow"); + + for (const t_physical_tile_type& physical_tile : device_ctx.physical_tile_types) { + /* Bypass empty type or nullptr */ + if (true == is_empty_type(&physical_tile)) { + continue; + } else { + VTR_ASSERT(1 == physical_tile.equivalent_sites.size()); + t_pb_graph_node* pb_graph_head = physical_tile.equivalent_sites[0]->pb_graph_head; + if (nullptr == pb_graph_head) { + continue; + } + /* Special for I/O block, generate one module for each border side */ + rec_print_pnr_sdc_constrain_pb_graph_timing(sdc_dir, module_manager, + device_annotation, + pb_graph_head); + } + } +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_sdc/pnr_sdc_grid_writer.h b/openfpga/src/fpga_sdc/pnr_sdc_grid_writer.h new file mode 100644 index 000000000..91c1b7030 --- /dev/null +++ b/openfpga/src/fpga_sdc/pnr_sdc_grid_writer.h @@ -0,0 +1,27 @@ +#ifndef PNR_SDC_GRID_WRITER_H +#define PNR_SDC_GRID_WRITER_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include +#include "vpr_context.h" +#include "vpr_device_annotation.h" +#include "module_manager.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +void print_pnr_sdc_constrain_grid_timing(const std::string& sdc_dir, + const DeviceContext& device_ctx, + const VprDeviceAnnotation& device_annotation, + const ModuleManager& module_manager); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/fpga_sdc/pnr_sdc_option.cpp b/openfpga/src/fpga_sdc/pnr_sdc_option.cpp new file mode 100644 index 000000000..3a3906b50 --- /dev/null +++ b/openfpga/src/fpga_sdc/pnr_sdc_option.cpp @@ -0,0 +1,113 @@ +/******************************************************************** + * Member functions for a data structure which includes all the options for the SDC generator + ********************************************************************/ +#include "pnr_sdc_option.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Public Constructors + ********************************************************************/ +PnrSdcOption::PnrSdcOption(const std::string& sdc_dir) { + sdc_dir_ = sdc_dir; + constrain_global_port_ = false; + constrain_grid_ = false; + constrain_sb_ = false; + constrain_cb_ = false; + constrain_configurable_memory_outputs_ = false; + constrain_routing_multiplexer_outputs_ = false; + constrain_switch_block_outputs_ = false; +} + +/******************************************************************** + * Public accessors + ********************************************************************/ +std::string PnrSdcOption::sdc_dir() const { + return sdc_dir_; +} + +bool PnrSdcOption::generate_sdc_pnr() const { + return constrain_global_port_ + || constrain_grid_ + || constrain_sb_ + || constrain_cb_ + || constrain_configurable_memory_outputs_ + || constrain_routing_multiplexer_outputs_ + || constrain_switch_block_outputs_; +} + +bool PnrSdcOption::constrain_global_port() const { + return constrain_global_port_; +} + +bool PnrSdcOption::constrain_grid() const { + return constrain_grid_; +} + +bool PnrSdcOption::constrain_sb() const { + return constrain_sb_; +} + +bool PnrSdcOption::constrain_cb() const { + return constrain_cb_; +} + +bool PnrSdcOption::constrain_configurable_memory_outputs() const { + return constrain_configurable_memory_outputs_; +} + +bool PnrSdcOption::constrain_routing_multiplexer_outputs() const { + return constrain_routing_multiplexer_outputs_; +} + +bool PnrSdcOption::constrain_switch_block_outputs() const { + return constrain_switch_block_outputs_; +} + +/******************************************************************** + * Public mutators + ********************************************************************/ +void PnrSdcOption::set_sdc_dir(const std::string& sdc_dir) { + sdc_dir_ = sdc_dir; +} + +void PnrSdcOption::set_generate_sdc_pnr(const bool& generate_sdc_pnr) { + constrain_global_port_ = generate_sdc_pnr; + constrain_grid_ = generate_sdc_pnr; + constrain_sb_ = generate_sdc_pnr; + constrain_cb_ = generate_sdc_pnr; + constrain_configurable_memory_outputs_ = generate_sdc_pnr; + constrain_routing_multiplexer_outputs_ = generate_sdc_pnr; + constrain_switch_block_outputs_ = generate_sdc_pnr; +} + +void PnrSdcOption::set_constrain_global_port(const bool& constrain_global_port) { + constrain_global_port_ = constrain_global_port; +} + +void PnrSdcOption::set_constrain_grid(const bool& constrain_grid) { + constrain_grid_ = constrain_grid; +} + +void PnrSdcOption::set_constrain_sb(const bool& constrain_sb) { + constrain_sb_ = constrain_sb; +} + +void PnrSdcOption::set_constrain_cb(const bool& constrain_cb) { + constrain_cb_ = constrain_cb; +} + +void PnrSdcOption::set_constrain_configurable_memory_outputs(const bool& constrain_config_mem_outputs) { + constrain_configurable_memory_outputs_ = constrain_config_mem_outputs; +} + +void PnrSdcOption::set_constrain_routing_multiplexer_outputs(const bool& constrain_routing_mux_outputs) { + constrain_routing_multiplexer_outputs_ = constrain_routing_mux_outputs; +} + +void PnrSdcOption::set_constrain_switch_block_outputs(const bool& constrain_sb_outputs) { + constrain_switch_block_outputs_ = constrain_sb_outputs; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_sdc/pnr_sdc_option.h b/openfpga/src/fpga_sdc/pnr_sdc_option.h new file mode 100644 index 000000000..a485a1946 --- /dev/null +++ b/openfpga/src/fpga_sdc/pnr_sdc_option.h @@ -0,0 +1,50 @@ +#ifndef PNR_SDC_OPTION_H +#define PNR_SDC_OPTION_H + +/******************************************************************** + * A data structure to include all the options for the SDC generator + * in purpose of constraining physical design of FPGA fabric in back-end flow + ********************************************************************/ + +#include + +/* begin namespace openfpga */ +namespace openfpga { + +class PnrSdcOption { + public: /* Public Constructors */ + PnrSdcOption(const std::string& sdc_dir); + public: /* Public accessors */ + std::string sdc_dir() const; + bool generate_sdc_pnr() const; + bool constrain_global_port() const; + bool constrain_grid() const; + bool constrain_sb() const; + bool constrain_cb() const; + bool constrain_configurable_memory_outputs() const; + bool constrain_routing_multiplexer_outputs() const; + bool constrain_switch_block_outputs() const; + public: /* Public mutators */ + void set_sdc_dir(const std::string& sdc_dir); + void set_generate_sdc_pnr(const bool& generate_sdc_pnr); + void set_constrain_global_port(const bool& constrain_global_port); + void set_constrain_grid(const bool& constrain_grid); + void set_constrain_sb(const bool& constrain_sb); + void set_constrain_cb(const bool& constrain_cb); + void set_constrain_configurable_memory_outputs(const bool& constrain_config_mem_outputs); + void set_constrain_routing_multiplexer_outputs(const bool& constrain_routing_mux_outputs); + void set_constrain_switch_block_outputs(const bool& constrain_sb_outputs); + private: /* Internal data */ + std::string sdc_dir_; + bool constrain_global_port_; + bool constrain_grid_; + bool constrain_sb_; + bool constrain_cb_; + bool constrain_configurable_memory_outputs_; + bool constrain_routing_multiplexer_outputs_; + bool constrain_switch_block_outputs_; +}; + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/fpga_sdc/pnr_sdc_routing_writer.cpp b/openfpga/src/fpga_sdc/pnr_sdc_routing_writer.cpp new file mode 100644 index 000000000..dcb5b6849 --- /dev/null +++ b/openfpga/src/fpga_sdc/pnr_sdc_routing_writer.cpp @@ -0,0 +1,411 @@ +/******************************************************************** + * This file includes functions that print SDC (Synopsys Design Constraint) + * files in physical design tools, i.e., Place & Route (PnR) tools + * The SDC files are used to constrain the physical design for each routing modules + * in FPGA fabric, such as Switch Blocks (SBs) and Connection Blocks (CBs) + * + * Note that this is different from the SDC to constrain VPR Place&Route + * engine! These SDCs are designed for PnR to generate FPGA layouts!!! + *******************************************************************/ +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_log.h" +#include "vtr_assert.h" +#include "vtr_time.h" + +/* Headers from openfpgautil library */ +#include "openfpga_port.h" +#include "openfpga_side_manager.h" +#include "openfpga_digest.h" + +#include "mux_utils.h" + +#include "openfpga_naming.h" + +#include "openfpga_rr_graph_utils.h" +#include "build_routing_module_utils.h" + +#include "sdc_writer_naming.h" +#include "sdc_writer_utils.h" +#include "pnr_sdc_routing_writer.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * 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_rr_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 RRGraph& rr_graph, + const RRGSB& rr_gsb, + const e_side& output_node_side, + const RRNodeId& output_rr_node) { + /* Validate file stream */ + valid_file_stream(fp); + + VTR_ASSERT( ( CHANX == rr_graph.node_type(output_rr_node) ) + || ( CHANY == rr_graph.node_type(output_rr_node) )); + + /* 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_graph, + 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 module_input_ports = find_switch_block_module_input_ports(module_manager, + sb_module, + rr_graph, + rr_gsb, + get_rr_graph_configurable_driver_nodes(rr_graph, output_rr_node)); + + /* Find timing constraints for each path (edge) */ + std::map switch_delays; + size_t edge_counter = 0; + for (const RREdgeId& edge : rr_graph.node_configurable_in_edges(output_rr_node)) { + /* Get the switch delay */ + const RRSwitchId& driver_switch = rr_graph.edge_switch(edge); + switch_delays[module_input_ports[edge_counter]] = find_pnr_sdc_switch_tmax(rr_graph.get_switch(driver_switch)); + edge_counter++; + } + + /* Find the starting points */ + for (const ModulePortId& module_input_port : module_input_ports) { + /* Constrain a path */ + print_pnr_sdc_constrain_port2port_timing(fp, + module_manager, + sb_module, module_input_port, + sb_module, 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 RRGraph& rr_graph, + 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_stream(sdc_fname.c_str(), 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 + " for PnR")); + + for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { + SideManager side_manager(side); + for (size_t itrack = 0; itrack < rr_gsb.get_chan_width(side_manager.get_side()); ++itrack) { + const RRNodeId& 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(rr_graph, 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_graph, + rr_gsb, + 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 + *******************************************************************/ +void print_pnr_sdc_flatten_routing_constrain_sb_timing(const std::string& sdc_dir, + const ModuleManager& module_manager, + const RRGraph& rr_graph, + const DeviceRRGSB& device_rr_gsb) { + + /* Start time count */ + vtr::ScopedStartFinishTimer timer("Write SDC for constrain Switch Block timing for P&R flow"); + + /* Get the range of SB array */ + vtr::Point sb_range = device_rr_gsb.get_gsb_range(); + /* Go for each SB */ + for (size_t ix = 0; ix < sb_range.x(); ++ix) { + for (size_t iy = 0; iy < sb_range.y(); ++iy) { + const RRGSB& rr_gsb = device_rr_gsb.get_gsb(ix, iy); + if (false == rr_gsb.is_sb_exist()) { + continue; + } + print_pnr_sdc_constrain_sb_timing(sdc_dir, + module_manager, + rr_graph, + rr_gsb); + } + } +} + +/******************************************************************** + * Print SDC timing constraints for Switch blocks + * This function is designed for compact routing hierarchy + *******************************************************************/ +void print_pnr_sdc_compact_routing_constrain_sb_timing(const std::string& sdc_dir, + const ModuleManager& module_manager, + const RRGraph& rr_graph, + const DeviceRRGSB& device_rr_gsb) { + + /* Start time count */ + vtr::ScopedStartFinishTimer timer("Write SDC for constrain Switch Block timing for P&R flow"); + + for (size_t isb = 0; isb < device_rr_gsb.get_num_sb_unique_module(); ++isb) { + const RRGSB& rr_gsb = device_rr_gsb.get_sb_unique_module(isb); + if (false == rr_gsb.is_sb_exist()) { + continue; + } + print_pnr_sdc_constrain_sb_timing(sdc_dir, + module_manager, + rr_graph, + rr_gsb); + } +} + +/******************************************************************** + * Set timing constraints between the inputs and outputs of a routing + * multiplexer in a Connection Block + *******************************************************************/ +static +void print_pnr_sdc_constrain_cb_mux_timing(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& cb_module, + const RRGraph& rr_graph, + const RRGSB& rr_gsb, + const t_rr_type& cb_type, + const RRNodeId& output_rr_node) { + /* Validate file stream */ + valid_file_stream(fp); + + VTR_ASSERT(IPIN == rr_graph.node_type(output_rr_node)); + + /* 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 (0 == get_rr_graph_configurable_driver_nodes(rr_graph, output_rr_node).size()) { + return; + } + + /* Find the module port corresponding to the output rr_node */ + ModulePortId module_output_port = find_connection_block_module_ipin_port(module_manager, + cb_module, + rr_graph, + rr_gsb, + output_rr_node); + + /* Find the module port corresponding to the fan-in rr_nodes of the output rr_node */ + std::vector module_input_ports = find_connection_block_module_input_ports(module_manager, + cb_module, + rr_graph, + rr_gsb, + cb_type, + get_rr_graph_configurable_driver_nodes(rr_graph, output_rr_node)); + + /* Find timing constraints for each path (edge) */ + std::map switch_delays; + size_t edge_counter = 0; + for (const RREdgeId& edge : rr_graph.node_configurable_in_edges(output_rr_node)) { + /* Get the switch delay */ + const RRSwitchId& driver_switch = rr_graph.edge_switch(edge); + switch_delays[module_input_ports[edge_counter]] = find_pnr_sdc_switch_tmax(rr_graph.get_switch(driver_switch)); + edge_counter++; + } + + /* Find the starting points */ + for (const ModulePortId& module_input_port : module_input_ports) { + /* Constrain a path */ + print_pnr_sdc_constrain_port2port_timing(fp, + module_manager, + cb_module, module_input_port, + cb_module, module_output_port, + switch_delays[module_input_port]); + } +} + + +/******************************************************************** + * Print SDC timing constraints for a Connection block + * This function is designed for compact routing hierarchy + *******************************************************************/ +static +void print_pnr_sdc_constrain_cb_timing(const std::string& sdc_dir, + const ModuleManager& module_manager, + const RRGraph& rr_graph, + const RRGSB& rr_gsb, + const t_rr_type& cb_type) { + /* Create the netlist */ + vtr::Point gsb_coordinate(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type)); + + /* Find the module name and create a SDC file for it */ + std::string sdc_fname(sdc_dir + generate_connection_block_module_name(cb_type, 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_stream(sdc_fname.c_str(), fp); + + std::string cb_module_name = generate_connection_block_module_name(cb_type, gsb_coordinate); + ModuleId cb_module = module_manager.find_module(cb_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(cb_module)); + + /* Generate the descriptions*/ + print_sdc_file_header(fp, std::string("Constrain timing of Connection Block " + cb_module_name + " for PnR")); + + std::vector cb_sides = rr_gsb.get_cb_ipin_sides(cb_type); + + for (size_t side = 0; side < cb_sides.size(); ++side) { + enum e_side cb_ipin_side = cb_sides[side]; + SideManager side_manager(cb_ipin_side); + for (size_t inode = 0; inode < rr_gsb.get_num_ipin_nodes(cb_ipin_side); ++inode) { + const RRNodeId& ipin_rr_node = rr_gsb.get_ipin_node(cb_ipin_side, inode); + print_pnr_sdc_constrain_cb_mux_timing(fp, + module_manager, cb_module, + rr_graph, rr_gsb, cb_type, + ipin_rr_node); + } + } + + /* Close file handler */ + fp.close(); +} + +/******************************************************************** + * Iterate over all the connection blocks in a device + * and print SDC file for each of them + *******************************************************************/ +static +void print_pnr_sdc_flatten_routing_constrain_cb_timing(const std::string& sdc_dir, + const ModuleManager& module_manager, + const RRGraph& rr_graph, + const DeviceRRGSB& device_rr_gsb, + const t_rr_type& cb_type) { + /* Build unique X-direction connection block modules */ + vtr::Point cb_range = device_rr_gsb.get_gsb_range(); + + for (size_t ix = 0; ix < cb_range.x(); ++ix) { + for (size_t iy = 0; iy < cb_range.y(); ++iy) { + /* Check if the connection block exists in the device! + * Some of them do NOT exist due to heterogeneous blocks (height > 1) + * We will skip those modules + */ + const RRGSB& rr_gsb = device_rr_gsb.get_gsb(ix, iy); + if (false == rr_gsb.is_cb_exist(cb_type)) { + continue; + } + print_pnr_sdc_constrain_cb_timing(sdc_dir, + module_manager, + rr_graph, + rr_gsb, + cb_type); + + } + } +} + +/******************************************************************** + * Iterate over all the connection blocks in a device + * and print SDC file for each of them + *******************************************************************/ +void print_pnr_sdc_flatten_routing_constrain_cb_timing(const std::string& sdc_dir, + const ModuleManager& module_manager, + const RRGraph& rr_graph, + const DeviceRRGSB& device_rr_gsb) { + + /* Start time count */ + vtr::ScopedStartFinishTimer timer("Write SDC for constrain Connection Block timing for P&R flow"); + + print_pnr_sdc_flatten_routing_constrain_cb_timing(sdc_dir, module_manager, + rr_graph, + device_rr_gsb, + CHANX); + + print_pnr_sdc_flatten_routing_constrain_cb_timing(sdc_dir, module_manager, + rr_graph, + device_rr_gsb, + CHANY); +} + +/******************************************************************** + * Print SDC timing constraints for Connection blocks + * This function is designed for compact routing hierarchy + *******************************************************************/ +void print_pnr_sdc_compact_routing_constrain_cb_timing(const std::string& sdc_dir, + const ModuleManager& module_manager, + const RRGraph& rr_graph, + const DeviceRRGSB& device_rr_gsb) { + + /* Start time count */ + vtr::ScopedStartFinishTimer timer("Write SDC for constrain Connection Block timing for P&R flow"); + + /* Print SDC for unique X-direction connection block modules */ + for (size_t icb = 0; icb < device_rr_gsb.get_num_cb_unique_module(CHANX); ++icb) { + const RRGSB& unique_mirror = device_rr_gsb.get_cb_unique_module(CHANX, icb); + print_pnr_sdc_constrain_cb_timing(sdc_dir, + module_manager, + rr_graph, + unique_mirror, + CHANX); + } + + /* Print SDC for unique Y-direction connection block modules */ + for (size_t icb = 0; icb < device_rr_gsb.get_num_cb_unique_module(CHANY); ++icb) { + const RRGSB& unique_mirror = device_rr_gsb.get_cb_unique_module(CHANY, icb); + print_pnr_sdc_constrain_cb_timing(sdc_dir, + module_manager, + rr_graph, + unique_mirror, + CHANY); + } +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_sdc/pnr_sdc_routing_writer.h b/openfpga/src/fpga_sdc/pnr_sdc_routing_writer.h new file mode 100644 index 000000000..65ce60ca8 --- /dev/null +++ b/openfpga/src/fpga_sdc/pnr_sdc_routing_writer.h @@ -0,0 +1,42 @@ +#ifndef PNR_SDC_ROUTING_WRITER_H +#define PNR_SDC_ROUTING_WRITER_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include +#include "module_manager.h" +#include "device_rr_gsb.h" +#include "rr_graph_obj.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +void print_pnr_sdc_flatten_routing_constrain_sb_timing(const std::string& sdc_dir, + const ModuleManager& module_manager, + const RRGraph& rr_graph, + const DeviceRRGSB& device_rr_gsb); + +void print_pnr_sdc_compact_routing_constrain_sb_timing(const std::string& sdc_dir, + const ModuleManager& module_manager, + const RRGraph& rr_graph, + const DeviceRRGSB& device_rr_gsb); + +void print_pnr_sdc_flatten_routing_constrain_cb_timing(const std::string& sdc_dir, + const ModuleManager& module_manager, + const RRGraph& rr_graph, + const DeviceRRGSB& device_rr_gsb); + +void print_pnr_sdc_compact_routing_constrain_cb_timing(const std::string& sdc_dir, + const ModuleManager& module_manager, + const RRGraph& rr_graph, + const DeviceRRGSB& device_rr_gsb); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp b/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp new file mode 100644 index 000000000..9d1478037 --- /dev/null +++ b/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp @@ -0,0 +1,431 @@ +/******************************************************************** + * This file includes functions that print SDC (Synopsys Design Constraint) + * files in physical design tools, i.e., Place & Route (PnR) tools + * The SDC files are used to constrain the physical design for each module + * in FPGA fabric, such as Configurable Logic Blocks (CLBs), + * Heterogeneous blocks, Switch Blocks (SBs) and Connection Blocks (CBs) + * + * Note that this is different from the SDC to constrain VPR Place&Route + * engine! These SDCs are designed for PnR to generate FPGA layouts!!! + *******************************************************************/ +#include +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_time.h" +#include "vtr_log.h" + +/* Headers from openfpgautil library */ +#include "openfpga_port.h" +#include "openfpga_digest.h" + +#include "mux_utils.h" + +#include "openfpga_naming.h" + +#include "sdc_writer_naming.h" +#include "sdc_writer_utils.h" +#include "sdc_memory_utils.h" +#include "pnr_sdc_routing_writer.h" +#include "pnr_sdc_grid_writer.h" +#include "pnr_sdc_writer.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Print a SDC file to constrain the global ports of FPGA fabric + * in particular clock ports + * + * For programming clock, we give a fixed period, while for operating + * clock, we constrain with critical path delay + *******************************************************************/ +static +void print_pnr_sdc_global_ports(const std::string& sdc_dir, + const float& programming_critical_path_delay, + const float& operating_critical_path_delay, + const CircuitLibrary& circuit_lib, + const std::vector& global_ports) { + + /* Create the file name for Verilog netlist */ + std::string sdc_fname(sdc_dir + std::string(SDC_GLOBAL_PORTS_FILE_NAME)); + + /* Start time count */ + std::string timer_message = std::string("Write SDC for constraining clocks for P&R flow '") + sdc_fname + std::string("'"); + vtr::ScopedStartFinishTimer timer(timer_message); + + /* Create the file stream */ + std::fstream fp; + fp.open(sdc_fname, std::fstream::out | std::fstream::trunc); + + check_file_stream(sdc_fname.c_str(), fp); + + /* Generate the descriptions*/ + print_sdc_file_header(fp, std::string("Clock contraints for PnR")); + + /* Get clock port from the global port */ + for (const CircuitPortId& clock_port : global_ports) { + if (CIRCUIT_MODEL_PORT_CLOCK != circuit_lib.port_type(clock_port)) { + continue; + } + /* Reach here, it means a clock port and we need print constraints */ + float clock_period = operating_critical_path_delay; + + /* For programming clock, we give a fixed period */ + if (true == circuit_lib.port_is_prog(clock_port)) { + clock_period = programming_critical_path_delay; + /* Print comments */ + fp << "##################################################" << std::endl; + fp << "# Create programmable clock " << std::endl; + fp << "##################################################" << std::endl; + } else { + /* Print comments */ + fp << "##################################################" << std::endl; + fp << "# Create clock " << std::endl; + fp << "##################################################" << std::endl; + } + + for (const size_t& pin : circuit_lib.pins(clock_port)) { + BasicPort port_to_constrain(circuit_lib.port_prefix(clock_port), pin, pin); + + fp << "create_clock "; + fp << generate_sdc_port(port_to_constrain) << "-period "; + fp << std::setprecision(10) << clock_period; + fp << " -waveform {0 "; + fp << std::setprecision(10) << clock_period / 2; + fp << "}" << std::endl; + + fp << std::endl; + } + } + + /* For non-clock port from the global port: give a fixed period */ + for (const CircuitPortId& global_port : global_ports) { + if (CIRCUIT_MODEL_PORT_CLOCK == circuit_lib.port_type(global_port)) { + continue; + } + + /* Print comments */ + fp << "##################################################" << std::endl; + fp << "# Constrain other global ports " << std::endl; + fp << "##################################################" << std::endl; + + /* Reach here, it means a non-clock global port and we need print constraints */ + float clock_period = operating_critical_path_delay; + for (const size_t& pin : circuit_lib.pins(global_port)) { + BasicPort port_to_constrain(circuit_lib.port_prefix(global_port), pin, pin); + fp << "create_clock "; + fp << generate_sdc_port(port_to_constrain) << "-period "; + fp << std::setprecision(10) << clock_period; + fp << " -waveform {0 "; + fp << std::setprecision(10) << clock_period / 2; + fp << "} "; + fp << "[list [get_ports { " << generate_sdc_port(port_to_constrain) << "}]]" << std::endl; + + fp << "set_drive 0 " << generate_sdc_port(port_to_constrain) << std::endl; + + fp << std::endl; + } + } + + /* Close file handler */ + fp.close(); +} + +/******************************************************************** + * Break combinational loops in FPGA fabric, which mainly come from + * configurable memory cells. + * To handle this, we disable the outputs of memory cells + *******************************************************************/ +static +void print_pnr_sdc_constrain_configurable_memory_outputs(const std::string& sdc_dir, + const ModuleManager& module_manager, + const ModuleId& top_module) { + + /* Create the file name for Verilog netlist */ + std::string sdc_fname(sdc_dir + std::string(SDC_DISABLE_CONFIG_MEM_OUTPUTS_FILE_NAME)); + + /* Start time count */ + std::string timer_message = std::string("Write SDC to disable configurable memory outputs for P&R flow '") + sdc_fname + std::string("'"); + vtr::ScopedStartFinishTimer timer(timer_message); + + /* Create the file stream */ + std::fstream fp; + fp.open(sdc_fname, std::fstream::out | std::fstream::trunc); + + check_file_stream(sdc_fname.c_str(), fp); + + /* Generate the descriptions*/ + print_sdc_file_header(fp, std::string("Disable configurable memory outputs for PnR")); + + /* Go recursively in the module manager, starting from the top-level module: instance id of the top-level module is 0 by default */ + rec_print_pnr_sdc_disable_configurable_memory_module_output(fp, module_manager, top_module, + format_dir_path(module_manager.module_name(top_module))); + + /* Close file handler */ + fp.close(); +} + +/******************************************************************** + * Break combinational loops in FPGA fabric, which mainly come from + * loops of multiplexers. + * To handle this, we disable the timing at outputs of routing multiplexers + *******************************************************************/ +static +void print_sdc_disable_routing_multiplexer_outputs(const std::string& sdc_dir, + const MuxLibrary& mux_lib, + const CircuitLibrary& circuit_lib, + const ModuleManager& module_manager) { + /* Create the file name for Verilog netlist */ + std::string sdc_fname(sdc_dir + std::string(SDC_DISABLE_MUX_OUTPUTS_FILE_NAME)); + + /* Start time count */ + std::string timer_message = std::string("Write SDC to disable routing multiplexer outputs for P&R flow '") + sdc_fname + std::string("'"); + vtr::ScopedStartFinishTimer timer(timer_message); + + /* Create the file stream */ + std::fstream fp; + fp.open(sdc_fname, std::fstream::out | std::fstream::trunc); + + check_file_stream(sdc_fname.c_str(), fp); + + /* Generate the descriptions*/ + print_sdc_file_header(fp, std::string("Disable routing multiplexer outputs for PnR")); + + /* Iterate over the MUX modules */ + for (const MuxId& mux_id : mux_lib.muxes()) { + const CircuitModelId& mux_model = mux_lib.mux_circuit_model(mux_id); + + /* Skip LUTs, we only care about multiplexers here */ + if (CIRCUIT_MODEL_MUX != circuit_lib.model_type(mux_model)) { + continue; + } + + const MuxGraph& mux_graph = mux_lib.mux_graph(mux_id); + std::string mux_module_name = generate_mux_subckt_name(circuit_lib, mux_model, + find_mux_num_datapath_inputs(circuit_lib, mux_model, mux_graph.num_inputs()), + std::string("")); + /* Find the module name in module manager */ + ModuleId mux_module = module_manager.find_module(mux_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(mux_module)); + + /* Disable the timing for the output ports */ + for (const BasicPort& output_port : module_manager.module_ports_by_type(mux_module, ModuleManager::MODULE_OUTPUT_PORT)) { + fp << "set_disable_timing [get_pins -filter \"name =~ " << output_port.get_name() << "*\" "; + fp << "-of [get_cells -hier -filter \"ref_lib_cell_name == " << mux_module_name << "\"]]" << std::endl; + fp << std::endl; + } + } + + /* Close file handler */ + fp.close(); +} + +/******************************************************************** + * 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& device_rr_gsb) { + /* Create the file name for Verilog netlist */ + std::string sdc_fname(sdc_dir + std::string(SDC_DISABLE_SB_OUTPUTS_FILE_NAME)); + + /* Start time count */ + std::string timer_message = std::string("Write SDC to disable switch block outputs for P&R flow '") + sdc_fname + std::string("'"); + vtr::ScopedStartFinishTimer timer(timer_message); + + /* Create the file stream */ + std::fstream fp; + fp.open(sdc_fname, std::fstream::out | std::fstream::trunc); + + check_file_stream(sdc_fname.c_str(), fp); + + /* Generate the descriptions*/ + print_sdc_file_header(fp, std::string("Disable Switch Block outputs for PnR")); + + /* Get the range of SB array */ + vtr::Point sb_range = device_rr_gsb.get_gsb_range(); + /* Go for each SB */ + for (size_t ix = 0; ix < sb_range.x(); ++ix) { + for (size_t iy = 0; iy < sb_range.y(); ++iy) { + const RRGSB& rr_gsb = device_rr_gsb.get_gsb(ix, iy); + + if (false == rr_gsb.is_sb_exist()) { + continue; + } + + 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(); +} + +/******************************************************************** + * 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& device_rr_gsb) { + /* Create the file name for Verilog netlist */ + std::string sdc_fname(sdc_dir + std::string(SDC_DISABLE_SB_OUTPUTS_FILE_NAME)); + + /* Start time count */ + std::string timer_message = std::string("Write SDC to disable switch block outputs for P&R flow '") + sdc_fname + std::string("'"); + vtr::ScopedStartFinishTimer timer(timer_message); + + /* Create the file stream */ + std::fstream fp; + fp.open(sdc_fname, std::fstream::out | std::fstream::trunc); + + check_file_stream(sdc_fname.c_str(), 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 < device_rr_gsb.get_num_sb_unique_module(); ++isb) { + const RRGSB& rr_gsb = 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(); +} + +/******************************************************************** + * Top-level function to print a number of SDC files in different purpose + * This function will generate files upon the options provided by users + * 1. Design constraints for CLBs + * 2. Design constraints for Switch Blocks + * 3. Design constraints for Connection Blocks + * 4. Design constraints for breaking the combinational loops in FPGA fabric + *******************************************************************/ +void print_pnr_sdc(const PnrSdcOption& sdc_options, + const float& programming_critical_path_delay, + const float& operating_critical_path_delay, + const DeviceContext& device_ctx, + const VprDeviceAnnotation& device_annotation, + const DeviceRRGSB& device_rr_gsb, + const ModuleManager& module_manager, + const MuxLibrary& mux_lib, + const CircuitLibrary& circuit_lib, + const std::vector& global_ports, + const bool& compact_routing_hierarchy) { + + /* Constrain global ports */ + if (true == sdc_options.constrain_global_port()) { + print_pnr_sdc_global_ports(sdc_options.sdc_dir(), + programming_critical_path_delay, + operating_critical_path_delay, + circuit_lib, global_ports); + } + + std::string top_module_name = generate_fpga_top_module_name(); + ModuleId top_module = module_manager.find_module(top_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(top_module)); + + /* 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); + } + + /* 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); + } + + /* 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, + 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, + 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, + device_ctx.rr_graph, + 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, + device_ctx.rr_graph, + device_rr_gsb); + } + } + + /* Output routing constraints for Connection Blocks */ + if (true == sdc_options.constrain_cb()) { + if (true == compact_routing_hierarchy) { + print_pnr_sdc_compact_routing_constrain_cb_timing(sdc_options.sdc_dir(), + module_manager, + device_ctx.rr_graph, + device_rr_gsb); + } else { + VTR_ASSERT_SAFE (false == compact_routing_hierarchy); + print_pnr_sdc_flatten_routing_constrain_cb_timing(sdc_options.sdc_dir(), + module_manager, + device_ctx.rr_graph, + device_rr_gsb); + } + } + + /* Output Timing constraints for Programmable blocks */ + if (true == sdc_options.constrain_grid()) { + print_pnr_sdc_constrain_grid_timing(sdc_options.sdc_dir(), + device_ctx, + device_annotation, + module_manager); + } +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_sdc/pnr_sdc_writer.h b/openfpga/src/fpga_sdc/pnr_sdc_writer.h new file mode 100644 index 000000000..1016d57da --- /dev/null +++ b/openfpga/src/fpga_sdc/pnr_sdc_writer.h @@ -0,0 +1,38 @@ +#ifndef PNR_SDC_WRITER_H +#define PNR_SDC_WRITER_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include +#include "vpr_context.h" +#include "vpr_device_annotation.h" +#include "device_rr_gsb.h" +#include "module_manager.h" +#include "mux_library.h" +#include "circuit_library.h" +#include "pnr_sdc_option.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +void print_pnr_sdc(const PnrSdcOption& sdc_options, + const float& programming_critical_path_delay, + const float& operating_critical_path_delay, + const DeviceContext& device_ctx, + const VprDeviceAnnotation& device_annotation, + const DeviceRRGSB& device_rr_gsb, + const ModuleManager& module_manager, + const MuxLibrary& mux_lib, + const CircuitLibrary& circuit_lib, + const std::vector& global_ports, + const bool& compact_routing_hierarchy); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/fpga_sdc/sdc_memory_utils.cpp b/openfpga/src/fpga_sdc/sdc_memory_utils.cpp new file mode 100644 index 000000000..1910b4197 --- /dev/null +++ b/openfpga/src/fpga_sdc/sdc_memory_utils.cpp @@ -0,0 +1,69 @@ +/******************************************************************** + * Most utilized function used to constrain memory cells in FPGA + * fabric using SDC commands + *******************************************************************/ + +/* Headers from openfpgautil library */ +#include "openfpga_digest.h" + +#include "sdc_writer_utils.h" + +#include "sdc_memory_utils.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Print SDC commands to disable outputs of all the configurable memory modules + * in a given module + * This function will be executed in a recursive way, + * using a Depth-First Search (DFS) strategy + * It will iterate over all the configurable children under each module + * and print a SDC command to disable its outputs + *******************************************************************/ +void rec_print_pnr_sdc_disable_configurable_memory_module_output(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& parent_module, + const std::string& parent_module_path) { + + /* For each configurable child, we will go one level down in priority */ + for (size_t child_index = 0; child_index < module_manager.configurable_children(parent_module).size(); ++child_index) { + std::string child_module_path = parent_module_path; + ModuleId child_module_id = module_manager.configurable_children(parent_module)[child_index]; + size_t child_instance_id = module_manager.configurable_child_instances(parent_module)[child_index]; + if (true == module_manager.instance_name(parent_module, child_module_id, child_instance_id).empty()) { + /* Give a default name __ */ + child_module_path += module_manager.module_name(child_module_id); + child_module_path += "_"; + child_module_path += std::to_string(child_instance_id); + child_module_path += "_"; + } else { + child_module_path += module_manager.instance_name(parent_module, child_module_id, child_instance_id); + } + child_module_path = format_dir_path(child_module_path); + + rec_print_pnr_sdc_disable_configurable_memory_module_output(fp, module_manager, + child_module_id, + child_module_path); + } + + /* If there is no configurable children any more, this is a leaf module, print a SDC command for disable timing */ + if (0 < module_manager.configurable_children(parent_module).size()) { + return; + } + + /* Validate file stream */ + valid_file_stream(fp); + + /* Disable timing for each output port of this module */ + for (const BasicPort& output_port : module_manager.module_ports_by_type(parent_module, ModuleManager::MODULE_OUTPUT_PORT)) { + for (const size_t& pin : output_port.pins()) { + BasicPort output_pin(output_port.get_name(), pin, pin); + fp << "set_disable_timing "; + fp << parent_module_path << generate_sdc_port(output_pin); + fp << std::endl; + } + } +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_sdc/sdc_memory_utils.h b/openfpga/src/fpga_sdc/sdc_memory_utils.h new file mode 100644 index 000000000..7fbf92294 --- /dev/null +++ b/openfpga/src/fpga_sdc/sdc_memory_utils.h @@ -0,0 +1,25 @@ +#ifndef SDC_MEMORY_UTILS_H +#define SDC_MEMORY_UTILS_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include +#include "module_manager.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +void rec_print_pnr_sdc_disable_configurable_memory_module_output(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& parent_module, + const std::string& parent_module_path); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/fpga_sdc/sdc_writer_naming.h b/openfpga/src/fpga_sdc/sdc_writer_naming.h new file mode 100644 index 000000000..9783e29f2 --- /dev/null +++ b/openfpga/src/fpga_sdc/sdc_writer_naming.h @@ -0,0 +1,20 @@ +#ifndef SDC_WRITER_NAMING_H +#define SDC_WRITER_NAMING_H + +/* begin namespace openfpga */ +namespace openfpga { + +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_ANALYSIS_FILE_NAME = "fpga_top_analysis.sdc"; + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/fpga_sdc/sdc_writer_utils.cpp b/openfpga/src/fpga_sdc/sdc_writer_utils.cpp new file mode 100644 index 000000000..328f87ce6 --- /dev/null +++ b/openfpga/src/fpga_sdc/sdc_writer_utils.cpp @@ -0,0 +1,202 @@ +/******************************************************************** + * This file include most utilized functions to be used in SDC writers + *******************************************************************/ +#include +#include +#include + +/* Headers from openfpgautil library */ +#include "openfpga_digest.h" + +#include "sdc_writer_utils.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Write a head (description) in SDC file + *******************************************************************/ +void print_sdc_file_header(std::fstream& fp, + const std::string& usage) { + + valid_file_stream(fp); + + auto end = std::chrono::system_clock::now(); + std::time_t end_time = std::chrono::system_clock::to_time_t(end); + + fp << "#############################################" << std::endl; + fp << "#\tSynopsys Design Constraints (SDC)" << std::endl; + fp << "#\tFor FPGA fabric " << std::endl; + fp << "#\tDescription: " << usage << std::endl; + fp << "#\tAuthor: Xifan TANG " << std::endl; + fp << "#\tOrganization: University of Utah " << std::endl; + fp << "#\tDate: " << std::ctime(&end_time); + fp << "#############################################" << std::endl; + fp << std::endl; +} + +/******************************************************************** + * Write a port in SDC format + *******************************************************************/ +std::string generate_sdc_port(const BasicPort& port) { + std::string sdc_line; + + std::string size_str = "[" + std::to_string(port.get_lsb()) + ":" + std::to_string(port.get_msb()) + "]"; + + /* Only connection require a format of [:] + * others require a format of [:] + */ + /* When LSB == MSB, we can use a simplified format []*/ + if ( 1 == port.get_width()) { + size_str = "[" + std::to_string(port.get_lsb()) + "]"; + } + sdc_line = port.get_name() + size_str; + + return sdc_line; +} + +/******************************************************************** + * Constrain a path between two ports of a module with a given maximum timing value + *******************************************************************/ +void print_pnr_sdc_constrain_max_delay(std::fstream& fp, + const std::string& src_instance_name, + const std::string& src_port_name, + const std::string& des_instance_name, + const std::string& des_port_name, + const float& delay) { + /* Validate file stream */ + valid_file_stream(fp); + + fp << "set_max_delay"; + + fp << " -from "; + if (!src_instance_name.empty()) { + fp << src_instance_name << "/"; + } + fp << src_port_name; + + fp << " -to "; + + if (!des_instance_name.empty()) { + fp << des_instance_name << "/"; + } + fp << des_port_name; + + fp << " " << std::setprecision(10) << delay; + + fp << std::endl; +} + +/******************************************************************** + * Constrain a path between two ports of a module with a given timing value + * Note: this function uses set_max_delay !!! + *******************************************************************/ +void print_pnr_sdc_constrain_module_port2port_timing(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& input_parent_module_id, + const ModulePortId& module_input_port_id, + const ModuleId& output_parent_module_id, + const ModulePortId& module_output_port_id, + const float& tmax) { + print_pnr_sdc_constrain_max_delay(fp, + module_manager.module_name(input_parent_module_id), + generate_sdc_port(module_manager.module_port(input_parent_module_id, module_input_port_id)), + module_manager.module_name(output_parent_module_id), + generate_sdc_port(module_manager.module_port(output_parent_module_id, module_output_port_id)), + tmax); + +} + +/******************************************************************** + * Constrain a path between two ports of a module with a given timing value + * This function will NOT output the module name + * Note: this function uses set_max_delay !!! + *******************************************************************/ +void print_pnr_sdc_constrain_port2port_timing(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& input_parent_module_id, + const ModulePortId& module_input_port_id, + const ModuleId& output_parent_module_id, + const ModulePortId& module_output_port_id, + const float& tmax) { + print_pnr_sdc_constrain_max_delay(fp, + std::string(), + generate_sdc_port(module_manager.module_port(input_parent_module_id, module_input_port_id)), + std::string(), + generate_sdc_port(module_manager.module_port(output_parent_module_id, module_output_port_id)), + tmax); + +} + +/******************************************************************** + * Disable timing for a port + *******************************************************************/ +void print_sdc_disable_port_timing(std::fstream& fp, + const BasicPort& port) { + /* Validate file stream */ + valid_file_stream(fp); + + fp << "set_disable_timing "; + + fp << generate_sdc_port(port); + + fp << std::endl; +} + +/******************************************************************** + * Set the input delay for a port in SDC format + * Note that the input delay will be bounded by a clock port + *******************************************************************/ +void print_sdc_set_port_input_delay(std::fstream& fp, + const BasicPort& port, + const BasicPort& clock_port, + const float& delay) { + /* Validate file stream */ + valid_file_stream(fp); + + fp << "set_input_delay "; + + fp << "-clock "; + + fp << generate_sdc_port(clock_port); + + fp << " -max "; + + fp << std::setprecision(10) << delay; + + fp << " "; + + fp << generate_sdc_port(port); + + fp << std::endl; +} + +/******************************************************************** + * Set the output delay for a port in SDC format + * Note that the output delay will be bounded by a clock port + *******************************************************************/ +void print_sdc_set_port_output_delay(std::fstream& fp, + const BasicPort& port, + const BasicPort& clock_port, + const float& delay) { + /* Validate file stream */ + valid_file_stream(fp); + + fp << "set_output_delay "; + + fp << "-clock "; + + fp << generate_sdc_port(clock_port); + + fp << " -max "; + + fp << std::setprecision(10) << delay; + + fp << " "; + + fp << generate_sdc_port(port); + + fp << std::endl; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_sdc/sdc_writer_utils.h b/openfpga/src/fpga_sdc/sdc_writer_utils.h new file mode 100644 index 000000000..4e0d86839 --- /dev/null +++ b/openfpga/src/fpga_sdc/sdc_writer_utils.h @@ -0,0 +1,62 @@ +#ifndef SDC_WRITER_UTILS_H +#define SDC_WRITER_UTILS_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include +#include "openfpga_port.h" +#include "module_manager.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +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_max_delay(std::fstream& fp, + const std::string& src_instance_name, + const std::string& src_port_name, + const std::string& des_instance_name, + const std::string& des_port_name, + const float& delay); + +void print_pnr_sdc_constrain_module_port2port_timing(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& input_parent_module_id, + const ModulePortId& module_input_port_id, + const ModuleId& output_parent_module_id, + const ModulePortId& module_output_port_id, + const float& tmax); + +void print_pnr_sdc_constrain_port2port_timing(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& input_parent_module_id, + const ModulePortId& module_input_port_id, + const ModuleId& output_parent_module_id, + const ModulePortId& module_output_port_id, + const float& tmax); + +void print_sdc_disable_port_timing(std::fstream& fp, + const BasicPort& port); + +void print_sdc_set_port_input_delay(std::fstream& fp, + const BasicPort& port, + const BasicPort& clock_port, + const float& delay); + +void print_sdc_set_port_output_delay(std::fstream& fp, + const BasicPort& port, + const BasicPort& clock_port, + const float& delay); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.cpp b/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.cpp index 9419f4172..fd01a462f 100644 --- a/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.cpp +++ b/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.cpp @@ -183,6 +183,12 @@ void print_verilog_simulation_preprocessing_flags(const std::string& src_dir, /* To enable pre-configured FPGA simulation */ if (true == verilog_testbench_opts.print_formal_verification_top_netlist()) { + print_verilog_define_flag(fp, std::string(VERILOG_FORMAL_VERIFICATION_PREPROC_FLAG), 1); + fp << std::endl; + } + + /* To enable pre-configured FPGA simulation */ + if (true == verilog_testbench_opts.print_preconfig_top_testbench()) { print_verilog_define_flag(fp, std::string(FORMAL_SIMULATION_FLAG), 1); fp << std::endl; } diff --git a/openfpga/src/fpga_verilog/verilog_formal_random_top_testbench.cpp b/openfpga/src/fpga_verilog/verilog_formal_random_top_testbench.cpp index 7aef8a72f..fa4f68938 100644 --- a/openfpga/src/fpga_verilog/verilog_formal_random_top_testbench.cpp +++ b/openfpga/src/fpga_verilog/verilog_formal_random_top_testbench.cpp @@ -228,6 +228,7 @@ void print_verilog_random_top_testbench(const std::string& circuit_name, clock_port); print_verilog_testbench_random_stimuli(fp, atom_ctx, netlist_annotation, + clock_port_names, std::string(CHECKFLAG_PORT_POSTFIX), clock_port); diff --git a/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp b/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp index 8d3e856c7..e952d13c4 100644 --- a/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp +++ b/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp @@ -5,6 +5,7 @@ * Note: please try to avoid using global variables in this file * so that we can make it free to use anywhere *******************************************************************/ +#include #include /* Headers from vtrutil library */ @@ -422,6 +423,7 @@ void print_verilog_testbench_clock_stimuli(std::fstream& fp, void print_verilog_testbench_random_stimuli(std::fstream& fp, const AtomContext& atom_ctx, const VprNetlistAnnotation& netlist_annotation, + const std::vector& clock_port_names, const std::string& check_flag_port_postfix, const BasicPort& clock_port) { /* Validate the file stream */ @@ -444,6 +446,11 @@ void print_verilog_testbench_random_stimuli(std::fstream& fp, block_name = netlist_annotation.block_name(atom_blk); } + /* Bypass clock ports */ + if (clock_port_names.end() != std::find(clock_port_names.begin(), clock_port_names.end(), block_name)) { + continue; + } + /* TODO: find the clock inputs will be initialized later */ if (AtomBlockType::INPAD == atom_ctx.nlist.block_type(atom_blk)) { fp << "\t\t" << block_name << " <= 1'b0;" << std::endl; @@ -510,8 +517,13 @@ void print_verilog_testbench_random_stimuli(std::fstream& fp, block_name = netlist_annotation.block_name(atom_blk); } + /* Bypass clock ports */ + if (clock_port_names.end() != std::find(clock_port_names.begin(), clock_port_names.end(), block_name)) { + continue; + } + /* TODO: find the clock inputs will be initialized later */ - if (AtomBlockType::INPAD != atom_ctx.nlist.block_type(atom_blk)) { + if (AtomBlockType::INPAD == atom_ctx.nlist.block_type(atom_blk)) { fp << "\t\t" << block_name << " <= $random;" << std::endl; } } diff --git a/openfpga/src/fpga_verilog/verilog_testbench_utils.h b/openfpga/src/fpga_verilog/verilog_testbench_utils.h index 61d5929c8..5e0bd69f9 100644 --- a/openfpga/src/fpga_verilog/verilog_testbench_utils.h +++ b/openfpga/src/fpga_verilog/verilog_testbench_utils.h @@ -76,6 +76,7 @@ void print_verilog_testbench_clock_stimuli(std::fstream& fp, void print_verilog_testbench_random_stimuli(std::fstream& fp, const AtomContext& atom_ctx, const VprNetlistAnnotation& netlist_annotation, + const std::vector& clock_port_names, const std::string& check_flag_port_postfix, const BasicPort& clock_port); diff --git a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp index f64558dda..911c6defb 100644 --- a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp +++ b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp @@ -855,6 +855,7 @@ void print_verilog_top_testbench(const ModuleManager& module_manager, /* Add stimuli for reset, set, clock and iopad signals */ print_verilog_testbench_random_stimuli(fp, atom_ctx, netlist_annotation, + clock_port_names, std::string(TOP_TESTBENCH_CHECKFLAG_PORT_POSTFIX), BasicPort(std::string(TOP_TB_OP_CLOCK_PORT_NAME), 1)); diff --git a/openfpga/src/main.cpp b/openfpga/src/main.cpp index c018db4bf..b2c9d2e6c 100644 --- a/openfpga/src/main.cpp +++ b/openfpga/src/main.cpp @@ -14,6 +14,7 @@ #include "openfpga_setup_command.h" #include "openfpga_verilog_command.h" #include "openfpga_bitstream_command.h" +#include "openfpga_sdc_command.h" #include "basic_command.h" #include "openfpga_title.h" @@ -60,6 +61,9 @@ int main(int argc, char** argv) { /* Add openfpga bitstream commands */ openfpga::add_openfpga_bitstream_commands(shell); + /* Add openfpga sdc commands */ + openfpga::add_openfpga_sdc_commands(shell); + /* Add basic commands: exit, help, etc. * Note: * This MUST be the last command group to be added! diff --git a/openfpga/test_blif/and.blif b/openfpga/test_blif/and.blif new file mode 100644 index 000000000..67d978741 --- /dev/null +++ b/openfpga/test_blif/and.blif @@ -0,0 +1,8 @@ +.model top +.inputs a b +.outputs c + +.names a b c +11 1 + +.end diff --git a/openfpga/test_blif/and.v b/openfpga/test_blif/and.v new file mode 100644 index 000000000..876f1c6fe --- /dev/null +++ b/openfpga/test_blif/and.v @@ -0,0 +1,14 @@ +`timescale 1ns / 1ps + +module top( + a, + b, + c); + +input wire a; +input wire b; +output wire c; + +assign c = a & b; + +endmodule diff --git a/openfpga/test_blif/s298.blif b/openfpga/test_blif/s298.blif deleted file mode 100644 index c588a6e0a..000000000 --- a/openfpga/test_blif/s298.blif +++ /dev/null @@ -1,90 +0,0 @@ -# Benchmark "s298" written by ABC on Tue Mar 12 09:40:31 2019 -.model s298 -.inputs clock G0 G1 G2 -.outputs G117 G132 G66 G118 G133 G67 - -.latch n21 G10 re clock 0 -.latch n26 G11 re clock 0 -.latch n31 G12 re clock 0 -.latch n36 G13 re clock 0 -.latch n41 G14 re clock 0 -.latch n46 G15 re clock 0 -.latch n51 G66 re clock 0 -.latch n55 G67 re clock 0 -.latch n59 G117 re clock 0 -.latch n63 G118 re clock 0 -.latch n67 G132 re clock 0 -.latch n71 G133 re clock 0 -.latch n75 G22 re clock 0 -.latch n80 G23 re clock 0 - -.names n56 n57 G10 n63 -0-0 1 -11- 1 -.names G15 G11 G13 G22 G14 G12 n56 -01---- 1 -0-0--- 1 -0--0-- 1 -0---1- 1 -0----1 1 --11000 1 -.names G14 G13 G12 G118 G11 n57 -01--- 1 -100-0 1 -1-11- 1 --1-1- 1 -.names n56 n59_1 G10 n67 -0-0 1 -11- 1 -.names G14 G13 G12 G132 G11 n59_1 -100-0 1 -11-1- 1 -1-11- 1 -.names G0 G10 n21 -00 1 -.names G10 G11 G0 G12 G13 n26 -010-- 1 -1001- 1 -100-0 1 -.names G12 G0 G11 G10 n31 -0011 1 -100- 1 -10-0 1 -.names G13 G0 G11 G12 G10 n36 -00111 1 -1001- 1 -1010- 1 -10--0 1 -.names n65 G14 G0 n41 -000 1 -110 1 -.names G23 G10 G13 G11 G12 n65 -1---- 0 --1100 0 -.names G0 n56 n46 -00 1 -.names n56 G66 G14 G13 G12 n51 -111-1 1 -11-1- 1 -1-01- 1 -.names n56 G13 G14 G11 G67 G12 n55 -1000-- 1 -10-1-0 1 -111-1- 1 -1-1-11 1 -.names n56 G13 G117 G14 G12 G11 n59 -10-0-- 1 -10--01 1 -1111-- 1 -1-111- 1 -.names n56 G14 G12 G13 G133 G11 n71 -1010-1 1 -111-1- 1 -11-11- 1 -.names G2 G22 G0 n75 -010 1 -100 1 -.names G1 G23 G0 n80 -010 1 -100 1 -.end diff --git a/openfpga/test_openfpga_arch/k6_frac_N10_40nm_openfpga.xml b/openfpga/test_openfpga_arch/k6_frac_N10_40nm_openfpga.xml index 2f318cbaa..9644530da 100644 --- a/openfpga/test_openfpga_arch/k6_frac_N10_40nm_openfpga.xml +++ b/openfpga/test_openfpga_arch/k6_frac_N10_40nm_openfpga.xml @@ -168,11 +168,11 @@ - + - + @@ -232,6 +232,7 @@ + diff --git a/openfpga/test_script/s298_k6_frac.openfpga b/openfpga/test_script/and_k6_frac.openfpga similarity index 51% rename from openfpga/test_script/s298_k6_frac.openfpga rename to openfpga/test_script/and_k6_frac.openfpga index d79fc3b9b..8a16f8cb5 100644 --- a/openfpga/test_script/s298_k6_frac.openfpga +++ b/openfpga/test_script/and_k6_frac.openfpga @@ -1,5 +1,5 @@ # Run VPR for the s298 design -vpr ./test_vpr_arch/k6_frac_N10_40nm.xml ./test_blif/s298.blif --write_rr_graph example_rr_graph.xml +vpr ./test_vpr_arch/k6_frac_N10_40nm.xml ./test_blif/and.blif --clock_modeling route #--write_rr_graph example_rr_graph.xml # Read OpenFPGA architecture definition read_openfpga_arch -f ./test_openfpga_arch/k6_frac_N10_40nm_openfpga.xml @@ -8,7 +8,7 @@ read_openfpga_arch -f ./test_openfpga_arch/k6_frac_N10_40nm_openfpga.xml #write_openfpga_arch -f ./arch_echo.xml # Annotate the OpenFPGA architecture to VPR data base -link_openfpga_arch #--verbose +link_openfpga_arch --activity_file ./test_blif/and.act #--verbose # Check and correct any naming conflicts in the BLIF netlist check_netlist_naming_conflict --fix --report ./netlist_renaming.xml @@ -35,10 +35,19 @@ fpga_bitstream --verbose --file /var/tmp/xtang/openfpga_test_src/fabric_indepene # Write the Verilog netlist for FPGA fabric # - Enable the use of explicit port mapping in Verilog netlist -write_fabric_verilog --file /var/tmp/xtang/openfpga_test_src --explicit_port_mapping --include_timing --include_signal_init --support_icarus_simulator --print_user_defined_template --verbose +write_fabric_verilog --file /var/tmp/xtang/openfpga_test_src/SRC --explicit_port_mapping --include_timing --include_signal_init --support_icarus_simulator --print_user_defined_template --verbose # Write the Verilog testbench for FPGA fabric -write_verilog_testbench --file /var/tmp/xtang/openfpga_test_src --reference_benchmark_file_path /var/tmp/xtang/s298.v --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini /var/tmp/xtang/openfpga_test_src/simulation_deck.ini +# - We suggest the use of same output directory as fabric Verilog netlists +# - Must specify the reference benchmark file if you want to output any testbenches +# - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA +# - Enable pre-configured top-level testbench which is a fast verification skipping programming phase +# - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts +write_verilog_testbench --file /var/tmp/xtang/openfpga_test_src/SRC --reference_benchmark_file_path /var/tmp/xtang/and.v --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini /var/tmp/xtang/openfpga_test_src/simulation_deck.ini + +# Write the SDC files for PnR backend +# - Turn on every options here +write_pnr_sdc --file /var/tmp/xtang/openfpga_test_src/SDC # Finish and exit OpenFPGA exit diff --git a/openfpga/test_script/s298.openfpga b/openfpga/test_script/s298.openfpga deleted file mode 100644 index 216f6d8c4..000000000 --- a/openfpga/test_script/s298.openfpga +++ /dev/null @@ -1,14 +0,0 @@ -# Run VPR for the s298 design -vpr ./test_vpr_arch/k6_N10_40nm.xml ./test_blif/s298.blif - -# Read OpenFPGA architecture definition -read_openfpga_arch -f ./test_openfpga_arch/k6_N10_40nm_openfpga.xml - -# Annotate the OpenFPGA architecture to VPR data base -link_openfpga_arch - -# Check and correct any naming conflicts in the BLIF netlist -check_netlist_naming_conflict --fix --report ./netlist_renaming.xml - -# Finish and exit OpenFPGA -exit diff --git a/openfpga/test_vpr_arch/k6_frac_N10_40nm.xml b/openfpga/test_vpr_arch/k6_frac_N10_40nm.xml index 97001c497..44e2ef289 100644 --- a/openfpga/test_vpr_arch/k6_frac_N10_40nm.xml +++ b/openfpga/test_vpr_arch/k6_frac_N10_40nm.xml @@ -45,19 +45,22 @@ + - - io.outpad io.inpad io.clock - io.outpad io.inpad io.clock - io.outpad io.inpad io.clock - io.outpad io.inpad io.clock + io.outpad io.inpad + io.outpad io.inpad + io.outpad io.inpad + io.outpad io.inpad @@ -145,7 +148,10 @@ - +