From 225a45c9bcde992a02c9817541c74a2317b030e5 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Wed, 16 Jul 2025 23:15:43 +0200 Subject: [PATCH 01/17] 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 2dbeadac253..bb726aa7db3 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 00000000000..19692fa0118 --- /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 aec8c98f0dc5bd30670becd2e6101be7b1186efc Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 18 Jul 2025 12:34:10 +0200 Subject: [PATCH 02/17] ff: split out type-only information --- kernel/ff.cc | 501 +++++++++++++++++++++++++++++---------------------- kernel/ff.h | 78 ++++---- 2 files changed, 329 insertions(+), 250 deletions(-) diff --git a/kernel/ff.cc b/kernel/ff.cc index a72e6a65c5a..7dd5e24ac78 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); - } - 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 (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 (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 (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 (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); - } - } else if (cell->type == ID($_FF_)) { - is_fine = true; - has_gclk = true; - 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 (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 (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 (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 (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 (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 (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 d6cf99b9bf8..217658d3505 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 764ccc8df4ec6229cb6581933a2e329488807dd7 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 18 Jul 2025 13:32:49 +0200 Subject: [PATCH 03/17] 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 19692fa0118..9b89c112536 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 943794152a9391ff1a2b4fb7d1f7220d4d8ba6a9 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 18 Jul 2025 22:27:11 +0200 Subject: [PATCH 04/17] 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 9b89c112536..83252ffb4ab 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 c9ef4340bf58fd677858e1849a69084e6f383cea Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 18 Jul 2025 22:45:31 +0200 Subject: [PATCH 05/17] 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 e76d70a1a8e..1ae2c7ce80f 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 00000000000..1b35719732c --- /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 00000000000..7efaf8c0d7e --- /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 f28475131681ee96ab78c4216a27b14fb786fcfc Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Thu, 31 Jul 2025 15:38:45 +0200 Subject: [PATCH 06/17] 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 1b35719732c..bfd36c38095 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 36c30dccb68f678ff1a571ea9950a45227805cc9 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Thu, 2 Oct 2025 15:26:24 +0200 Subject: [PATCH 07/17] 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 bfd36c38095..0a93a915c52 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 8993ba2b7881dfa7fedf9f39d3392b3d453587dc Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Thu, 2 Oct 2025 16:45:32 +0200 Subject: [PATCH 08/17] 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 7efaf8c0d7e..baa281dd798 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 f027cfb89e54d8eb0ccb1003dadd888bd73cd063 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Wed, 8 Oct 2025 16:11:03 +0200 Subject: [PATCH 09/17] 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 baa281dd798..2fd5f380e84 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 b2ed77265ad81f75e0139ff7f8a18b8ceabbfbe0 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Wed, 8 Oct 2025 16:25:47 +0200 Subject: [PATCH 10/17] 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 6098d916b01..766b3730f44 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 8179ba1e919ae3a4a2e9d7179cfc75f44df17a25 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 14 Nov 2025 13:23:14 +0100 Subject: [PATCH 11/17] 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 83252ffb4ab..01e25b9e9e0 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 8f178222c792b03288fdf25a9effba76f4969254 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 14 Nov 2025 13:30:07 +0100 Subject: [PATCH 12/17] 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 0a93a915c52..7f2b1524333 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 2fd5f380e84..1b13923f0e9 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 ab03d331c4d7a92ffc73fa5750e964a2c0d4c341 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 14 Nov 2025 13:35:51 +0100 Subject: [PATCH 13/17] 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 7f2b1524333..fbdf2730476 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 838732ef2fe44ec311939ade927cd921dd1b84b3 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 14 Nov 2025 13:37:19 +0100 Subject: [PATCH 14/17] 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 01e25b9e9e0..d49cd360aa5 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 526c5e915b4c0af2417585c92fc74f093374b9d6 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 14 Nov 2025 13:44:12 +0100 Subject: [PATCH 15/17] 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 fbdf2730476..6d793b3e1f5 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 1b13923f0e9..cec5e8c1e2a 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 3651eecdf8afc40e5d323f97b2ca95ee0c5fd37b Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 14 Nov 2025 16:06:46 +0100 Subject: [PATCH 16/17] opensta: refactor default command --- techlibs/common/opensta.cc | 6 +++--- techlibs/common/sdc_expand.cc | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/techlibs/common/opensta.cc b/techlibs/common/opensta.cc index 6d793b3e1f5..2a806edf62d 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/sdc_expand.cc b/techlibs/common/sdc_expand.cc index cec5e8c1e2a..95254d1f4b6 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 db6d50ed204b7c8d99b7bbf4b6b79bf5229b13c9 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 14 Nov 2025 16:16:33 +0100 Subject: [PATCH 17/17] 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 2a806edf62d..2824819532a 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 95254d1f4b6..fe8f20aa94f 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);