From 87b2c1f3b850db70c5d8ac43128c7a44ea51a5df Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 15 Jan 2021 12:01:53 -0700 Subject: [PATCH] [Tool] Upgrade openfpga engine to support multi-clock frequency definiton and their usage in testbench/SDC generation --- .../src/read_xml_simulation_setting.cpp | 24 +++++- .../src/simulation_setting.cpp | 28 ++++++- .../libarchopenfpga/src/simulation_setting.h | 20 ++++- .../src/write_xml_simulation_setting.cpp | 1 + .../src/write_xml_tile_annotation.cpp | 21 +---- .../libarchopenfpga/src/write_xml_utils.cpp | 18 +++++ .../libarchopenfpga/src/write_xml_utils.h | 3 + .../annotate_simulation_setting.cpp | 12 +++ openfpga/src/base/openfpga_sdc.cpp | 3 +- openfpga/src/fpga_sdc/analysis_sdc_writer.cpp | 5 +- openfpga/src/fpga_sdc/pnr_sdc_global_port.cpp | 27 ++++--- openfpga/src/fpga_sdc/pnr_sdc_global_port.h | 4 +- openfpga/src/fpga_sdc/pnr_sdc_writer.cpp | 6 +- openfpga/src/fpga_sdc/pnr_sdc_writer.h | 4 +- .../fpga_verilog/verilog_testbench_utils.cpp | 14 +++- .../fpga_verilog/verilog_top_testbench.cpp | 81 +++++++++++++++++-- 16 files changed, 210 insertions(+), 61 deletions(-) diff --git a/libopenfpga/libarchopenfpga/src/read_xml_simulation_setting.cpp b/libopenfpga/libarchopenfpga/src/read_xml_simulation_setting.cpp index 34af94912..b9ea51859 100644 --- a/libopenfpga/libarchopenfpga/src/read_xml_simulation_setting.cpp +++ b/libopenfpga/libarchopenfpga/src/read_xml_simulation_setting.cpp @@ -12,6 +12,9 @@ /* Headers from vtr util library */ #include "vtr_assert.h" +/* Headers from openfpga util library */ +#include "openfpga_port_parser.h" + /* Headers from libarchfpga */ #include "arch_error.h" #include "read_xml_util.h" @@ -39,11 +42,24 @@ static void read_xml_operating_clock_override_setting(pugi::xml_node& xml_clock_override_setting, const pugiutil::loc_data& loc_data, openfpga::SimulationSetting& sim_setting) { - /* Create a new clock override object in the sim_setting object with the given name */ - SimulationClockId clock_id = sim_setting.create_simulation_clock(get_attribute(xml_clock_override_setting, "name", loc_data).as_string()); - VTR_ASSERT(sim_setting.valid_clock_id(clock_id)); + std::string clock_name = get_attribute(xml_clock_override_setting, "name", loc_data).as_string(); - sim_setting.set_simulation_clock_frequency(clock_id, get_attribute(xml_clock_override_setting, "frequency", loc_data).as_float(0.)); + /* Create a new clock override object in the sim_setting object with the given name */ + SimulationClockId clock_id = sim_setting.create_clock(clock_name); + + /* Report if the clock creation failed, this is due to a conflicts in naming*/ + if (false == sim_setting.valid_clock_id(clock_id)) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_clock_override_setting), + "Fail to create simulation clock '%s', it may share the same name as other simulation clock definition!\n", + clock_name.c_str()); + } + + /* Parse port information */ + openfpga::PortParser clock_port_parser(get_attribute(xml_clock_override_setting, "port", loc_data).as_string()); + sim_setting.set_clock_port(clock_id, clock_port_parser.port()); + + /* Parse frequency information */ + sim_setting.set_clock_frequency(clock_id, get_attribute(xml_clock_override_setting, "frequency", loc_data).as_float(0.)); } /******************************************************************** diff --git a/libopenfpga/libarchopenfpga/src/simulation_setting.cpp b/libopenfpga/libarchopenfpga/src/simulation_setting.cpp index 231908b2b..88b398441 100644 --- a/libopenfpga/libarchopenfpga/src/simulation_setting.cpp +++ b/libopenfpga/libarchopenfpga/src/simulation_setting.cpp @@ -43,6 +43,11 @@ std::string SimulationSetting::clock_name(const SimulationClockId& clock_id) con return clock_names_[clock_id]; } +BasicPort SimulationSetting::clock_port(const SimulationClockId& clock_id) const { + VTR_ASSERT(valid_clock_id(clock_id)); + return clock_ports_[clock_id]; +} + float SimulationSetting::clock_frequency(const SimulationClockId& clock_id) const { VTR_ASSERT(valid_clock_id(clock_id)); return clock_frequencies_[clock_id]; @@ -139,17 +144,34 @@ void SimulationSetting::set_programming_clock_frequency(const float& clock_freq) default_clock_frequencies_.set_y(clock_freq); } -SimulationClockId SimulationSetting::create_simulation_clock(const std::string& name) { +SimulationClockId SimulationSetting::create_clock(const std::string& name) { + /* Ensure a unique name for the clock definition */ + std::map::iterator it = clock_name2ids_.find(name); + if (it != clock_name2ids_.end()) { + return SimulationClockId::INVALID(); + } + + /* This is a legal name. we can create a new id */ SimulationClockId clock_id = SimulationClockId(clock_ids_.size()); clock_ids_.push_back(clock_id); clock_names_.push_back(name); + clock_ports_.emplace_back(); clock_frequencies_.push_back(0.); + /* Register in the name-to-id map */ + clock_name2ids_[name] = clock_id; + return clock_id; } -void SimulationSetting::set_simulation_clock_frequency(const SimulationClockId& clock_id, - const float& frequency) { +void SimulationSetting::set_clock_port(const SimulationClockId& clock_id, + const BasicPort& port) { + VTR_ASSERT(valid_clock_id(clock_id)); + clock_ports_[clock_id] = port; +} + +void SimulationSetting::set_clock_frequency(const SimulationClockId& clock_id, + const float& frequency) { VTR_ASSERT(valid_clock_id(clock_id)); clock_frequencies_[clock_id] = frequency; } diff --git a/libopenfpga/libarchopenfpga/src/simulation_setting.h b/libopenfpga/libarchopenfpga/src/simulation_setting.h index e044b2809..2447cfd3d 100644 --- a/libopenfpga/libarchopenfpga/src/simulation_setting.h +++ b/libopenfpga/libarchopenfpga/src/simulation_setting.h @@ -7,10 +7,13 @@ *******************************************************************/ #include #include +#include #include "vtr_vector.h" #include "vtr_geometry.h" +#include "openfpga_port.h" + #include "simulation_setting_fwd.h" /******************************************************************** @@ -64,6 +67,7 @@ class SimulationSetting { float programming_clock_frequency() const; size_t num_simulation_clock_cycles() const; std::string clock_name(const SimulationClockId& clock_id) const; + BasicPort clock_port(const SimulationClockId& clock_id) const; float clock_frequency(const SimulationClockId& clock_id) const; bool auto_select_num_clock_cycles() const; size_t num_clock_cycles() const; @@ -89,12 +93,15 @@ class SimulationSetting { void set_programming_clock_frequency(const float& clock_freq); /* Add a new simulation clock with * - a given name + * - a given port description * - a default zero frequency which can be overwritten by * the operating_clock_frequency() */ - SimulationClockId create_simulation_clock(const std::string& name); - void set_simulation_clock_frequency(const SimulationClockId& clock_id, - const float& frequency); + SimulationClockId create_clock(const std::string& name); + void set_clock_port(const SimulationClockId& clock_id, + const BasicPort& port); + void set_clock_frequency(const SimulationClockId& clock_id, + const float& frequency); void set_num_clock_cycles(const size_t& num_clk_cycles); void set_operating_clock_frequency_slack(const float& op_clk_freq_slack); void set_simulation_temperature(const float& sim_temp); @@ -134,14 +141,19 @@ class SimulationSetting { /* Multiple simulation clocks with detailed information * Each clock has * - a unique id - * - a unique name which is supposed + * - a unique name + * - a unique port definition which is supposed * to match the clock port definition in OpenFPGA documentation * - a frequency which is only applicable to this clock name */ vtr::vector clock_ids_; vtr::vector clock_names_; + vtr::vector clock_ports_; vtr::vector clock_frequencies_; + /* Fast name-to-id lookup */ + std::map clock_name2ids_; + /* Number of clock cycles to be used in simulation * If the value is 0, the clock cycles can be automatically * inferred from the signal activities of users' implementation diff --git a/libopenfpga/libarchopenfpga/src/write_xml_simulation_setting.cpp b/libopenfpga/libarchopenfpga/src/write_xml_simulation_setting.cpp index a70ed85e7..dc68f1924 100644 --- a/libopenfpga/libarchopenfpga/src/write_xml_simulation_setting.cpp +++ b/libopenfpga/libarchopenfpga/src/write_xml_simulation_setting.cpp @@ -44,6 +44,7 @@ void write_xml_clock_setting(std::fstream& fp, for (const SimulationClockId& clock_id : sim_setting.clocks()) { fp << "\t\t\t" << "" << "\n"; } diff --git a/libopenfpga/libarchopenfpga/src/write_xml_tile_annotation.cpp b/libopenfpga/libarchopenfpga/src/write_xml_tile_annotation.cpp index 69a568fc5..8e5ef0d5e 100644 --- a/libopenfpga/libarchopenfpga/src/write_xml_tile_annotation.cpp +++ b/libopenfpga/libarchopenfpga/src/write_xml_tile_annotation.cpp @@ -17,25 +17,6 @@ /* namespace openfpga begins */ namespace openfpga { -/******************************************************************** - * FIXME: Use a common function to output ports - * Generate the full hierarchy name for a operating pb_type - *******************************************************************/ -static -std::string generate_tile_port_name(const BasicPort& pb_port) { - std::string port_name; - - /* Output format: [:] */ - port_name += pb_port.get_name(); - port_name += std::string("["); - port_name += std::to_string(pb_port.get_lsb()); - port_name += std::string(":"); - port_name += std::to_string(pb_port.get_msb()); - port_name += std::string("]"); - - return port_name; -} - /******************************************************************** * A writer to output a device variation in a technology library to XML format *******************************************************************/ @@ -64,7 +45,7 @@ void write_xml_tile_annotation_global_port(std::fstream& fp, for (size_t tile_info_id = 0; tile_info_id < tile_annotation.global_port_tile_names(global_port_id).size(); ++tile_info_id) { fp << "\t\t\t" << ""; diff --git a/libopenfpga/libarchopenfpga/src/write_xml_utils.cpp b/libopenfpga/libarchopenfpga/src/write_xml_utils.cpp index a416203ee..0ed6ad5a8 100644 --- a/libopenfpga/libarchopenfpga/src/write_xml_utils.cpp +++ b/libopenfpga/libarchopenfpga/src/write_xml_utils.cpp @@ -90,3 +90,21 @@ void write_xml_attribute(std::fstream& fp, fp << std::scientific << value; fp << "\""; } + +/******************************************************************** + * FIXME: Use a common function to output ports + * Generate the full hierarchy name for a operating pb_type + *******************************************************************/ +std::string generate_xml_port_name(const openfpga::BasicPort& pb_port) { + std::string port_name; + + /* Output format: [:] */ + port_name += pb_port.get_name(); + port_name += std::string("["); + port_name += std::to_string(pb_port.get_lsb()); + port_name += std::string(":"); + port_name += std::to_string(pb_port.get_msb()); + port_name += std::string("]"); + + return port_name; +} diff --git a/libopenfpga/libarchopenfpga/src/write_xml_utils.h b/libopenfpga/libarchopenfpga/src/write_xml_utils.h index b54caf6db..e29342a54 100644 --- a/libopenfpga/libarchopenfpga/src/write_xml_utils.h +++ b/libopenfpga/libarchopenfpga/src/write_xml_utils.h @@ -6,6 +6,7 @@ *******************************************************************/ #include #include "circuit_library.h" +#include "openfpga_port.h" /******************************************************************** * Function declaration @@ -30,4 +31,6 @@ void write_xml_attribute(std::fstream& fp, const char* attr, const size_t& value); +std::string generate_xml_port_name(const openfpga::BasicPort& pb_port); + #endif diff --git a/openfpga/src/annotation/annotate_simulation_setting.cpp b/openfpga/src/annotation/annotate_simulation_setting.cpp index 19066d10e..960be6e68 100644 --- a/openfpga/src/annotation/annotate_simulation_setting.cpp +++ b/openfpga/src/annotation/annotate_simulation_setting.cpp @@ -219,6 +219,18 @@ int annotate_simulation_setting(const AtomContext& atom_ctx, VTR_LOG("Will apply operating clock frequency %g [MHz] to simulations\n", sim_setting.default_operating_clock_frequency() / 1e6); + /* Walk through all the clock information */ + for (const SimulationClockId& clock_id : sim_setting.clocks()) { + if (0. == sim_setting.clock_frequency(clock_id)) { + sim_setting.set_clock_frequency(clock_id, sim_setting.default_operating_clock_frequency()); + } + VTR_LOG("Will apply clock frequency %g [MHz] to clock '%s[%d:%d]' in simulations\n", + sim_setting.clock_frequency(clock_id) / 1e6, + sim_setting.clock_port(clock_id).get_name().c_str(), + sim_setting.clock_port(clock_id).get_lsb(), + sim_setting.clock_port(clock_id).get_msb()); + } + if (0. == sim_setting.num_clock_cycles()) { /* Find the number of clock cycles to be used in simulation * by average over the signal activity diff --git a/openfpga/src/base/openfpga_sdc.cpp b/openfpga/src/base/openfpga_sdc.cpp index 6f53d944a..b1ed229d2 100644 --- a/openfpga/src/base/openfpga_sdc.cpp +++ b/openfpga/src/base/openfpga_sdc.cpp @@ -83,8 +83,6 @@ int write_pnr_sdc(const OpenfpgaContext& openfpga_ctx, /* Execute only when sdc is enabled */ if (true == options.generate_sdc_pnr()) { print_pnr_sdc(options, - 1./openfpga_ctx.simulation_setting().programming_clock_frequency(), - 1./openfpga_ctx.simulation_setting().default_operating_clock_frequency(), g_vpr_ctx.device(), openfpga_ctx.vpr_device_annotation(), openfpga_ctx.device_rr_gsb(), @@ -92,6 +90,7 @@ int write_pnr_sdc(const OpenfpgaContext& openfpga_ctx, openfpga_ctx.mux_lib(), openfpga_ctx.arch().circuit_lib, openfpga_ctx.fabric_global_port_info(), + openfpga_ctx.simulation_setting(), openfpga_ctx.flow_manager().compress_routing()); } diff --git a/openfpga/src/fpga_sdc/analysis_sdc_writer.cpp b/openfpga/src/fpga_sdc/analysis_sdc_writer.cpp index f8dec09be..12defbac7 100644 --- a/openfpga/src/fpga_sdc/analysis_sdc_writer.cpp +++ b/openfpga/src/fpga_sdc/analysis_sdc_writer.cpp @@ -241,7 +241,10 @@ void print_analysis_sdc(const AnalysisSdcOption& option, ModuleId top_module = openfpga_ctx.module_graph().find_module(generate_fpga_top_module_name()); VTR_ASSERT(true == openfpga_ctx.module_graph().valid_module_id(top_module)); - /* Create clock and set I/O ports with input/output delays */ + /* Create clock and set I/O ports with input/output delays + * FIXME: Now different I/Os have different delays due to multiple clock frequency + * We need to consider these impacts and constrain different I/Os with different delays!!! + */ print_analysis_sdc_io_delays(fp, option.time_unit(), vpr_ctx.atom(), vpr_ctx.placement(), openfpga_ctx.vpr_netlist_annotation(), openfpga_ctx.io_location_map(), diff --git a/openfpga/src/fpga_sdc/pnr_sdc_global_port.cpp b/openfpga/src/fpga_sdc/pnr_sdc_global_port.cpp index d258ca640..8f7c92046 100644 --- a/openfpga/src/fpga_sdc/pnr_sdc_global_port.cpp +++ b/openfpga/src/fpga_sdc/pnr_sdc_global_port.cpp @@ -59,11 +59,10 @@ void print_pnr_sdc_clock_port(std::fstream& fp, *******************************************************************/ static void print_pnr_sdc_global_clock_ports(std::fstream& fp, - const float& programming_critical_path_delay, - const float& operating_critical_path_delay, const ModuleManager& module_manager, const ModuleId& top_module, - const FabricGlobalPortInfo& fabric_global_port_info) { + const FabricGlobalPortInfo& fabric_global_port_info, + const SimulationSetting& sim_setting) { valid_file_stream(fp); @@ -73,11 +72,11 @@ void print_pnr_sdc_global_clock_ports(std::fstream& fp, continue; } /* Reach here, it means a clock port and we need print constraints */ - float clock_period = operating_critical_path_delay; + float clock_period = 1./sim_setting.default_operating_clock_frequency(); /* For programming clock, we give a fixed period */ if (true == fabric_global_port_info.global_port_is_prog(global_port)) { - clock_period = programming_critical_path_delay; + clock_period = 1./sim_setting.programming_clock_frequency(); /* Print comments */ fp << "##################################################" << std::endl; fp << "# Create programmable clock " << std::endl; @@ -93,6 +92,15 @@ void print_pnr_sdc_global_clock_ports(std::fstream& fp, for (const size_t& pin : clock_port.pins()) { BasicPort port_to_constrain(clock_port.get_name(), pin, pin); + /* Should try to find a port defintion from simulation parameters + * If found, it means that we need to use special clock name! + */ + for (const SimulationClockId& sim_clock : sim_setting.clocks()) { + if (port_to_constrain == sim_setting.clock_port(sim_clock)) { + clock_period = 1./sim_setting.clock_frequency(sim_clock); + } + } + print_pnr_sdc_clock_port(fp, port_to_constrain, clock_period); @@ -153,11 +161,10 @@ void print_pnr_sdc_global_non_clock_ports(std::fstream& fp, * In general, we do not recommend to do this *******************************************************************/ void print_pnr_sdc_global_ports(const std::string& sdc_dir, - const float& programming_critical_path_delay, - const float& operating_critical_path_delay, const ModuleManager& module_manager, const ModuleId& top_module, const FabricGlobalPortInfo& global_ports, + const SimulationSetting& sim_setting, const bool& constrain_non_clock_port) { /* Create the file name for Verilog netlist */ @@ -177,14 +184,12 @@ void print_pnr_sdc_global_ports(const std::string& sdc_dir, print_sdc_file_header(fp, std::string("Clock contraints for PnR")); print_pnr_sdc_global_clock_ports(fp, - programming_critical_path_delay, - operating_critical_path_delay, module_manager, top_module, - global_ports); + global_ports, sim_setting); if (true == constrain_non_clock_port) { print_pnr_sdc_global_non_clock_ports(fp, - operating_critical_path_delay, + 1./sim_setting.default_operating_clock_frequency(), module_manager, top_module, global_ports); diff --git a/openfpga/src/fpga_sdc/pnr_sdc_global_port.h b/openfpga/src/fpga_sdc/pnr_sdc_global_port.h index e17dd0d9b..15808e623 100644 --- a/openfpga/src/fpga_sdc/pnr_sdc_global_port.h +++ b/openfpga/src/fpga_sdc/pnr_sdc_global_port.h @@ -8,6 +8,7 @@ #include #include "module_manager.h" #include "fabric_global_port_info.h" +#include "simulation_setting.h" /******************************************************************** * Function declaration @@ -17,11 +18,10 @@ namespace openfpga { void print_pnr_sdc_global_ports(const std::string& sdc_dir, - const float& programming_critical_path_delay, - const float& operating_critical_path_delay, const ModuleManager& module_manager, const ModuleId& top_module, const FabricGlobalPortInfo& global_ports, + const SimulationSetting& sim_setting, const bool& constrain_non_clock_port); } /* end namespace openfpga */ diff --git a/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp b/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp index d6596eba1..fd74cfba7 100644 --- a/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp +++ b/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp @@ -319,8 +319,6 @@ void print_pnr_sdc_compact_routing_disable_switch_block_outputs(const std::strin * 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, @@ -328,6 +326,7 @@ void print_pnr_sdc(const PnrSdcOption& sdc_options, const MuxLibrary& mux_lib, const CircuitLibrary& circuit_lib, const FabricGlobalPortInfo& global_ports, + const SimulationSetting& sim_setting, const bool& compact_routing_hierarchy) { std::string top_module_name = generate_fpga_top_module_name(); @@ -337,9 +336,8 @@ void print_pnr_sdc(const PnrSdcOption& sdc_options, /* 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, module_manager, top_module, global_ports, + sim_setting, sdc_options.constrain_non_clock_global_port()); } diff --git a/openfpga/src/fpga_sdc/pnr_sdc_writer.h b/openfpga/src/fpga_sdc/pnr_sdc_writer.h index 38aff2a2c..169d91543 100644 --- a/openfpga/src/fpga_sdc/pnr_sdc_writer.h +++ b/openfpga/src/fpga_sdc/pnr_sdc_writer.h @@ -12,6 +12,7 @@ #include "module_manager.h" #include "mux_library.h" #include "circuit_library.h" +#include "simulation_setting.h" #include "fabric_global_port_info.h" #include "pnr_sdc_option.h" @@ -23,8 +24,6 @@ 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, @@ -32,6 +31,7 @@ void print_pnr_sdc(const PnrSdcOption& sdc_options, const MuxLibrary& mux_lib, const CircuitLibrary& circuit_lib, const FabricGlobalPortInfo& global_ports, + const SimulationSetting& sim_setting, const bool& compact_routing_hierarchy); } /* end namespace openfpga */ diff --git a/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp b/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp index 60d4b8f45..8f9e3df79 100644 --- a/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp +++ b/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp @@ -484,11 +484,23 @@ void print_verilog_testbench_clock_stimuli(std::fstream& fp, for (const BasicPort& clock_port : clock_ports) { print_verilog_comment(fp, std::string("----- Clock '") + clock_port.get_name() + std::string("' Initialization -------")); + /* Find the corresponding clock frequency from the simulation parameters */ + float clk_freq_to_use = (0.5 / simulation_parameters.default_operating_clock_frequency()) / VERILOG_SIM_TIMESCALE; + /* FIXME: This could be buggy because the implementation clock names do NOT have to + * be the same as the clock definition in simulation settings!!! + */ + for (const SimulationClockId& sim_clock_id : simulation_parameters.clocks()) { + /* If the clock name matches, we can use the clock frequency */ + if (simulation_parameters.clock_port(sim_clock_id) == clock_port) { + clk_freq_to_use = (0.5 / simulation_parameters.clock_frequency(sim_clock_id)) / VERILOG_SIM_TIMESCALE; + } + } + fp << "\tinitial begin" << std::endl; /* Create clock stimuli */ fp << "\t\t" << generate_verilog_port(VERILOG_PORT_CONKT, clock_port) << " <= 1'b0;" << std::endl; fp << "\t\twhile(1) begin" << std::endl; - fp << "\t\t\t#" << std::setprecision(10) << ((0.5/simulation_parameters.default_operating_clock_frequency())/VERILOG_SIM_TIMESCALE) << std::endl; + fp << "\t\t\t#" << std::setprecision(10) << clk_freq_to_use << std::endl; fp << "\t\t\t" << generate_verilog_port(VERILOG_PORT_CONKT, clock_port); fp << " <= !"; fp << generate_verilog_port(VERILOG_PORT_CONKT, clock_port); diff --git a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp index 378b7372b..24c66b22b 100644 --- a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp +++ b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp @@ -56,12 +56,24 @@ constexpr char* TOP_TB_PROG_RESET_PORT_NAME = "prog_reset"; constexpr char* TOP_TB_PROG_SET_PORT_NAME = "prog_set"; constexpr char* TOP_TB_CONFIG_DONE_PORT_NAME = "config_done"; constexpr char* TOP_TB_OP_CLOCK_PORT_NAME = "op_clock"; +constexpr char* TOP_TB_OP_CLOCK_PORT_PREFIX = "operating_clk_"; constexpr char* TOP_TB_PROG_CLOCK_PORT_NAME = "prog_clock"; constexpr char* TOP_TB_INOUT_REG_POSTFIX = "_reg"; constexpr char* TOP_TB_CLOCK_REG_POSTFIX = "_reg"; constexpr char* AUTOCHECK_TOP_TESTBENCH_VERILOG_MODULE_POSTFIX = "_autocheck_top_tb"; +/******************************************************************** + * Generate a simulation clock port name + * This function is designed to produce a uniform clock naming for these ports + *******************************************************************/ +static +std::string generate_top_testbench_clock_name(const std::string& prefix, + const std::string& port_name) { + return prefix + port_name; +} + + /******************************************************************** * Print local wires for flatten memory (standalone) configuration protocols *******************************************************************/ @@ -253,6 +265,7 @@ void print_verilog_top_testbench_global_ports_stimuli(std::fstream& fp, const ModuleManager& module_manager, const ModuleId& top_module, const FabricGlobalPortInfo& fabric_global_port_info, + const SimulationSetting& simulation_parameters, const bool& active_global_prog_reset, const bool& active_global_prog_set) { /* Validate the file stream */ @@ -290,10 +303,15 @@ void print_verilog_top_testbench_global_ports_stimuli(std::fstream& fp, */ for (const size_t& pin : module_manager.module_port(top_module, module_global_port).pins()) { BasicPort global_port_to_connect(module_manager.module_port(top_module, module_global_port).get_name(), pin, pin); - /* TODO: This is a temporary fix to make the testbench generator run in multi-clock scenario - * Need to consider multiple clock sources to connect - * each of which may operate in different ferquency!!! + /* Should try to find a port defintion from simulation parameters + * If found, it means that we need to use special clock name! */ + for (const SimulationClockId& sim_clock : simulation_parameters.clocks()) { + if (global_port_to_connect == simulation_parameters.clock_port(sim_clock)) { + stimuli_clock_port.set_name(generate_top_testbench_clock_name(std::string(TOP_TB_OP_CLOCK_PORT_PREFIX), simulation_parameters.clock_name(sim_clock))); + } + } + print_verilog_wire_connection(fp, global_port_to_connect, stimuli_clock_port, 1 == fabric_global_port_info.global_port_default_value(fabric_global_port)); @@ -488,6 +506,7 @@ void print_verilog_top_testbench_ports(std::fstream& fp, const AtomContext& atom_ctx, const VprNetlistAnnotation& netlist_annotation, const std::vector& clock_port_names, + const SimulationSetting& simulation_parameters, const ConfigProtocol& config_protocol, const std::string& circuit_name){ /* Validate the file stream */ @@ -543,7 +562,21 @@ void print_verilog_top_testbench_ports(std::fstream& fp, BasicPort prog_clock_register_port(std::string(std::string(TOP_TB_PROG_CLOCK_PORT_NAME) + std::string(TOP_TB_CLOCK_REG_POSTFIX)), 1); fp << generate_verilog_port(VERILOG_PORT_REG, prog_clock_register_port) << ";" << std::endl; - /* Operating clock */ + /* Multiple operating clocks based on the simulation settings */ + for (const SimulationClockId& sim_clock : simulation_parameters.clocks()) { + std::string sim_clock_port_name = generate_top_testbench_clock_name(std::string(TOP_TB_OP_CLOCK_PORT_PREFIX), simulation_parameters.clock_name(sim_clock)); + BasicPort sim_clock_port(sim_clock_port_name, 1); + fp << generate_verilog_port(VERILOG_PORT_WIRE, sim_clock_port) << ";" << std::endl; + BasicPort sim_clock_register_port(std::string(sim_clock_port_name + std::string(TOP_TB_CLOCK_REG_POSTFIX)), 1); + fp << generate_verilog_port(VERILOG_PORT_REG, sim_clock_register_port) << ";" << std::endl; + } + + /* FIXME: Actually, for multi-clock implementations, input and output ports + * should be synchronized by different clocks. Currently, we lack the information + * about what inputs are driven by which clock. Therefore, we use a unified clock + * signal to do the job. However, this has to be fixed later!!! + * Create an operating clock_port to synchronize checkers stimulus generator + */ BasicPort op_clock_port(std::string(TOP_TB_OP_CLOCK_PORT_NAME), 1); fp << generate_verilog_port(VERILOG_PORT_WIRE, op_clock_port) << ";" << std::endl; BasicPort op_clock_register_port(std::string(std::string(TOP_TB_OP_CLOCK_PORT_NAME) + std::string(TOP_TB_CLOCK_REG_POSTFIX)), 1); @@ -969,6 +1002,7 @@ void print_verilog_top_testbench_load_bitstream_task(std::fstream& fp, *******************************************************************/ static void print_verilog_top_testbench_generic_stimulus(std::fstream& fp, + const SimulationSetting& simulation_parameters, const size_t& num_config_clock_cycles, const float& prog_clock_period, const float& op_clock_period, @@ -1021,6 +1055,32 @@ void print_verilog_top_testbench_generic_stimulus(std::fstream& fp, fp << std::endl; + /* Generate stimuli waveform for multiple user-defined operating clock signals */ + for (const SimulationClockId& sim_clock : simulation_parameters.clocks()) { + print_verilog_comment(fp, "----- Begin raw operating clock signal '" + simulation_parameters.clock_name(sim_clock) + "' generation -----"); + std::string sim_clock_port_name = generate_top_testbench_clock_name(std::string(TOP_TB_OP_CLOCK_PORT_PREFIX), simulation_parameters.clock_name(sim_clock)); + BasicPort sim_clock_port(sim_clock_port_name, 1); + BasicPort sim_clock_register_port(std::string(sim_clock_port_name + std::string(TOP_TB_CLOCK_REG_POSTFIX)), 1); + + float sim_clock_period = 1. / simulation_parameters.clock_frequency(sim_clock); + print_verilog_clock_stimuli(fp, sim_clock_register_port, + 0, /* Initial value */ + 0.5 * sim_clock_period / timescale, + std::string("~" + reset_port.get_name())); + print_verilog_comment(fp, "----- End raw operating clock signal generation -----"); + + /* Operation clock should be enabled after programming phase finishes. + * Before configuration is done (config_done is enabled), operation clock should be always zero. + */ + print_verilog_comment(fp, std::string("----- Actual operating clock is triggered only when " + config_done_port.get_name() + " is enabled -----")); + fp << "\tassign " << generate_verilog_port(VERILOG_PORT_CONKT, sim_clock_port); + fp << " = " << generate_verilog_port(VERILOG_PORT_CONKT, sim_clock_register_port); + fp << " & " << generate_verilog_port(VERILOG_PORT_CONKT, config_done_port); + fp << ";" << std::endl; + + fp << std::endl; + } + /* Generate stimuli waveform for operating clock signals */ print_verilog_comment(fp, "----- Begin raw operating clock signal generation -----"); print_verilog_clock_stimuli(fp, op_clock_register_port, @@ -1834,12 +1894,17 @@ void print_verilog_top_testbench(const ModuleManager& module_manager, /* Start of testbench */ print_verilog_top_testbench_ports(fp, module_manager, top_module, atom_ctx, netlist_annotation, clock_port_names, - config_protocol, + simulation_parameters, config_protocol, circuit_name); /* Find the clock period */ float prog_clock_period = (1./simulation_parameters.programming_clock_frequency()); - float op_clock_period = (1./simulation_parameters.default_operating_clock_frequency()); + float default_op_clock_period = (1./simulation_parameters.default_operating_clock_frequency()); + float max_op_clock_period = 0.; + for (const SimulationClockId& clock_id : simulation_parameters.clocks()) { + max_op_clock_period = std::max(max_op_clock_period, (float)(1./simulation_parameters.clock_frequency(clock_id))); + } + /* Estimate the number of configuration clock cycles */ size_t num_config_clock_cycles = calculate_num_config_clock_cycles(config_protocol.type(), apply_fast_configuration, @@ -1849,9 +1914,10 @@ void print_verilog_top_testbench(const ModuleManager& module_manager, /* Generate stimuli for general control signals */ print_verilog_top_testbench_generic_stimulus(fp, + simulation_parameters, num_config_clock_cycles, prog_clock_period, - op_clock_period, + default_op_clock_period, VERILOG_SIM_TIMESCALE); /* Generate stimuli for programming interface */ @@ -1891,6 +1957,7 @@ void print_verilog_top_testbench(const ModuleManager& module_manager, print_verilog_top_testbench_global_ports_stimuli(fp, module_manager, top_module, global_ports, + simulation_parameters, active_global_prog_reset, active_global_prog_set);