From b3ead7e47dfb42aa2c35457bfccb7496618a8c85 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Wed, 16 Jul 2025 23:15:43 +0200 Subject: [PATCH 01/18] icell_liberty: start --- passes/cmds/Makefile.inc | 1 + passes/cmds/icell_liberty.cc | 138 +++++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 passes/cmds/icell_liberty.cc diff --git a/passes/cmds/Makefile.inc b/passes/cmds/Makefile.inc index 2dbeadac2..bb726aa7d 100644 --- a/passes/cmds/Makefile.inc +++ b/passes/cmds/Makefile.inc @@ -58,3 +58,4 @@ OBJS += passes/cmds/test_select.o OBJS += passes/cmds/timeest.o OBJS += passes/cmds/linecoverage.o OBJS += passes/cmds/sort.o +OBJS += passes/cmds/icell_liberty.o diff --git a/passes/cmds/icell_liberty.cc b/passes/cmds/icell_liberty.cc new file mode 100644 index 000000000..19692fa01 --- /dev/null +++ b/passes/cmds/icell_liberty.cc @@ -0,0 +1,138 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2024 Martin Povišer + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ +#include "kernel/yosys.h" +#include "kernel/celltypes.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct LibertyStubber { + CellTypes ct; + CellTypes ct_ff; + LibertyStubber() { + ct.setup(); + ct.setup_internals_ff(); + } + void liberty_prefix(std::ostream& f) + { + f << "library (yosys) {\n"; + f << "\tinput_threshold_pct_fall : 50;\n"; + f << "\tinput_threshold_pct_rise : 50;\n"; + f << "\toutput_threshold_pct_fall : 50;\n"; + f << "\toutput_threshold_pct_rise : 50;\n"; + f << "\tslew_lower_threshold_pct_fall : 1;\n"; + f << "\tslew_lower_threshold_pct_rise : 1;\n"; + f << "\tslew_upper_threshold_pct_fall : 99;\n"; + f << "\tslew_upper_threshold_pct_rise : 99;\n"; + } + void liberty_suffix(std::ostream& f) + { + f << "}\n"; + } + void liberty_cell(Module* base, Module* derived, std::ostream& f) + { + auto base_name = base->name.str().substr(1); + auto derived_name = derived->name.str().substr(1); + if (!ct.cell_types.count(base_name)) { + log_debug("skip skeleton for %s\n", base_name.c_str()); + return; + } + auto& base_type = ct.cell_types[base_name]; + f << "\tcell (\"" << derived_name << "\") {\n"; + for (auto x : derived->ports) { + bool is_input = base_type.inputs.count(x); + bool is_output = base_type.outputs.count(x); + f << "\t\tpin (" << RTLIL::unescape_id(x.str()) << ") {\n"; + if (is_input && !is_output) { + f << "\t\t\tdirection : input;\n"; + } else if (!is_input && is_output) { + f << "\t\t\tdirection : output;\n"; + } else { + f << "\t\t\tdirection : inout;\n"; + } + f << "\t\t}\n"; + } + f << "\t}\n"; + } +}; + +struct IcellLiberty : Pass { + IcellLiberty() : Pass("icell_liberty", "derive box modules") {} + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" icell_liberty \n"); // TODO + log("\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *d) override + { + log_header(d, "Executing ICELL_LIBERTY pass.\n"); + + size_t argidx; + IdString naming_attr; + std::string liberty_filename; + std::ofstream* liberty_file = new std::ofstream; + + for (argidx = 1; argidx < args.size(); argidx++) { + break; + } + if (argidx < args.size()) + liberty_filename = args[argidx++]; + else + log_error("no Liberty filename specified\n"); + + // extra_args(args, argidx, d); + + if (liberty_filename.size()) { + liberty_file->open(liberty_filename.c_str()); + if (liberty_file->fail()) { + delete liberty_file; + log_error("Can't open file `%s' for writing: %s\n", liberty_filename.c_str(), strerror(errno)); + } + } + + pool done; + LibertyStubber stubber = {}; + + if (liberty_file) + stubber.liberty_prefix(*liberty_file); + + for (auto module : d->selected_modules()) { + for (auto cell : module->selected_cells()) { + Module *inst_module = d->module(cell->type); + if (!inst_module || !inst_module->get_blackbox_attribute()) + continue; + Module *base = inst_module; + if (!done.count(base->name)) { + stubber.liberty_cell(base, base, *liberty_file); + done.insert(base->name); + } + } + } + + if (liberty_file) { + stubber.liberty_suffix(*liberty_file); + delete liberty_file; + } + } +} IcellLiberty; + +PRIVATE_NAMESPACE_END From 9e81db43731bc90f42612191f33f63af509774d1 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 18 Jul 2025 12:34:10 +0200 Subject: [PATCH 02/18] ff: split out type-only information --- kernel/ff.cc | 489 +++++++++++++++++++++++++++++---------------------- kernel/ff.h | 78 ++++---- 2 files changed, 323 insertions(+), 244 deletions(-) diff --git a/kernel/ff.cc b/kernel/ff.cc index a72e6a65c..7dd5e24ac 100644 --- a/kernel/ff.cc +++ b/kernel/ff.cc @@ -21,245 +21,316 @@ USING_YOSYS_NAMESPACE -FfData::FfData(FfInitVals *initvals, Cell *cell_) : FfData(cell_->module, initvals, cell_->name) -{ - cell = cell_; - sig_q = cell->getPort(ID::Q); - width = GetSize(sig_q); - attributes = cell->attributes; +// sorry +template>> +void manufacture_info(InputType flop, OutputType& info, FfInitVals *initvals) { + Cell* cell = nullptr; + IdString type; + constexpr bool have_cell = std::is_same_v; + if constexpr (std::is_same_v) { + type = flop; + } else { + static_assert(std::is_same_v); + cell = flop; + type = flop->type; + } + if constexpr (have_cell) { + info.sig_q = cell->getPort(ID::Q); + info.width = GetSize(info.sig_q); + info.attributes = cell->attributes; + if (initvals) + info.val_init = (*initvals)(info.sig_q); + } - if (initvals) - val_init = (*initvals)(sig_q); - std::string type_str = cell->type.str(); + std::string type_str = type.str(); - if (cell->type.in(ID($anyinit), ID($ff), ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($aldff), ID($aldffe), ID($sdff), ID($sdffe), ID($sdffce), ID($dlatch), ID($adlatch), ID($dlatchsr), ID($sr))) { - if (cell->type.in(ID($anyinit), ID($ff))) { - has_gclk = true; - sig_d = cell->getPort(ID::D); - if (cell->type == ID($anyinit)) { - is_anyinit = true; - log_assert(val_init.is_fully_undef()); + if (type.in(ID($anyinit), ID($ff), ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($aldff), ID($aldffe), ID($sdff), ID($sdffe), ID($sdffce), ID($dlatch), ID($adlatch), ID($dlatchsr), ID($sr))) { + if (type.in(ID($anyinit), ID($ff))) { + info.has_gclk = true; + if constexpr (have_cell) + info.sig_d = cell->getPort(ID::D); + if (type == ID($anyinit)) { + info.is_anyinit = true; + if constexpr (have_cell) + log_assert(info.val_init.is_fully_undef()); } - } else if (cell->type == ID($sr)) { + } else if (type == ID($sr)) { // No data input at all. - } else if (cell->type.in(ID($dlatch), ID($adlatch), ID($dlatchsr))) { - has_aload = true; - sig_aload = cell->getPort(ID::EN); - pol_aload = cell->getParam(ID::EN_POLARITY).as_bool(); - sig_ad = cell->getPort(ID::D); + } else if (type.in(ID($dlatch), ID($adlatch), ID($dlatchsr))) { + info.has_aload = true; + if constexpr (have_cell) { + info.sig_aload = cell->getPort(ID::EN); + info.pol_aload = cell->getParam(ID::EN_POLARITY).as_bool(); + info.sig_ad = cell->getPort(ID::D); + } } else { - has_clk = true; - sig_clk = cell->getPort(ID::CLK); - pol_clk = cell->getParam(ID::CLK_POLARITY).as_bool(); - sig_d = cell->getPort(ID::D); + info.has_clk = true; + if constexpr (have_cell) { + info.sig_clk = cell->getPort(ID::CLK); + info.pol_clk = cell->getParam(ID::CLK_POLARITY).as_bool(); + info.sig_d = cell->getPort(ID::D); + } } - if (cell->type.in(ID($dffe), ID($dffsre), ID($adffe), ID($aldffe), ID($sdffe), ID($sdffce))) { - has_ce = true; - sig_ce = cell->getPort(ID::EN); - pol_ce = cell->getParam(ID::EN_POLARITY).as_bool(); + if (type.in(ID($dffe), ID($dffsre), ID($adffe), ID($aldffe), ID($sdffe), ID($sdffce))) { + info.has_ce = true; + if constexpr (have_cell) { + info.sig_ce = cell->getPort(ID::EN); + info.pol_ce = cell->getParam(ID::EN_POLARITY).as_bool(); + } } - if (cell->type.in(ID($dffsr), ID($dffsre), ID($dlatchsr), ID($sr))) { - has_sr = true; - sig_clr = cell->getPort(ID::CLR); - sig_set = cell->getPort(ID::SET); - pol_clr = cell->getParam(ID::CLR_POLARITY).as_bool(); - pol_set = cell->getParam(ID::SET_POLARITY).as_bool(); + if (type.in(ID($dffsr), ID($dffsre), ID($dlatchsr), ID($sr))) { + info.has_sr = true; + if constexpr (have_cell) { + info.sig_clr = cell->getPort(ID::CLR); + info.sig_set = cell->getPort(ID::SET); + info.pol_clr = cell->getParam(ID::CLR_POLARITY).as_bool(); + info.pol_set = cell->getParam(ID::SET_POLARITY).as_bool(); + } } - if (cell->type.in(ID($aldff), ID($aldffe))) { - has_aload = true; - sig_aload = cell->getPort(ID::ALOAD); - pol_aload = cell->getParam(ID::ALOAD_POLARITY).as_bool(); - sig_ad = cell->getPort(ID::AD); + if (type.in(ID($aldff), ID($aldffe))) { + info.has_aload = true; + if constexpr (have_cell) { + info.sig_aload = cell->getPort(ID::ALOAD); + info.pol_aload = cell->getParam(ID::ALOAD_POLARITY).as_bool(); + info.sig_ad = cell->getPort(ID::AD); + } } - if (cell->type.in(ID($adff), ID($adffe), ID($adlatch))) { - has_arst = true; - sig_arst = cell->getPort(ID::ARST); - pol_arst = cell->getParam(ID::ARST_POLARITY).as_bool(); - val_arst = cell->getParam(ID::ARST_VALUE); + if (type.in(ID($adff), ID($adffe), ID($adlatch))) { + info.has_arst = true; + if constexpr (have_cell) { + info.sig_arst = cell->getPort(ID::ARST); + info.pol_arst = cell->getParam(ID::ARST_POLARITY).as_bool(); + info.val_arst = cell->getParam(ID::ARST_VALUE); + } } - if (cell->type.in(ID($sdff), ID($sdffe), ID($sdffce))) { - has_srst = true; - sig_srst = cell->getPort(ID::SRST); - pol_srst = cell->getParam(ID::SRST_POLARITY).as_bool(); - val_srst = cell->getParam(ID::SRST_VALUE); - ce_over_srst = cell->type == ID($sdffce); + if (type.in(ID($sdff), ID($sdffe), ID($sdffce))) { + info.has_srst = true; + if constexpr (have_cell) { + info.sig_srst = cell->getPort(ID::SRST); + info.pol_srst = cell->getParam(ID::SRST_POLARITY).as_bool(); + info.val_srst = cell->getParam(ID::SRST_VALUE); + } + info.ce_over_srst = type == ID($sdffce); } - } else if (cell->type == ID($_FF_)) { - is_fine = true; - has_gclk = true; - sig_d = cell->getPort(ID::D); + } else if (type == ID($_FF_)) { + info.is_fine = true; + info.has_gclk = true; + if constexpr (have_cell) + info.sig_d = cell->getPort(ID::D); } else if (type_str.substr(0, 5) == "$_SR_") { - is_fine = true; - has_sr = true; - pol_set = type_str[5] == 'P'; - pol_clr = type_str[6] == 'P'; - sig_set = cell->getPort(ID::S); - sig_clr = cell->getPort(ID::R); + info.is_fine = true; + info.has_sr = true; + info.pol_set = type_str[5] == 'P'; + info.pol_clr = type_str[6] == 'P'; + if constexpr (have_cell) { + info.sig_set = cell->getPort(ID::S); + info.sig_clr = cell->getPort(ID::R); + } } else if (type_str.substr(0, 6) == "$_DFF_" && type_str.size() == 8) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_clk = true; - pol_clk = type_str[6] == 'P'; - sig_clk = cell->getPort(ID::C); + info.is_fine = true; + info.has_clk = true; + info.pol_clk = type_str[6] == 'P'; + if constexpr (have_cell) { + info.sig_d = cell->getPort(ID::D); + info.sig_clk = cell->getPort(ID::C); + } } else if (type_str.substr(0, 7) == "$_DFFE_" && type_str.size() == 10) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_clk = true; - pol_clk = type_str[7] == 'P'; - sig_clk = cell->getPort(ID::C); - has_ce = true; - pol_ce = type_str[8] == 'P'; - sig_ce = cell->getPort(ID::E); + info.is_fine = true; + info.has_clk = true; + info.pol_clk = type_str[7] == 'P'; + info.has_ce = true; + info.pol_ce = type_str[8] == 'P'; + if constexpr (have_cell) { + info.sig_d = cell->getPort(ID::D); + info.sig_clk = cell->getPort(ID::C); + info.sig_ce = cell->getPort(ID::E); + } } else if (type_str.substr(0, 6) == "$_DFF_" && type_str.size() == 10) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_clk = true; - pol_clk = type_str[6] == 'P'; - sig_clk = cell->getPort(ID::C); - has_arst = true; - pol_arst = type_str[7] == 'P'; - sig_arst = cell->getPort(ID::R); - val_arst = type_str[8] == '1' ? State::S1 : State::S0; + info.is_fine = true; + info.has_clk = true; + info.pol_clk = type_str[6] == 'P'; + info.has_arst = true; + info.pol_arst = type_str[7] == 'P'; + info.val_arst = type_str[8] == '1' ? State::S1 : State::S0; + if constexpr (have_cell) { + info.sig_d = cell->getPort(ID::D); + info.sig_clk = cell->getPort(ID::C); + info.sig_arst = cell->getPort(ID::R); + } } else if (type_str.substr(0, 7) == "$_DFFE_" && type_str.size() == 12) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_clk = true; - pol_clk = type_str[7] == 'P'; - sig_clk = cell->getPort(ID::C); - has_arst = true; - pol_arst = type_str[8] == 'P'; - sig_arst = cell->getPort(ID::R); - val_arst = type_str[9] == '1' ? State::S1 : State::S0; - has_ce = true; - pol_ce = type_str[10] == 'P'; - sig_ce = cell->getPort(ID::E); + info.is_fine = true; + info.has_clk = true; + info.pol_clk = type_str[7] == 'P'; + info.has_arst = true; + info.pol_arst = type_str[8] == 'P'; + info.val_arst = type_str[9] == '1' ? State::S1 : State::S0; + info.has_ce = true; + info.pol_ce = type_str[10] == 'P'; + if constexpr (have_cell) { + info.sig_d = cell->getPort(ID::D); + info.sig_clk = cell->getPort(ID::C); + info.sig_arst = cell->getPort(ID::R); + info.sig_ce = cell->getPort(ID::E); + } } else if (type_str.substr(0, 8) == "$_ALDFF_" && type_str.size() == 11) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_clk = true; - pol_clk = type_str[8] == 'P'; - sig_clk = cell->getPort(ID::C); - has_aload = true; - pol_aload = type_str[9] == 'P'; - sig_aload = cell->getPort(ID::L); - sig_ad = cell->getPort(ID::AD); + info.is_fine = true; + info.has_clk = true; + info.pol_clk = type_str[8] == 'P'; + info.has_aload = true; + info.pol_aload = type_str[9] == 'P'; + if constexpr (have_cell) { + info.sig_d = cell->getPort(ID::D); + info.sig_clk = cell->getPort(ID::C); + info.sig_aload = cell->getPort(ID::L); + info.sig_ad = cell->getPort(ID::AD); + } } else if (type_str.substr(0, 9) == "$_ALDFFE_" && type_str.size() == 13) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_clk = true; - pol_clk = type_str[9] == 'P'; - sig_clk = cell->getPort(ID::C); - has_aload = true; - pol_aload = type_str[10] == 'P'; - sig_aload = cell->getPort(ID::L); - sig_ad = cell->getPort(ID::AD); - has_ce = true; - pol_ce = type_str[11] == 'P'; - sig_ce = cell->getPort(ID::E); + info.is_fine = true; + info.has_clk = true; + info.pol_clk = type_str[9] == 'P'; + info.has_aload = true; + info.pol_aload = type_str[10] == 'P'; + info.has_ce = true; + info.pol_ce = type_str[11] == 'P'; + if constexpr (have_cell) { + info.sig_d = cell->getPort(ID::D); + info.sig_clk = cell->getPort(ID::C); + info.sig_aload = cell->getPort(ID::L); + info.sig_ad = cell->getPort(ID::AD); + info.sig_ce = cell->getPort(ID::E); + } } else if (type_str.substr(0, 8) == "$_DFFSR_" && type_str.size() == 12) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_clk = true; - pol_clk = type_str[8] == 'P'; - sig_clk = cell->getPort(ID::C); - has_sr = true; - pol_set = type_str[9] == 'P'; - pol_clr = type_str[10] == 'P'; - sig_set = cell->getPort(ID::S); - sig_clr = cell->getPort(ID::R); + info.is_fine = true; + info.has_clk = true; + info.pol_clk = type_str[8] == 'P'; + info.has_sr = true; + info.pol_set = type_str[9] == 'P'; + info.pol_clr = type_str[10] == 'P'; + if constexpr (have_cell) { + info.sig_d = cell->getPort(ID::D); + info.sig_clk = cell->getPort(ID::C); + info.sig_set = cell->getPort(ID::S); + info.sig_clr = cell->getPort(ID::R); + } } else if (type_str.substr(0, 9) == "$_DFFSRE_" && type_str.size() == 14) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_clk = true; - pol_clk = type_str[9] == 'P'; - sig_clk = cell->getPort(ID::C); - has_sr = true; - pol_set = type_str[10] == 'P'; - pol_clr = type_str[11] == 'P'; - sig_set = cell->getPort(ID::S); - sig_clr = cell->getPort(ID::R); - has_ce = true; - pol_ce = type_str[12] == 'P'; - sig_ce = cell->getPort(ID::E); + info.is_fine = true; + info.has_clk = true; + info.pol_clk = type_str[9] == 'P'; + info.has_sr = true; + info.pol_set = type_str[10] == 'P'; + info.pol_clr = type_str[11] == 'P'; + info.has_ce = true; + info.pol_ce = type_str[12] == 'P'; + if constexpr (have_cell) { + info.sig_d = cell->getPort(ID::D); + info.sig_clk = cell->getPort(ID::C); + info.sig_set = cell->getPort(ID::S); + info.sig_clr = cell->getPort(ID::R); + info.sig_ce = cell->getPort(ID::E); + } } else if (type_str.substr(0, 7) == "$_SDFF_" && type_str.size() == 11) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_clk = true; - pol_clk = type_str[7] == 'P'; - sig_clk = cell->getPort(ID::C); - has_srst = true; - pol_srst = type_str[8] == 'P'; - sig_srst = cell->getPort(ID::R); - val_srst = type_str[9] == '1' ? State::S1 : State::S0; + info.is_fine = true; + info.has_clk = true; + info.pol_clk = type_str[7] == 'P'; + info.has_srst = true; + info.pol_srst = type_str[8] == 'P'; + info.val_srst = type_str[9] == '1' ? State::S1 : State::S0; + if constexpr (have_cell) { + info.sig_d = cell->getPort(ID::D); + info.sig_clk = cell->getPort(ID::C); + info.sig_srst = cell->getPort(ID::R); + } } else if (type_str.substr(0, 8) == "$_SDFFE_" && type_str.size() == 13) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_clk = true; - pol_clk = type_str[8] == 'P'; - sig_clk = cell->getPort(ID::C); - has_srst = true; - pol_srst = type_str[9] == 'P'; - sig_srst = cell->getPort(ID::R); - val_srst = type_str[10] == '1' ? State::S1 : State::S0; - has_ce = true; - pol_ce = type_str[11] == 'P'; - sig_ce = cell->getPort(ID::E); + info.is_fine = true; + info.has_clk = true; + info.pol_clk = type_str[8] == 'P'; + info.has_srst = true; + info.pol_srst = type_str[9] == 'P'; + info.val_srst = type_str[10] == '1' ? State::S1 : State::S0; + info.has_ce = true; + info.pol_ce = type_str[11] == 'P'; + if constexpr (have_cell) { + info.sig_d = cell->getPort(ID::D); + info.sig_clk = cell->getPort(ID::C); + info.sig_srst = cell->getPort(ID::R); + info.sig_ce = cell->getPort(ID::E); + } } else if (type_str.substr(0, 9) == "$_SDFFCE_" && type_str.size() == 14) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_clk = true; - pol_clk = type_str[9] == 'P'; - sig_clk = cell->getPort(ID::C); - has_srst = true; - pol_srst = type_str[10] == 'P'; - sig_srst = cell->getPort(ID::R); - val_srst = type_str[11] == '1' ? State::S1 : State::S0; - has_ce = true; - pol_ce = type_str[12] == 'P'; - sig_ce = cell->getPort(ID::E); - ce_over_srst = true; + info.is_fine = true; + info.has_clk = true; + info.pol_clk = type_str[9] == 'P'; + info.has_srst = true; + info.pol_srst = type_str[10] == 'P'; + info.val_srst = type_str[11] == '1' ? State::S1 : State::S0; + info.has_ce = true; + info.pol_ce = type_str[12] == 'P'; + info.ce_over_srst = true; + if constexpr (have_cell) { + info.sig_d = cell->getPort(ID::D); + info.sig_clk = cell->getPort(ID::C); + info.sig_srst = cell->getPort(ID::R); + info.sig_ce = cell->getPort(ID::E); + } } else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 11) { - is_fine = true; - has_aload = true; - sig_ad = cell->getPort(ID::D); - has_aload = true; - pol_aload = type_str[9] == 'P'; - sig_aload = cell->getPort(ID::E); + info.is_fine = true; + info.has_aload = true; + info.has_aload = true; + info.pol_aload = type_str[9] == 'P'; + if constexpr (have_cell) { + info.sig_ad = cell->getPort(ID::D); + info.sig_aload = cell->getPort(ID::E); + } } else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 13) { - is_fine = true; - has_aload = true; - sig_ad = cell->getPort(ID::D); - has_aload = true; - pol_aload = type_str[9] == 'P'; - sig_aload = cell->getPort(ID::E); - has_arst = true; - pol_arst = type_str[10] == 'P'; - sig_arst = cell->getPort(ID::R); - val_arst = type_str[11] == '1' ? State::S1 : State::S0; + info.is_fine = true; + info.has_aload = true; + info.has_aload = true; + info.pol_aload = type_str[9] == 'P'; + info.has_arst = true; + info.pol_arst = type_str[10] == 'P'; + info.val_arst = type_str[11] == '1' ? State::S1 : State::S0; + if constexpr (have_cell) { + info.sig_ad = cell->getPort(ID::D); + info.sig_aload = cell->getPort(ID::E); + info.sig_arst = cell->getPort(ID::R); + } } else if (type_str.substr(0, 11) == "$_DLATCHSR_" && type_str.size() == 15) { - is_fine = true; - has_aload = true; - sig_ad = cell->getPort(ID::D); - has_aload = true; - pol_aload = type_str[11] == 'P'; - sig_aload = cell->getPort(ID::E); - has_sr = true; - pol_set = type_str[12] == 'P'; - pol_clr = type_str[13] == 'P'; - sig_set = cell->getPort(ID::S); - sig_clr = cell->getPort(ID::R); + info.is_fine = true; + info.has_aload = true; + info.has_aload = true; + info.pol_aload = type_str[11] == 'P'; + info.has_sr = true; + info.pol_set = type_str[12] == 'P'; + info.pol_clr = type_str[13] == 'P'; + if constexpr (have_cell) { + info.sig_ad = cell->getPort(ID::D); + info.sig_aload = cell->getPort(ID::E); + info.sig_set = cell->getPort(ID::S); + info.sig_clr = cell->getPort(ID::R); + } } else { log_assert(0); } - if (has_aload && !has_clk && !has_sr && !has_arst && sig_ad.is_fully_const()) { - // Plain D latches with const D treated specially. - has_aload = false; - has_arst = true; - sig_arst = sig_aload; - pol_arst = pol_aload; - val_arst = sig_ad.as_const(); - } + if constexpr (have_cell) + if (info.has_aload && !info.has_clk && !info.has_sr && !info.has_arst && info.sig_ad.is_fully_const()) { + // Plain D latches with const D treated specially. + info.has_aload = false; + info.has_arst = true; + info.sig_arst = info.sig_aload; + info.pol_arst = info.pol_aload; + info.val_arst = info.sig_ad.as_const(); + } +} + +FfTypeData::FfTypeData(IdString type) : FfTypeData() +{ + manufacture_info(type, *this, nullptr); +} + +FfData::FfData(FfInitVals *initvals, Cell *cell_) : FfData(cell_->module, initvals, cell_->name) +{ + cell = cell_; + manufacture_info(cell, *this, initvals); } FfData FfData::slice(const std::vector &bits) { diff --git a/kernel/ff.h b/kernel/ff.h index d6cf99b9b..217658d35 100644 --- a/kernel/ff.h +++ b/kernel/ff.h @@ -78,31 +78,20 @@ YOSYS_NAMESPACE_BEGIN // - has_arst [does not correspond to a native cell, represented as dlatch with const D input] // - empty set [not a cell — will be emitted as a simple direct connection] -struct FfData { - Module *module; - FfInitVals *initvals; - Cell *cell; - IdString name; - // The FF output. - SigSpec sig_q; - // The sync data input, present if has_clk or has_gclk. - SigSpec sig_d; - // The async data input, present if has_aload. - SigSpec sig_ad; - // The sync clock, present if has_clk. - SigSpec sig_clk; - // The clock enable, present if has_ce. - SigSpec sig_ce; - // The async load enable, present if has_aload. - SigSpec sig_aload; - // The async reset, preset if has_arst. - SigSpec sig_arst; - // The sync reset, preset if has_srst. - SigSpec sig_srst; - // The async clear (per-lane), present if has_sr. - SigSpec sig_clr; - // The async set (per-lane), present if has_sr. - SigSpec sig_set; +struct FfTypeData { + FfTypeData(IdString type); + FfTypeData() { + has_clk = false; + has_gclk = false; + has_ce = false; + has_aload = false; + has_srst = false; + has_arst = false; + has_sr = false; + ce_over_srst = false; + is_fine = false; + is_anyinit = false; + } // True if this is a clocked (edge-sensitive) flip-flop. bool has_clk; // True if this is a $ff, exclusive with every other has_*. @@ -143,9 +132,38 @@ struct FfData { bool pol_clr; bool pol_set; // The value loaded by sig_arst. + // Zero length if unknowable from just type Const val_arst; // The value loaded by sig_srst. + // Zero length if unknowable from just type Const val_srst; +}; + +struct FfData : FfTypeData { + Module *module; + FfInitVals *initvals; + Cell *cell; + IdString name; + // The FF output. + SigSpec sig_q; + // The sync data input, present if has_clk or has_gclk. + SigSpec sig_d; + // The async data input, present if has_aload. + SigSpec sig_ad; + // The sync clock, present if has_clk. + SigSpec sig_clk; + // The clock enable, present if has_ce. + SigSpec sig_ce; + // The async load enable, present if has_aload. + SigSpec sig_aload; + // The async reset, preset if has_arst. + SigSpec sig_arst; + // The sync reset, preset if has_srst. + SigSpec sig_srst; + // The async clear (per-lane), present if has_sr. + SigSpec sig_clr; + // The async set (per-lane), present if has_sr. + SigSpec sig_set; // The initial value at power-up. Const val_init; // The FF data width in bits. @@ -154,16 +172,6 @@ struct FfData { FfData(Module *module = nullptr, FfInitVals *initvals = nullptr, IdString name = IdString()) : module(module), initvals(initvals), cell(nullptr), name(name) { width = 0; - has_clk = false; - has_gclk = false; - has_ce = false; - has_aload = false; - has_srst = false; - has_arst = false; - has_sr = false; - ce_over_srst = false; - is_fine = false; - is_anyinit = false; pol_clk = false; pol_aload = false; pol_ce = false; From c2c9506f4f0bd3d0d0a59bc904258d1c24cd9341 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 18 Jul 2025 13:32:49 +0200 Subject: [PATCH 03/18] icell_liberty: flops --- passes/cmds/icell_liberty.cc | 86 +++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 2 deletions(-) diff --git a/passes/cmds/icell_liberty.cc b/passes/cmds/icell_liberty.cc index 19692fa01..9b89c1125 100644 --- a/passes/cmds/icell_liberty.cc +++ b/passes/cmds/icell_liberty.cc @@ -18,19 +18,27 @@ */ #include "kernel/yosys.h" #include "kernel/celltypes.h" +#include "kernel/ff.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct LibertyStubber { CellTypes ct; - CellTypes ct_ff; LibertyStubber() { ct.setup(); ct.setup_internals_ff(); } void liberty_prefix(std::ostream& f) { + f << "/*\n"; + f << stringf("Models interfaces of select Yosys internal cell.\n"); + f << stringf("Likely contains INCORRECT POLARITIES.\n"); + f << stringf("Impractical for any simulation, synthesis, or timing.\n"); + f << stringf("Intended purely for SDC expansion.\n"); + f << stringf("Do not microwave or tumble dry.\n"); + f << stringf("Generated by %s\n", yosys_maybe_version()); + f << "*/\n"; f << "library (yosys) {\n"; f << "\tinput_threshold_pct_fall : 50;\n"; f << "\tinput_threshold_pct_rise : 50;\n"; @@ -45,6 +53,76 @@ struct LibertyStubber { { f << "}\n"; } + struct LibertyItemizer { + std::ostream& f; + int indent; + LibertyItemizer(std::ostream& f) : f(f), indent(0) {}; + void item(std::string key, std::string val) + { + f << std::string(indent, '\t') << key << " : \"" << val << "\";\n"; + } + }; + void liberty_flop(Module* base, Module* derived, std::ostream& f) + { + auto base_name = base->name.str().substr(1); + auto derived_name = derived->name.str().substr(1); + + FfTypeData ffType(base_name); + LibertyItemizer i(f); + + if (ffType.has_gclk) { + log_warning("Formal flip flop %s can't be modeled\n", base_name.c_str()); + return; + } + if (ffType.has_ce) { + log_warning("DFFE %s can't be modeled\n", base_name.c_str()); + return; + } + + f << "\tcell (\"" << derived_name << "\") {\n"; + auto& base_type = ct.cell_types[base_name]; + i.indent = 3; + for (auto x : derived->ports) { + bool is_input = base_type.inputs.count(x); + bool is_output = base_type.outputs.count(x); + f << "\t\tpin (" << RTLIL::unescape_id(x.str()) << ") {\n"; + if (is_input && !is_output) { + i.item("direction", "input"); + } else if (!is_input && is_output) { + i.item("direction", "output"); + } else { + i.item("direction", "inout"); + } + if (RTLIL::unescape_id(x) == "CLK" || RTLIL::unescape_id(x) == "C") + i.item("clock", "true"); + if (RTLIL::unescape_id(x) == "Q") + i.item("function", "IQ"); + f << "\t\t}\n"; + } + + f << "\t\tff (\"IQ\",\"IQ_N\") {\n"; + i.indent = 3; + // TODO polarities? + if (ffType.has_clk) { + auto pin = ffType.is_fine ? "C" : "CLK"; + i.item("clocked_on", pin); + } + if (ffType.has_arst) { + auto meaning = (ffType.val_arst == State::S1) ? "preset" : "clear"; + auto pin = ffType.is_fine ? "R" : "ARST"; + i.item(meaning, pin); + } + auto next_state = ffType.has_ce ? "D & EN" : "D"; + i.item("next_state", next_state); + f << "\t\t}\n"; + + + // bool has_aload; + // bool has_srst; + // bool has_arst; + // bool has_sr; + f << "\t}\n"; + } void liberty_cell(Module* base, Module* derived, std::ostream& f) { auto base_name = base->name.str().substr(1); @@ -53,6 +131,10 @@ struct LibertyStubber { log_debug("skip skeleton for %s\n", base_name.c_str()); return; } + + if (RTLIL::builtin_ff_cell_types().count(base_name)) + return liberty_flop(base, derived, f); + auto& base_type = ct.cell_types[base_name]; f << "\tcell (\"" << derived_name << "\") {\n"; for (auto x : derived->ports) { @@ -73,7 +155,7 @@ struct LibertyStubber { }; struct IcellLiberty : Pass { - IcellLiberty() : Pass("icell_liberty", "derive box modules") {} + IcellLiberty() : Pass("icell_liberty", "write Liberty interfaces for used internal cells") {} void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| From e4e32d7966865f107ba63349d6551ce39f6c6ff4 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 18 Jul 2025 22:27:11 +0200 Subject: [PATCH 04/18] icell_liberty: flop harder --- passes/cmds/icell_liberty.cc | 51 +++++++++++++++++------------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/passes/cmds/icell_liberty.cc b/passes/cmds/icell_liberty.cc index 9b89c1125..83252ffb4 100644 --- a/passes/cmds/icell_liberty.cc +++ b/passes/cmds/icell_liberty.cc @@ -1,21 +1,3 @@ -/* - * yosys -- Yosys Open SYnthesis Suite - * - * Copyright (C) 2024 Martin Povišer - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ #include "kernel/yosys.h" #include "kernel/celltypes.h" #include "kernel/ff.h" @@ -32,12 +14,12 @@ struct LibertyStubber { void liberty_prefix(std::ostream& f) { f << "/*\n"; - f << stringf("Models interfaces of select Yosys internal cell.\n"); - f << stringf("Likely contains INCORRECT POLARITIES.\n"); - f << stringf("Impractical for any simulation, synthesis, or timing.\n"); - f << stringf("Intended purely for SDC expansion.\n"); - f << stringf("Do not microwave or tumble dry.\n"); - f << stringf("Generated by %s\n", yosys_maybe_version()); + f << stringf("\tModels interfaces of select Yosys internal cell.\n"); + f << stringf("\tLikely contains INCORRECT POLARITIES.\n"); + f << stringf("\tImpractical for any simulation, synthesis, or timing.\n"); + f << stringf("\tIntended purely for SDC expansion.\n"); + f << stringf("\tDo not microwave or tumble dry.\n"); + f << stringf("\tGenerated by %s\n", yosys_maybe_version()); f << "*/\n"; f << "library (yosys) {\n"; f << "\tinput_threshold_pct_fall : 50;\n"; @@ -82,7 +64,13 @@ struct LibertyStubber { f << "\tcell (\"" << derived_name << "\") {\n"; auto& base_type = ct.cell_types[base_name]; i.indent = 3; - for (auto x : derived->ports) { + auto sorted_ports = derived->ports; + // Hack for CLK and C coming before Q does + auto cmp = [](IdString l, IdString r) { return l.str() < r.str(); }; + std::sort(sorted_ports.begin(), sorted_ports.end(), cmp); + std::string clock_pin_name = ""; + for (auto x : sorted_ports) { + std::string port_name = RTLIL::unescape_id(x); bool is_input = base_type.inputs.count(x); bool is_output = base_type.outputs.count(x); f << "\t\tpin (" << RTLIL::unescape_id(x.str()) << ") {\n"; @@ -93,10 +81,19 @@ struct LibertyStubber { } else { i.item("direction", "inout"); } - if (RTLIL::unescape_id(x) == "CLK" || RTLIL::unescape_id(x) == "C") + if (port_name == "CLK" || port_name == "C") { i.item("clock", "true"); - if (RTLIL::unescape_id(x) == "Q") + clock_pin_name = port_name; + } + if (port_name == "Q") { i.item("function", "IQ"); + f << "\t\t\ttiming () {\n"; + i.indent++; + log_assert(clock_pin_name.size()); + i.item("related_pin", clock_pin_name); + i.indent--; + f << "\t\t\t}\n"; + } f << "\t\t}\n"; } From bbf1e4bca2a1e55ad2534efb35196a3150b7c341 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 18 Jul 2025 22:45:31 +0200 Subject: [PATCH 05/18] sdc_expand, opensta: start --- techlibs/common/Makefile.inc | 2 + techlibs/common/opensta.cc | 118 ++++++++++++++++++++++++++ techlibs/common/sdc_expand.cc | 150 ++++++++++++++++++++++++++++++++++ 3 files changed, 270 insertions(+) create mode 100644 techlibs/common/opensta.cc create mode 100644 techlibs/common/sdc_expand.cc diff --git a/techlibs/common/Makefile.inc b/techlibs/common/Makefile.inc index 22b119800..df96fdd46 100644 --- a/techlibs/common/Makefile.inc +++ b/techlibs/common/Makefile.inc @@ -2,6 +2,8 @@ ifneq ($(SMALL),1) OBJS += techlibs/common/synth.o OBJS += techlibs/common/prep.o +OBJS += techlibs/common/opensta.o +OBJS += techlibs/common/sdc_expand.o endif GENFILES += techlibs/common/simlib_help.inc diff --git a/techlibs/common/opensta.cc b/techlibs/common/opensta.cc new file mode 100644 index 000000000..1b3571973 --- /dev/null +++ b/techlibs/common/opensta.cc @@ -0,0 +1,118 @@ +#include "kernel/rtlil.h" +#include "kernel/log.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +#if !defined(YOSYS_DISABLE_SPAWN) +struct OpenstaPass : public Pass +{ + const char* default_sta_cmd = "sta"; + OpenstaPass() : Pass("opensta", "run OpenSTA") { } + + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" opensta [options]\n"); + log("\n"); + // TOOD + log("\n"); + log(" -exe \n"); + log(" use to run OpenSTA instead of \"%s\"\n", default_sta_cmd); + log("\n"); + log(" -sdc-in \n"); + log(" expand SDC input file \n"); + log("\n"); + log(" -sdc-out \n"); + log(" expand SDC file to output file \n"); + log("\n"); + log(" -nocleanup\n"); + log("\n"); + log("\n"); + } + + void execute(std::vector args, RTLIL::Design *design) override + { + string run_from, run_to; + string opensta_exe = "sta"; + string sdc_filename, sdc_expanded_filename; + string tempdir_name, script_filename; + string verilog_filename, liberty_filename; + bool cleanup = true; + + log_header(design, "Executing OPENSTA pass.\n"); + log_push(); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-exe" && argidx+1 < args.size()) { + opensta_exe = args[++argidx]; + continue; + } + if (args[argidx] == "-sdc-in" && argidx+1 < args.size()) { + sdc_filename = args[++argidx]; + continue; + } + if (args[argidx] == "-sdc-out" && argidx+1 < args.size()) { + sdc_expanded_filename = args[++argidx]; + continue; + } + if (args[argidx] == "-verilog" && argidx+1 < args.size()) { + verilog_filename = args[++argidx]; + continue; + } + if (args[argidx] == "-liberty" && argidx+1 < args.size()) { + liberty_filename = args[++argidx]; + continue; + } + if (args[argidx] == "-nocleanup") { + cleanup = false; + continue; + } + break; + } + extra_args(args, argidx, design); + if (!design->full_selection()) + log_cmd_error("This command only operates on fully selected designs!\n"); + + if (cleanup) + tempdir_name = get_base_tmpdir() + "/"; + else + tempdir_name = "_tmp_"; + tempdir_name += proc_program_prefix() + "yosys-opensta-XXXXXX"; + tempdir_name = make_temp_dir(tempdir_name); + script_filename = tempdir_name + "/opensta.tcl"; + // script_filename + + auto top_mod = design->top_module(); + if (!top_mod) + log_error("Can't find top module in current design!\n"); + + std::ofstream f_script; + f_script.open(script_filename); + + f_script << "read_verilog " << verilog_filename << "\n"; + f_script << "read_lib " << liberty_filename << "\n"; + f_script << "link_design " << RTLIL::unescape_id(top_mod->name) << "\n"; + f_script << "read_sdc " << sdc_filename << "\n"; + f_script << "write_sdc " << sdc_expanded_filename << "\n"; + f_script.close(); + std::string command = opensta_exe + " -exit " + script_filename; + int ret = run_command(command); + if (ret) + log_error("OpenSTA return %d (error)\n", ret); + else + log("sdc_expanded_filename: %s\n", sdc_expanded_filename.c_str()); + + if (cleanup) { + log("Removing temp directory.\n"); + remove_directory(tempdir_name); + } + log_pop(); + } +} OpenstaPass; +#endif + +PRIVATE_NAMESPACE_END diff --git a/techlibs/common/sdc_expand.cc b/techlibs/common/sdc_expand.cc new file mode 100644 index 000000000..7efaf8c0d --- /dev/null +++ b/techlibs/common/sdc_expand.cc @@ -0,0 +1,150 @@ +#include "kernel/rtlil.h" +#include "kernel/log.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct SdcexpandPass : public ScriptPass +{ + const char* default_sta_cmd = "sta"; + SdcexpandPass() : ScriptPass("sdc_expand", "run OpenSTA") { } + + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" sdc_expand [options]\n"); + log("\n"); + // TODO + log("\n"); + log(" -exe \n"); + log(" use to run OpenSTA instead of \"%s\"\n", default_sta_cmd); + log("\n"); + log(" -sdc-in \n"); + log(" expand SDC file \n"); + log("\n"); + log(" -nocleanup\n"); + log("\n"); + log("\n"); + log("The following commands are executed by this synthesis command:\n"); + help_script(); + log("\n"); + } + + string opensta_exe, sdc_filename, sdc_expanded_filename; + bool cleanup; + void execute(std::vector args, RTLIL::Design *design) override + { + string run_from, run_to; + cleanup = true; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-exe" && argidx+1 < args.size()) { + opensta_exe = args[++argidx]; + continue; + } + if (args[argidx] == "-sdc-in" && argidx+1 < args.size()) { + sdc_filename = args[++argidx]; + continue; + } + if (args[argidx] == "-sdc-out" && argidx+1 < args.size()) { + sdc_expanded_filename = args[++argidx]; + continue; + } + if (args[argidx] == "-nocleanup") { + cleanup = false; + continue; + } + // repetitive boring bit + if (args[argidx] == "-run" && argidx+1 < args.size()) { + size_t pos = args[argidx+1].find(':'); + if (pos == std::string::npos) { + run_from = args[++argidx]; + run_to = args[argidx]; + } else { + run_from = args[++argidx].substr(0, pos); + run_to = args[argidx].substr(pos+1); + } + continue; + } + break; + } + extra_args(args, argidx, design); + + if (!design->full_selection()) + log_cmd_error("This command only operates on fully selected designs!\n"); + + log_header(design, "Executing OPENSTA pass.\n"); + log_push(); + + run_script(design, run_from, run_to); + + log_pop(); + } + + void script() override + { + std::string tempdir_name; + std::string liberty_path; + std::string verilog_path; + + run("design -save pre_expand"); + run("proc"); + run("memory"); + // run("dfflegalize -cell $dff"); + + std::string write_verilog_cmd = "write_verilog -norename -noexpr -attr2comment -defparam "; + if (help_mode) { + run(write_verilog_cmd + "/elaborated.v"); + } else { + if (cleanup) + tempdir_name = get_base_tmpdir() + "/"; + else + tempdir_name = "_tmp_"; + tempdir_name += proc_program_prefix() + "yosys-sdc_expand-XXXXXX"; + tempdir_name = make_temp_dir(tempdir_name); + verilog_path = tempdir_name + "/elaborated.v"; + run(write_verilog_cmd + verilog_path); + } + + run("read_verilog -setattr whitebox -defer -DSIMLIB_NOCHECKS +/simlib.v"); + run("proc"); + run("hierarchy -auto-top"); + run("chtype -publish_icells"); + + if (help_mode) { + run("icell_liberty /yosys.lib"); + } else { + liberty_path = tempdir_name + "/yosys.lib"; + run(stringf("icell_liberty %s", liberty_path.c_str())); + } + + std::string opensta_pass_call = "opensta -exe "; + opensta_pass_call += help_mode ? "" : opensta_exe; + opensta_pass_call += " -sdc-in "; + opensta_pass_call += help_mode ? "" : sdc_filename; + opensta_pass_call += " -sdc-out "; + opensta_pass_call += help_mode ? "" : sdc_expanded_filename; + opensta_pass_call += " -verilog "; + opensta_pass_call += help_mode ? "" : verilog_path; + opensta_pass_call += " -liberty "; + opensta_pass_call += help_mode ? "/yosys.lib" : liberty_path; + if (!cleanup) + opensta_pass_call += " -nocleanup"; + run(opensta_pass_call.c_str()); + + if (!help_mode) { + if (cleanup) { + log("Removing temp directory.\n"); + remove_directory(tempdir_name); + } else { + log("Keeping temp directory %s\n", tempdir_name.c_str()); + } + } + run("design -load pre_expand"); + } +} SdcexpandPass; + +PRIVATE_NAMESPACE_END From 0c4105d72c3d5b512176b75a8c825307cd95debb Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Thu, 31 Jul 2025 15:38:45 +0200 Subject: [PATCH 06/18] opensta: quiet net width mismatch warning --- techlibs/common/opensta.cc | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/techlibs/common/opensta.cc b/techlibs/common/opensta.cc index 1b3571973..bfd36c380 100644 --- a/techlibs/common/opensta.cc +++ b/techlibs/common/opensta.cc @@ -99,8 +99,13 @@ struct OpenstaPass : public Pass f_script << "read_sdc " << sdc_filename << "\n"; f_script << "write_sdc " << sdc_expanded_filename << "\n"; f_script.close(); - std::string command = opensta_exe + " -exit " + script_filename; - int ret = run_command(command); + std::string command = opensta_exe + " -exit " + script_filename; + auto process_line = [](const std::string &line) { + if (line.find("does not match net size") != std::string::npos) + return; + log("OpenSTA: %s", line.c_str()); + }; + int ret = run_command(command, process_line); if (ret) log_error("OpenSTA return %d (error)\n", ret); else From 7bed6ec658e2f9137131dca03b8a7b55a5d843a6 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Thu, 2 Oct 2025 15:26:24 +0200 Subject: [PATCH 07/18] opensta: quiet blackbox warning --- techlibs/common/opensta.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/techlibs/common/opensta.cc b/techlibs/common/opensta.cc index bfd36c380..0a93a915c 100644 --- a/techlibs/common/opensta.cc +++ b/techlibs/common/opensta.cc @@ -101,6 +101,8 @@ struct OpenstaPass : public Pass f_script.close(); std::string command = opensta_exe + " -exit " + script_filename; auto process_line = [](const std::string &line) { + if (line.find("Creating black box") != std::string::npos) + return; if (line.find("does not match net size") != std::string::npos) return; log("OpenSTA: %s", line.c_str()); From 793594bd5973d2f48100aaec7c67c93d439d9956 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Thu, 2 Oct 2025 16:45:32 +0200 Subject: [PATCH 08/18] sdc_expand: log header --- techlibs/common/sdc_expand.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/techlibs/common/sdc_expand.cc b/techlibs/common/sdc_expand.cc index 7efaf8c0d..baa281dd7 100644 --- a/techlibs/common/sdc_expand.cc +++ b/techlibs/common/sdc_expand.cc @@ -35,6 +35,7 @@ struct SdcexpandPass : public ScriptPass bool cleanup; void execute(std::vector args, RTLIL::Design *design) override { + log_header(design, "Executing SDC_EXPAND pass.\n"); string run_from, run_to; cleanup = true; From 7bc88d5c4098255a8be65cba16c0e12b35c8d0e5 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Wed, 8 Oct 2025 16:11:03 +0200 Subject: [PATCH 09/18] sdc_expand: cleanup --- techlibs/common/sdc_expand.cc | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/techlibs/common/sdc_expand.cc b/techlibs/common/sdc_expand.cc index baa281dd7..2fd5f380e 100644 --- a/techlibs/common/sdc_expand.cc +++ b/techlibs/common/sdc_expand.cc @@ -32,12 +32,12 @@ struct SdcexpandPass : public ScriptPass } string opensta_exe, sdc_filename, sdc_expanded_filename; - bool cleanup; + bool cleanup = true; void execute(std::vector args, RTLIL::Design *design) override { log_header(design, "Executing SDC_EXPAND pass.\n"); string run_from, run_to; - cleanup = true; + opensta_exe = "sta"; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -58,7 +58,6 @@ struct SdcexpandPass : public ScriptPass cleanup = false; continue; } - // repetitive boring bit if (args[argidx] == "-run" && argidx+1 < args.size()) { size_t pos = args[argidx+1].find(':'); if (pos == std::string::npos) { @@ -72,6 +71,12 @@ struct SdcexpandPass : public ScriptPass } break; } + + if (sdc_filename.empty()) + log_cmd_error("Missing -sdc-in argument\n"); + if (sdc_expanded_filename.empty()) + log_cmd_error("Missing -sdc-out argument\n"); + extra_args(args, argidx, design); if (!design->full_selection()) @@ -88,17 +93,14 @@ struct SdcexpandPass : public ScriptPass void script() override { std::string tempdir_name; - std::string liberty_path; - std::string verilog_path; run("design -save pre_expand"); run("proc"); run("memory"); // run("dfflegalize -cell $dff"); - std::string write_verilog_cmd = "write_verilog -norename -noexpr -attr2comment -defparam "; if (help_mode) { - run(write_verilog_cmd + "/elaborated.v"); + tempdir_name = ""; } else { if (cleanup) tempdir_name = get_base_tmpdir() + "/"; @@ -106,21 +108,18 @@ struct SdcexpandPass : public ScriptPass tempdir_name = "_tmp_"; tempdir_name += proc_program_prefix() + "yosys-sdc_expand-XXXXXX"; tempdir_name = make_temp_dir(tempdir_name); - verilog_path = tempdir_name + "/elaborated.v"; - run(write_verilog_cmd + verilog_path); } + std::string verilog_path = tempdir_name + "/elaborated.v"; + std::string write_verilog_cmd = "write_verilog -norename -noexpr -attr2comment -defparam "; + run(write_verilog_cmd + verilog_path); run("read_verilog -setattr whitebox -defer -DSIMLIB_NOCHECKS +/simlib.v"); run("proc"); run("hierarchy -auto-top"); run("chtype -publish_icells"); - if (help_mode) { - run("icell_liberty /yosys.lib"); - } else { - liberty_path = tempdir_name + "/yosys.lib"; - run(stringf("icell_liberty %s", liberty_path.c_str())); - } + std::string liberty_path = tempdir_name + "/yosys.lib"; + run("icell_liberty " + liberty_path); std::string opensta_pass_call = "opensta -exe "; opensta_pass_call += help_mode ? "" : opensta_exe; @@ -134,7 +133,7 @@ struct SdcexpandPass : public ScriptPass opensta_pass_call += help_mode ? "/yosys.lib" : liberty_path; if (!cleanup) opensta_pass_call += " -nocleanup"; - run(opensta_pass_call.c_str()); + run(opensta_pass_call); if (!help_mode) { if (cleanup) { From 9f07e21e35558200fc180c98bf301942bdc6861c Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Wed, 8 Oct 2025 16:25:47 +0200 Subject: [PATCH 10/18] rtlil: undeprecate builtin_ff_cell_types --- kernel/rtlil.h | 1 - 1 file changed, 1 deletion(-) diff --git a/kernel/rtlil.h b/kernel/rtlil.h index a8e43fe1d..4da030e8d 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -728,7 +728,6 @@ template <> struct IDMacroHelper<-1> { namespace RTLIL { extern dict constpad; - [[deprecated("Call cell->is_builtin_ff() instead")]] const pool &builtin_ff_cell_types(); static inline std::string escape_id(const std::string &str) { From cee3d0b598046f6410f417891f5942372a1a09d6 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 14 Nov 2025 13:23:14 +0100 Subject: [PATCH 11/18] icell_liberty: refactor and add help --- passes/cmds/icell_liberty.cc | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/passes/cmds/icell_liberty.cc b/passes/cmds/icell_liberty.cc index 83252ffb4..01e25b9e9 100644 --- a/passes/cmds/icell_liberty.cc +++ b/passes/cmds/icell_liberty.cc @@ -112,12 +112,6 @@ struct LibertyStubber { auto next_state = ffType.has_ce ? "D & EN" : "D"; i.item("next_state", next_state); f << "\t\t}\n"; - - - // bool has_aload; - // bool has_srst; - // bool has_arst; - // bool has_sr; f << "\t}\n"; } void liberty_cell(Module* base, Module* derived, std::ostream& f) @@ -157,8 +151,11 @@ struct IcellLiberty : Pass { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" icell_liberty \n"); // TODO + log(" icell_liberty \n"); log("\n"); + log("Write Liberty files modeling the interfaces of used internal cells.\n"); + log("\n"); + log("Models are not guaranteed to be logically sound.\n"); log("\n"); } void execute(std::vector args, RTLIL::Design *d) override @@ -168,7 +165,7 @@ struct IcellLiberty : Pass { size_t argidx; IdString naming_attr; std::string liberty_filename; - std::ofstream* liberty_file = new std::ofstream; + auto liberty_file = std::make_unique(); for (argidx = 1; argidx < args.size(); argidx++) { break; @@ -176,15 +173,12 @@ struct IcellLiberty : Pass { if (argidx < args.size()) liberty_filename = args[argidx++]; else - log_error("no Liberty filename specified\n"); - - // extra_args(args, argidx, d); + log_cmd_error("no Liberty filename specified\n"); if (liberty_filename.size()) { liberty_file->open(liberty_filename.c_str()); if (liberty_file->fail()) { - delete liberty_file; - log_error("Can't open file `%s' for writing: %s\n", liberty_filename.c_str(), strerror(errno)); + log_cmd_error("Can't open file `%s' for writing: %s\n", liberty_filename.c_str(), strerror(errno)); } } @@ -209,7 +203,6 @@ struct IcellLiberty : Pass { if (liberty_file) { stubber.liberty_suffix(*liberty_file); - delete liberty_file; } } } IcellLiberty; From 5acb77cab1a9b1c67f5f978771c9d336756f4293 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 14 Nov 2025 13:30:07 +0100 Subject: [PATCH 12/18] sdc_expand, opensta: typos --- techlibs/common/opensta.cc | 2 +- techlibs/common/sdc_expand.cc | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/techlibs/common/opensta.cc b/techlibs/common/opensta.cc index 0a93a915c..7f2b15243 100644 --- a/techlibs/common/opensta.cc +++ b/techlibs/common/opensta.cc @@ -109,7 +109,7 @@ struct OpenstaPass : public Pass }; int ret = run_command(command, process_line); if (ret) - log_error("OpenSTA return %d (error)\n", ret); + log_error("OpenSTA returned %d (error)\n", ret); else log("sdc_expanded_filename: %s\n", sdc_expanded_filename.c_str()); diff --git a/techlibs/common/sdc_expand.cc b/techlibs/common/sdc_expand.cc index 2fd5f380e..1b13923f0 100644 --- a/techlibs/common/sdc_expand.cc +++ b/techlibs/common/sdc_expand.cc @@ -4,10 +4,10 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -struct SdcexpandPass : public ScriptPass +struct SdcExpandPass : public ScriptPass { const char* default_sta_cmd = "sta"; - SdcexpandPass() : ScriptPass("sdc_expand", "run OpenSTA") { } + SdcExpandPass() : ScriptPass("sdc_expand", "run OpenSTA") { } void help() override { @@ -145,6 +145,6 @@ struct SdcexpandPass : public ScriptPass } run("design -load pre_expand"); } -} SdcexpandPass; +} SdcExpandPass; PRIVATE_NAMESPACE_END From 6846168db3e634a737cd1349458de35d55a5e8c2 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 14 Nov 2025 13:35:51 +0100 Subject: [PATCH 13/18] opensta: opensta.exe scratchpad variable --- techlibs/common/opensta.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/techlibs/common/opensta.cc b/techlibs/common/opensta.cc index 7f2b15243..fbdf27304 100644 --- a/techlibs/common/opensta.cc +++ b/techlibs/common/opensta.cc @@ -35,7 +35,7 @@ struct OpenstaPass : public Pass void execute(std::vector args, RTLIL::Design *design) override { string run_from, run_to; - string opensta_exe = "sta"; + string opensta_exe = design->scratchpad_get_string("opensta.exe", "sta"); string sdc_filename, sdc_expanded_filename; string tempdir_name, script_filename; string verilog_filename, liberty_filename; From 9a5465bc83c568b7890bbd5b506a642bba8ebe5c Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 14 Nov 2025 13:37:19 +0100 Subject: [PATCH 14/18] icell_liberty: simplify --- passes/cmds/icell_liberty.cc | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/passes/cmds/icell_liberty.cc b/passes/cmds/icell_liberty.cc index 01e25b9e9..d49cd360a 100644 --- a/passes/cmds/icell_liberty.cc +++ b/passes/cmds/icell_liberty.cc @@ -185,8 +185,7 @@ struct IcellLiberty : Pass { pool done; LibertyStubber stubber = {}; - if (liberty_file) - stubber.liberty_prefix(*liberty_file); + stubber.liberty_prefix(*liberty_file); for (auto module : d->selected_modules()) { for (auto cell : module->selected_cells()) { @@ -201,9 +200,7 @@ struct IcellLiberty : Pass { } } - if (liberty_file) { - stubber.liberty_suffix(*liberty_file); - } + stubber.liberty_suffix(*liberty_file); } } IcellLiberty; From a5b6c3cc191c2f18f52f45c1e34588a7bbe639b4 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 14 Nov 2025 13:44:12 +0100 Subject: [PATCH 15/18] opensta, sdc_expand: more scratchpad --- techlibs/common/opensta.cc | 2 +- techlibs/common/sdc_expand.cc | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/techlibs/common/opensta.cc b/techlibs/common/opensta.cc index fbdf27304..6d793b3e1 100644 --- a/techlibs/common/opensta.cc +++ b/techlibs/common/opensta.cc @@ -39,7 +39,7 @@ struct OpenstaPass : public Pass string sdc_filename, sdc_expanded_filename; string tempdir_name, script_filename; string verilog_filename, liberty_filename; - bool cleanup = true; + bool cleanup = design->scratchpad_get_bool("opensta.cleanup", true); log_header(design, "Executing OPENSTA pass.\n"); log_push(); diff --git a/techlibs/common/sdc_expand.cc b/techlibs/common/sdc_expand.cc index 1b13923f0..cec5e8c1e 100644 --- a/techlibs/common/sdc_expand.cc +++ b/techlibs/common/sdc_expand.cc @@ -32,12 +32,13 @@ struct SdcExpandPass : public ScriptPass } string opensta_exe, sdc_filename, sdc_expanded_filename; - bool cleanup = true; + bool cleanup; void execute(std::vector args, RTLIL::Design *design) override { log_header(design, "Executing SDC_EXPAND pass.\n"); string run_from, run_to; - opensta_exe = "sta"; + opensta_exe = ""; + cleanup = design->scratchpad_get_bool("sdc_expand.cleanup", true); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -121,8 +122,11 @@ struct SdcExpandPass : public ScriptPass std::string liberty_path = tempdir_name + "/yosys.lib"; run("icell_liberty " + liberty_path); - std::string opensta_pass_call = "opensta -exe "; - opensta_pass_call += help_mode ? "" : opensta_exe; + std::string opensta_pass_call = "opensta "; + if (opensta_exe.length()) { + opensta_pass_call += "-exe "; + opensta_pass_call += help_mode ? "" : opensta_exe; + } opensta_pass_call += " -sdc-in "; opensta_pass_call += help_mode ? "" : sdc_filename; opensta_pass_call += " -sdc-out "; From 411fc149df9d727f561ca04e2149e1dc4af2abf3 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 14 Nov 2025 16:06:46 +0100 Subject: [PATCH 16/18] opensta: refactor default command --- techlibs/common/opensta.cc | 6 +++--- techlibs/common/opensta.h | 8 ++++++++ techlibs/common/sdc_expand.cc | 4 ++-- 3 files changed, 13 insertions(+), 5 deletions(-) create mode 100644 techlibs/common/opensta.h diff --git a/techlibs/common/opensta.cc b/techlibs/common/opensta.cc index 6d793b3e1..2a806edf6 100644 --- a/techlibs/common/opensta.cc +++ b/techlibs/common/opensta.cc @@ -1,5 +1,6 @@ #include "kernel/rtlil.h" #include "kernel/log.h" +#include "techlibs/common/opensta.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -7,7 +8,6 @@ PRIVATE_NAMESPACE_BEGIN #if !defined(YOSYS_DISABLE_SPAWN) struct OpenstaPass : public Pass { - const char* default_sta_cmd = "sta"; OpenstaPass() : Pass("opensta", "run OpenSTA") { } void help() override @@ -19,7 +19,7 @@ struct OpenstaPass : public Pass // TOOD log("\n"); log(" -exe \n"); - log(" use to run OpenSTA instead of \"%s\"\n", default_sta_cmd); + log(" use to run OpenSTA instead of \"%s\"\n", default_opensta_cmd); log("\n"); log(" -sdc-in \n"); log(" expand SDC input file \n"); @@ -35,7 +35,7 @@ struct OpenstaPass : public Pass void execute(std::vector args, RTLIL::Design *design) override { string run_from, run_to; - string opensta_exe = design->scratchpad_get_string("opensta.exe", "sta"); + string opensta_exe = design->scratchpad_get_string("opensta.exe", default_opensta_cmd); string sdc_filename, sdc_expanded_filename; string tempdir_name, script_filename; string verilog_filename, liberty_filename; diff --git a/techlibs/common/opensta.h b/techlibs/common/opensta.h new file mode 100644 index 000000000..4c5ac871f --- /dev/null +++ b/techlibs/common/opensta.h @@ -0,0 +1,8 @@ +#include "kernel/yosys_common.h" +#ifndef OPENSTA_H +YOSYS_NAMESPACE_BEGIN + +static const char* default_opensta_cmd = "sta"; + +YOSYS_NAMESPACE_END +#endif /* OPENSTA_H */ diff --git a/techlibs/common/sdc_expand.cc b/techlibs/common/sdc_expand.cc index cec5e8c1e..95254d1f4 100644 --- a/techlibs/common/sdc_expand.cc +++ b/techlibs/common/sdc_expand.cc @@ -1,12 +1,12 @@ #include "kernel/rtlil.h" #include "kernel/log.h" +#include "techlibs/common/opensta.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct SdcExpandPass : public ScriptPass { - const char* default_sta_cmd = "sta"; SdcExpandPass() : ScriptPass("sdc_expand", "run OpenSTA") { } void help() override @@ -18,7 +18,7 @@ struct SdcExpandPass : public ScriptPass // TODO log("\n"); log(" -exe \n"); - log(" use to run OpenSTA instead of \"%s\"\n", default_sta_cmd); + log(" use to run OpenSTA instead of \"%s\"\n", default_opensta_cmd); log("\n"); log(" -sdc-in \n"); log(" expand SDC file \n"); From 85d2702ef6b78dbcc6138cc8745f6bd796fa2d23 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 14 Nov 2025 16:16:33 +0100 Subject: [PATCH 17/18] opensta, sdc_expand: fix help --- techlibs/common/opensta.cc | 4 +++- techlibs/common/sdc_expand.cc | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/techlibs/common/opensta.cc b/techlibs/common/opensta.cc index 2a806edf6..282481953 100644 --- a/techlibs/common/opensta.cc +++ b/techlibs/common/opensta.cc @@ -16,7 +16,9 @@ struct OpenstaPass : public Pass log("\n"); log(" opensta [options]\n"); log("\n"); - // TOOD + log("Expand SDC file with OpenSTA.\n"); + log("Internal command like abc. Requires a well-formed design.\n"); + log("For general SDC expansion with OpenSTA, use the sdc_expand command.\n"); log("\n"); log(" -exe \n"); log(" use to run OpenSTA instead of \"%s\"\n", default_opensta_cmd); diff --git a/techlibs/common/sdc_expand.cc b/techlibs/common/sdc_expand.cc index 95254d1f4..fe8f20aa9 100644 --- a/techlibs/common/sdc_expand.cc +++ b/techlibs/common/sdc_expand.cc @@ -7,7 +7,7 @@ PRIVATE_NAMESPACE_BEGIN struct SdcExpandPass : public ScriptPass { - SdcExpandPass() : ScriptPass("sdc_expand", "run OpenSTA") { } + SdcExpandPass() : ScriptPass("sdc_expand", "expand SDC design with opensta") { } void help() override { @@ -15,7 +15,8 @@ struct SdcExpandPass : public ScriptPass log("\n"); log(" sdc_expand [options]\n"); log("\n"); - // TODO + log("Expand SDC file with opensta based on arbitrary current design.\n"); + log("Changes the design but only temporarily.\n"); log("\n"); log(" -exe \n"); log(" use to run OpenSTA instead of \"%s\"\n", default_opensta_cmd); From 1edc32dcd03a6c20ef2389aa3591cfbda8b1080f Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Wed, 19 Nov 2025 15:31:17 +0100 Subject: [PATCH 18/18] opensta, sdc_expand: mark as experimental --- techlibs/common/opensta.cc | 1 + techlibs/common/sdc_expand.cc | 1 + 2 files changed, 2 insertions(+) diff --git a/techlibs/common/opensta.cc b/techlibs/common/opensta.cc index 282481953..655fdbf2d 100644 --- a/techlibs/common/opensta.cc +++ b/techlibs/common/opensta.cc @@ -44,6 +44,7 @@ struct OpenstaPass : public Pass bool cleanup = design->scratchpad_get_bool("opensta.cleanup", true); log_header(design, "Executing OPENSTA pass.\n"); + log_experimental("opensta"); log_push(); size_t argidx; diff --git a/techlibs/common/sdc_expand.cc b/techlibs/common/sdc_expand.cc index fe8f20aa9..39c50875f 100644 --- a/techlibs/common/sdc_expand.cc +++ b/techlibs/common/sdc_expand.cc @@ -37,6 +37,7 @@ struct SdcExpandPass : public ScriptPass void execute(std::vector args, RTLIL::Design *design) override { log_header(design, "Executing SDC_EXPAND pass.\n"); + log_experimental("sdc_expand"); string run_from, run_to; opensta_exe = ""; cleanup = design->scratchpad_get_bool("sdc_expand.cleanup", true);