From 97e1a3b7c779da179cc6c2b84f83f18d22f77b1f Mon Sep 17 00:00:00 2001 From: nella Date: Sat, 21 Feb 2026 12:23:15 +0100 Subject: [PATCH] Add celltype unit tests. --- kernel/celltypes.h | 34 +- misc/gen_celltypes.py | 49 +- tests/unit/kernel/celltypesTest.cc | 1215 ++++++++++++++++++++++++++++ 3 files changed, 1256 insertions(+), 42 deletions(-) create mode 100644 tests/unit/kernel/celltypesTest.cc diff --git a/kernel/celltypes.h b/kernel/celltypes.h index c7126dfca..ac55ee2a3 100644 --- a/kernel/celltypes.h +++ b/kernel/celltypes.h @@ -146,7 +146,7 @@ struct CellTypes bool builtin_is_known(size_t idx) const { return enabled_cats && idx < (size_t)StaticCellTypes::GEN_MAX_CELLS && - StaticCellTypes::GeneratedData::is_known[idx]; + StaticCellTypes::GeneratedData::is_known[idx]; } bool cell_known(const RTLIL::IdString &type) const @@ -157,7 +157,6 @@ struct CellTypes } else if (builtin_match(type.index_)) { return true; } - return cell_types.count(type) != 0; } @@ -171,10 +170,8 @@ struct CellTypes for (uint16_t i = 0; i < count; i++) if (StaticCellTypes::GeneratedData::port_outputs_ports[idx][i] == target) return true; - return false; } - auto it = cell_types.find(type); return it != cell_types.end() && it->second.outputs.count(port) != 0; } @@ -189,10 +186,8 @@ struct CellTypes for (uint16_t i = 0; i < count; i++) if (StaticCellTypes::GeneratedData::port_inputs_ports[idx][i] == target) return true; - return false; } - auto it = cell_types.find(type); return it != cell_types.end() && it->second.inputs.count(port) != 0; } @@ -206,24 +201,17 @@ struct CellTypes uint32_t target = (uint32_t)port.index_; uint16_t ic = StaticCellTypes::GeneratedData::port_inputs_counts[idx]; for (uint16_t i = 0; i < ic; i++) - if (StaticCellTypes::GeneratedData::port_inputs_ports[idx][i] == target) { - is_in = true; - break; - } - + if (StaticCellTypes::GeneratedData::port_inputs_ports[idx][i] == target) + { is_in = true; break; } uint16_t oc = StaticCellTypes::GeneratedData::port_outputs_counts[idx]; for (uint16_t i = 0; i < oc; i++) - if (StaticCellTypes::GeneratedData::port_outputs_ports[idx][i] == target) { - is_out = true; - break; - } - + if (StaticCellTypes::GeneratedData::port_outputs_ports[idx][i] == target) + { is_out = true; break; } return RTLIL::PortDir(is_in + is_out * 2); } auto it = cell_types.find(type); if (it == cell_types.end()) return RTLIL::PD_UNKNOWN; - bool is_in = it->second.inputs.count(port); bool is_out = it->second.outputs.count(port); return RTLIL::PortDir(is_in + is_out * 2); @@ -234,8 +222,8 @@ struct CellTypes size_t idx = type.index_; bool is_builtin = (enabled_cats == BITS_ALL) ? builtin_is_known(idx) : builtin_match(idx); if (is_builtin) - return idx < (size_t)StaticCellTypes::GEN_MAX_CELLS && StaticCellTypes::GeneratedData::is_evaluable[idx]; - + return idx < (size_t)StaticCellTypes::GEN_MAX_CELLS && + StaticCellTypes::GeneratedData::is_cell_evaluable[idx]; auto it = cell_types.find(type); return it != cell_types.end() && it->second.is_evaluable; } @@ -245,7 +233,6 @@ struct CellTypes for (auto bit : v) if (bit == State::S0) bit = State::S1; else if (bit == State::S1) bit = State::S0; - return v; } @@ -361,7 +348,6 @@ struct CellTypes std::vector t = cell->parameters.at(ID::LUT).to_bits(); while (GetSize(t) < (1 << width)) t.push_back(State::S0); - t.resize(1 << width); return const_bmux(t, arg1); @@ -373,7 +359,7 @@ struct CellTypes int depth = cell->parameters.at(ID::DEPTH).as_int(); std::vector t = cell->parameters.at(ID::TABLE).to_bits(); - while (GetSize(t) < width * depth * 2) + while (GetSize(t) < width*depth*2) t.push_back(State::S0); RTLIL::State default_ret = State::S0; @@ -385,11 +371,11 @@ struct CellTypes for (int j = 0; j < width; j++) { RTLIL::State a = arg1.at(j); - if (t.at(2 * width * i + 2 * j + 0) == State::S1) { + if (t.at(2*width*i + 2*j + 0) == State::S1) { if (a == State::S1) match_x = false; if (a != State::S0) match = false; } - if (t.at(2 * width * i + 2 * j + 1) == State::S1) { + if (t.at(2*width*i + 2*j + 1) == State::S1) { if (a == State::S0) match_x = false; if (a != State::S1) match = false; } diff --git a/misc/gen_celltypes.py b/misc/gen_celltypes.py index aefff07e1..01f7498b9 100644 --- a/misc/gen_celltypes.py +++ b/misc/gen_celltypes.py @@ -39,6 +39,7 @@ def resolve_id(index_map, name): @dataclass class Features: is_evaluable: bool = False + is_cell_evaluable: bool = False is_combinatorial: bool = False is_synthesizable: bool = False is_stdcell: bool = False @@ -51,7 +52,7 @@ class Features: @dataclass class CellInfo: type_name: str - inputs: MAX_CELLS + inputs: list outputs: list features: Features @@ -64,7 +65,7 @@ def build_cell_table(): Features(**vars(features)))) # setup_internals_other - f = Features(is_tristate=True) + f = Features(is_tristate=True, is_cell_evaluable=True) setup_type("$tribuf", ["A", "EN"], ["Y"], f) f = Features() @@ -79,9 +80,13 @@ def build_cell_table(): setup_type("$allconst", [], ["Y"], f) setup_type("$allseq", [], ["Y"], f) setup_type("$equiv", ["A", "B"], ["Y"], f) + + f = Features(is_cell_evaluable=True) setup_type("$specify2", ["EN", "SRC", "DST"], [], f) setup_type("$specify3", ["EN", "SRC", "DST", "DAT"], [], f) setup_type("$specrule", ["EN_SRC", "EN_DST", "SRC", "DST"], [], f) + + f = Features() setup_type("$print", ["EN", "ARGS", "TRG"], [], f) setup_type("$check", ["A", "EN", "ARGS", "TRG"], [], f) setup_type("$set_tag", ["A", "SET", "CLR"], ["Y"], f) @@ -94,7 +99,7 @@ def build_cell_table(): setup_type("$connect", ["A", "B"], [], f) # setup_internals_eval - f = Features(is_evaluable=True) + f = Features(is_evaluable=True, is_cell_evaluable=True) unary_ops = [ "$not", "$pos", "$buf", "$neg", @@ -150,7 +155,7 @@ def build_cell_table(): f = Features(is_anyinit=True) setup_type("$anyinit", ["D"], ["Q"], f) - # setup_internals_mem_noff + # setup_internals_mem_noff (mem cells that are NOT FFs) f = Features(is_mem_noff=True) setup_type("$memrd", ["CLK", "EN", "ADDR"], ["DATA"], f) setup_type("$memrd_v2", ["CLK", "EN", "ARST", "SRST", "ADDR"], @@ -170,11 +175,11 @@ def build_cell_table(): setup_type("$fsm", ["CLK", "ARST", "CTRL_IN"], ["CTRL_OUT"], f) # setup_stdcells_tristate - f = Features(is_stdcell=True, is_tristate=True) + f = Features(is_stdcell=True, is_tristate=True, is_cell_evaluable=True) setup_type("$_TBUF_", ["A", "E"], ["Y"], f) # setup_stdcells_eval - f = Features(is_stdcell=True, is_evaluable=True) + f = Features(is_stdcell=True, is_evaluable=True, is_cell_evaluable=True) setup_type("$_BUF_", ["A"], ["Y"], f) setup_type("$_NOT_", ["A"], ["Y"], f) setup_type("$_AND_", ["A", "B"], ["Y"], f) @@ -298,6 +303,7 @@ def build_arrays(cells, index_map): cats = { "is_known": [0] * MAX_CELLS, "is_evaluable": [0] * MAX_CELLS, + "is_cell_evaluable": [0] * MAX_CELLS, "is_combinatorial": [0] * MAX_CELLS, "is_synthesizable": [0] * MAX_CELLS, "is_stdcell": [0] * MAX_CELLS, @@ -320,9 +326,9 @@ def build_arrays(cells, index_map): continue cats["is_known"][idx] = 1 - for feat in ["is_evaluable", "is_combinatorial", "is_synthesizable", - "is_stdcell", "is_ff", "is_mem_noff", "is_anyinit", - "is_tristate"]: + for feat in ["is_evaluable", "is_cell_evaluable", "is_combinatorial", + "is_synthesizable", "is_stdcell", "is_ff", "is_mem_noff", + "is_anyinit", "is_tristate"]: if getattr(cell.features, feat): cats[feat][idx] = 1 @@ -449,7 +455,13 @@ constexpr int GEN_MAX_PORTS = {MAX_PORTS}; namespace GeneratedData {{ extern const uint8_t is_known[GEN_MAX_CELLS]; +// Category flag: true for cells in setup_internals_eval / setup_stdcells_eval. +// Used by builtin_match() for subset filtering. extern const uint8_t is_evaluable[GEN_MAX_CELLS]; +// Property flag: true for any cell whose original setup_type() had is_evaluable=true. +// Superset of is_evaluable (includes $tribuf, $specify*, $_TBUF_). +// Used by cell_evaluable() to answer "can this cell be const-evaluated?" +extern const uint8_t is_cell_evaluable[GEN_MAX_CELLS]; extern const uint8_t is_combinatorial[GEN_MAX_CELLS]; extern const uint8_t is_synthesizable[GEN_MAX_CELLS]; extern const uint8_t is_stdcell[GEN_MAX_CELLS]; @@ -498,15 +510,16 @@ struct GenPortLookup {{ }} }}; -inline GenCategory gen_is_known() {{ return {{GeneratedData::is_known}}; }} -inline GenCategory gen_is_evaluable() {{ return {{GeneratedData::is_evaluable}}; }} -inline GenCategory gen_is_combinatorial() {{ return {{GeneratedData::is_combinatorial}}; }} -inline GenCategory gen_is_synthesizable() {{ return {{GeneratedData::is_synthesizable}}; }} -inline GenCategory gen_is_stdcell() {{ return {{GeneratedData::is_stdcell}}; }} -inline GenCategory gen_is_ff() {{ return {{GeneratedData::is_ff}}; }} -inline GenCategory gen_is_mem_noff() {{ return {{GeneratedData::is_mem_noff}}; }} -inline GenCategory gen_is_anyinit() {{ return {{GeneratedData::is_anyinit}}; }} -inline GenCategory gen_is_tristate() {{ return {{GeneratedData::is_tristate}}; }} +inline GenCategory gen_is_known() {{ return {{GeneratedData::is_known}}; }} +inline GenCategory gen_is_evaluable() {{ return {{GeneratedData::is_evaluable}}; }} +inline GenCategory gen_is_cell_evaluable() {{ return {{GeneratedData::is_cell_evaluable}}; }} +inline GenCategory gen_is_combinatorial() {{ return {{GeneratedData::is_combinatorial}}; }} +inline GenCategory gen_is_synthesizable() {{ return {{GeneratedData::is_synthesizable}}; }} +inline GenCategory gen_is_stdcell() {{ return {{GeneratedData::is_stdcell}}; }} +inline GenCategory gen_is_ff() {{ return {{GeneratedData::is_ff}}; }} +inline GenCategory gen_is_mem_noff() {{ return {{GeneratedData::is_mem_noff}}; }} +inline GenCategory gen_is_anyinit() {{ return {{GeneratedData::is_anyinit}}; }} +inline GenCategory gen_is_tristate() {{ return {{GeneratedData::is_tristate}}; }} inline GenCategory gen_compat_internals_all() {{ return {{GeneratedData::compat_internals_all}}; }} inline GenCategory gen_compat_mem_ff() {{ return {{GeneratedData::compat_mem_ff}}; }} diff --git a/tests/unit/kernel/celltypesTest.cc b/tests/unit/kernel/celltypesTest.cc new file mode 100644 index 000000000..298839d2a --- /dev/null +++ b/tests/unit/kernel/celltypesTest.cc @@ -0,0 +1,1215 @@ +#include +#include "kernel/celltypes.h" +#include "kernel/yosys.h" + +YOSYS_NAMESPACE_BEGIN + +struct ReferenceCellTypes +{ + CellTypes ct; + + void setup_internals_eval() + { + for (auto &t : std::vector{ + "$not", "$pos", "$buf", "$neg", + "$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_xnor", + "$reduce_bool", + "$logic_not", "$slice", "$lut", "$sop"}) + ct.setup_type(t, {ID::A}, {ID::Y}, true); + for (auto &t : std::vector{ + "$and", "$or", "$xor", "$xnor", + "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", + "$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt", + "$add", "$sub", "$mul", "$div", "$mod", "$divfloor", "$modfloor", + "$pow", + "$logic_and", "$logic_or", "$concat", "$macc", + "$bweqx"}) + ct.setup_type(t, {ID::A, ID::B}, {ID::Y}, true); + ct.setup_type(ID($mux), {ID::A, ID::B, ID::S}, {ID::Y}, true); + ct.setup_type(ID($pmux), {ID::A, ID::B, ID::S}, {ID::Y}, true); + ct.setup_type(ID($bwmux), {ID::A, ID::B, ID::S}, {ID::Y}, true); + ct.setup_type(ID($bmux), {ID::A, ID::S}, {ID::Y}, true); + ct.setup_type(ID($demux), {ID::A, ID::S}, {ID::Y}, true); + ct.setup_type(ID($lcu), {ID::P, ID::G, ID::CI}, {ID::CO}, true); + ct.setup_type(ID($alu), {ID::A, ID::B, ID::CI, ID::BI}, {ID::X, ID::Y, ID::CO}, true); + ct.setup_type(ID($macc_v2), {ID::A, ID::B, ID::C}, {ID::Y}, true); + ct.setup_type(ID($fa), {ID::A, ID::B, ID::C}, {ID::X, ID::Y}, true); + } + + void setup_internals() + { + setup_internals_eval(); + ct.setup_type(ID($tribuf), {ID::A, ID::EN}, {ID::Y}, true); + ct.setup_type(ID($assert), {ID::A, ID::EN}, pool()); + ct.setup_type(ID($assume), {ID::A, ID::EN}, pool()); + ct.setup_type(ID($live), {ID::A, ID::EN}, pool()); + ct.setup_type(ID($fair), {ID::A, ID::EN}, pool()); + ct.setup_type(ID($cover), {ID::A, ID::EN}, pool()); + ct.setup_type(ID($initstate), pool(), {ID::Y}); + ct.setup_type(ID($anyconst), pool(), {ID::Y}); + ct.setup_type(ID($anyseq), pool(), {ID::Y}); + ct.setup_type(ID($allconst), pool(), {ID::Y}); + ct.setup_type(ID($allseq), pool(), {ID::Y}); + ct.setup_type(ID($equiv), {ID::A, ID::B}, {ID::Y}); + ct.setup_type(ID($specify2), {ID::EN, ID::SRC, ID::DST}, pool(), true); + ct.setup_type(ID($specify3), {ID::EN, ID::SRC, ID::DST, ID::DAT}, pool(), true); + ct.setup_type(ID($specrule), {ID::EN_SRC, ID::EN_DST, ID::SRC, ID::DST}, pool(), true); + ct.setup_type(ID($print), {ID::EN, ID::ARGS, ID::TRG}, pool()); + ct.setup_type(ID($check), {ID::A, ID::EN, ID::ARGS, ID::TRG}, pool()); + ct.setup_type(ID($set_tag), {ID::A, ID::SET, ID::CLR}, {ID::Y}); + ct.setup_type(ID($get_tag), {ID::A}, {ID::Y}); + ct.setup_type(ID($overwrite_tag), {ID::A, ID::SET, ID::CLR}, pool()); + ct.setup_type(ID($original_tag), {ID::A}, {ID::Y}); + ct.setup_type(ID($future_ff), {ID::A}, {ID::Y}); + ct.setup_type(ID($scopeinfo), pool(), pool()); + ct.setup_type(ID($input_port), pool(), {ID::Y}); + ct.setup_type(ID($connect), {ID::A, ID::B}, pool()); + } + + void setup_internals_ff() + { + ct.setup_type(ID($sr), {ID::SET, ID::CLR}, {ID::Q}); + ct.setup_type(ID($ff), {ID::D}, {ID::Q}); + ct.setup_type(ID($dff), {ID::CLK, ID::D}, {ID::Q}); + ct.setup_type(ID($dffe), {ID::CLK, ID::EN, ID::D}, {ID::Q}); + ct.setup_type(ID($dffsr), {ID::CLK, ID::SET, ID::CLR, ID::D}, {ID::Q}); + ct.setup_type(ID($dffsre), {ID::CLK, ID::SET, ID::CLR, ID::D, ID::EN}, {ID::Q}); + ct.setup_type(ID($adff), {ID::CLK, ID::ARST, ID::D}, {ID::Q}); + ct.setup_type(ID($adffe), {ID::CLK, ID::ARST, ID::D, ID::EN}, {ID::Q}); + ct.setup_type(ID($aldff), {ID::CLK, ID::ALOAD, ID::AD, ID::D}, {ID::Q}); + ct.setup_type(ID($aldffe), {ID::CLK, ID::ALOAD, ID::AD, ID::D, ID::EN}, {ID::Q}); + ct.setup_type(ID($sdff), {ID::CLK, ID::SRST, ID::D}, {ID::Q}); + ct.setup_type(ID($sdffe), {ID::CLK, ID::SRST, ID::D, ID::EN}, {ID::Q}); + ct.setup_type(ID($sdffce), {ID::CLK, ID::SRST, ID::D, ID::EN}, {ID::Q}); + ct.setup_type(ID($dlatch), {ID::EN, ID::D}, {ID::Q}); + ct.setup_type(ID($adlatch), {ID::EN, ID::D, ID::ARST}, {ID::Q}); + ct.setup_type(ID($dlatchsr), {ID::EN, ID::SET, ID::CLR, ID::D}, {ID::Q}); + } + + void setup_internals_anyinit() { ct.setup_type(ID($anyinit), {ID::D}, {ID::Q}); } + + void setup_internals_mem() + { + setup_internals_ff(); + ct.setup_type(ID($memrd), {ID::CLK, ID::EN, ID::ADDR}, {ID::DATA}); + ct.setup_type(ID($memrd_v2), {ID::CLK, ID::EN, ID::ARST, ID::SRST, ID::ADDR}, {ID::DATA}); + ct.setup_type(ID($memwr), {ID::CLK, ID::EN, ID::ADDR, ID::DATA}, pool()); + ct.setup_type(ID($memwr_v2), {ID::CLK, ID::EN, ID::ADDR, ID::DATA}, pool()); + ct.setup_type(ID($meminit), {ID::ADDR, ID::DATA}, pool()); + ct.setup_type(ID($meminit_v2), {ID::ADDR, ID::DATA, ID::EN}, pool()); + ct.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}); + ct.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}); + ct.setup_type(ID($fsm), {ID::CLK, ID::ARST, ID::CTRL_IN}, {ID::CTRL_OUT}); + } + + void setup_stdcells_eval() + { + ct.setup_type(ID($_BUF_), {ID::A}, {ID::Y}, true); + ct.setup_type(ID($_NOT_), {ID::A}, {ID::Y}, true); + ct.setup_type(ID($_AND_), {ID::A, ID::B}, {ID::Y}, true); + ct.setup_type(ID($_NAND_), {ID::A, ID::B}, {ID::Y}, true); + ct.setup_type(ID($_OR_), {ID::A, ID::B}, {ID::Y}, true); + ct.setup_type(ID($_NOR_), {ID::A, ID::B}, {ID::Y}, true); + ct.setup_type(ID($_XOR_), {ID::A, ID::B}, {ID::Y}, true); + ct.setup_type(ID($_XNOR_), {ID::A, ID::B}, {ID::Y}, true); + ct.setup_type(ID($_ANDNOT_), {ID::A, ID::B}, {ID::Y}, true); + ct.setup_type(ID($_ORNOT_), {ID::A, ID::B}, {ID::Y}, true); + ct.setup_type(ID($_MUX_), {ID::A, ID::B, ID::S}, {ID::Y}, true); + ct.setup_type(ID($_NMUX_), {ID::A, ID::B, ID::S}, {ID::Y}, true); + ct.setup_type(ID($_MUX4_), {ID::A, ID::B, ID::C, ID::D, ID::S, ID::T}, {ID::Y}, true); + ct.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); + ct.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); + ct.setup_type(ID($_AOI3_), {ID::A, ID::B, ID::C}, {ID::Y}, true); + ct.setup_type(ID($_OAI3_), {ID::A, ID::B, ID::C}, {ID::Y}, true); + ct.setup_type(ID($_AOI4_), {ID::A, ID::B, ID::C, ID::D}, {ID::Y}, true); + ct.setup_type(ID($_OAI4_), {ID::A, ID::B, ID::C, ID::D}, {ID::Y}, true); + } + + void setup_stdcells() { + setup_stdcells_eval(); + ct.setup_type(ID($_TBUF_), {ID::A, ID::E}, {ID::Y}, true); + } + + void setup_stdcells_mem() + { + std::string NP = "NP", ZO = "01"; + for (auto c1 : NP) for (auto c2 : NP) + ct.setup_type(stringf("$_SR_%c%c_", c1, c2), {ID::S, ID::R}, {ID::Q}); + + ct.setup_type(ID($_FF_), {ID::D}, {ID::Q}); + for (auto c1 : NP) + ct.setup_type(stringf("$_DFF_%c_", c1), {ID::C, ID::D}, {ID::Q}); + + for (auto c1 : NP) + for (auto c2 : NP) + ct.setup_type(stringf("$_DFFE_%c%c_", c1, c2), {ID::C, ID::D, ID::E}, {ID::Q}); + + for (auto c1 : NP) + for (auto c2 : NP) + for (auto c3 : ZO) + ct.setup_type(stringf("$_DFF_%c%c%c_", c1, c2, c3), {ID::C, ID::R, ID::D}, {ID::Q}); + + for (auto c1 : NP) + for (auto c2 : NP) + for (auto c3 : ZO) + for (auto c4 : NP) + ct.setup_type(stringf("$_DFFE_%c%c%c%c_", c1, c2, c3, c4), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}); + for (auto c1 : NP) + for (auto c2 : NP) + ct.setup_type(stringf("$_ALDFF_%c%c_", c1, c2), {ID::C, ID::L, ID::AD, ID::D}, {ID::Q}); + + for (auto c1 : NP) + for (auto c2 : NP) + for (auto c3 : NP) + ct.setup_type(stringf("$_ALDFFE_%c%c%c_", c1, c2, c3), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}); + + for (auto c1 : NP) + for (auto c2 : NP) + for (auto c3 : NP) + ct.setup_type(stringf("$_DFFSR_%c%c%c_", c1, c2, c3), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}); + + for (auto c1 : NP) + for (auto c2 : NP) + for (auto c3 : NP) + for (auto c4 : NP) + ct.setup_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 : NP) + for (auto c2 : NP) + for (auto c3 : ZO) + ct.setup_type(stringf("$_SDFF_%c%c%c_", c1, c2, c3), {ID::C, ID::R, ID::D}, {ID::Q}); + + for (auto c1 : NP) + for (auto c2 : NP) + for (auto c3 : ZO) + for (auto c4 : NP) + ct.setup_type(stringf("$_SDFFE_%c%c%c%c_", c1, c2, c3, c4), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}); + + for (auto c1 : NP) + for (auto c2 : NP) + for (auto c3 : ZO) + for (auto c4 : NP) + ct.setup_type(stringf("$_SDFFCE_%c%c%c%c_", c1, c2, c3, c4), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}); + + for (auto c1 : NP) + ct.setup_type(stringf("$_DLATCH_%c_", c1), {ID::E, ID::D}, {ID::Q}); + + for (auto c1 : NP) + for (auto c2 : NP) + for (auto c3 : ZO) + ct.setup_type(stringf("$_DLATCH_%c%c%c_", c1, c2, c3), {ID::E, ID::R, ID::D}, {ID::Q}); + + for (auto c1 : NP) + for (auto c2 : NP) + for (auto c3 : NP) + ct.setup_type(stringf("$_DLATCHSR_%c%c%c_", c1, c2, c3), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}); + } + + void setup() { + setup_internals(); + setup_internals_mem(); + setup_internals_anyinit(); + setup_stdcells(); + setup_stdcells_mem(); + } +}; + +static std::vector all_cell_type_names() { + ReferenceCellTypes ref; + ref.setup(); + std::vector n; + for (auto &it : ref.ct.cell_types) + n.push_back(it.first); + + std::sort(n.begin(), n.end()); return n; +} + +static std::vector all_port_names() { + ReferenceCellTypes ref; + ref.setup(); + pool ports; + for (auto &it : ref.ct.cell_types) { + for (auto &p : it.second.inputs) + ports.insert(p); + for (auto &p : it.second.outputs) + ports.insert(p); + } + + for (auto id : {ID::Y, ID::Q, ID::A, ID::B, ID::D, ID::S, ID::EN, ID::CLK, ID::SET, ID::CLR, ID::ARST, ID::SRST, ID::ALOAD}) + ports.insert(id); + + std::vector r(ports.begin(), ports.end()); std::sort(r.begin(), r.end()); + return r; +} + +struct SetupScenario { std::string name; std::function setup_ref; std::function setup_dut; }; + +static void ref_setup_internals_eval(ReferenceCellTypes &r) { r.setup_internals_eval(); } +static void ref_setup_internals(ReferenceCellTypes &r) { r.setup_internals(); } +static void ref_setup_internals_ff(ReferenceCellTypes &r) { r.setup_internals_ff(); } +static void ref_setup_internals_anyinit(ReferenceCellTypes &r) { r.setup_internals_anyinit(); } +static void ref_setup_internals_mem(ReferenceCellTypes &r) { r.setup_internals_mem(); } +static void ref_setup_stdcells_eval(ReferenceCellTypes &r) { r.setup_stdcells_eval(); } +static void ref_setup_stdcells(ReferenceCellTypes &r) { r.setup_stdcells(); } +static void ref_setup_stdcells_mem(ReferenceCellTypes &r) { r.setup_stdcells_mem(); } +static void ref_const_eval(ReferenceCellTypes &r) { r.setup_internals(); r.setup_stdcells(); } +static void ref_int_mem_std(ReferenceCellTypes &r) { r.setup_internals(); r.setup_internals_mem(); r.setup_stdcells(); } +static void ref_full_setup(ReferenceCellTypes &r) { r.setup(); } +static void ref_only_ffs(ReferenceCellTypes &r) { r.setup_internals_ff(); r.setup_stdcells_mem(); } +static void ref_only_eval(ReferenceCellTypes &r) { r.setup_internals_eval(); r.setup_stdcells_eval(); } +static void ref_inc_eval_ff_mem(ReferenceCellTypes &r) { r.setup_internals_eval(); r.setup_internals_ff(); r.setup_internals_mem(); } +static void ref_std_eval_mem(ReferenceCellTypes &r) { r.setup_stdcells_eval(); r.setup_stdcells_mem(); } +static void ref_everything_no_anyinit(ReferenceCellTypes &r) { r.setup_internals(); r.setup_internals_mem(); r.setup_stdcells(); r.setup_stdcells_mem(); } + +static void dut_setup_internals_eval(CellTypes &d) { d.setup_internals_eval(); } +static void dut_setup_internals(CellTypes &d) { d.setup_internals(); } +static void dut_setup_internals_ff(CellTypes &d) { d.setup_internals_ff(); } +static void dut_setup_internals_anyinit(CellTypes &d) { d.setup_internals_anyinit(); } +static void dut_setup_internals_mem(CellTypes &d) { d.setup_internals_mem(); } +static void dut_setup_stdcells_eval(CellTypes &d) { d.setup_stdcells_eval(); } +static void dut_setup_stdcells(CellTypes &d) { d.setup_stdcells(); } +static void dut_setup_stdcells_mem(CellTypes &d) { d.setup_stdcells_mem(); } +static void dut_const_eval(CellTypes &d) { d.setup_internals(); d.setup_stdcells(); } +static void dut_int_mem_std(CellTypes &d) { d.setup_internals(); d.setup_internals_mem(); d.setup_stdcells(); } +static void dut_full_setup(CellTypes &d) { d.setup(); } +static void dut_only_ffs(CellTypes &d) { d.setup_internals_ff(); d.setup_stdcells_mem(); } +static void dut_only_eval(CellTypes &d) { d.setup_internals_eval(); d.setup_stdcells_eval(); } +static void dut_inc_eval_ff_mem(CellTypes &d) { d.setup_internals_eval(); d.setup_internals_ff(); d.setup_internals_mem(); } +static void dut_std_eval_mem(CellTypes &d) { d.setup_stdcells_eval(); d.setup_stdcells_mem(); } +static void dut_everything_no_anyinit(CellTypes &d) { d.setup_internals(); d.setup_internals_mem(); d.setup_stdcells(); d.setup_stdcells_mem(); } + +static std::vector all_scenarios() { + return { + {"setup_internals_eval", ref_setup_internals_eval, dut_setup_internals_eval}, + {"setup_internals", ref_setup_internals, dut_setup_internals}, + {"setup_internals_ff", ref_setup_internals_ff, dut_setup_internals_ff}, + {"setup_internals_anyinit", ref_setup_internals_anyinit, dut_setup_internals_anyinit}, + {"setup_internals_mem", ref_setup_internals_mem, dut_setup_internals_mem}, + {"setup_stdcells_eval", ref_setup_stdcells_eval, dut_setup_stdcells_eval}, + {"setup_stdcells", ref_setup_stdcells, dut_setup_stdcells}, + {"setup_stdcells_mem", ref_setup_stdcells_mem, dut_setup_stdcells_mem}, + {"ConstEval pattern", ref_const_eval, dut_const_eval}, + {"internals+mem+stdcells", ref_int_mem_std, dut_int_mem_std}, + {"full setup()", ref_full_setup, dut_full_setup}, + {"only FFs", ref_only_ffs, dut_only_ffs}, + {"only eval", ref_only_eval, dut_only_eval}, + {"incremental: eval+ff+mem", ref_inc_eval_ff_mem, dut_inc_eval_ff_mem}, + {"anyinit alone", ref_setup_internals_anyinit, dut_setup_internals_anyinit}, + {"stdcells_eval+stdcells_mem", ref_std_eval_mem, dut_std_eval_mem}, + {"everything except anyinit", ref_everything_no_anyinit, dut_everything_no_anyinit}, + }; +} +class CellTypesTest : public testing::Test { +protected: + CellTypesTest() { if (log_files.empty()) log_files.emplace_back(stdout); } + virtual void SetUp() override { IdString::ensure_prepopulated(); } +}; + +TEST_F(CellTypesTest, MuxPorts) +{ + CellTypes dut; + dut.setup(); + EXPECT_TRUE(dut.cell_input(ID($mux), ID::A)); + EXPECT_TRUE(dut.cell_input(ID($mux), ID::B)); + EXPECT_TRUE(dut.cell_input(ID($mux), ID::S)); + EXPECT_TRUE(dut.cell_output(ID($mux), ID::Y)); + EXPECT_FALSE(dut.cell_input(ID($mux), ID::Y)); + EXPECT_FALSE(dut.cell_output(ID($mux), ID::A)); +} + +TEST_F(CellTypesTest, MemCells_CorrectPorts) +{ + CellTypes dut; + dut.setup(); + EXPECT_TRUE(dut.cell_output(ID($mem), ID::RD_DATA)); + EXPECT_FALSE(dut.cell_output(ID($mem), ID::Y)); + EXPECT_FALSE(dut.cell_output(ID($mem), ID::Q)); + EXPECT_TRUE(dut.cell_output(ID($mem_v2), ID::RD_DATA)); + EXPECT_TRUE(dut.cell_input(ID($mem_v2), ID::WR_DATA)); + EXPECT_TRUE(dut.cell_input(ID($mem_v2), ID::RD_ADDR)); +} + +TEST_F(CellTypesTest, MemrdPorts) +{ + CellTypes dut; + dut.setup(); + EXPECT_TRUE(dut.cell_input(ID($memrd), ID::ADDR)); + EXPECT_TRUE(dut.cell_input(ID($memrd), ID::EN)); + EXPECT_TRUE(dut.cell_input(ID($memrd), ID::CLK)); + EXPECT_TRUE(dut.cell_output(ID($memrd), ID::DATA)); + EXPECT_FALSE(dut.cell_output(ID($memrd), ID::Y)); +} + +TEST_F(CellTypesTest, FsmPorts) +{ + CellTypes dut; + dut.setup(); + EXPECT_TRUE(dut.cell_input(ID($fsm), ID::CLK)); + EXPECT_TRUE(dut.cell_input(ID($fsm), ID::ARST)); + EXPECT_TRUE(dut.cell_input(ID($fsm), ID::CTRL_IN)); + EXPECT_TRUE(dut.cell_output(ID($fsm), ID::CTRL_OUT)); + EXPECT_FALSE(dut.cell_output(ID($fsm), ID::Y)); +} + +TEST_F(CellTypesTest, TribufPorts) +{ + CellTypes dut; + dut.setup(); + EXPECT_TRUE(dut.cell_input(ID($tribuf), ID::A)); + EXPECT_TRUE(dut.cell_input(ID($tribuf), ID::EN)); + EXPECT_TRUE(dut.cell_output(ID($tribuf), ID::Y)); + EXPECT_FALSE(dut.cell_output(ID($tribuf), ID::Q)); +} + +TEST_F(CellTypesTest, TbufPorts) +{ + CellTypes dut; + dut.setup(); + EXPECT_TRUE(dut.cell_input(ID($_TBUF_), ID::A)); + EXPECT_TRUE(dut.cell_input(ID($_TBUF_), ID::E)); + EXPECT_TRUE(dut.cell_output(ID($_TBUF_), ID::Y)); +} + +TEST_F(CellTypesTest, SpecifyPorts) +{ + CellTypes dut; + dut.setup(); + EXPECT_TRUE(dut.cell_input(ID($specify2), ID::EN)); + EXPECT_TRUE(dut.cell_input(ID($specify2), ID::SRC)); + EXPECT_TRUE(dut.cell_input(ID($specify2), ID::DST)); + EXPECT_FALSE(dut.cell_output(ID($specify2), ID::Y)); + EXPECT_TRUE(dut.cell_input(ID($specify3), ID::DAT)); + EXPECT_TRUE(dut.cell_input(ID($specrule), ID::EN_SRC)); + EXPECT_TRUE(dut.cell_input(ID($specrule), ID::EN_DST)); +} + +TEST_F(CellTypesTest, CellsWithNoOutputs) +{ + CellTypes dut; + dut.setup(); + auto ports = all_port_names(); + for (auto &id : {ID($assert), ID($assume), ID($live), ID($fair), ID($cover), + ID($memwr), ID($memwr_v2), ID($meminit), ID($meminit_v2), + ID($print), ID($check), ID($overwrite_tag), ID($connect)}) { + EXPECT_TRUE(dut.cell_known(id)) << id.c_str(); + bool has_output = false; + for (auto &p : ports) + if (dut.cell_output(id, p)) + has_output = true; + + EXPECT_FALSE(has_output) << id.c_str() << " should have no outputs"; + } +} + +TEST_F(CellTypesTest, CellsWithNoInputs) +{ + CellTypes dut; + dut.setup(); + auto ports = all_port_names(); + for (auto &id : {ID($initstate), ID($anyconst), ID($anyseq), + ID($allconst), ID($allseq), ID($scopeinfo), ID($input_port)}) { + EXPECT_TRUE(dut.cell_known(id)) << id.c_str(); + bool has_input = false; + for (auto &p : ports) + if (dut.cell_input(id, p)) + has_input = true; + + EXPECT_FALSE(has_input) << id.c_str() << " should have no inputs"; + } +} + +TEST_F(CellTypesTest, ScopeinfoHasNoPorts) +{ + CellTypes dut; + dut.setup(); + EXPECT_TRUE(dut.cell_known(ID($scopeinfo))); + EXPECT_FALSE(dut.cell_input(ID($scopeinfo), ID::A)); + EXPECT_FALSE(dut.cell_output(ID($scopeinfo), ID::Y)); +} + +TEST_F(CellTypesTest, InternalFFs_HaveQ_NotY) +{ + CellTypes dut; + dut.setup(); + for (auto &type : {ID($sr), 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)}) { + SCOPED_TRACE(std::string("cell: ") + type.c_str()); + EXPECT_TRUE(dut.cell_known(type)); + EXPECT_TRUE(dut.cell_output(type, ID::Q)); + EXPECT_FALSE(dut.cell_output(type, ID::Y)); + } +} + +TEST_F(CellTypesTest, StdcellFFs_HaveQ_NotY) +{ + CellTypes dut; + dut.setup(); + ReferenceCellTypes ref; + ref.setup_stdcells_mem(); + for (auto &it : ref.ct.cell_types) { + SCOPED_TRACE(std::string("cell: ") + it.first.c_str()); + EXPECT_TRUE(dut.cell_known(it.first)); + EXPECT_TRUE(dut.cell_output(it.first, ID::Q)); + EXPECT_FALSE(dut.cell_output(it.first, ID::Y)); + } +} + +TEST_F(CellTypesTest, AnyinitHasQ_NotY) +{ + CellTypes dut; + dut.setup(); + EXPECT_TRUE(dut.cell_output(ID($anyinit), ID::Q)); + EXPECT_FALSE(dut.cell_output(ID($anyinit), ID::Y)); + EXPECT_TRUE(dut.cell_input(ID($anyinit), ID::D)); +} + +TEST_F(CellTypesTest, PortDirConsistency) +{ + CellTypes dut; + dut.setup(); + auto cell_names = all_cell_type_names(); + auto ports = all_port_names(); + for (auto &type : cell_names) { + if (!dut.cell_known(type)) + continue; + for (auto &port : ports) { + auto dir = dut.cell_port_dir(type, port); + bool is_in = dut.cell_input(type, port); + bool is_out = dut.cell_output(type, port); + auto expected = RTLIL::PortDir((int)is_in + (int)is_out * 2); + EXPECT_EQ(dir, expected) << type.c_str() << " port " << port.c_str(); + } + } +} + +TEST_F(CellTypesTest, PortDirUnknownCell) +{ + CellTypes dut; + dut.setup(); + EXPECT_EQ(dut.cell_port_dir(IdString("$fake_cell"), ID::A), RTLIL::PD_UNKNOWN); +} + +TEST_F(CellTypesTest, IdempotentSetup) +{ + auto cell_names = all_cell_type_names(); + CellTypes dut1; + dut1.setup_internals(); + dut1.setup_stdcells(); + CellTypes dut2; + dut2.setup_internals(); + dut2.setup_stdcells(); + dut2.setup_internals(); + dut2.setup_stdcells(); + for (auto &type : cell_names) { + EXPECT_EQ(dut1.cell_known(type), dut2.cell_known(type)) << type.c_str(); + EXPECT_EQ(dut1.cell_evaluable(type), dut2.cell_evaluable(type)) << type.c_str(); + } +} + +TEST_F(CellTypesTest, IncrementalSetupAddsCategories) +{ + CellTypes dut; + dut.setup_internals_eval(); + EXPECT_TRUE(dut.cell_known(ID($add))); + EXPECT_FALSE(dut.cell_known(ID($dff))); + EXPECT_FALSE(dut.cell_known(ID($tribuf))); + dut.setup_internals(); + EXPECT_TRUE(dut.cell_known(ID($add))); + EXPECT_FALSE(dut.cell_known(ID($dff))); + EXPECT_TRUE(dut.cell_known(ID($tribuf))); + dut.setup_internals_mem(); + EXPECT_TRUE(dut.cell_known(ID($add))); + EXPECT_TRUE(dut.cell_known(ID($dff))); + EXPECT_TRUE(dut.cell_known(ID($tribuf))); + EXPECT_TRUE(dut.cell_known(ID($memrd))); +} + +TEST_F(CellTypesTest, IncrementalSetupStdcells) +{ + CellTypes dut; + dut.setup_stdcells_eval(); + EXPECT_TRUE(dut.cell_known(ID($_AND_))); + EXPECT_FALSE(dut.cell_known(ID($_TBUF_))); + EXPECT_FALSE(dut.cell_known(ID($_FF_))); + dut.setup_stdcells(); + EXPECT_TRUE(dut.cell_known(ID($_AND_))); + EXPECT_TRUE(dut.cell_known(ID($_TBUF_))); + EXPECT_FALSE(dut.cell_known(ID($_FF_))); + dut.setup_stdcells_mem(); + EXPECT_TRUE(dut.cell_known(ID($_AND_))); + EXPECT_TRUE(dut.cell_known(ID($_TBUF_))); + EXPECT_TRUE(dut.cell_known(ID($_FF_))); +} + +TEST_F(CellTypesTest, CustomCellsAlongsideBuiltins) +{ + CellTypes dut; + dut.setup(); + IdString custom_id("$my_custom_cell"); + dut.setup_type(custom_id, {ID::A, ID::B}, {ID::Y}, true); + EXPECT_TRUE(dut.cell_known(custom_id)); + EXPECT_TRUE(dut.cell_output(custom_id, ID::Y)); + EXPECT_TRUE(dut.cell_input(custom_id, ID::A)); + EXPECT_TRUE(dut.cell_evaluable(custom_id)); + EXPECT_TRUE(dut.cell_known(ID($add))); + EXPECT_TRUE(dut.cell_known(ID($dff))); +} + +TEST_F(CellTypesTest, CustomCellOverridesBuiltin) +{ + CellTypes dut; + dut.setup(); + dut.setup_type(ID($add), {ID::A}, {ID::Q}, false); + EXPECT_TRUE(dut.cell_known(ID($add))); +} + +TEST_F(CellTypesTest, BitmaskConstants) +{ + EXPECT_EQ(CellTypes::BITS_ALL, + CellTypes::BIT_INTERNALS_OTHER | CellTypes::BIT_INTERNALS_EVAL | + CellTypes::BIT_INTERNALS_FF | CellTypes::BIT_INTERNALS_ANYINIT | + CellTypes::BIT_INTERNALS_MEM | CellTypes::BIT_STDCELLS_EVAL | + CellTypes::BIT_STDCELLS_TRISTATE | CellTypes::BIT_STDCELLS_FF); + EXPECT_EQ(CellTypes::BIT_INTERNALS_OTHER, 0x0001); + EXPECT_EQ(CellTypes::BIT_INTERNALS_EVAL, 0x0002); + EXPECT_EQ(CellTypes::BIT_INTERNALS_FF, 0x0004); + EXPECT_EQ(CellTypes::BIT_INTERNALS_ANYINIT, 0x0008); + EXPECT_EQ(CellTypes::BIT_INTERNALS_MEM, 0x0010); + EXPECT_EQ(CellTypes::BIT_STDCELLS_EVAL, 0x0020); + EXPECT_EQ(CellTypes::BIT_STDCELLS_TRISTATE, 0x0040); + EXPECT_EQ(CellTypes::BIT_STDCELLS_FF, 0x0080); + EXPECT_EQ(CellTypes::BITS_ALL, 0x00FF); +} + +TEST_F(CellTypesTest, EnabledCatsStartsAtZero) +{ + CellTypes dut; + EXPECT_EQ(dut.enabled_cats, 0); +} + +TEST_F(CellTypesTest, SetupMethodsSetsCorrectBits) +{ + { + CellTypes dut; + dut.setup_internals_eval(); + EXPECT_TRUE(dut.enabled_cats & CellTypes::BIT_INTERNALS_EVAL); + EXPECT_FALSE(dut.enabled_cats & CellTypes::BIT_INTERNALS_OTHER); + EXPECT_FALSE(dut.enabled_cats & CellTypes::BIT_INTERNALS_FF); + } + { + CellTypes dut; + dut.setup_internals(); + EXPECT_TRUE(dut.enabled_cats & CellTypes::BIT_INTERNALS_EVAL); + EXPECT_TRUE(dut.enabled_cats & CellTypes::BIT_INTERNALS_OTHER); + EXPECT_FALSE(dut.enabled_cats & CellTypes::BIT_INTERNALS_FF); + EXPECT_FALSE(dut.enabled_cats & CellTypes::BIT_INTERNALS_MEM); + EXPECT_FALSE(dut.enabled_cats & CellTypes::BIT_INTERNALS_ANYINIT); + } + { + CellTypes dut; + dut.setup_internals_ff(); + EXPECT_TRUE(dut.enabled_cats & CellTypes::BIT_INTERNALS_FF); + EXPECT_FALSE(dut.enabled_cats & CellTypes::BIT_INTERNALS_EVAL); + } + { + CellTypes dut; + dut.setup_internals_mem(); + EXPECT_TRUE(dut.enabled_cats & CellTypes::BIT_INTERNALS_FF); + EXPECT_TRUE(dut.enabled_cats & CellTypes::BIT_INTERNALS_MEM); + } + { + CellTypes dut; + dut.setup_internals_anyinit(); + EXPECT_TRUE(dut.enabled_cats & CellTypes::BIT_INTERNALS_ANYINIT); + EXPECT_EQ(dut.enabled_cats, CellTypes::BIT_INTERNALS_ANYINIT); + } + { + CellTypes dut; + dut.setup_stdcells_eval(); + EXPECT_TRUE(dut.enabled_cats & CellTypes::BIT_STDCELLS_EVAL); + EXPECT_FALSE(dut.enabled_cats & CellTypes::BIT_STDCELLS_TRISTATE); + EXPECT_FALSE(dut.enabled_cats & CellTypes::BIT_STDCELLS_FF); + } + { + CellTypes dut; + dut.setup_stdcells(); + EXPECT_TRUE(dut.enabled_cats & CellTypes::BIT_STDCELLS_EVAL); + EXPECT_TRUE(dut.enabled_cats & CellTypes::BIT_STDCELLS_TRISTATE); + EXPECT_FALSE(dut.enabled_cats & CellTypes::BIT_STDCELLS_FF); + } + { + CellTypes dut; + dut.setup_stdcells_mem(); + EXPECT_TRUE(dut.enabled_cats & CellTypes::BIT_STDCELLS_FF); + EXPECT_FALSE(dut.enabled_cats & CellTypes::BIT_STDCELLS_EVAL); + } + { + CellTypes dut; + dut.setup(); + EXPECT_EQ(dut.enabled_cats, CellTypes::BITS_ALL); + } +} + +TEST_F(CellTypesTest, Mux16Ports) +{ + CellTypes dut; + dut.setup(); + for (auto &id : {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}) + EXPECT_TRUE(dut.cell_input(ID($_MUX16_), id)) << id.c_str(); + EXPECT_TRUE(dut.cell_output(ID($_MUX16_), ID::Y)); + EXPECT_FALSE(dut.cell_output(ID($_MUX16_), ID::Q)); +} + +TEST_F(CellTypesTest, Mux8Ports) +{ + CellTypes dut; + dut.setup(); + for (auto &id : {ID::A, ID::B, ID::C, ID::D, ID::E, ID::F, ID::G, ID::H, + ID::S, ID::T, ID::U}) + EXPECT_TRUE(dut.cell_input(ID($_MUX8_), id)) << id.c_str(); + EXPECT_TRUE(dut.cell_output(ID($_MUX8_), ID::Y)); +} + +TEST_F(CellTypesTest, StdcellDffVariants) +{ + CellTypes dut; + dut.setup(); + for (auto &name : {"$_DFF_P_", "$_DFF_N_"}) { + EXPECT_TRUE(dut.cell_known(IdString(name))) << name; + EXPECT_TRUE(dut.cell_input(IdString(name), ID::C)); + EXPECT_TRUE(dut.cell_input(IdString(name), ID::D)); + EXPECT_TRUE(dut.cell_output(IdString(name), ID::Q)); + } + for (auto &name : {"$_DFFE_PP_", "$_DFFE_PN_", "$_DFFE_NP_", "$_DFFE_NN_"}) { + EXPECT_TRUE(dut.cell_known(IdString(name))) << name; + EXPECT_TRUE(dut.cell_input(IdString(name), ID::E)); + } +} + +TEST_F(CellTypesTest, StdcellDffResetVariants) +{ + CellTypes dut; + dut.setup(); + for (auto &name : {"$_DFF_PP0_", "$_DFF_PP1_", "$_DFF_PN0_", "$_DFF_PN1_", + "$_DFF_NP0_", "$_DFF_NP1_", "$_DFF_NN0_", "$_DFF_NN1_"}) { + EXPECT_TRUE(dut.cell_known(IdString(name))) << name; + EXPECT_TRUE(dut.cell_input(IdString(name), ID::R)); + } +} + +TEST_F(CellTypesTest, StdcellLatchVariants) +{ + CellTypes dut; + dut.setup(); + EXPECT_TRUE(dut.cell_known(ID($_DLATCH_P_))); + EXPECT_TRUE(dut.cell_known(ID($_DLATCH_N_))); + EXPECT_TRUE(dut.cell_input(ID($_DLATCH_P_), ID::E)); + EXPECT_TRUE(dut.cell_input(ID($_DLATCH_P_), ID::D)); + EXPECT_TRUE(dut.cell_output(ID($_DLATCH_P_), ID::Q)); +} + +TEST_F(CellTypesTest, StdcellSrVariants) +{ + CellTypes dut; + dut.setup(); + for (auto &name : {"$_SR_PP_", "$_SR_PN_", "$_SR_NP_", "$_SR_NN_"}) { + EXPECT_TRUE(dut.cell_known(IdString(name))) << name; + EXPECT_TRUE(dut.cell_input(IdString(name), ID::S)); + EXPECT_TRUE(dut.cell_input(IdString(name), ID::R)); + EXPECT_TRUE(dut.cell_output(IdString(name), ID::Q)); + } +} + +TEST_F(CellTypesTest, FastPathEquivalence) +{ + auto cell_names = all_cell_type_names(); + auto ports = all_port_names(); + + CellTypes fast; + fast.setup(); + EXPECT_EQ(fast.enabled_cats, CellTypes::BITS_ALL); + + CellTypes manual; + manual.setup_internals_eval(); + manual.setup_internals(); + manual.setup_internals_ff(); + manual.setup_internals_anyinit(); + manual.setup_internals_mem(); + manual.setup_stdcells_eval(); + manual.setup_stdcells(); + manual.setup_stdcells_mem(); + EXPECT_EQ(manual.enabled_cats, CellTypes::BITS_ALL); + + for (auto &type : cell_names) { + EXPECT_EQ(fast.cell_known(type), manual.cell_known(type)) << type.c_str(); + EXPECT_EQ(fast.cell_evaluable(type), manual.cell_evaluable(type)) << type.c_str(); + for (auto &port : ports) { + EXPECT_EQ(fast.cell_output(type, port), manual.cell_output(type, port)) + << type.c_str() << " " << port.c_str(); + EXPECT_EQ(fast.cell_input(type, port), manual.cell_input(type, port)) + << type.c_str() << " " << port.c_str(); + } + } +} + +TEST_F(CellTypesTest, CellKnown_AllScenarios) +{ + auto cn = all_cell_type_names(); + for (auto &sc : all_scenarios()) { + SCOPED_TRACE("scenario: " + sc.name); + ReferenceCellTypes ref; + sc.setup_ref(ref); + CellTypes dut; + sc.setup_dut(dut); + for (auto &t : cn) { + EXPECT_EQ(ref.ct.cell_known(t), dut.cell_known(t)) << t.c_str(); + } + } +} + +TEST_F(CellTypesTest, CellOutput_AllScenarios) +{ + auto cn = all_cell_type_names(); + auto pp = all_port_names(); + for (auto &sc : all_scenarios()) { + SCOPED_TRACE("scenario: " + sc.name); + ReferenceCellTypes ref; + sc.setup_ref(ref); + CellTypes dut; + sc.setup_dut(dut); + for (auto &t : cn) { + for (auto &p : pp) { + EXPECT_EQ(ref.ct.cell_output(t, p), dut.cell_output(t, p)) << t.c_str() << " " << p.c_str(); + } + } + } +} + +TEST_F(CellTypesTest, CellInput_AllScenarios) +{ + auto cn = all_cell_type_names(); + auto pp = all_port_names(); + for (auto &sc : all_scenarios()) { + SCOPED_TRACE("scenario: " + sc.name); + ReferenceCellTypes ref; + sc.setup_ref(ref); + CellTypes dut; + sc.setup_dut(dut); + for (auto &t : cn) { + for (auto &p : pp) { + EXPECT_EQ(ref.ct.cell_input(t, p), dut.cell_input(t, p)) << t.c_str() << " " << p.c_str(); + } + } + } +} + +TEST_F(CellTypesTest, CellEvaluable_AllScenarios) +{ + auto cn = all_cell_type_names(); + for (auto &sc : all_scenarios()) { + SCOPED_TRACE("scenario: " + sc.name); + ReferenceCellTypes ref; + sc.setup_ref(ref); + CellTypes dut; + sc.setup_dut(dut); + for (auto &t : cn) { + EXPECT_EQ(ref.ct.cell_evaluable(t), dut.cell_evaluable(t)) << t.c_str(); + } + } +} + +TEST_F(CellTypesTest, CellPortDir_AllScenarios) +{ + auto cn = all_cell_type_names(); + auto pp = all_port_names(); + for (auto &sc : all_scenarios()) { + SCOPED_TRACE("scenario: " + sc.name); + ReferenceCellTypes ref; + sc.setup_ref(ref); + CellTypes dut; + sc.setup_dut(dut); + for (auto &t : cn) { + for (auto &p : pp) { + EXPECT_EQ(ref.ct.cell_port_dir(t, p), dut.cell_port_dir(t, p)) << t.c_str() << " " << p.c_str(); + } + } + } +} + +TEST_F(CellTypesTest, CellCount_MatchesReference) +{ + auto cn = all_cell_type_names(); + for (auto &sc : all_scenarios()) { + SCOPED_TRACE("scenario: " + sc.name); + ReferenceCellTypes ref; + sc.setup_ref(ref); + CellTypes dut; + sc.setup_dut(dut); + int rc = 0, dc = 0; + for (auto &t : cn) { + if (ref.ct.cell_known(t)) rc++; + if (dut.cell_known(t)) dc++; + } + EXPECT_EQ(rc, dc); + } +} + +TEST_F(CellTypesTest, UnknownCells_NotKnown) +{ + std::vector nc = { + ID($bogus_cell), IdString("$fake"), IdString("$_FAKE_"), + ID::A, ID::B, ID::Y, ID::Q, ID::WIDTH + }; + for (auto &sc : all_scenarios()) { + SCOPED_TRACE("scenario: " + sc.name); + CellTypes dut; + sc.setup_dut(dut); + for (auto &id : nc) { + EXPECT_FALSE(dut.cell_known(id)) << id.c_str(); + } + } +} + +TEST_F(CellTypesTest, EmptyKnowsNothing) +{ + CellTypes dut; + for (auto &t : all_cell_type_names()) { + EXPECT_FALSE(dut.cell_known(t)) << t.c_str(); + } +} + +TEST_F(CellTypesTest, ClearResetsState) +{ + CellTypes dut; + dut.setup(); + EXPECT_TRUE(dut.cell_known(ID($add))); + EXPECT_TRUE(dut.cell_known(ID($dff))); + EXPECT_TRUE(dut.cell_known(ID($_AND_))); + dut.clear(); + EXPECT_FALSE(dut.cell_known(ID($add))); + EXPECT_FALSE(dut.cell_known(ID($dff))); + EXPECT_FALSE(dut.cell_known(ID($_AND_))); +} + +TEST_F(CellTypesTest, ClearThenResetup) +{ + CellTypes dut; + dut.setup(); + dut.clear(); + dut.setup_internals_eval(); + EXPECT_TRUE(dut.cell_known(ID($add))); + EXPECT_FALSE(dut.cell_known(ID($dff))); + EXPECT_FALSE(dut.cell_known(ID($_AND_))); +} + +TEST_F(CellTypesTest, ConstEvalPattern_NoFFs) +{ + CellTypes dut; + dut.setup_internals(); + dut.setup_stdcells(); + EXPECT_FALSE(dut.cell_known(ID($dff))); + EXPECT_FALSE(dut.cell_known(ID($ff))); + EXPECT_FALSE(dut.cell_known(ID($adff))); + EXPECT_FALSE(dut.cell_known(ID($sr))); + EXPECT_FALSE(dut.cell_known(ID($dlatch))); + EXPECT_FALSE(dut.cell_known(ID($memrd))); + EXPECT_FALSE(dut.cell_known(ID($mem))); + EXPECT_FALSE(dut.cell_known(ID($fsm))); + EXPECT_FALSE(dut.cell_known(ID($anyinit))); + EXPECT_FALSE(dut.cell_known(ID($_FF_))); + EXPECT_FALSE(dut.cell_known(ID($_DFF_P_))); + EXPECT_FALSE(dut.cell_known(ID($_SR_PP_))); + EXPECT_TRUE(dut.cell_known(ID($add))); + EXPECT_TRUE(dut.cell_known(ID($mux))); + EXPECT_TRUE(dut.cell_known(ID($_AND_))); + EXPECT_TRUE(dut.cell_known(ID($tribuf))); + EXPECT_TRUE(dut.cell_known(ID($assert))); + EXPECT_TRUE(dut.cell_known(ID($_TBUF_))); +} + +TEST_F(CellTypesTest, EvalOnlyExcludes_OtherCells) +{ + CellTypes dut; + dut.setup_internals_eval(); + EXPECT_TRUE(dut.cell_known(ID($add))); + EXPECT_FALSE(dut.cell_known(ID($tribuf))); + EXPECT_FALSE(dut.cell_known(ID($assert))); + EXPECT_FALSE(dut.cell_known(ID($specify2))); + EXPECT_FALSE(dut.cell_known(ID($scopeinfo))); +} + +TEST_F(CellTypesTest, StdcellEvalOnlyExcludes_TBUF) +{ + CellTypes dut; + dut.setup_stdcells_eval(); + EXPECT_TRUE(dut.cell_known(ID($_AND_))); + EXPECT_FALSE(dut.cell_known(ID($_TBUF_))); + EXPECT_FALSE(dut.cell_known(ID($_FF_))); +} + +TEST_F(CellTypesTest, FFOnlyDoesNotKnowEvalCells) +{ + CellTypes dut; + dut.setup_internals_ff(); + EXPECT_TRUE(dut.cell_known(ID($dff))); + EXPECT_FALSE(dut.cell_known(ID($add))); + EXPECT_FALSE(dut.cell_known(ID($_AND_))); +} + +TEST_F(CellTypesTest, AnyinitOnlyDoesNotKnowAnythingElse) +{ + CellTypes dut; + dut.setup_internals_anyinit(); + EXPECT_TRUE(dut.cell_known(ID($anyinit))); + EXPECT_FALSE(dut.cell_known(ID($dff))); + EXPECT_FALSE(dut.cell_known(ID($add))); + EXPECT_FALSE(dut.cell_known(ID($memrd))); +} + +TEST_F(CellTypesTest, StdcellMemDoesNotKnowInternalFFs) +{ + CellTypes dut; + dut.setup_stdcells_mem(); + EXPECT_TRUE(dut.cell_known(ID($_FF_))); + EXPECT_FALSE(dut.cell_known(ID($dff))); + EXPECT_FALSE(dut.cell_known(ID($add))); +} + +TEST_F(CellTypesTest, InternalFFDoesNotKnowStdcellFF) +{ + CellTypes dut; + dut.setup_internals_ff(); + EXPECT_TRUE(dut.cell_known(ID($dff))); + EXPECT_FALSE(dut.cell_known(ID($_DFF_P_))); +} + +TEST_F(CellTypesTest, FullSetup_AllKnown) +{ + CellTypes dut; + dut.setup(); + for (auto &t : all_cell_type_names()) { + EXPECT_TRUE(dut.cell_known(t)) << t.c_str(); + } +} + +TEST_F(CellTypesTest, TribufEvaluable) +{ + CellTypes dut; + dut.setup(); + EXPECT_TRUE(dut.cell_evaluable(ID($tribuf))); +} + +TEST_F(CellTypesTest, SpecifyCellsEvaluable) +{ + CellTypes dut; + dut.setup(); + EXPECT_TRUE(dut.cell_evaluable(ID($specify2))); + EXPECT_TRUE(dut.cell_evaluable(ID($specify3))); + EXPECT_TRUE(dut.cell_evaluable(ID($specrule))); +} + +TEST_F(CellTypesTest, TbufEvaluable) +{ + CellTypes dut; + dut.setup(); + EXPECT_TRUE(dut.cell_evaluable(ID($_TBUF_))); +} + +TEST_F(CellTypesTest, FFsNotEvaluable) +{ + CellTypes dut; + dut.setup(); + for (auto &id : {ID($dff), ID($ff), ID($adff), ID($sdff), ID($sr), ID($dlatch)}) { + EXPECT_TRUE(dut.cell_known(id)) << id.c_str(); + EXPECT_FALSE(dut.cell_evaluable(id)) << id.c_str(); + } +} + +TEST_F(CellTypesTest, StdcellFFsNotEvaluable) +{ + CellTypes dut; + dut.setup(); + for (auto &id : {ID($_FF_), ID($_DFF_P_), ID($_DFFE_PP_), ID($_DLATCH_P_), ID($_SR_PP_)}) { + EXPECT_TRUE(dut.cell_known(id)) << id.c_str(); + EXPECT_FALSE(dut.cell_evaluable(id)) << id.c_str(); + } +} + +TEST_F(CellTypesTest, MemCellsNotEvaluable) +{ + CellTypes dut; + dut.setup(); + for (auto &id : {ID($memrd), ID($memrd_v2), ID($memwr), ID($memwr_v2), ID($meminit), ID($meminit_v2), ID($mem), ID($mem_v2), ID($fsm)}) { + EXPECT_TRUE(dut.cell_known(id)) << id.c_str(); + EXPECT_FALSE(dut.cell_evaluable(id)) << id.c_str(); + } +} + +TEST_F(CellTypesTest, AnyinitNotEvaluable) +{ + CellTypes dut; + dut.setup(); + EXPECT_TRUE(dut.cell_known(ID($anyinit))); + EXPECT_FALSE(dut.cell_evaluable(ID($anyinit))); +} + +TEST_F(CellTypesTest, OtherCellsNotEvaluable) +{ + CellTypes dut; + dut.setup(); + for (auto &id : {ID($assert), ID($assume), ID($cover), ID($print), ID($check), ID($connect), ID($scopeinfo)}) { + EXPECT_TRUE(dut.cell_known(id)) << id.c_str(); + EXPECT_FALSE(dut.cell_evaluable(id)) << id.c_str(); + } +} + +TEST_F(CellTypesTest, AllEvalCellsAreEvaluable) +{ + CellTypes dut; + dut.setup(); + for (auto &id : {ID($add), ID($sub), ID($mul), ID($and), ID($or), ID($xor), ID($not), ID($mux), ID($pmux), ID($shl), ID($shr), ID($lt), ID($le), ID($eq), ID($ne), ID($ge), ID($gt), ID($lcu), ID($alu), ID($fa), ID($bmux), ID($demux), ID($reduce_and), ID($reduce_or), ID($logic_not), ID($logic_and), ID($logic_or), ID($concat), ID($bweqx)}) { + EXPECT_TRUE(dut.cell_evaluable(id)) << id.c_str(); + } +} + +TEST_F(CellTypesTest, AllStdcellEvalCellsAreEvaluable) +{ + CellTypes dut; + dut.setup(); + for (auto &id : {ID($_BUF_), ID($_NOT_), ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_), ID($_MUX_), ID($_NMUX_), ID($_MUX4_), ID($_MUX8_), ID($_MUX16_), ID($_AOI3_), ID($_OAI3_), ID($_AOI4_), ID($_OAI4_)}) { + EXPECT_TRUE(dut.cell_evaluable(id)) << id.c_str(); + } +} + +TEST_F(CellTypesTest, CellOutput_FalseForNonOutputPorts) +{ + CellTypes dut; + dut.setup(); + EXPECT_TRUE(dut.cell_output(ID($add), ID::Y)); + EXPECT_FALSE(dut.cell_output(ID($add), ID::A)); + EXPECT_TRUE(dut.cell_output(ID($dff), ID::Q)); + EXPECT_FALSE(dut.cell_output(ID($dff), ID::Y)); +} + +TEST_F(CellTypesTest, CellInput_FalseForNonInputPorts) +{ + CellTypes dut; + dut.setup(); + EXPECT_TRUE(dut.cell_input(ID($add), ID::A)); + EXPECT_FALSE(dut.cell_input(ID($add), ID::Y)); + EXPECT_TRUE(dut.cell_input(ID($dff), ID::D)); + EXPECT_FALSE(dut.cell_input(ID($dff), ID::Q)); +} + +TEST_F(CellTypesTest, AluPorts) +{ + CellTypes dut; + dut.setup(); + EXPECT_TRUE(dut.cell_output(ID($alu), ID::X)); + EXPECT_TRUE(dut.cell_output(ID($alu), ID::Y)); + EXPECT_TRUE(dut.cell_output(ID($alu), ID::CO)); + EXPECT_TRUE(dut.cell_input(ID($alu), ID::A)); + EXPECT_TRUE(dut.cell_input(ID($alu), ID::BI)); + EXPECT_FALSE(dut.cell_input(ID($alu), ID::Y)); + EXPECT_TRUE(dut.cell_evaluable(ID($alu))); +} + +TEST_F(CellTypesTest, FaPorts) +{ + CellTypes dut; + dut.setup(); + EXPECT_TRUE(dut.cell_output(ID($fa), ID::X)); + EXPECT_TRUE(dut.cell_output(ID($fa), ID::Y)); + EXPECT_TRUE(dut.cell_input(ID($fa), ID::C)); + EXPECT_FALSE(dut.cell_output(ID($fa), ID::Q)); +} + +TEST_F(CellTypesTest, LcuPorts) +{ + CellTypes dut; + dut.setup(); + EXPECT_TRUE(dut.cell_input(ID($lcu), ID::P)); + EXPECT_TRUE(dut.cell_input(ID($lcu), ID::G)); + EXPECT_TRUE(dut.cell_output(ID($lcu), ID::CO)); + EXPECT_FALSE(dut.cell_output(ID($lcu), ID::Y)); +} + +TEST_F(CellTypesTest, SetupSetsCorrectBits_Eval) +{ + CellTypes dut; + dut.setup_internals_eval(); + EXPECT_TRUE(dut.enabled_cats & CellTypes::BIT_INTERNALS_EVAL); + EXPECT_FALSE(dut.enabled_cats & CellTypes::BIT_INTERNALS_OTHER); + EXPECT_FALSE(dut.enabled_cats & CellTypes::BIT_INTERNALS_FF); +} + +TEST_F(CellTypesTest, SetupSetsCorrectBits_Internals) +{ + CellTypes dut; + dut.setup_internals(); + EXPECT_TRUE(dut.enabled_cats & CellTypes::BIT_INTERNALS_EVAL); + EXPECT_TRUE(dut.enabled_cats & CellTypes::BIT_INTERNALS_OTHER); + EXPECT_FALSE(dut.enabled_cats & CellTypes::BIT_INTERNALS_FF); + EXPECT_FALSE(dut.enabled_cats & CellTypes::BIT_INTERNALS_MEM); +} + +TEST_F(CellTypesTest, SetupSetsCorrectBits_FF) +{ + CellTypes dut; + dut.setup_internals_ff(); + EXPECT_TRUE(dut.enabled_cats & CellTypes::BIT_INTERNALS_FF); + EXPECT_FALSE(dut.enabled_cats & CellTypes::BIT_INTERNALS_EVAL); +} + +TEST_F(CellTypesTest, SetupSetsCorrectBits_Mem) +{ + CellTypes dut; + dut.setup_internals_mem(); + EXPECT_TRUE(dut.enabled_cats & CellTypes::BIT_INTERNALS_FF); + EXPECT_TRUE(dut.enabled_cats & CellTypes::BIT_INTERNALS_MEM); +} + +TEST_F(CellTypesTest, SetupSetsCorrectBits_Anyinit) +{ + CellTypes dut; + dut.setup_internals_anyinit(); + EXPECT_EQ(dut.enabled_cats, CellTypes::BIT_INTERNALS_ANYINIT); +} + +TEST_F(CellTypesTest, SetupSetsCorrectBits_StdcellsEval) +{ + CellTypes dut; + dut.setup_stdcells_eval(); + EXPECT_TRUE(dut.enabled_cats & CellTypes::BIT_STDCELLS_EVAL); + EXPECT_FALSE(dut.enabled_cats & CellTypes::BIT_STDCELLS_TRISTATE); +} + +TEST_F(CellTypesTest, SetupSetsCorrectBits_Stdcells) +{ + CellTypes dut; + dut.setup_stdcells(); + EXPECT_TRUE(dut.enabled_cats & CellTypes::BIT_STDCELLS_EVAL); + EXPECT_TRUE(dut.enabled_cats & CellTypes::BIT_STDCELLS_TRISTATE); + EXPECT_FALSE(dut.enabled_cats & CellTypes::BIT_STDCELLS_FF); +} + +TEST_F(CellTypesTest, SetupSetsCorrectBits_StdcellsMem) +{ + CellTypes dut; + dut.setup_stdcells_mem(); + EXPECT_TRUE(dut.enabled_cats & CellTypes::BIT_STDCELLS_FF); + EXPECT_FALSE(dut.enabled_cats & CellTypes::BIT_STDCELLS_EVAL); +} + +TEST_F(CellTypesTest, SetupSetsCorrectBits_Full) +{ + CellTypes dut; + dut.setup(); + EXPECT_EQ(dut.enabled_cats, CellTypes::BITS_ALL); +} + +YOSYS_NAMESPACE_END