mirror of https://github.com/YosysHQ/yosys.git
Merge branch 'YosysHQ:main' into main
This commit is contained in:
commit
a705042d2e
3
Makefile
3
Makefile
|
|
@ -177,7 +177,7 @@ ifeq ($(OS), Haiku)
|
|||
CXXFLAGS += -D_DEFAULT_SOURCE
|
||||
endif
|
||||
|
||||
YOSYS_VER := 0.59+62
|
||||
YOSYS_VER := 0.59+110
|
||||
YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1)
|
||||
YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2)
|
||||
YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'.' -f3)
|
||||
|
|
@ -955,6 +955,7 @@ MK_TEST_DIRS += tests/arch/xilinx
|
|||
MK_TEST_DIRS += tests/bugpoint
|
||||
MK_TEST_DIRS += tests/opt
|
||||
MK_TEST_DIRS += tests/sat
|
||||
MK_TEST_DIRS += tests/sdc
|
||||
MK_TEST_DIRS += tests/sim
|
||||
MK_TEST_DIRS += tests/svtypes
|
||||
MK_TEST_DIRS += tests/techmap
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ extern char yosys_path[PATH_MAX];
|
|||
#endif
|
||||
#ifdef YOSYS_ENABLE_TCL
|
||||
namespace Yosys {
|
||||
extern int yosys_tcl_iterp_init(Tcl_Interp *interp);
|
||||
extern int yosys_tcl_interp_init(Tcl_Interp *interp);
|
||||
extern void yosys_tcl_activate_repl();
|
||||
};
|
||||
#endif
|
||||
|
|
@ -610,7 +610,7 @@ int main(int argc, char **argv)
|
|||
if (run_tcl_shell) {
|
||||
#ifdef YOSYS_ENABLE_TCL
|
||||
yosys_tcl_activate_repl();
|
||||
Tcl_Main(argc, argv, yosys_tcl_iterp_init);
|
||||
Tcl_Main(argc, argv, yosys_tcl_interp_init);
|
||||
#else
|
||||
log_error("Can't exectue TCL shell: this version of yosys is not built with TCL support enabled.\n");
|
||||
#endif
|
||||
|
|
|
|||
489
kernel/ff.cc
489
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<typename InputType, typename OutputType, typename = std::enable_if_t<std::is_base_of_v<FfTypeData, OutputType>>>
|
||||
void manufacture_info(InputType flop, OutputType& info, FfInitVals *initvals) {
|
||||
Cell* cell = nullptr;
|
||||
IdString type;
|
||||
constexpr bool have_cell = std::is_same_v<InputType, Cell*>;
|
||||
if constexpr (std::is_same_v<InputType, IdString>) {
|
||||
type = flop;
|
||||
} else {
|
||||
static_assert(std::is_same_v<InputType, Cell*>);
|
||||
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<int> &bits) {
|
||||
|
|
|
|||
78
kernel/ff.h
78
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;
|
||||
|
|
|
|||
|
|
@ -728,7 +728,6 @@ template <> struct IDMacroHelper<-1> {
|
|||
namespace RTLIL {
|
||||
extern dict<std::string, std::string> constpad;
|
||||
|
||||
[[deprecated("Call cell->is_builtin_ff() instead")]]
|
||||
const pool<IdString> &builtin_ff_cell_types();
|
||||
|
||||
static inline std::string escape_id(const std::string &str) {
|
||||
|
|
|
|||
|
|
@ -533,7 +533,7 @@ static int tcl_set_param(ClientData, Tcl_Interp *interp, int objc, Tcl_Obj *cons
|
|||
return TCL_OK;
|
||||
}
|
||||
|
||||
int yosys_tcl_iterp_init(Tcl_Interp *interp)
|
||||
int yosys_tcl_interp_init(Tcl_Interp *interp)
|
||||
{
|
||||
if (Tcl_Init(interp)!=TCL_OK)
|
||||
log_warning("Tcl_Init() call failed - %s\n",Tcl_ErrnoMsg(Tcl_GetErrno()));
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ CellTypes yosys_celltypes;
|
|||
|
||||
#ifdef YOSYS_ENABLE_TCL
|
||||
Tcl_Interp *yosys_tcl_interp = NULL;
|
||||
Tcl_Interp *yosys_sdc_interp = NULL;
|
||||
#endif
|
||||
|
||||
std::set<std::string> yosys_input_files, yosys_output_files;
|
||||
|
|
@ -396,17 +397,18 @@ void rewrite_filename(std::string &filename)
|
|||
#ifdef YOSYS_ENABLE_TCL
|
||||
|
||||
// defined in tclapi.cc
|
||||
extern int yosys_tcl_iterp_init(Tcl_Interp *interp);
|
||||
extern int yosys_tcl_interp_init(Tcl_Interp *interp);
|
||||
|
||||
extern Tcl_Interp *yosys_get_tcl_interp()
|
||||
{
|
||||
if (yosys_tcl_interp == NULL) {
|
||||
yosys_tcl_interp = Tcl_CreateInterp();
|
||||
yosys_tcl_iterp_init(yosys_tcl_interp);
|
||||
yosys_tcl_interp_init(yosys_tcl_interp);
|
||||
}
|
||||
return yosys_tcl_interp;
|
||||
}
|
||||
|
||||
// Also see SdcPass
|
||||
struct TclPass : public Pass {
|
||||
TclPass() : Pass("tcl", "execute a TCL script file") { }
|
||||
void help() override {
|
||||
|
|
@ -449,6 +451,7 @@ struct TclPass : public Pass {
|
|||
Tcl_Release(interp);
|
||||
}
|
||||
} TclPass;
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) || defined(__CYGWIN__)
|
||||
|
|
|
|||
|
|
@ -58,3 +58,6 @@ 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
|
||||
|
||||
include $(YOSYS_SRC)/passes/cmds/sdc/Makefile.inc
|
||||
|
|
|
|||
|
|
@ -0,0 +1,207 @@
|
|||
#include "kernel/yosys.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/ff.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct LibertyStubber {
|
||||
CellTypes ct;
|
||||
LibertyStubber() {
|
||||
ct.setup();
|
||||
ct.setup_internals_ff();
|
||||
}
|
||||
void liberty_prefix(std::ostream& f)
|
||||
{
|
||||
f << "/*\n";
|
||||
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";
|
||||
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";
|
||||
}
|
||||
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;
|
||||
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";
|
||||
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 (port_name == "CLK" || port_name == "C") {
|
||||
i.item("clock", "true");
|
||||
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";
|
||||
}
|
||||
|
||||
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";
|
||||
f << "\t}\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;
|
||||
}
|
||||
|
||||
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) {
|
||||
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", "write Liberty interfaces for used internal cells") {}
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" icell_liberty <liberty_file>\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<std::string> args, RTLIL::Design *d) override
|
||||
{
|
||||
log_header(d, "Executing ICELL_LIBERTY pass.\n");
|
||||
|
||||
size_t argidx;
|
||||
IdString naming_attr;
|
||||
std::string liberty_filename;
|
||||
auto liberty_file = std::make_unique<std::ofstream>();
|
||||
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
break;
|
||||
}
|
||||
if (argidx < args.size())
|
||||
liberty_filename = args[argidx++];
|
||||
else
|
||||
log_cmd_error("no Liberty filename specified\n");
|
||||
|
||||
if (liberty_filename.size()) {
|
||||
liberty_file->open(liberty_filename.c_str());
|
||||
if (liberty_file->fail()) {
|
||||
log_cmd_error("Can't open file `%s' for writing: %s\n", liberty_filename.c_str(), strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
pool<RTLIL::IdString> done;
|
||||
LibertyStubber stubber = {};
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stubber.liberty_suffix(*liberty_file);
|
||||
}
|
||||
} IcellLiberty;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
OBJS += passes/cmds/sdc/sdc.o
|
||||
|
||||
$(eval $(call add_share_file,share/sdc,passes/cmds/sdc/graph-stubs.sdc))
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
proc unknown {args} {
|
||||
# Check if it's a getter
|
||||
if {[llength $args] > 0} {
|
||||
set first_arg [lindex $args 0]
|
||||
if {[string match "get_*" $first_arg]} {
|
||||
# It's a getter, has it been redirected from specialized C++ code?
|
||||
if {[llength $args] > 1} {
|
||||
set second_arg [lindex $args 1]
|
||||
if {$second_arg ne "-getter-validated"} {
|
||||
error "Unknown getter: $first_arg"
|
||||
}
|
||||
} else {
|
||||
error "Unknown getter: $first_arg"
|
||||
}
|
||||
}
|
||||
}
|
||||
# TODO this safety feature could be optional via a global
|
||||
|
||||
global sdc_call_index
|
||||
global sdc_calls
|
||||
if {![info exists sdc_call_index]} {
|
||||
set sdc_call_index 0
|
||||
}
|
||||
if {![info exists sdc_calls]} {
|
||||
set sdc_calls {}
|
||||
}
|
||||
set ret "YOSYS_SDC_MAGIC_NODE_$sdc_call_index"
|
||||
incr sdc_call_index
|
||||
lappend sdc_calls $args
|
||||
# puts "unknown $args, returning YOSYS_SDC_MAGIC_NODE_$sdc_call_index"
|
||||
return $ret
|
||||
}
|
||||
proc list {args} {
|
||||
return [unknown "list" {*}$args]
|
||||
}
|
||||
proc get_clocks {args} {
|
||||
# get_clocks isn't a design object getter
|
||||
# because clocks aren't design objects, just aliases
|
||||
# so the referred to clock pin already are being tracked
|
||||
# as arguments of uninterpreted create_clock command or similar
|
||||
return [unknown "get_clocks" "-getter-validated" {*}$args]
|
||||
}
|
||||
|
|
@ -0,0 +1,785 @@
|
|||
#ifdef YOSYS_ENABLE_TCL
|
||||
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/rtlil.h"
|
||||
#include "kernel/log.h"
|
||||
#include <tcl.h>
|
||||
#include <list>
|
||||
#include <optional>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct TclCall {
|
||||
Tcl_Interp *interp;
|
||||
int objc;
|
||||
Tcl_Obj* const* objv;
|
||||
};
|
||||
|
||||
static int redirect_unknown(TclCall call);
|
||||
static size_t get_node_count(Tcl_Interp* interp);
|
||||
|
||||
struct BitSelection {
|
||||
bool all = false;
|
||||
std::vector<bool> bits = {};
|
||||
void set_all() {
|
||||
bits.clear();
|
||||
all = true;
|
||||
}
|
||||
void clear() {
|
||||
bits.clear();
|
||||
all = false;
|
||||
}
|
||||
void set(size_t idx) {
|
||||
if (all)
|
||||
return;
|
||||
if (idx >= bits.size())
|
||||
bits.resize(idx + 1);
|
||||
bits[idx] = true;
|
||||
}
|
||||
void merge(const BitSelection& other) {
|
||||
if (all)
|
||||
return;
|
||||
if (other.all) {
|
||||
set_all();
|
||||
return;
|
||||
}
|
||||
if (other.bits.size() > bits.size())
|
||||
bits.resize(other.bits.size());
|
||||
for (size_t other_idx = 0; other_idx < other.bits.size(); other_idx++) {
|
||||
bool other_bit = other.bits[other_idx];
|
||||
if (other_bit)
|
||||
set(other_idx);
|
||||
}
|
||||
}
|
||||
void dump() {
|
||||
if (!all) {
|
||||
for (size_t i = 0; i < bits.size(); i++)
|
||||
if (bits[i])
|
||||
log("\t\t [%zu]\n", i);
|
||||
} else {
|
||||
log("\t\t FULL\n");
|
||||
}
|
||||
}
|
||||
bool is_set(size_t idx) const {
|
||||
if (all)
|
||||
return true;
|
||||
if (idx >= bits.size())
|
||||
return false;
|
||||
return bits[idx];
|
||||
}
|
||||
// TODO actually use this
|
||||
void compress(size_t size) {
|
||||
if (bits.size() < size)
|
||||
return;
|
||||
for (size_t i = 0; i < size; i++)
|
||||
if (!bits[i])
|
||||
return;
|
||||
bits.clear();
|
||||
bits.shrink_to_fit();
|
||||
all = true;
|
||||
}
|
||||
};
|
||||
|
||||
struct SdcObjects {
|
||||
enum CollectMode {
|
||||
// getter-side object tracking with minimal features
|
||||
SimpleGetter,
|
||||
// getter-side object tracking with everything
|
||||
FullGetter,
|
||||
// constraint-side tracking
|
||||
FullConstraint,
|
||||
} collect_mode;
|
||||
using CellPin = std::pair<Cell*, IdString>;
|
||||
Design* design;
|
||||
std::vector<std::pair<std::string, Wire*>> design_ports;
|
||||
std::vector<std::pair<std::string, Cell*>> design_cells;
|
||||
std::vector<std::pair<std::string, CellPin>> design_pins;
|
||||
std::vector<std::pair<std::string, Wire*>> design_nets;
|
||||
|
||||
using PortPattern = std::tuple<std::string, Wire*, BitSelection>;
|
||||
using PinPattern = std::tuple<std::string, SdcObjects::CellPin, BitSelection>;
|
||||
std::vector<std::vector<PortPattern>> resolved_port_pattern_sets;
|
||||
std::vector<std::vector<PinPattern>> resolved_pin_pattern_sets;
|
||||
// TODO
|
||||
|
||||
dict<std::pair<std::string, Wire*>, BitSelection> constrained_ports;
|
||||
pool<std::pair<std::string, Cell*>> constrained_cells;
|
||||
dict<std::pair<std::string, CellPin>, BitSelection> constrained_pins;
|
||||
dict<std::pair<std::string, Wire*>, BitSelection> constrained_nets;
|
||||
|
||||
void sniff_module(std::list<std::string>& hierarchy, Module* mod) {
|
||||
std::string prefix;
|
||||
for (auto mod_name : hierarchy) {
|
||||
if (prefix.length())
|
||||
prefix += "/"; // TODO seperator?
|
||||
prefix += mod_name;
|
||||
}
|
||||
|
||||
for (auto* wire : mod->wires()) {
|
||||
std::string name = wire->name.str();
|
||||
log_assert(name.length());
|
||||
// TODO: really skip internal wires?
|
||||
if (name[0] == '$')
|
||||
continue;
|
||||
name = name.substr(1);
|
||||
std::string path = prefix;
|
||||
if (path.length())
|
||||
path += "/";
|
||||
path += name;
|
||||
design_nets.push_back(std::make_pair(path, wire));
|
||||
}
|
||||
|
||||
for (auto* cell : mod->cells()) {
|
||||
std::string name = cell->name.str();
|
||||
// TODO: really skip internal cells?
|
||||
if (name[0] == '$')
|
||||
continue;
|
||||
name = name.substr(1);
|
||||
std::string path = prefix;
|
||||
if (path.length())
|
||||
path += "/";
|
||||
path += name;
|
||||
design_cells.push_back(std::make_pair(path, cell));
|
||||
for (auto pin : cell->connections()) {
|
||||
IdString pin_name = pin.first;
|
||||
std::string pin_name_sdc = path + "/" + pin.first.str().substr(1);
|
||||
design_pins.push_back(std::make_pair(pin_name_sdc, std::make_pair(cell, pin_name)));
|
||||
}
|
||||
if (auto sub_mod = mod->design->module(cell->type)) {
|
||||
hierarchy.push_back(name);
|
||||
sniff_module(hierarchy, sub_mod);
|
||||
hierarchy.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
SdcObjects(Design* design) : design(design) {
|
||||
Module* top = design->top_module();
|
||||
if (!top)
|
||||
log_error("Top module couldn't be determined. Check 'top' attribute usage");
|
||||
for (auto port : top->ports) {
|
||||
design_ports.push_back(std::make_pair(port.str().substr(1), top->wire(port)));
|
||||
}
|
||||
std::list<std::string> hierarchy{};
|
||||
sniff_module(hierarchy, top);
|
||||
}
|
||||
~SdcObjects() = default;
|
||||
|
||||
template <typename T, typename U>
|
||||
void build_normal_result(Tcl_Interp* interp, std::vector<std::tuple<std::string, T, BitSelection>>&& resolved, U& tgt, std::function<size_t(T&)> width, Tcl_Obj*& result) {
|
||||
if (!result)
|
||||
result = Tcl_NewListObj(resolved.size(), nullptr);
|
||||
for (auto [name, obj, matching_bits] : resolved) {
|
||||
for (size_t i = 0; i < width(obj); i++)
|
||||
if (matching_bits.is_set(i)) {
|
||||
Tcl_ListObjAppendElement(interp, result, Tcl_NewStringObj(name.c_str(), name.size()));
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
size_t node_count = get_node_count(interp);
|
||||
tgt.emplace_back(std::move(resolved));
|
||||
log("%zu %zu\n", node_count, tgt.size());
|
||||
log_assert(node_count == tgt.size());
|
||||
}
|
||||
template <typename T>
|
||||
void merge_as_constrained(std::vector<std::tuple<std::string, T, BitSelection>>&& resolved) {
|
||||
for (auto [name, obj, matching_bits] : resolved) {
|
||||
merge_or_init(std::make_pair(name, obj), constrained_pins, matching_bits);
|
||||
}
|
||||
}
|
||||
void dump() {
|
||||
std::sort(design_ports.begin(), design_ports.end());
|
||||
std::sort(design_cells.begin(), design_cells.end());
|
||||
std::sort(design_pins.begin(), design_pins.end());
|
||||
std::sort(design_nets.begin(), design_nets.end());
|
||||
constrained_ports.sort();
|
||||
constrained_cells.sort();
|
||||
constrained_pins.sort();
|
||||
constrained_nets.sort();
|
||||
// log("Design ports:\n");
|
||||
// for (auto name : design_ports) {
|
||||
// log("\t%s\n", name.c_str());
|
||||
// }
|
||||
// log("Design cells:\n");
|
||||
// for (auto [name, cell] : design_cells) {
|
||||
// (void)cell;
|
||||
// log("\t%s\n", name.c_str());
|
||||
// }
|
||||
// log("Design pins:\n");
|
||||
// for (auto [name, pin] : design_pins) {
|
||||
// (void)pin;
|
||||
// log("\t%s\n", name.c_str());
|
||||
// }
|
||||
// log("Design nets:\n");
|
||||
// for (auto [name, net] : design_nets) {
|
||||
// (void)net;
|
||||
// log("\t%s\n", name.c_str());
|
||||
// }
|
||||
// log("\n");
|
||||
log("Constrained ports:\n");
|
||||
for (auto [ref, bits] : constrained_ports) {
|
||||
auto [name, port] = ref;
|
||||
(void)port;
|
||||
log("\t%s\n", name.c_str());
|
||||
bits.dump();
|
||||
}
|
||||
log("Constrained cells:\n");
|
||||
for (auto& [name, cell] : constrained_cells) {
|
||||
(void)cell;
|
||||
log("\t%s\n", name.c_str());
|
||||
}
|
||||
log("Constrained pins:\n");
|
||||
for (auto& [ref, bits] : constrained_pins) {
|
||||
auto [name, pin] = ref;
|
||||
(void)pin;
|
||||
log("\t%s\n", name.c_str());
|
||||
bits.dump();
|
||||
}
|
||||
log("Constrained nets:\n");
|
||||
for (auto& [ref, bits] : constrained_nets) {
|
||||
auto [name, net] = ref;
|
||||
(void)net;
|
||||
log("\t%s\n", name.c_str());
|
||||
bits.dump();
|
||||
}
|
||||
log("\n");
|
||||
}
|
||||
|
||||
class KeepHierarchyWorker {
|
||||
std::unordered_set<Module*> tracked_modules = {};
|
||||
Design* design = nullptr;
|
||||
bool mark(Module* mod) {
|
||||
for (auto* cell : mod->cells()) {
|
||||
if (auto* submod = design->module(cell->type))
|
||||
if (mark(submod)) {
|
||||
mod->set_bool_attribute(ID::keep_hierarchy);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (tracked_modules.count(mod)) {
|
||||
mod->set_bool_attribute(ID::keep_hierarchy);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
public:
|
||||
KeepHierarchyWorker(SdcObjects* objects, Design* d) : design(d) {
|
||||
for (auto [ref, _] : objects->constrained_ports) {
|
||||
tracked_modules.insert(ref.second->module);
|
||||
}
|
||||
for (auto& [_, cell] : objects->constrained_cells) {
|
||||
tracked_modules.insert(cell->module);
|
||||
}
|
||||
for (auto& [ref, _] : objects->constrained_pins) {
|
||||
tracked_modules.insert(ref.second.first->module);
|
||||
}
|
||||
for (auto& [ref, _] : objects->constrained_nets) {
|
||||
tracked_modules.insert(ref.second->module);
|
||||
}
|
||||
log_debug("keep_hierarchy tracked modules:\n");
|
||||
for (auto* mod : tracked_modules)
|
||||
log_debug("\t%s\n", mod->name);
|
||||
}
|
||||
bool mark() {
|
||||
return mark(design->top_module());
|
||||
}
|
||||
};
|
||||
void keep_hierarchy() {
|
||||
(void)KeepHierarchyWorker(this, design).mark();
|
||||
}
|
||||
};
|
||||
|
||||
// TODO vectors
|
||||
// TODO cell arrays?
|
||||
struct MatchConfig {
|
||||
enum MatchMode {
|
||||
WILDCARD,
|
||||
REGEX,
|
||||
} match;
|
||||
bool match_case;
|
||||
enum HierMode {
|
||||
FLAT,
|
||||
TREE,
|
||||
} hier;
|
||||
MatchConfig(bool regexp_flag, bool nocase_flag, bool hierarchical_flag) :
|
||||
match(regexp_flag ? REGEX : WILDCARD),
|
||||
match_case(!nocase_flag),
|
||||
hier(hierarchical_flag ? FLAT : TREE) { }
|
||||
};
|
||||
|
||||
static std::pair<bool, BitSelection> matches(std::string name, const std::string& pat, const MatchConfig& config) {
|
||||
(void)config;
|
||||
bool got_bit_index = false;;
|
||||
int bit_idx;
|
||||
std::string pat_base = pat;
|
||||
size_t pos = pat.rfind('[');
|
||||
if (pos != std::string::npos) {
|
||||
got_bit_index = true;
|
||||
pat_base = pat.substr(0, pos);
|
||||
std::string bit_selector = pat.substr(pos + 1, pat.rfind(']') - pos - 1);
|
||||
for (auto c : bit_selector)
|
||||
if (!std::isdigit(c))
|
||||
log_error("Unsupported bit selector %s in SDC pattern %s\n",
|
||||
bit_selector.c_str(), pat.c_str());
|
||||
bit_idx = std::stoi(bit_selector);
|
||||
|
||||
}
|
||||
BitSelection bits = {};
|
||||
if (name == pat_base) {
|
||||
if (got_bit_index) {
|
||||
bits.set(bit_idx);
|
||||
return std::make_pair(true, bits);
|
||||
} else {
|
||||
bits.set_all();
|
||||
return std::make_pair(true, bits);
|
||||
|
||||
}
|
||||
} else {
|
||||
return std::make_pair(false, bits);
|
||||
}
|
||||
}
|
||||
|
||||
static int getter_graph_node(TclCall call) {
|
||||
// Insert -getter-validated as first argument for passing to unknown
|
||||
// to distinguish resolved and unknown getters.
|
||||
// For example, if call is ["get_foo", "-bar"]
|
||||
// then newCall is ["get_foo", "-getter-validated", "-bar"]
|
||||
Tcl_Obj* validity_tag = Tcl_NewStringObj("-getter-validated", -1);
|
||||
Tcl_IncrRefCount(validity_tag);
|
||||
std::vector<Tcl_Obj*> newObjv(call.objc + 1);
|
||||
log_assert(call.objc > 0);
|
||||
newObjv[0] = call.objv[0];
|
||||
newObjv[1] = validity_tag;
|
||||
for (int i = 1; i < call.objc; ++i) {
|
||||
newObjv[i + 1] = call.objv[i];
|
||||
}
|
||||
// Send the vector to the Tcl land
|
||||
Tcl_Obj** allocatedObjv = (Tcl_Obj**)Tcl_Alloc((call.objc + 1) * sizeof(Tcl_Obj*));
|
||||
for (int i = 0; i < call.objc + 1; ++i) {
|
||||
allocatedObjv[i] = newObjv[i];
|
||||
}
|
||||
TclCall newCall {
|
||||
.interp = call.interp,
|
||||
.objc = call.objc + 1,
|
||||
.objv = allocatedObjv
|
||||
};
|
||||
// Finally, redirect to unknown handler
|
||||
return redirect_unknown(newCall);
|
||||
}
|
||||
|
||||
static int redirect_unknown(TclCall call) {
|
||||
// TODO redirect to different command
|
||||
Tcl_Obj *newCmd = Tcl_NewStringObj("unknown", -1);
|
||||
auto newObjc = call.objc + 1;
|
||||
Tcl_Obj** newObjv = (Tcl_Obj**)Tcl_Alloc(newObjc * sizeof(Tcl_Obj*));
|
||||
newObjv[0] = newCmd;
|
||||
for (int i = 1; i < newObjc; i++) {
|
||||
newObjv[i] = call.objv[i - 1];
|
||||
}
|
||||
int result = Tcl_EvalObjv(call.interp, newObjc, newObjv, 0);
|
||||
Tcl_DecrRefCount(newCmd);
|
||||
Tcl_Free((char*) newObjv);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
struct SdcGraphNode {
|
||||
using Child = std::variant<SdcGraphNode*, std::string>;
|
||||
std::vector<Child> children;
|
||||
SdcGraphNode() = default;
|
||||
void addChild(SdcGraphNode* child) {
|
||||
children.push_back(child);
|
||||
}
|
||||
void addChild(std::string tcl_string) {
|
||||
children.push_back(tcl_string);
|
||||
}
|
||||
void dump(std::ostream& os) const {
|
||||
bool first = true;
|
||||
for (auto child : children) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
os << " ";
|
||||
}
|
||||
if (auto* s = std::get_if<std::string>(&child))
|
||||
os << *s;
|
||||
else if (SdcGraphNode*& c = *std::get_if<SdcGraphNode*>(&child)) {
|
||||
os << "[";
|
||||
c->dump(os);
|
||||
os << "]";
|
||||
} else {
|
||||
log_assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static size_t get_node_count(Tcl_Interp* interp) {
|
||||
const char* idx_raw = Tcl_GetVar(interp, "sdc_call_index", TCL_GLOBAL_ONLY);
|
||||
log_assert(idx_raw);
|
||||
std::string idx(idx_raw);
|
||||
for (auto c : idx)
|
||||
if (!std::isdigit(c))
|
||||
log_error("sdc_call_index non-numeric value %s\n", idx.c_str());
|
||||
return std::stoi(idx);
|
||||
}
|
||||
|
||||
std::vector<std::vector<std::string>> gather_nested_calls(Tcl_Interp* interp) {
|
||||
|
||||
Tcl_Obj* listObj = Tcl_GetVar2Ex(interp, "sdc_calls", nullptr, TCL_GLOBAL_ONLY);
|
||||
int listLength;
|
||||
|
||||
std::vector<std::vector<std::string>> sdc_calls;
|
||||
if (Tcl_ListObjLength(interp, listObj, &listLength) == TCL_OK) {
|
||||
for (int i = 0; i < listLength; i++) {
|
||||
Tcl_Obj* subListObj;
|
||||
std::vector<std::string> subList;
|
||||
if (Tcl_ListObjIndex(interp, listObj, i, &subListObj) != TCL_OK) {
|
||||
log_error("broken list of lists\n");
|
||||
}
|
||||
int subListLength;
|
||||
if (Tcl_ListObjLength(interp, subListObj, &subListLength) == TCL_OK) {
|
||||
// Valid list - extract elements
|
||||
for (int j = 0; j < subListLength; j++) {
|
||||
Tcl_Obj* elementObj;
|
||||
if (Tcl_ListObjIndex(interp, subListObj, j, &elementObj) == TCL_OK) {
|
||||
const char* elementStr = Tcl_GetString(elementObj);
|
||||
subList.push_back(std::string(elementStr));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Single element, not a list
|
||||
const char* elementStr = Tcl_GetString(subListObj);
|
||||
subList.push_back(std::string(elementStr));
|
||||
}
|
||||
sdc_calls.push_back(subList);
|
||||
}
|
||||
}
|
||||
log_assert(sdc_calls.size() == get_node_count(interp));
|
||||
return sdc_calls;
|
||||
}
|
||||
|
||||
std::vector<SdcGraphNode> build_graph(const std::vector<std::vector<std::string>>& sdc_calls) {
|
||||
size_t node_count = sdc_calls.size();
|
||||
std::vector<SdcGraphNode> graph(node_count);
|
||||
for (size_t i = 0; i < node_count; i++) {
|
||||
auto& new_node = graph[i];
|
||||
for (size_t j = 0; j < sdc_calls[i].size(); j++) {
|
||||
auto arg = sdc_calls[i][j];
|
||||
const std::string prefix = "YOSYS_SDC_MAGIC_NODE_";
|
||||
auto pos = arg.find(prefix);
|
||||
if (pos != std::string::npos) {
|
||||
std::string rest = arg.substr(pos + prefix.length());
|
||||
for (auto c : rest)
|
||||
if (!std::isdigit(c))
|
||||
log_error("weird thing %s in %s\n", rest.c_str(), arg.c_str());
|
||||
size_t arg_node_idx = std::stoi(rest);
|
||||
log_assert(arg_node_idx < graph.size());
|
||||
new_node.addChild(&graph[arg_node_idx]);
|
||||
} else {
|
||||
new_node.addChild(arg);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return graph;
|
||||
}
|
||||
|
||||
std::vector<bool> node_ownership(const std::vector<SdcGraphNode>& graph) {
|
||||
std::vector<bool> has_parent(graph.size());
|
||||
for (auto node : graph) {
|
||||
for (auto child : node.children) {
|
||||
if (SdcGraphNode** pp = std::get_if<SdcGraphNode*>(&child)) {
|
||||
size_t idx = std::distance(&graph.front(), (const SdcGraphNode*)*pp);
|
||||
log_assert(idx < has_parent.size());
|
||||
has_parent[idx] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return has_parent;
|
||||
}
|
||||
|
||||
void dump_sdc_graph(const std::vector<SdcGraphNode>& graph, const std::vector<bool>& has_parent) {
|
||||
for (size_t i = 0; i < graph.size(); i++) {
|
||||
if (!has_parent[i]) {
|
||||
graph[i].dump(std::cout);
|
||||
std::cout << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void inspect_globals(Tcl_Interp* interp, bool dump_mode) {
|
||||
std::vector<std::vector<std::string>> sdc_calls = gather_nested_calls(interp);
|
||||
std::vector<SdcGraphNode> graph = build_graph(sdc_calls);
|
||||
if (dump_mode)
|
||||
dump_sdc_graph(graph, node_ownership(graph));
|
||||
}
|
||||
|
||||
// patterns -> (pattern-object-bit)s
|
||||
template <typename T, typename U>
|
||||
std::vector<std::tuple<std::string, T, BitSelection>>
|
||||
find_matching(U objects, const MatchConfig& config, const std::vector<std::string> &patterns, const char* obj_type)
|
||||
{
|
||||
std::vector<std::tuple<std::string, T, BitSelection>> resolved;
|
||||
for (auto pat : patterns) {
|
||||
bool found = false;
|
||||
for (auto [name, obj] : objects) {
|
||||
auto [does_match, matching_bits] = matches(name, pat, config);
|
||||
if (does_match) {
|
||||
found = true;
|
||||
resolved.push_back(std::make_tuple(name, obj, matching_bits));
|
||||
// TODO add a continue statement, conditional on config
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
log_warning("No matches in design for %s %s\n", obj_type, pat.c_str());
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
struct TclOpts {
|
||||
const char* name;
|
||||
std::initializer_list<const char*> legals;
|
||||
TclOpts(const char* name, std::initializer_list<const char*> legals) : name(name), legals(legals) {}
|
||||
bool parse_opt(Tcl_Obj* obj, const char* opt_name) {
|
||||
char* arg = Tcl_GetString(obj);
|
||||
std::string expected = std::string("-") + opt_name;
|
||||
if (expected == arg) {
|
||||
if (!std::find_if(legals.begin(), legals.end(),
|
||||
[&opt_name](const char* str) { return opt_name == str; }))
|
||||
log_cmd_error("Illegal argument %s for %s.\n", expected.c_str(), name);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct GetterOpts : TclOpts {
|
||||
bool hierarchical_flag = false;
|
||||
bool regexp_flag = false;
|
||||
bool nocase_flag = false;
|
||||
std::string separator = "/";
|
||||
Tcl_Obj* of_objects = nullptr;
|
||||
std::vector<std::string> patterns = {};
|
||||
GetterOpts(const char* name, std::initializer_list<const char*> legals) : TclOpts(name, legals) {}
|
||||
template<typename T>
|
||||
bool parse_flag(Tcl_Obj* obj, const char* flag_name, T& flag_var) {
|
||||
bool ret = parse_opt(obj, flag_name);
|
||||
if (ret)
|
||||
flag_var = true;
|
||||
return ret;
|
||||
}
|
||||
void parse(int objc, Tcl_Obj* const objv[]) {
|
||||
int i = 1;
|
||||
for (; i < objc; i++) {
|
||||
if (parse_flag(objv[i], "hierarchical", hierarchical_flag)) continue;
|
||||
if (parse_flag(objv[i], "hier", hierarchical_flag)) continue;
|
||||
if (parse_flag(objv[i], "regexp", regexp_flag)) continue;
|
||||
if (parse_flag(objv[i], "nocase", nocase_flag)) continue;
|
||||
if (parse_opt(objv[i], "hsc")) {
|
||||
log_assert(i + 1 < objc);
|
||||
separator = Tcl_GetString(objv[++i]);
|
||||
continue;
|
||||
}
|
||||
if (parse_opt(objv[i], "of_objects")) {
|
||||
log_assert(i + 1 < objc);
|
||||
of_objects = objv[++i];
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
for (; i < objc; i++) {
|
||||
patterns.push_back(Tcl_GetString(objv[i]));
|
||||
}
|
||||
};
|
||||
void check_simple() {
|
||||
if (regexp_flag || hierarchical_flag || nocase_flag || separator != "/" || of_objects) {
|
||||
log_error("%s got unexpected flags in simple mode\n", name);
|
||||
}
|
||||
if (patterns.size() != 1)
|
||||
log_error("%s got unexpected number of patterns in simple mode: %zu\n", name, patterns.size());
|
||||
}
|
||||
void check_simple_sep() {
|
||||
if (separator != "/")
|
||||
log_error("Only '/' accepted as separator");
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void merge_or_init(const T& key, dict<T, BitSelection>& dst, const BitSelection& src) {
|
||||
if (dst.count(key) == 0) {
|
||||
dst[key] = src;
|
||||
} else {
|
||||
dst[key].merge(src);
|
||||
}
|
||||
}
|
||||
|
||||
static int sdc_get_pins_cmd(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj* const objv[])
|
||||
{
|
||||
auto* objects = (SdcObjects*)data;
|
||||
GetterOpts opts("get_pins", {"hierarchical", "hier", "regexp", "nocase", "hsc", "of_objects"});
|
||||
opts.parse(objc, objv);
|
||||
if (objects->collect_mode == SdcObjects::CollectMode::SimpleGetter)
|
||||
opts.check_simple();
|
||||
opts.check_simple_sep();
|
||||
|
||||
MatchConfig config(opts.regexp_flag, opts.nocase_flag, opts.hierarchical_flag);
|
||||
std::vector<std::tuple<std::string, SdcObjects::CellPin, BitSelection>> resolved;
|
||||
const auto& pins = objects->design_pins;
|
||||
resolved = find_matching<SdcObjects::CellPin, decltype(pins)>(pins, config, opts.patterns, "pin");
|
||||
|
||||
return getter_graph_node(TclCall{interp, objc, objv});
|
||||
}
|
||||
|
||||
static int sdc_get_ports_cmd(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj* const objv[])
|
||||
{
|
||||
auto* objects = (SdcObjects*)data;
|
||||
GetterOpts opts("get_ports", {"regexp", "nocase"});
|
||||
opts.parse(objc, objv);
|
||||
if (objects->collect_mode == SdcObjects::CollectMode::SimpleGetter)
|
||||
opts.check_simple();
|
||||
|
||||
MatchConfig config(opts.regexp_flag, opts.nocase_flag, false);
|
||||
std::vector<std::tuple<std::string, Wire*, BitSelection>> resolved;
|
||||
const auto& ports = objects->design_ports;
|
||||
resolved = find_matching<Wire*, decltype(ports)>(ports, config, opts.patterns, "port");
|
||||
|
||||
for (auto [name, wire, matching_bits] : resolved) {
|
||||
if (objects->collect_mode != SdcObjects::CollectMode::FullConstraint)
|
||||
merge_or_init(std::make_pair(name, wire), objects->constrained_ports, matching_bits);
|
||||
}
|
||||
|
||||
return getter_graph_node(TclCall{interp, objc, objv});
|
||||
}
|
||||
|
||||
static int sdc_get_nets_cmd(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj* const objv[])
|
||||
{
|
||||
auto* objects = (SdcObjects*)data;
|
||||
GetterOpts opts("get_nets", {"hierarchical", "hier", "regexp", "nocase", "hsc", "of_objects"});
|
||||
opts.parse(objc, objv);
|
||||
if (objects->collect_mode == SdcObjects::CollectMode::SimpleGetter)
|
||||
opts.check_simple();
|
||||
|
||||
MatchConfig config(opts.regexp_flag, opts.nocase_flag, false);
|
||||
std::vector<std::tuple<std::string, Wire*, BitSelection>> resolved;
|
||||
const auto& ports = objects->design_nets;
|
||||
resolved = find_matching<Wire*, decltype(ports)>(ports, config, opts.patterns, "net");
|
||||
|
||||
for (auto [name, wire, matching_bits] : resolved) {
|
||||
if (objects->collect_mode != SdcObjects::CollectMode::FullConstraint)
|
||||
merge_or_init(std::make_pair(name, wire), objects->constrained_nets, matching_bits);
|
||||
}
|
||||
|
||||
return getter_graph_node(TclCall{interp, objc, objv});
|
||||
}
|
||||
|
||||
class SDCInterpreter
|
||||
{
|
||||
private:
|
||||
Tcl_Interp* interp = nullptr;
|
||||
public:
|
||||
std::unique_ptr<SdcObjects> objects;
|
||||
~SDCInterpreter() {
|
||||
if (interp)
|
||||
Tcl_DeleteInterp(interp);
|
||||
}
|
||||
static SDCInterpreter& get() {
|
||||
static SDCInterpreter instance;
|
||||
return instance;
|
||||
}
|
||||
Tcl_Interp* fresh_interp(Design* design) {
|
||||
if (interp)
|
||||
Tcl_DeleteInterp(interp);
|
||||
|
||||
interp = Tcl_CreateInterp();
|
||||
if (Tcl_Init(interp)!=TCL_OK)
|
||||
log_error("Tcl_Init() call failed - %s\n",Tcl_ErrnoMsg(Tcl_GetErrno()));
|
||||
|
||||
objects = std::make_unique<SdcObjects>(design);
|
||||
objects->collect_mode = SdcObjects::CollectMode::SimpleGetter;
|
||||
Tcl_CreateObjCommand(interp, "get_pins", sdc_get_pins_cmd, (ClientData) objects.get(), NULL);
|
||||
Tcl_CreateObjCommand(interp, "get_nets", sdc_get_nets_cmd, (ClientData) objects.get(), NULL);
|
||||
Tcl_CreateObjCommand(interp, "get_ports", sdc_get_ports_cmd, (ClientData) objects.get(), NULL);
|
||||
return interp;
|
||||
}
|
||||
};
|
||||
|
||||
// Also see TclPass
|
||||
struct SdcPass : public Pass {
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" sdc [options] file\n");
|
||||
log("\n");
|
||||
log("Read the SDC file for the current design.\n");
|
||||
log("\n");
|
||||
log(" -dump\n");
|
||||
log(" Dump the referenced design objects.\n");
|
||||
log("\n");
|
||||
log(" -dump-graph\n");
|
||||
log(" Dump the uninterpreted call graph.\n");
|
||||
log("\n");
|
||||
log(" -keep_hierarchy\n");
|
||||
log(" Add keep_hierarchy attributes while retaining SDC validity.\n");
|
||||
log("\n");
|
||||
}
|
||||
SdcPass() : Pass("sdc", "Read an SDC file") { }
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override {
|
||||
log_header(design, "Executing SDC pass.\n");
|
||||
log_experimental("sdc");
|
||||
size_t argidx;
|
||||
bool dump_mode = false;
|
||||
bool dump_graph_mode = false;
|
||||
bool keep_hierarchy_mode = false;
|
||||
std::vector<std::string> stubs_paths;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
if (args[argidx] == "-dump") {
|
||||
dump_mode = true;
|
||||
continue;
|
||||
} else if (args[argidx] == "-dump-graph") {
|
||||
dump_graph_mode = true;
|
||||
continue;
|
||||
} else if (args[argidx] == "-keep_hierarchy") {
|
||||
keep_hierarchy_mode = true;
|
||||
continue;
|
||||
} else if (args[argidx] == "-stubs" && argidx+1 < args.size()) {
|
||||
stubs_paths.push_back(args[++argidx]);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (argidx >= args.size())
|
||||
log_cmd_error("Missing SDC file.\n");
|
||||
|
||||
std::string sdc_path = args[argidx++];
|
||||
if (argidx < args.size())
|
||||
log_cmd_error("Unexpected extra positional argument %s after SDC file %s.\n", args[argidx], sdc_path);
|
||||
SDCInterpreter& sdc = SDCInterpreter::get();
|
||||
Tcl_Interp *interp = sdc.fresh_interp(design);
|
||||
Tcl_Preserve(interp);
|
||||
std::string stub_path = "+/sdc/graph-stubs.sdc";
|
||||
rewrite_filename(stub_path);
|
||||
if (Tcl_EvalFile(interp, stub_path.c_str()) != TCL_OK)
|
||||
log_cmd_error("SDC interpreter returned an error in stub preamble file: %s\n", Tcl_GetStringResult(interp));
|
||||
for (auto path : stubs_paths)
|
||||
if (Tcl_EvalFile(interp, path.c_str()) != TCL_OK)
|
||||
log_cmd_error("SDC interpreter returned an error in OpenSTA stub file %s: %s\n", path.c_str(), Tcl_GetStringResult(interp));
|
||||
if (Tcl_EvalFile(interp, sdc_path.c_str()) != TCL_OK)
|
||||
log_cmd_error("SDC interpreter returned an error: %s\n", Tcl_GetStringResult(interp));
|
||||
if (dump_mode)
|
||||
sdc.objects->dump();
|
||||
if (keep_hierarchy_mode)
|
||||
sdc.objects->keep_hierarchy();
|
||||
inspect_globals(interp, dump_graph_mode);
|
||||
Tcl_Release(interp);
|
||||
}
|
||||
} SdcPass;
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
#endif
|
||||
|
|
@ -193,7 +193,7 @@ pyosys_headers = [
|
|||
),
|
||||
PyosysClass("SigChunk"),
|
||||
PyosysClass("SigBit", hash_expr="s"),
|
||||
PyosysClass("SigSpec", hash_expr="s", denylist={"chunks"}),
|
||||
PyosysClass("SigSpec", hash_expr="s", denylist=frozenset({"chunks"})),
|
||||
PyosysClass(
|
||||
"Cell",
|
||||
ref_only=True,
|
||||
|
|
@ -539,6 +539,8 @@ class PyosysWrapperGenerator(object):
|
|||
python_name_override = "__ne__"
|
||||
elif function.operator == "<":
|
||||
python_name_override = "__lt__"
|
||||
elif function.operator == "[]" and function.const:
|
||||
python_name_override = "__getitem__"
|
||||
else:
|
||||
return
|
||||
|
||||
|
|
@ -599,7 +601,10 @@ class PyosysWrapperGenerator(object):
|
|||
# care
|
||||
return
|
||||
|
||||
has_containers = self.register_containers(field)
|
||||
self.register_containers(field)
|
||||
rvp = "py::return_value_policy::copy"
|
||||
if isinstance(field.type, Pointer):
|
||||
rvp = "py::return_value_policy::reference_internal"
|
||||
|
||||
definition_fn = f"def_{'readonly' if field.type.const else 'readwrite'}"
|
||||
if field.static:
|
||||
|
|
@ -611,7 +616,7 @@ class PyosysWrapperGenerator(object):
|
|||
f'"{field_python_basename}"',
|
||||
f"&{metadata.name}::{field.name}",
|
||||
]
|
||||
def_args.append("py::return_value_policy::copy")
|
||||
def_args.append(rvp)
|
||||
print(
|
||||
f"\t\t\t.{definition_fn}({', '.join(def_args)})",
|
||||
file=self.f,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,128 @@
|
|||
#include "kernel/rtlil.h"
|
||||
#include "kernel/log.h"
|
||||
#include "techlibs/common/opensta.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
#if !defined(YOSYS_DISABLE_SPAWN)
|
||||
struct OpenstaPass : public Pass
|
||||
{
|
||||
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");
|
||||
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 <command>\n");
|
||||
log(" use <command> to run OpenSTA instead of \"%s\"\n", default_opensta_cmd);
|
||||
log("\n");
|
||||
log(" -sdc-in <filename>\n");
|
||||
log(" expand SDC input file <filename>\n");
|
||||
log("\n");
|
||||
log(" -sdc-out <filename>\n");
|
||||
log(" expand SDC file to output file <filename>\n");
|
||||
log("\n");
|
||||
log(" -nocleanup\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
}
|
||||
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
string run_from, run_to;
|
||||
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;
|
||||
bool cleanup = design->scratchpad_get_bool("opensta.cleanup", true);
|
||||
|
||||
log_header(design, "Executing OPENSTA pass.\n");
|
||||
log_experimental("opensta");
|
||||
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;
|
||||
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());
|
||||
};
|
||||
int ret = run_command(command, process_line);
|
||||
if (ret)
|
||||
log_error("OpenSTA returned %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
|
||||
|
|
@ -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 */
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
#include "kernel/rtlil.h"
|
||||
#include "kernel/log.h"
|
||||
#include "techlibs/common/opensta.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct SdcExpandPass : public ScriptPass
|
||||
{
|
||||
SdcExpandPass() : ScriptPass("sdc_expand", "expand SDC design with opensta") { }
|
||||
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" sdc_expand [options]\n");
|
||||
log("\n");
|
||||
log("Expand SDC file with opensta based on arbitrary current design.\n");
|
||||
log("Changes the design but only temporarily.\n");
|
||||
log("\n");
|
||||
log(" -exe <command>\n");
|
||||
log(" use <command> to run OpenSTA instead of \"%s\"\n", default_opensta_cmd);
|
||||
log("\n");
|
||||
log(" -sdc-in <filename>\n");
|
||||
log(" expand SDC file <filename>\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<std::string> 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);
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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())
|
||||
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;
|
||||
|
||||
run("design -save pre_expand");
|
||||
run("proc");
|
||||
run("memory");
|
||||
// run("dfflegalize -cell $dff");
|
||||
|
||||
if (help_mode) {
|
||||
tempdir_name = "<tmp_dir>";
|
||||
} 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);
|
||||
}
|
||||
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");
|
||||
|
||||
std::string liberty_path = tempdir_name + "/yosys.lib";
|
||||
run("icell_liberty " + liberty_path);
|
||||
|
||||
std::string opensta_pass_call = "opensta ";
|
||||
if (opensta_exe.length()) {
|
||||
opensta_pass_call += "-exe ";
|
||||
opensta_pass_call += help_mode ? "<exe>" : opensta_exe;
|
||||
}
|
||||
opensta_pass_call += " -sdc-in ";
|
||||
opensta_pass_call += help_mode ? "<sdc-in>" : sdc_filename;
|
||||
opensta_pass_call += " -sdc-out ";
|
||||
opensta_pass_call += help_mode ? "<sdc-out>" : sdc_expanded_filename;
|
||||
opensta_pass_call += " -verilog ";
|
||||
opensta_pass_call += help_mode ? "<verilog>" : verilog_path;
|
||||
opensta_pass_call += " -liberty ";
|
||||
opensta_pass_call += help_mode ? "<tmp_dir>/yosys.lib" : liberty_path;
|
||||
if (!cleanup)
|
||||
opensta_pass_call += " -nocleanup";
|
||||
run(opensta_pass_call);
|
||||
|
||||
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
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
import os
|
||||
from pyosys import libyosys as ys
|
||||
|
||||
__dir__ = os.path.dirname(os.path.abspath(__file__))
|
||||
add_sub = os.path.join(__dir__, "..", "common", "add_sub.v")
|
||||
|
||||
base = ys.Design()
|
||||
ys.run_pass(f"read_verilog {add_sub}", base)
|
||||
ys.run_pass("hierarchy -top top", base)
|
||||
ys.run_pass("proc", base)
|
||||
ys.run_pass("equiv_opt -assert -map +/ecp5/cells_sim.v synth_ecp5", base)
|
||||
|
||||
postopt = ys.Design()
|
||||
ys.run_pass("design -load postopt", postopt)
|
||||
ys.run_pass("cd top", postopt)
|
||||
ys.run_pass("select -assert-min 25 t:LUT4", postopt)
|
||||
ys.run_pass("select -assert-max 26 t:LUT4", postopt)
|
||||
ys.run_pass("select -assert-count 10 t:PFUMX", postopt)
|
||||
ys.run_pass("select -assert-count 6 t:L6MUX21", postopt)
|
||||
ys.run_pass("select -assert-none t:LUT4 t:PFUMX t:L6MUX21 %% t:* %D", postopt)
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
from pathlib import Path
|
||||
from pyosys import libyosys as ys
|
||||
|
||||
|
||||
__file_dir__ = Path(__file__).absolute().parent
|
||||
add_sub = __file_dir__.parent / "arch" / "common" / "add_sub.v"
|
||||
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
from pyosys import libyosys as ys
|
||||
from pathlib import Path
|
||||
|
||||
__file_dir__ = Path(__file__).absolute().parent
|
||||
|
||||
def _dump_sigbit(bit):
|
||||
if bit.is_wire():
|
||||
if bit.wire.width == 1:
|
||||
return bit.wire.name.str()
|
||||
else:
|
||||
return f"{bit.wire.name} [{bit.offset}]"
|
||||
else:
|
||||
if bit.data == ys.State.S1:
|
||||
return 1
|
||||
elif bit.data == ys.State.S0:
|
||||
return 0
|
||||
else:
|
||||
assert "unknown constants not supported"
|
||||
|
||||
d = ys.Design()
|
||||
|
||||
ys.run_pass(f"read_verilog {__file_dir__ / 'spm.cut.v.gz'}", d)
|
||||
ys.run_pass(f"hierarchy -top spm", d)
|
||||
module = d.module(r"\spm")
|
||||
for conn_from, conn_to in module.connections_:
|
||||
for bit_from, bit_to in zip(conn_from, conn_to):
|
||||
print(f"assign {_dump_sigbit(bit_from)} = {_dump_sigbit(bit_to)};")
|
||||
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
###############################################################################
|
||||
# Created by write_sdc
|
||||
# Fri Oct 3 11:26:00 2025
|
||||
###############################################################################
|
||||
current_design wrapper
|
||||
###############################################################################
|
||||
# Timing Constraints
|
||||
###############################################################################
|
||||
create_clock -name this_clk -period 1.0000 [get_ports {clk}]
|
||||
create_clock -name that_clk -period 2.0000
|
||||
create_clock -name another_clk -period 2.0000 \
|
||||
[list [get_ports {A[0]}]\
|
||||
[get_ports {A[1]}]\
|
||||
[get_ports {A[2]}]\
|
||||
[get_ports {A[3]}]\
|
||||
[get_ports {A[4]}]\
|
||||
[get_ports {A[5]}]\
|
||||
[get_ports {A[6]}]\
|
||||
[get_ports {A[7]}]\
|
||||
[get_ports {B[0]}]\
|
||||
[get_ports {B[1]}]\
|
||||
[get_ports {B[2]}]\
|
||||
[get_ports {B[3]}]\
|
||||
[get_ports {B[4]}]\
|
||||
[get_ports {B[5]}]\
|
||||
[get_ports {B[6]}]\
|
||||
[get_ports {B[7]}]]
|
||||
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -rise -min -add_delay [get_ports {A[0]}]
|
||||
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -fall -min -add_delay [get_ports {A[0]}]
|
||||
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -rise -min -add_delay [get_ports {A[1]}]
|
||||
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -fall -min -add_delay [get_ports {A[1]}]
|
||||
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -rise -min -add_delay [get_ports {A[2]}]
|
||||
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -fall -min -add_delay [get_ports {A[2]}]
|
||||
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -rise -min -add_delay [get_ports {A[3]}]
|
||||
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -fall -min -add_delay [get_ports {A[3]}]
|
||||
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -rise -min -add_delay [get_ports {A[4]}]
|
||||
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -fall -min -add_delay [get_ports {A[4]}]
|
||||
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -rise -min -add_delay [get_ports {A[5]}]
|
||||
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -fall -min -add_delay [get_ports {A[5]}]
|
||||
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -rise -min -add_delay [get_ports {A[6]}]
|
||||
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -fall -min -add_delay [get_ports {A[6]}]
|
||||
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -rise -min -add_delay [get_ports {A[7]}]
|
||||
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -fall -min -add_delay [get_ports {A[7]}]
|
||||
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -rise -min -add_delay [get_ports {B[0]}]
|
||||
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -fall -min -add_delay [get_ports {B[0]}]
|
||||
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -rise -min -add_delay [get_ports {B[1]}]
|
||||
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -fall -min -add_delay [get_ports {B[1]}]
|
||||
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -rise -min -add_delay [get_ports {B[2]}]
|
||||
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -fall -min -add_delay [get_ports {B[2]}]
|
||||
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -rise -min -add_delay [get_ports {B[3]}]
|
||||
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -fall -min -add_delay [get_ports {B[3]}]
|
||||
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -rise -min -add_delay [get_ports {B[4]}]
|
||||
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -fall -min -add_delay [get_ports {B[4]}]
|
||||
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -rise -min -add_delay [get_ports {B[5]}]
|
||||
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -fall -min -add_delay [get_ports {B[5]}]
|
||||
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -rise -min -add_delay [get_ports {B[6]}]
|
||||
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -fall -min -add_delay [get_ports {B[6]}]
|
||||
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -rise -min -add_delay [get_ports {B[7]}]
|
||||
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -fall -min -add_delay [get_ports {B[7]}]
|
||||
group_path -name operation_group\
|
||||
-through [list [get_nets {alu/operation[0]}]\
|
||||
[get_nets {alu/operation[1]}]\
|
||||
[get_nets {alu/operation[2]}]\
|
||||
[get_nets {alu/operation[3]}]]
|
||||
###############################################################################
|
||||
# Environment
|
||||
###############################################################################
|
||||
###############################################################################
|
||||
# Design Rules
|
||||
###############################################################################
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
module adder(
|
||||
input [7:0] a, input [7:0] b, output [7:0] y
|
||||
);
|
||||
assign y = a + b;
|
||||
endmodule
|
||||
|
||||
module wrapper(
|
||||
input clk,
|
||||
input [7:0] A,
|
||||
input [7:0] B,
|
||||
input [3:0] op,
|
||||
output reg [7:0] result
|
||||
);
|
||||
wire CF, ZF, SF;
|
||||
alu alu(
|
||||
.clk(clk),
|
||||
.A(A),
|
||||
.B(B),
|
||||
.operation(op),
|
||||
.result(result),
|
||||
.CF(CF),
|
||||
.ZF(ZF),
|
||||
.SF(SF)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module alu(
|
||||
input clk,
|
||||
input [7:0] A,
|
||||
input [7:0] B,
|
||||
input [3:0] operation,
|
||||
output reg [7:0] result,
|
||||
output reg CF,
|
||||
output reg ZF,
|
||||
output reg SF
|
||||
);
|
||||
|
||||
localparam ALU_OP_ADD /* verilator public_flat */ = 4'b0000;
|
||||
localparam ALU_OP_SUB /* verilator public_flat */ = 4'b0001;
|
||||
|
||||
reg [8:0] tmp;
|
||||
reg [7:0] added;
|
||||
|
||||
adder adder(.a(A), .b(B), .y(added));
|
||||
|
||||
always @(posedge clk)
|
||||
begin
|
||||
case (operation)
|
||||
ALU_OP_ADD :
|
||||
tmp = added;
|
||||
ALU_OP_SUB :
|
||||
tmp = A - B;
|
||||
endcase
|
||||
|
||||
CF <= tmp[8];
|
||||
ZF <= tmp[7:0] == 0;
|
||||
SF <= tmp[7];
|
||||
|
||||
result <= tmp[7:0];
|
||||
end
|
||||
endmodule
|
||||
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
read_verilog alu_sub.v
|
||||
proc
|
||||
hierarchy -auto-top
|
||||
|
||||
select -assert-mod-count 1 adder
|
||||
select -assert-mod-count 1 wrapper
|
||||
select -assert-mod-count 1 alu
|
||||
|
||||
sdc -keep_hierarchy alu_sub.sdc
|
||||
flatten
|
||||
|
||||
select -assert-mod-count 0 adder
|
||||
select -assert-mod-count 1 wrapper
|
||||
select -assert-mod-count 1 alu
|
||||
|
|
@ -0,0 +1 @@
|
|||
get_foo -bar 1
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/env bash
|
||||
set -eu
|
||||
source ../gen-tests-makefile.sh
|
||||
generate_mk --yosys-scripts --bash
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
puts "This should print something:"
|
||||
puts [get_ports {A[0]}]
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
../../yosys -p 'read_verilog alu_sub.v; proc; hierarchy -auto-top; sdc side-effects.sdc' | grep 'This should print something:
|
||||
YOSYS_SDC_MAGIC_NODE_0'
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
! ../../yosys -p 'read_verilog alu_sub.v; proc; hierarchy -auto-top; sdc get_foo.sdc' 2>&1 | grep 'Unknown getter'
|
||||
Loading…
Reference in New Issue