[Tool] Upgrade openfpga engine to support multi-clock frequency definiton and their usage in testbench/SDC generation

This commit is contained in:
tangxifan 2021-01-15 12:01:53 -07:00
parent 89f9d24d32
commit 87b2c1f3b8
16 changed files with 210 additions and 61 deletions

View File

@ -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.));
}
/********************************************************************

View File

@ -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,16 +144,33 @@ 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<std::string, SimulationClockId>::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,
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;

View File

@ -7,10 +7,13 @@
*******************************************************************/
#include <string>
#include <array>
#include <map>
#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,11 +93,14 @@ 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,
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);
@ -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<SimulationClockId, SimulationClockId> clock_ids_;
vtr::vector<SimulationClockId, std::string> clock_names_;
vtr::vector<SimulationClockId, BasicPort> clock_ports_;
vtr::vector<SimulationClockId, float> clock_frequencies_;
/* Fast name-to-id lookup */
std::map<std::string, SimulationClockId> 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

View File

@ -44,6 +44,7 @@ void write_xml_clock_setting(std::fstream& fp,
for (const SimulationClockId& clock_id : sim_setting.clocks()) {
fp << "\t\t\t" << "<clock";
write_xml_attribute(fp, "name", sim_setting.clock_name(clock_id).c_str());
write_xml_attribute(fp, "port", generate_xml_port_name(sim_setting.clock_port(clock_id)).c_str());
write_xml_attribute(fp, "frequency", std::to_string(sim_setting.clock_frequency(clock_id)).c_str());
fp << ">" << "\n";
}

View File

@ -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>[<LSB>:<MSB>] */
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" << "<tile ";
write_xml_attribute(fp, "name", tile_annotation.global_port_tile_names(global_port_id)[tile_info_id].c_str());
write_xml_attribute(fp, "port", generate_tile_port_name(tile_annotation.global_port_tile_ports(global_port_id)[tile_info_id]).c_str());
write_xml_attribute(fp, "port", generate_xml_port_name(tile_annotation.global_port_tile_ports(global_port_id)[tile_info_id]).c_str());
write_xml_attribute(fp, "x", tile_annotation.global_port_tile_coordinates(global_port_id)[tile_info_id].x());
write_xml_attribute(fp, "y", tile_annotation.global_port_tile_coordinates(global_port_id)[tile_info_id].y());
fp << "/>";

View File

@ -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>[<LSB>:<MSB>] */
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;
}

View File

@ -6,6 +6,7 @@
*******************************************************************/
#include <fstream>
#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

View File

@ -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

View File

@ -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());
}

View File

@ -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(),

View File

@ -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);

View File

@ -8,6 +8,7 @@
#include <vector>
#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 */

View File

@ -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());
}

View File

@ -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 */

View File

@ -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);

View File

@ -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<std::string>& 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);