diff --git a/kernel/celltypes.h b/kernel/celltypes.h index 3167a9addf9..f59469d1a85 100644 --- a/kernel/celltypes.h +++ b/kernel/celltypes.h @@ -28,9 +28,25 @@ struct CellType { RTLIL::IdString type; pool inputs, outputs; - bool is_evaluable; - bool is_combinatorial; - bool is_synthesizable; + // Cell is defined in celltypes.h, as opposed to design-specific + bool is_internal; + // Cell can be handled by CellTypes::eval() + bool is_evaluable = false; + // Cell has no state; outputs are determined solely by inputs + bool is_combinatorial = false; + // Cell is able to be synthesized + bool is_synthesizable = false; + // Cell is built-in memory logic, includes flip-flops and latches, but not complex + // cells like $mem + bool is_builtin_ff = false; + // Cell is non-synthesizable, but used for formal verification + bool is_formal = false; + // Cell is intended for internal Yosys use, containing informational metadata and + // shouldn't be automatically cleaned; currently only used for $scopeinfo + bool is_metainfo = false; + // Non-synthesizable cell with effects that mean it shouldn't be automatically + // cleaned, e.g. $print + bool has_effects = false; }; struct CellTypes @@ -58,12 +74,60 @@ struct CellTypes setup_stdcells_mem(); } - void setup_type(RTLIL::IdString type, const pool &inputs, const pool &outputs, bool is_evaluable = false, bool is_combinatorial = false, bool is_synthesizable = false) + void setup_type(RTLIL::IdString type, const pool &inputs, const pool &outputs) { - CellType ct = {type, inputs, outputs, is_evaluable, is_combinatorial, is_synthesizable}; + CellType ct = { type, inputs, outputs, false }; cell_types[ct.type] = ct; } + // Setup internal cell type with no other default properties + void setup_internal_type(RTLIL::IdString type, const pool &inputs, const pool &outputs, + bool is_combinatorial = false, bool is_evaluable = false, + bool is_synthesizable = false, bool is_builtin_ff = false, bool is_formal = false, + bool is_metainfo = false, bool has_effects = false) + { + CellType ct = { + type, inputs, outputs, true, + is_evaluable, is_combinatorial, + is_synthesizable, is_builtin_ff, is_formal, + is_metainfo, has_effects, + }; + cell_types[ct.type] = ct; + } + + // Setup combinatorial cell type which is synthesizable and evaluable (by default) + void setup_comb_type(RTLIL::IdString type, const pool &inputs, const pool &outputs, + bool is_evaluable = true) + { + setup_internal_type(type, inputs, outputs, true, is_evaluable, true); + } + + // Setup builtin ff cell type which is synthesizable + void setup_ff_type(RTLIL::IdString type, const pool &inputs, const pool &outputs) + { + setup_internal_type(type, inputs, outputs, false, false, true, true); + } + + // Setup formal cell type which may be combinatorial, and may have effects + void setup_formal_type(RTLIL::IdString type, const pool &inputs, const pool &outputs, + bool is_combinatorial = false) + { + setup_internal_type(type, inputs, outputs, is_combinatorial, false, false, false, true); + } + + // Setup cell type which has effects, and may be formal + void setup_effects_type(RTLIL::IdString type, const pool &inputs, const pool &outputs, + bool is_formal = false) + { + setup_internal_type(type, inputs, outputs, false, false, false, false, is_formal, false, true); + } + + // Setup meta-info cell type + void setup_metainfo_type(RTLIL::IdString type, const pool &inputs, const pool &outputs) + { + setup_internal_type(type, inputs, outputs, false, false, false, false, false, true); + } + void setup_module(RTLIL::Module *module) { pool inputs, outputs; @@ -87,30 +151,40 @@ struct CellTypes { setup_internals_eval(); - setup_type(ID($tribuf), {ID::A, ID::EN}, {ID::Y}, true); - - setup_type(ID($assert), {ID::A, ID::EN}, pool(), true); - setup_type(ID($assume), {ID::A, ID::EN}, pool(), true); - setup_type(ID($live), {ID::A, ID::EN}, pool(), true); - setup_type(ID($fair), {ID::A, ID::EN}, pool(), true); - setup_type(ID($cover), {ID::A, ID::EN}, pool(), true); - setup_type(ID($initstate), pool(), {ID::Y}, true); - setup_type(ID($anyconst), pool(), {ID::Y}, true); - setup_type(ID($anyseq), pool(), {ID::Y}, true); - setup_type(ID($allconst), pool(), {ID::Y}, true); - setup_type(ID($allseq), pool(), {ID::Y}, true); - setup_type(ID($equiv), {ID::A, ID::B}, {ID::Y}, true); - setup_type(ID($specify2), {ID::EN, ID::SRC, ID::DST}, pool(), true); - setup_type(ID($specify3), {ID::EN, ID::SRC, ID::DST, ID::DAT}, pool(), true); - setup_type(ID($specrule), {ID::EN_SRC, ID::EN_DST, ID::SRC, ID::DST}, pool(), true); - setup_type(ID($print), {ID::EN, ID::ARGS, ID::TRG}, pool()); - setup_type(ID($check), {ID::A, ID::EN, ID::ARGS, ID::TRG}, pool()); - setup_type(ID($set_tag), {ID::A, ID::SET, ID::CLR}, {ID::Y}); - setup_type(ID($get_tag), {ID::A}, {ID::Y}); - setup_type(ID($overwrite_tag), {ID::A, ID::SET, ID::CLR}, pool()); - setup_type(ID($original_tag), {ID::A}, {ID::Y}); - setup_type(ID($future_ff), {ID::A}, {ID::Y}); - setup_type(ID($scopeinfo), {}, {}); + // synthesizable + setup_comb_type(ID($tribuf), {ID::A, ID::EN}, {ID::Y}, false); + + // combinatorial formal + setup_formal_type(ID($assert), {ID::A, ID::EN}, pool(), true); + setup_formal_type(ID($assume), {ID::A, ID::EN}, pool(), true); + setup_formal_type(ID($live), {ID::A, ID::EN}, pool(), true); + setup_formal_type(ID($fair), {ID::A, ID::EN}, pool(), true); + setup_formal_type(ID($cover), {ID::A, ID::EN}, pool(), true); + setup_formal_type(ID($initstate), pool(), {ID::Y}, true); + setup_formal_type(ID($anyconst), pool(), {ID::Y}, true); + setup_formal_type(ID($anyseq), pool(), {ID::Y}, true); + setup_formal_type(ID($allconst), pool(), {ID::Y}, true); + setup_formal_type(ID($allseq), pool(), {ID::Y}, true); + setup_formal_type(ID($equiv), {ID::A, ID::B}, {ID::Y}, true); + + // combinatorial non-synthesizable + setup_internal_type(ID($specify2), {ID::EN, ID::SRC, ID::DST}, pool(), true); + setup_internal_type(ID($specify3), {ID::EN, ID::SRC, ID::DST, ID::DAT}, pool(), true); + setup_internal_type(ID($specrule), {ID::EN_SRC, ID::EN_DST, ID::SRC, ID::DST}, pool(), true); + + // non-combinatorial formal + setup_formal_type(ID($set_tag), {ID::A, ID::SET, ID::CLR}, {ID::Y}); + setup_formal_type(ID($get_tag), {ID::A}, {ID::Y}); + setup_formal_type(ID($overwrite_tag), {ID::A, ID::SET, ID::CLR}, pool()); + setup_formal_type(ID($original_tag), {ID::A}, {ID::Y}); + setup_formal_type(ID($future_ff), {ID::A}, {ID::Y}); + + // has effects + setup_effects_type(ID($print), {ID::EN, ID::ARGS, ID::TRG}, pool()); + setup_effects_type(ID($check), {ID::A, ID::EN, ID::ARGS, ID::TRG}, pool(), true); + + // meta-info + setup_metainfo_type(ID($scopeinfo), {}, {}); } void setup_internals_eval() @@ -131,91 +205,91 @@ struct CellTypes }; for (auto type : unary_ops) - setup_type(type, {ID::A}, {ID::Y}, true); + setup_comb_type(type, {ID::A}, {ID::Y}); for (auto type : binary_ops) - setup_type(type, {ID::A, ID::B}, {ID::Y}, true); + setup_comb_type(type, {ID::A, ID::B}, {ID::Y}); for (auto type : std::vector({ID($mux), ID($pmux), ID($bwmux)})) - setup_type(type, {ID::A, ID::B, ID::S}, {ID::Y}, true); + setup_comb_type(type, {ID::A, ID::B, ID::S}, {ID::Y}); for (auto type : std::vector({ID($bmux), ID($demux)})) - setup_type(type, {ID::A, ID::S}, {ID::Y}, true); + setup_comb_type(type, {ID::A, ID::S}, {ID::Y}); - setup_type(ID($lcu), {ID::P, ID::G, ID::CI}, {ID::CO}, true); - setup_type(ID($alu), {ID::A, ID::B, ID::CI, ID::BI}, {ID::X, ID::Y, ID::CO}, true); - setup_type(ID($fa), {ID::A, ID::B, ID::C}, {ID::X, ID::Y}, true); + setup_comb_type(ID($lcu), {ID::P, ID::G, ID::CI}, {ID::CO}); + setup_comb_type(ID($alu), {ID::A, ID::B, ID::CI, ID::BI}, {ID::X, ID::Y, ID::CO}); + setup_comb_type(ID($fa), {ID::A, ID::B, ID::C}, {ID::X, ID::Y}); } void setup_internals_ff() { - setup_type(ID($sr), {ID::SET, ID::CLR}, {ID::Q}); - setup_type(ID($ff), {ID::D}, {ID::Q}); - setup_type(ID($dff), {ID::CLK, ID::D}, {ID::Q}); - setup_type(ID($dffe), {ID::CLK, ID::EN, ID::D}, {ID::Q}); - setup_type(ID($dffsr), {ID::CLK, ID::SET, ID::CLR, ID::D}, {ID::Q}); - setup_type(ID($dffsre), {ID::CLK, ID::SET, ID::CLR, ID::D, ID::EN}, {ID::Q}); - setup_type(ID($adff), {ID::CLK, ID::ARST, ID::D}, {ID::Q}); - setup_type(ID($adffe), {ID::CLK, ID::ARST, ID::D, ID::EN}, {ID::Q}); - setup_type(ID($aldff), {ID::CLK, ID::ALOAD, ID::AD, ID::D}, {ID::Q}); - setup_type(ID($aldffe), {ID::CLK, ID::ALOAD, ID::AD, ID::D, ID::EN}, {ID::Q}); - setup_type(ID($sdff), {ID::CLK, ID::SRST, ID::D}, {ID::Q}); - setup_type(ID($sdffe), {ID::CLK, ID::SRST, ID::D, ID::EN}, {ID::Q}); - setup_type(ID($sdffce), {ID::CLK, ID::SRST, ID::D, ID::EN}, {ID::Q}); - setup_type(ID($dlatch), {ID::EN, ID::D}, {ID::Q}); - setup_type(ID($adlatch), {ID::EN, ID::D, ID::ARST}, {ID::Q}); - setup_type(ID($dlatchsr), {ID::EN, ID::SET, ID::CLR, ID::D}, {ID::Q}); + setup_ff_type(ID($sr), {ID::SET, ID::CLR}, {ID::Q}); + setup_ff_type(ID($ff), {ID::D}, {ID::Q}); + setup_ff_type(ID($dff), {ID::CLK, ID::D}, {ID::Q}); + setup_ff_type(ID($dffe), {ID::CLK, ID::EN, ID::D}, {ID::Q}); + setup_ff_type(ID($dffsr), {ID::CLK, ID::SET, ID::CLR, ID::D}, {ID::Q}); + setup_ff_type(ID($dffsre), {ID::CLK, ID::SET, ID::CLR, ID::D, ID::EN}, {ID::Q}); + setup_ff_type(ID($adff), {ID::CLK, ID::ARST, ID::D}, {ID::Q}); + setup_ff_type(ID($adffe), {ID::CLK, ID::ARST, ID::D, ID::EN}, {ID::Q}); + setup_ff_type(ID($aldff), {ID::CLK, ID::ALOAD, ID::AD, ID::D}, {ID::Q}); + setup_ff_type(ID($aldffe), {ID::CLK, ID::ALOAD, ID::AD, ID::D, ID::EN}, {ID::Q}); + setup_ff_type(ID($sdff), {ID::CLK, ID::SRST, ID::D}, {ID::Q}); + setup_ff_type(ID($sdffe), {ID::CLK, ID::SRST, ID::D, ID::EN}, {ID::Q}); + setup_ff_type(ID($sdffce), {ID::CLK, ID::SRST, ID::D, ID::EN}, {ID::Q}); + setup_ff_type(ID($dlatch), {ID::EN, ID::D}, {ID::Q}); + setup_ff_type(ID($adlatch), {ID::EN, ID::D, ID::ARST}, {ID::Q}); + setup_ff_type(ID($dlatchsr), {ID::EN, ID::SET, ID::CLR, ID::D}, {ID::Q}); } void setup_internals_anyinit() { - setup_type(ID($anyinit), {ID::D}, {ID::Q}); + setup_formal_type(ID($anyinit), {ID::D}, {ID::Q}); } void setup_internals_mem() { setup_internals_ff(); - setup_type(ID($memrd), {ID::CLK, ID::EN, ID::ADDR}, {ID::DATA}); - setup_type(ID($memrd_v2), {ID::CLK, ID::EN, ID::ARST, ID::SRST, ID::ADDR}, {ID::DATA}); - setup_type(ID($memwr), {ID::CLK, ID::EN, ID::ADDR, ID::DATA}, pool()); - setup_type(ID($memwr_v2), {ID::CLK, ID::EN, ID::ADDR, ID::DATA}, pool()); - setup_type(ID($meminit), {ID::ADDR, ID::DATA}, pool()); - setup_type(ID($meminit_v2), {ID::ADDR, ID::DATA, ID::EN}, pool()); - setup_type(ID($mem), {ID::RD_CLK, ID::RD_EN, ID::RD_ADDR, ID::WR_CLK, ID::WR_EN, ID::WR_ADDR, ID::WR_DATA}, {ID::RD_DATA}); - setup_type(ID($mem_v2), {ID::RD_CLK, ID::RD_EN, ID::RD_ARST, ID::RD_SRST, ID::RD_ADDR, ID::WR_CLK, ID::WR_EN, ID::WR_ADDR, ID::WR_DATA}, {ID::RD_DATA}); + setup_internal_type(ID($memrd), {ID::CLK, ID::EN, ID::ADDR}, {ID::DATA}); + setup_internal_type(ID($memrd_v2), {ID::CLK, ID::EN, ID::ARST, ID::SRST, ID::ADDR}, {ID::DATA}); + setup_internal_type(ID($memwr), {ID::CLK, ID::EN, ID::ADDR, ID::DATA}, pool()); + setup_internal_type(ID($memwr_v2), {ID::CLK, ID::EN, ID::ADDR, ID::DATA}, pool()); + setup_internal_type(ID($meminit), {ID::ADDR, ID::DATA}, pool()); + setup_internal_type(ID($meminit_v2), {ID::ADDR, ID::DATA, ID::EN}, pool()); + setup_internal_type(ID($mem), {ID::RD_CLK, ID::RD_EN, ID::RD_ADDR, ID::WR_CLK, ID::WR_EN, ID::WR_ADDR, ID::WR_DATA}, {ID::RD_DATA}); + setup_internal_type(ID($mem_v2), {ID::RD_CLK, ID::RD_EN, ID::RD_ARST, ID::RD_SRST, ID::RD_ADDR, ID::WR_CLK, ID::WR_EN, ID::WR_ADDR, ID::WR_DATA}, {ID::RD_DATA}); - setup_type(ID($fsm), {ID::CLK, ID::ARST, ID::CTRL_IN}, {ID::CTRL_OUT}); + setup_internal_type(ID($fsm), {ID::CLK, ID::ARST, ID::CTRL_IN}, {ID::CTRL_OUT}); } void setup_stdcells() { setup_stdcells_eval(); - setup_type(ID($_TBUF_), {ID::A, ID::E}, {ID::Y}, true); + setup_comb_type(ID($_TBUF_), {ID::A, ID::E}, {ID::Y}, false); } void setup_stdcells_eval() { - setup_type(ID($_BUF_), {ID::A}, {ID::Y}, true); - setup_type(ID($_NOT_), {ID::A}, {ID::Y}, true); - setup_type(ID($_AND_), {ID::A, ID::B}, {ID::Y}, true); - setup_type(ID($_NAND_), {ID::A, ID::B}, {ID::Y}, true); - setup_type(ID($_OR_), {ID::A, ID::B}, {ID::Y}, true); - setup_type(ID($_NOR_), {ID::A, ID::B}, {ID::Y}, true); - setup_type(ID($_XOR_), {ID::A, ID::B}, {ID::Y}, true); - setup_type(ID($_XNOR_), {ID::A, ID::B}, {ID::Y}, true); - setup_type(ID($_ANDNOT_), {ID::A, ID::B}, {ID::Y}, true); - setup_type(ID($_ORNOT_), {ID::A, ID::B}, {ID::Y}, true); - setup_type(ID($_MUX_), {ID::A, ID::B, ID::S}, {ID::Y}, true); - setup_type(ID($_NMUX_), {ID::A, ID::B, ID::S}, {ID::Y}, true); - setup_type(ID($_MUX4_), {ID::A, ID::B, ID::C, ID::D, ID::S, ID::T}, {ID::Y}, true); - setup_type(ID($_MUX8_), {ID::A, ID::B, ID::C, ID::D, ID::E, ID::F, ID::G, ID::H, ID::S, ID::T, ID::U}, {ID::Y}, true); - setup_type(ID($_MUX16_), {ID::A, ID::B, ID::C, ID::D, ID::E, ID::F, ID::G, ID::H, ID::I, ID::J, ID::K, ID::L, ID::M, ID::N, ID::O, ID::P, ID::S, ID::T, ID::U, ID::V}, {ID::Y}, true); - setup_type(ID($_AOI3_), {ID::A, ID::B, ID::C}, {ID::Y}, true); - setup_type(ID($_OAI3_), {ID::A, ID::B, ID::C}, {ID::Y}, true); - setup_type(ID($_AOI4_), {ID::A, ID::B, ID::C, ID::D}, {ID::Y}, true); - setup_type(ID($_OAI4_), {ID::A, ID::B, ID::C, ID::D}, {ID::Y}, true); + setup_comb_type(ID($_BUF_), {ID::A}, {ID::Y}); + setup_comb_type(ID($_NOT_), {ID::A}, {ID::Y}); + setup_comb_type(ID($_AND_), {ID::A, ID::B}, {ID::Y}); + setup_comb_type(ID($_NAND_), {ID::A, ID::B}, {ID::Y}); + setup_comb_type(ID($_OR_), {ID::A, ID::B}, {ID::Y}); + setup_comb_type(ID($_NOR_), {ID::A, ID::B}, {ID::Y}); + setup_comb_type(ID($_XOR_), {ID::A, ID::B}, {ID::Y}); + setup_comb_type(ID($_XNOR_), {ID::A, ID::B}, {ID::Y}); + setup_comb_type(ID($_ANDNOT_), {ID::A, ID::B}, {ID::Y}); + setup_comb_type(ID($_ORNOT_), {ID::A, ID::B}, {ID::Y}); + setup_comb_type(ID($_MUX_), {ID::A, ID::B, ID::S}, {ID::Y}); + setup_comb_type(ID($_NMUX_), {ID::A, ID::B, ID::S}, {ID::Y}); + setup_comb_type(ID($_MUX4_), {ID::A, ID::B, ID::C, ID::D, ID::S, ID::T}, {ID::Y}); + setup_comb_type(ID($_MUX8_), {ID::A, ID::B, ID::C, ID::D, ID::E, ID::F, ID::G, ID::H, ID::S, ID::T, ID::U}, {ID::Y}); + setup_comb_type(ID($_MUX16_), {ID::A, ID::B, ID::C, ID::D, ID::E, ID::F, ID::G, ID::H, ID::I, ID::J, ID::K, ID::L, ID::M, ID::N, ID::O, ID::P, ID::S, ID::T, ID::U, ID::V}, {ID::Y}); + setup_comb_type(ID($_AOI3_), {ID::A, ID::B, ID::C}, {ID::Y}); + setup_comb_type(ID($_OAI3_), {ID::A, ID::B, ID::C}, {ID::Y}); + setup_comb_type(ID($_AOI4_), {ID::A, ID::B, ID::C, ID::D}, {ID::Y}); + setup_comb_type(ID($_OAI4_), {ID::A, ID::B, ID::C, ID::D}, {ID::Y}); } void setup_stdcells_mem() @@ -224,77 +298,77 @@ struct CellTypes for (auto c1 : list_np) for (auto c2 : list_np) - setup_type(stringf("$_SR_%c%c_", c1, c2), {ID::S, ID::R}, {ID::Q}); + setup_ff_type(stringf("$_SR_%c%c_", c1, c2), {ID::S, ID::R}, {ID::Q}); - setup_type(ID($_FF_), {ID::D}, {ID::Q}); + setup_ff_type(ID($_FF_), {ID::D}, {ID::Q}); for (auto c1 : list_np) - setup_type(stringf("$_DFF_%c_", c1), {ID::C, ID::D}, {ID::Q}); + setup_ff_type(stringf("$_DFF_%c_", c1), {ID::C, ID::D}, {ID::Q}); for (auto c1 : list_np) for (auto c2 : list_np) - setup_type(stringf("$_DFFE_%c%c_", c1, c2), {ID::C, ID::D, ID::E}, {ID::Q}); + setup_ff_type(stringf("$_DFFE_%c%c_", c1, c2), {ID::C, ID::D, ID::E}, {ID::Q}); for (auto c1 : list_np) for (auto c2 : list_np) for (auto c3 : list_01) - setup_type(stringf("$_DFF_%c%c%c_", c1, c2, c3), {ID::C, ID::R, ID::D}, {ID::Q}); + setup_ff_type(stringf("$_DFF_%c%c%c_", c1, c2, c3), {ID::C, ID::R, ID::D}, {ID::Q}); for (auto c1 : list_np) for (auto c2 : list_np) for (auto c3 : list_01) for (auto c4 : list_np) - setup_type(stringf("$_DFFE_%c%c%c%c_", c1, c2, c3, c4), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}); + setup_ff_type(stringf("$_DFFE_%c%c%c%c_", c1, c2, c3, c4), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}); for (auto c1 : list_np) for (auto c2 : list_np) - setup_type(stringf("$_ALDFF_%c%c_", c1, c2), {ID::C, ID::L, ID::AD, ID::D}, {ID::Q}); + setup_ff_type(stringf("$_ALDFF_%c%c_", c1, c2), {ID::C, ID::L, ID::AD, ID::D}, {ID::Q}); for (auto c1 : list_np) for (auto c2 : list_np) for (auto c3 : list_np) - setup_type(stringf("$_ALDFFE_%c%c%c_", c1, c2, c3), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}); + setup_ff_type(stringf("$_ALDFFE_%c%c%c_", c1, c2, c3), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}); for (auto c1 : list_np) for (auto c2 : list_np) for (auto c3 : list_np) - setup_type(stringf("$_DFFSR_%c%c%c_", c1, c2, c3), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}); + setup_ff_type(stringf("$_DFFSR_%c%c%c_", c1, c2, c3), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}); for (auto c1 : list_np) for (auto c2 : list_np) for (auto c3 : list_np) for (auto c4 : list_np) - setup_type(stringf("$_DFFSRE_%c%c%c%c_", c1, c2, c3, c4), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}); + setup_ff_type(stringf("$_DFFSRE_%c%c%c%c_", c1, c2, c3, c4), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}); for (auto c1 : list_np) for (auto c2 : list_np) for (auto c3 : list_01) - setup_type(stringf("$_SDFF_%c%c%c_", c1, c2, c3), {ID::C, ID::R, ID::D}, {ID::Q}); + setup_ff_type(stringf("$_SDFF_%c%c%c_", c1, c2, c3), {ID::C, ID::R, ID::D}, {ID::Q}); for (auto c1 : list_np) for (auto c2 : list_np) for (auto c3 : list_01) for (auto c4 : list_np) - setup_type(stringf("$_SDFFE_%c%c%c%c_", c1, c2, c3, c4), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}); + setup_ff_type(stringf("$_SDFFE_%c%c%c%c_", c1, c2, c3, c4), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}); for (auto c1 : list_np) for (auto c2 : list_np) for (auto c3 : list_01) for (auto c4 : list_np) - setup_type(stringf("$_SDFFCE_%c%c%c%c_", c1, c2, c3, c4), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}); + setup_ff_type(stringf("$_SDFFCE_%c%c%c%c_", c1, c2, c3, c4), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}); for (auto c1 : list_np) - setup_type(stringf("$_DLATCH_%c_", c1), {ID::E, ID::D}, {ID::Q}); + setup_ff_type(stringf("$_DLATCH_%c_", c1), {ID::E, ID::D}, {ID::Q}); for (auto c1 : list_np) for (auto c2 : list_np) for (auto c3 : list_01) - setup_type(stringf("$_DLATCH_%c%c%c_", c1, c2, c3), {ID::E, ID::R, ID::D}, {ID::Q}); + setup_ff_type(stringf("$_DLATCH_%c%c%c_", c1, c2, c3), {ID::E, ID::R, ID::D}, {ID::Q}); for (auto c1 : list_np) for (auto c2 : list_np) for (auto c3 : list_np) - setup_type(stringf("$_DLATCHSR_%c%c%c_", c1, c2, c3), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}); + setup_ff_type(stringf("$_DLATCHSR_%c%c%c_", c1, c2, c3), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}); } void clear() @@ -302,6 +376,15 @@ struct CellTypes cell_types.clear(); } + const CellType* get_cell(RTLIL::IdString type) const + { + auto it = cell_types.find(type); + if (it == cell_types.end()) + return nullptr; + else + return &(it->second); + } + bool cell_known(RTLIL::IdString type) const { return cell_types.count(type) != 0; @@ -506,6 +589,8 @@ struct CellTypes { if (cell->type.in(ID($mux), ID($_MUX_))) return const_mux(arg1, arg2, arg3); + if (cell->type == ID($_NMUX_)) + return eval_not(const_mux(arg1, arg2, arg3)); if (cell->type == ID($bwmux)) return const_bwmux(arg1, arg2, arg3); if (cell->type == ID($pmux)) diff --git a/kernel/register.cc b/kernel/register.cc index 11cf5b0e4fe..dbdcfc408cc 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -1074,6 +1074,36 @@ struct HelpPass : public Pass { log("\n"); return; } + else if (args[1] == "-celltypes") { + log("\n"); + for (auto &it : cell_help_messages.cell_help) { + SimHelper help_cell = it.second; + auto *ct = yosys_celltypes.get_cell(it.first); + char ct_flags[8] = ""; + if (ct != nullptr && ct->is_internal) { + ct_flags[0] = ct->is_evaluable ? 'E' : '-'; + ct_flags[1] = ct->is_combinatorial ? 'C' : '-'; + ct_flags[2] = ct->is_synthesizable ? 'S' : '-'; + ct_flags[3] = ct->is_builtin_ff ? 'M' : '-'; + ct_flags[4] = ct->is_formal ? 'F' : '-'; + ct_flags[5] = ct->is_metainfo ? 'I' : '-'; + ct_flags[6] = ct->has_effects ? 'X' : '-'; + ct_flags[7] = 0; + } + log(" %-15s %s\n", help_cell.name.c_str(), ct_flags); + } + log("\n"); + log("Legend:\n"); + log(" E = evaluable\n"); + log(" C = combinatorial\n"); + log(" S = synthesizable\n"); + log(" M = builtin_ff\n"); + log(" F = formal\n"); + log(" I = metainfo\n"); + log(" X = effects\n"); + log("\n"); + return; + } // this option is undocumented as it is for internal use only else if (args[1] == "-write-rst-command-reference-manual") { for (auto &it : pass_register) { diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index cb0f7da7807..790480edf45 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -4063,9 +4063,9 @@ void RTLIL::Cell::fixup_parameters(bool set_a_signed, bool set_b_signed) type.begins_with("$verific$") || type.begins_with("$array:") || type.begins_with("$extern:")) return; - if (type == ID($buf) || type == ID($mux) || type == ID($pmux) || type == ID($bmux)) { + if (type == ID($buf) || type == ID($mux) || type == ID($pmux) || type == ID($bmux) || type == ID($bwmux) || type == ID($bweqx)) { parameters[ID::WIDTH] = GetSize(connections_[ID::Y]); - if (type != ID($buf) && type != ID($mux)) + if (type.in(ID($pmux), ID($bmux))) parameters[ID::S_WIDTH] = GetSize(connections_[ID::S]); check(); return; @@ -4115,7 +4115,7 @@ void RTLIL::Cell::fixup_parameters(bool set_a_signed, bool set_b_signed) parameters[ID::B_WIDTH] = GetSize(connections_[ID::B]); } - if (connections_.count(ID::Y)) + if (connections_.count(ID::Y) && type != ID($concat)) parameters[ID::Y_WIDTH] = GetSize(connections_[ID::Y]); if (connections_.count(ID::Q)) diff --git a/passes/cmds/select.cc b/passes/cmds/select.cc index aec4c964b28..2915552f95e 100644 --- a/passes/cmds/select.cc +++ b/passes/cmds/select.cc @@ -141,6 +141,39 @@ static bool match_attr(const dict &attributes, co return match_attr(attributes, match_expr, std::string(), 0); } +static bool match_type_prop(RTLIL::IdString type, const std::string &property) +{ + auto *ct = yosys_celltypes.get_cell(type); + if (ct == nullptr) { + return false; + } else + if (property.compare("internal") == 0) { + return ct->is_internal; + } else + if (property.compare("evaluable") == 0) { + return ct->is_evaluable; + } else + if (property.compare("combinatorial") == 0) { + return ct->is_combinatorial; + } else + if (property.compare("synthesizable") == 0) { + return ct->is_synthesizable; + } else + if (property.compare("builtin_ff") == 0) { + return ct->is_builtin_ff; + } else + if (property.compare("formal") == 0) { + return ct->is_formal; + } else + if (property.compare("metainfo") == 0) { + return ct->is_metainfo; + } else + if (property.compare("effects") == 0) { + return ct->has_effects; + } else + log_cmd_error("Unsupported type property '%s'!\n", property.c_str()); +} + static void select_op_neg(RTLIL::Design *design, RTLIL::Selection &lhs) { if (lhs.full_selection) { @@ -891,6 +924,11 @@ static void select_stmt(RTLIL::Design *design, std::string arg, bool disable_emp sel.selected_members[mod->name].insert(cell->name); } } else + if (arg_memb.compare(0, 2, "y:") == 0) { + for (auto cell : mod->cells()) + if (match_type_prop(cell->type, arg_memb.substr(2))) + sel.selected_members[mod->name].insert(cell->name); + } else if (arg_memb.compare(0, 2, "p:") == 0) { for (auto &it : mod->processes) if (match_ids(it.first, arg_memb.substr(2))) @@ -1178,6 +1216,11 @@ struct SelectPass : public Pass { log(" t:@\n"); log(" all cells with a type matching a module in the saved selection \n"); log("\n"); + log(" y:\n"); + log(" all cells with a given type property, possible values are:\n"); + log(" evaluable, combinatorial, synthesizable, builtin_ff, formal\n"); + log(" (currently only internal cells can have type properties)\n"); + log("\n"); log(" p:\n"); log(" all processes with a name matching the given pattern\n"); log("\n"); diff --git a/passes/tests/test_cell.cc b/passes/tests/test_cell.cc index 39b7ccd3ae9..d1beafd7bc1 100644 --- a/passes/tests/test_cell.cc +++ b/passes/tests/test_cell.cc @@ -24,6 +24,7 @@ #include "kernel/celledges.h" #include "kernel/macc.h" #include "kernel/cost.h" +#include "kernel/celltypes.h" #include USING_YOSYS_NAMESPACE @@ -71,6 +72,29 @@ static RTLIL::Cell* create_gold_module(RTLIL::Design *design, RTLIL::IdString ce cell->setPort(ID::Y, wire); } + if (cell_type.in(ID($_MUX_), ID($_NMUX_))) + { + wire = module->addWire(ID::A); + wire->width = 1; + wire->port_input = true; + cell->setPort(ID::A, wire); + + wire = module->addWire(ID::B); + wire->width = 1; + wire->port_input = true; + cell->setPort(ID::B, wire); + + wire = module->addWire(ID::S); + wire->width = 1; + wire->port_input = true; + cell->setPort(ID::S, wire); + + wire = module->addWire(ID::Y); + wire->width = 1; + wire->port_output = true; + cell->setPort(ID::Y, wire); + } + if (cell_type == ID($bmux)) { int width = 1 + xorshift32(8 * bloat_factor); @@ -273,14 +297,19 @@ static RTLIL::Cell* create_gold_module(RTLIL::Design *design, RTLIL::IdString ce if (cell_type_flags.find('A') != std::string::npos) { wire = module->addWire(ID::A); - wire->width = 1 + xorshift32(8 * bloat_factor); + if (cell_type_flags.find('b') != std::string::npos) + wire->width = 1; + else + wire->width = 1 + xorshift32(8 * bloat_factor); wire->port_input = true; cell->setPort(ID::A, wire); } if (cell_type_flags.find('B') != std::string::npos) { wire = module->addWire(ID::B); - if (cell_type_flags.find('h') != std::string::npos) + if (cell_type_flags.find('b') != std::string::npos) + wire->width = 1; + else if (cell_type_flags.find('h') != std::string::npos) wire->width = 1 + xorshift32(6 * bloat_factor); else wire->width = 1 + xorshift32(8 * bloat_factor); @@ -288,6 +317,26 @@ static RTLIL::Cell* create_gold_module(RTLIL::Design *design, RTLIL::IdString ce cell->setPort(ID::B, wire); } + if (cell_type_flags.find('C') != std::string::npos) { + wire = module->addWire(ID::C); + if (cell_type_flags.find('b') != std::string::npos) + wire->width = 1; + else + wire->width = 1 + xorshift32(8 * bloat_factor); + wire->port_input = true; + cell->setPort(ID::C, wire); + } + + if (cell_type_flags.find('D') != std::string::npos) { + wire = module->addWire(ID::D); + if (cell_type_flags.find('b') != std::string::npos) + wire->width = 1; + else + wire->width = 1 + xorshift32(8 * bloat_factor); + wire->port_input = true; + cell->setPort(ID::D, wire); + } + if (cell_type_flags.find('S') != std::string::npos && xorshift32(2)) { if (cell_type_flags.find('A') != std::string::npos) cell->parameters[ID::A_SIGNED] = true; @@ -304,7 +353,10 @@ static RTLIL::Cell* create_gold_module(RTLIL::Design *design, RTLIL::IdString ce if (cell_type_flags.find('Y') != std::string::npos) { wire = module->addWire(ID::Y); - wire->width = 1 + xorshift32(8 * bloat_factor); + if (cell_type_flags.find('b') != std::string::npos) + wire->width = 1; + else + wire->width = 1 + xorshift32(8 * bloat_factor); wire->port_output = true; cell->setPort(ID::Y, wire); } @@ -345,6 +397,58 @@ static RTLIL::Cell* create_gold_module(RTLIL::Design *design, RTLIL::IdString ce cell->setPort(ID::CO, wire); } + if (cell_type == ID($slice)) + { + int a_size = GetSize(cell->getPort(ID::A)); + int y_size = 1; + if (a_size > 1) + y_size += (xorshift32(8 * bloat_factor) % (a_size - 1)); + wire = module->addWire(ID::Y); + wire->width = y_size; + wire->port_output = true; + cell->setPort(ID::Y, wire); + if (a_size > y_size) + cell->setParam(ID::OFFSET, (xorshift32(8 * bloat_factor) % (a_size - y_size))); + else + cell->setParam(ID::OFFSET, 0); + } + + if (cell_type == ID($concat)) + { + wire = module->addWire(ID::Y); + wire->width = GetSize(cell->getPort(ID::A)) + GetSize(cell->getPort(ID::B)); + wire->port_output = true; + cell->setPort(ID::Y, wire); + } + + if (cell_type == ID($buf)) + { + wire = module->addWire(ID::Y); + wire->width = GetSize(cell->getPort(ID::A)); + wire->port_output = true; + cell->setPort(ID::Y, wire); + } + + if (cell_type.in(ID($bwmux), ID($bweqx))) + { + int a_size = GetSize(cell->getPort(ID::A)); + wire = module->addWire(ID::B); + wire->width = a_size; + wire->port_output = true; + cell->setPort(ID::B, wire); + if (cell_type == ID($bwmux)) + { + wire = module->addWire(ID::S); + wire->width = a_size; + wire->port_output = true; + cell->setPort(ID::S, wire); + } + wire = module->addWire(ID::Y); + wire->width = a_size; + wire->port_output = true; + cell->setPort(ID::Y, wire); + } + if (constmode) { auto conn_list = cell->connections(); @@ -471,7 +575,7 @@ static void run_edges_test(RTLIL::Design *design, bool verbose) log_error("SAT-based edge table does not match the database!\n"); } -static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std::string uut_name, std::ofstream &vlog_file) +static void run_eval_test(RTLIL::Design *design, const CellType *ct, bool verbose, bool nosat, std::string uut_name, std::ofstream &vlog_file) { log("Eval testing:%c", verbose ? '\n' : ' '); @@ -530,6 +634,8 @@ static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std:: RTLIL::SigSpec out_sig, out_val; std::string vlog_pattern_info; + std::map in_map; + for (auto port : gold_mod->ports) { RTLIL::Wire *gold_wire = gold_mod->wire(port); @@ -559,6 +665,7 @@ static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std:: in_sig.append(gold_wire); in_val.append(in_value); + in_map[port] = in_value; gold_ce.set(gold_wire, in_value); gate_ce.set(gate_wire, in_value); @@ -626,6 +733,49 @@ static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std:: if (verbose) log("EVAL: %s\n", out_val.as_string().c_str()); + if (ct != nullptr && ct->is_evaluable) { + auto *cell = gold_mod->cell(ID(UUT)); + log_assert(cell != nullptr); + bool err = false; + Const result; + + // Based on passes/sat/sim.cc + bool has_a, has_b, has_c, has_d, has_s, has_x, has_y; + + has_a = cell->hasPort(ID::A); + has_b = cell->hasPort(ID::B); + has_c = cell->hasPort(ID::C); + has_d = cell->hasPort(ID::D); + has_s = cell->hasPort(ID::S); + has_x = cell->hasPort(ID::X); + has_y = cell->hasPort(ID::Y); + + // Simple (A -> Y) and (A,B -> Y) cells + if (has_a && !has_c && !has_d && !has_s && !has_x && has_y) + result = CellTypes::eval(cell, in_map[ID::A], in_map[ID::B], &err); + // (A,B,C -> Y) and (A,B,C,D -> Y) cells + else if (has_a && has_b && has_c && !has_s && !has_x && has_y) + result = CellTypes::eval(cell, in_map[ID::A], in_map[ID::B], in_map[ID::C], in_map[ID::D], &err); + // (A,S -> Y) cells + else if (has_a && !has_b && !has_c && !has_d && has_s && !has_x && has_y) + result = CellTypes::eval(cell, in_map[ID::A], in_map[ID::S], &err); + // (A,B,S -> Y) cells + else if (has_a && has_b && !has_c && !has_d && has_s && !has_x && has_y) + result = CellTypes::eval(cell, in_map[ID::A], in_map[ID::B], in_map[ID::S], &err); + // anything else + else + log_error("Unable to handle ports for ID::%s\n", id2cstr(ct->type)); + + if (err) + log_error("CellTypes::eval(ID::%s) returned error\n", id2cstr(ct->type)); + + if (result != out_val.as_const()) + log_error("CellTypes/ConstEval mismatch for ID::%s, %s != %s\n", id2cstr(ct->type), log_const(result), log_signal(out_val)); + + if (verbose) + log("eval: %s\n", result.as_string().c_str()); + } + if (!nosat) { std::vector sat1_in_sig = satgen1.importSigSpec(in_sig); @@ -756,6 +906,9 @@ struct TestCellPass : public Pass { log(" -noeval\n"); log(" do not check const-eval models\n"); log("\n"); + log(" -nocteval\n"); + log(" do not check celltype-eval models\n"); + log("\n"); log(" -noopt\n"); log(" do not opt tecchmapped design\n"); log("\n"); @@ -773,6 +926,18 @@ struct TestCellPass : public Pass { log(" check if the estimated cell cost is a valid upper bound for\n"); log(" the techmapped cell count \n"); log("\n"); + log(" test_cell -list all\n"); + log("\n"); + log("List all cell types supported by test_cell.\n"); + log("\n"); + log(" test_cell -list evaluable\n"); + log("\n"); + log("List all cell types marked evaluable.\n"); + log("\n"); + log(" test_cell -list missing\n"); + log("\n"); + log("List all cell types marked evaluable but not supported by test_cell.\n"); + log("\n"); } void execute(std::vector args, RTLIL::Design*) override { @@ -786,9 +951,14 @@ struct TestCellPass : public Pass { bool constmode = false; bool nosat = false; bool noeval = false; + bool nocteval = false; bool noopt = false; bool edges = false; bool check_cost = false; + bool list = false; + bool list_all = false; + bool list_evaluable = false; + bool list_missing = false; int argidx; for (argidx = 1; argidx < GetSize(args); argidx++) @@ -842,6 +1012,10 @@ struct TestCellPass : public Pass { noeval = true; continue; } + if (args[argidx] == "-nocteval") { + nocteval = true; + continue; + } if (args[argidx] == "-noopt") { noopt = true; continue; @@ -868,20 +1042,30 @@ struct TestCellPass : public Pass { check_cost = true; continue; } + if (args[argidx] == "-list" && argidx+1 < GetSize(args)) { + list = true; + argidx++; + if (args[argidx].compare("all") == 0) + list_all = true; + else if (args[argidx].compare("evaluable") == 0) + list_evaluable = true; + else if (args[argidx].compare("missing") == 0) + list_missing = true; + else + log_cmd_error("Unknown list type '%s'.\n", args[argidx].c_str()); + continue; + } break; } - if (xorshift32_state == 0) { - xorshift32_state = time(NULL) & 0x7fffffff; - log("Rng seed value: %d\n", int(xorshift32_state)); - } - std::map cell_types; std::vector selected_cell_types; cell_types[ID($not)] = "ASY"; cell_types[ID($pos)] = "ASY"; cell_types[ID($neg)] = "ASY"; + if (techmap_cmd.compare("techmap -assert") != 0) + cell_types[ID($buf)] = "A"; cell_types[ID($and)] = "ABSY"; cell_types[ID($or)] = "ABSY"; @@ -917,7 +1101,8 @@ struct TestCellPass : public Pass { cell_types[ID($mod)] = "ABSY"; cell_types[ID($divfloor)] = "ABSY"; cell_types[ID($modfloor)] = "ABSY"; - // cell_types[ID($pow)] = "ABsY"; + if (nosat && techmap_cmd.compare("aigmap") == 0) + cell_types[ID($pow)] = "ABsY"; cell_types[ID($logic_not)] = "ASY"; cell_types[ID($logic_and)] = "ABSY"; @@ -926,12 +1111,15 @@ struct TestCellPass : public Pass { cell_types[ID($mux)] = "*"; cell_types[ID($bmux)] = "*"; cell_types[ID($demux)] = "*"; - if (edges) { + if (edges) cell_types[ID($pmux)] = "*"; - } + if (noeval) + cell_types[ID($bwmux)] = "A"; + if (nosat && noeval) + cell_types[ID($bweqx)] = "A"; - // cell_types[ID($slice)] = "A"; - // cell_types[ID($concat)] = "A"; + cell_types[ID($slice)] = "A"; + cell_types[ID($concat)] = "AB"; cell_types[ID($lut)] = "*"; cell_types[ID($sop)] = "*"; @@ -940,6 +1128,53 @@ struct TestCellPass : public Pass { cell_types[ID($macc)] = "*"; cell_types[ID($fa)] = "*"; + cell_types[ID($_BUF_)] = "AYb"; + cell_types[ID($_NOT_)] = "AYb"; + cell_types[ID($_AND_)] = "ABYb"; + cell_types[ID($_NAND_)] = "ABYb"; + cell_types[ID($_OR_)] = "ABYb"; + cell_types[ID($_NOR_)] = "ABYb"; + cell_types[ID($_XOR_)] = "ABYb"; + cell_types[ID($_XNOR_)] = "ABYb"; + cell_types[ID($_ANDNOT_)] = "ABYb"; + cell_types[ID($_ORNOT_)] = "ABYb"; + cell_types[ID($_MUX_)] = "*"; + cell_types[ID($_NMUX_)] = "*"; + // cell_types[ID($_MUX4_)] = "*"; + // cell_types[ID($_MUX8_)] = "*"; + // cell_types[ID($_MUX16_)] = "*"; + cell_types[ID($_AOI3_)] = "ABCYb"; + cell_types[ID($_OAI3_)] = "ABCYb"; + cell_types[ID($_AOI4_)] = "ABCDYb"; + cell_types[ID($_OAI4_)] = "ABCDYb"; + + if (list_all) { + log("test_cell supports the following cell types:\n"); + for (auto it : cell_types) + log("%s\n", id2cstr(it.first)); + } + + if (list_evaluable) { + log("cell types marked evaluable:\n"); + for (auto it : yosys_celltypes.cell_types) + if (it.second.is_evaluable) + log("%s\n", id2cstr(it.first)); + } + + if (list_missing) { + log("test_cell missing support for evaluable cell types:\n"); + for (auto it : yosys_celltypes.cell_types) + if (it.second.is_evaluable && cell_types.count(it.first) == 0) + log("%s\n", id2cstr(it.first)); + } + + if (list) return; + + if (xorshift32_state == 0) { + xorshift32_state = time(NULL) & 0x7fffffff; + log("Rng seed value: %d\n", int(xorshift32_state)); + } + for (; argidx < GetSize(args); argidx++) { if (args[argidx].rfind("-", 0) == 0) @@ -998,6 +1233,14 @@ struct TestCellPass : public Pass { int worst_abs = 0; // How many times is it bigger than estimated? float worst_rel = 0.0; + // Get cell type for cell (if possible) + const CellType *ct = nullptr; + if (!nocteval) { + if (yosys_celltypes.cell_known(cell_type)) + ct = yosys_celltypes.get_cell(cell_type); + else if (cell_type != ID(rtlil)) + log_warning("%s does not have an internal cell type!\n", id2cstr(cell_type)); + } for (int i = 0; i < num_iter; i++) { Cell* uut = nullptr; @@ -1031,7 +1274,7 @@ struct TestCellPass : public Pass { uut_names.push_back(uut_name); } if (!noeval) - run_eval_test(design, verbose, nosat, uut_name, vlog_file); + run_eval_test(design, ct, verbose, nosat, uut_name, vlog_file); if (check_cost && uut) { Pass::call(design, "select gate"); int num_cells = 0; diff --git a/tests/select/type_props.ys b/tests/select/type_props.ys new file mode 100644 index 00000000000..44f1810548f --- /dev/null +++ b/tests/select/type_props.ys @@ -0,0 +1,42 @@ +read_rtlil << EOF +module \sm2 + + wire input 1 \clk + wire input 2 \rst + wire width 2 input 3 \a + + wire width 2 \add_Y + + attribute \init 2'00 + wire width 2 output 4 \y + + cell $add \add + parameter \A_SIGNED 0 + parameter \A_WIDTH 2 + parameter \B_SIGNED 0 + parameter \B_WIDTH 2 + parameter \Y_WIDTH 2 + connect \A \y + connect \B \a + connect \Y \add_Y + end + + cell $sdff \sdff + parameter \CLK_POLARITY 1 + parameter \SRST_POLARITY 1 + parameter \SRST_VALUE 2'00 + parameter \WIDTH 2 + connect \CLK \clk + connect \D \add_Y + connect \Q \y + connect \SRST \rst + end +end + +EOF + +select -assert-count 1 y:evaluable +select -assert-count 1 y:combinatorial +select -assert-count 2 y:synthesizable +select -assert-count 1 y:builtin_ff +select -assert-count 0 y:formal diff --git a/tests/various/evaluable.sh b/tests/various/evaluable.sh new file mode 100755 index 00000000000..604045d5dd1 --- /dev/null +++ b/tests/various/evaluable.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +yosys=../../yosys +log="evalcells.log" + +bad= + +$yosys -QTL $log -qp 'test_cell -list all' +while read line; +do + if [[ "$line" =~ ^\$.* ]]; then + if ! $yosys -qqp test_cell\ -n\ 1\ -s\ 1\ "$line"; then + bad=1 + fi + fi +done <$log + +if [ $bad ]; then + echo 'One or more evaluable cells failed testing.' + exit 1 +else + echo 'All evaluable cells match behavior.' +fi