diff --git a/.gitignore b/.gitignore index a8b04ac45..ce3f9b2b0 100644 --- a/.gitignore +++ b/.gitignore @@ -67,7 +67,8 @@ # other /coverage.info /coverage_html - +kernel/gen_celltypes_data.h +kernel/gen_celltypes_data.cc # these really belong in global gitignore since they're not specific to this project but rather to user tool choice # but too many people don't have a global gitignore configured: diff --git a/Makefile b/Makefile index 28ee48ef9..f22530255 100644 --- a/Makefile +++ b/Makefile @@ -741,6 +741,8 @@ ifeq ($(LINK_ABC),1) OBJS += $(PROGRAM_PREFIX)yosys-libabc.a endif +OBJS += kernel/gen_celltypes_data.o + # prevent the CXXFLAGS set by this Makefile from reaching abc/Makefile, # especially the -MD flag which will break the build when CXX is clang unexport CXXFLAGS @@ -1183,6 +1185,12 @@ clean-abc: mrproper: clean git clean -xdf +# Generate cell type tables +GENFILES += kernel/gen_celltypes_data.cc kernel/gen_celltypes_data.h +kernel/gen_celltypes_data.cc kernel/gen_celltypes_data.h: kernel/constids.inc misc/gen_celltypes.py + $(P) $(PYTHON_EXECUTABLE) $(YOSYS_SRC)/misc/gen_celltypes.py $(YOSYS_SRC) +$(OBJS): | kernel/gen_celltypes_data.h + coverage: ./$(PROGRAM_PREFIX)yosys -qp 'help; help -all' rm -rf coverage.info coverage_html diff --git a/kernel/celltypes.h b/kernel/celltypes.h index 34b013dd9..c7126dfca 100644 --- a/kernel/celltypes.h +++ b/kernel/celltypes.h @@ -21,6 +21,7 @@ #define CELLTYPES_H #include "kernel/yosys.h" +#include "kernel/gen_celltypes_data.h" YOSYS_NAMESPACE_BEGIN @@ -37,20 +38,29 @@ struct CellTypes { dict cell_types; - CellTypes() - { - } + static constexpr uint16_t BIT_INTERNALS_OTHER = 0x0001; + static constexpr uint16_t BIT_INTERNALS_EVAL = 0x0002; + static constexpr uint16_t BIT_INTERNALS_FF = 0x0004; + static constexpr uint16_t BIT_INTERNALS_ANYINIT = 0x0008; + static constexpr uint16_t BIT_INTERNALS_MEM = 0x0010; + static constexpr uint16_t BIT_STDCELLS_EVAL = 0x0020; + static constexpr uint16_t BIT_STDCELLS_TRISTATE = 0x0040; + static constexpr uint16_t BIT_STDCELLS_FF = 0x0080; - CellTypes(RTLIL::Design *design) - { - setup(design); - } + static constexpr uint16_t BITS_ALL = BIT_INTERNALS_OTHER | BIT_INTERNALS_EVAL | + BIT_INTERNALS_FF | BIT_INTERNALS_ANYINIT | BIT_INTERNALS_MEM | + BIT_STDCELLS_EVAL | BIT_STDCELLS_TRISTATE | BIT_STDCELLS_FF; + + uint16_t enabled_cats = 0; + + CellTypes() {} + + CellTypes(RTLIL::Design *design) { setup(design); } void setup(RTLIL::Design *design = NULL) { if (design) setup_design(design); - setup_internals(); setup_internals_mem(); setup_internals_anyinit(); @@ -83,257 +93,149 @@ struct CellTypes setup_module(module); } - void setup_internals() - { - 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), {}, {}); - setup_type(ID($input_port), {}, {ID::Y}); - setup_type(ID($connect), {ID::A, ID::B}, {}); - } - - void setup_internals_eval() - { - std::vector unary_ops = { - ID($not), ID($pos), ID($buf), ID($neg), - ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool), - ID($logic_not), ID($slice), ID($lut), ID($sop) - }; - - std::vector binary_ops = { - ID($and), ID($or), ID($xor), ID($xnor), - ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx), - ID($lt), ID($le), ID($eq), ID($ne), ID($eqx), ID($nex), ID($ge), ID($gt), - ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($pow), - ID($logic_and), ID($logic_or), ID($concat), ID($macc), - ID($bweqx) - }; - - for (auto type : unary_ops) - setup_type(type, {ID::A}, {ID::Y}, true); - - for (auto type : binary_ops) - setup_type(type, {ID::A, ID::B}, {ID::Y}, true); - - for (auto type : std::vector({ID($mux), ID($pmux), ID($bwmux)})) - setup_type(type, {ID::A, ID::B, ID::S}, {ID::Y}, true); - - for (auto type : std::vector({ID($bmux), ID($demux)})) - setup_type(type, {ID::A, ID::S}, {ID::Y}, true); - - 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($macc_v2), {ID::A, ID::B, ID::C}, {ID::Y}, true); - setup_type(ID($fa), {ID::A, ID::B, ID::C}, {ID::X, ID::Y}, true); - } - - 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}); - } - - void setup_internals_anyinit() - { - setup_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_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); - } - - 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); - } - - void setup_stdcells_mem() - { - std::vector list_np = {'N', 'P'}, list_01 = {'0', '1'}; - - 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_type(ID($_FF_), {ID::D}, {ID::Q}); - - for (auto c1 : list_np) - setup_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}); - - 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}); - - 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}); - - 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}); - - 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}); - - 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}); - - 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}); - - 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}); - - 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}); - - 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}); - - for (auto c1 : list_np) - setup_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}); - - 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}); - } + void setup_internals() { enabled_cats |= BIT_INTERNALS_OTHER | BIT_INTERNALS_EVAL; } + void setup_internals_eval() { enabled_cats |= BIT_INTERNALS_EVAL; } + void setup_internals_ff() { enabled_cats |= BIT_INTERNALS_FF; } + void setup_internals_anyinit() { enabled_cats |= BIT_INTERNALS_ANYINIT; } + void setup_internals_mem() { enabled_cats |= BIT_INTERNALS_FF | BIT_INTERNALS_MEM; } + void setup_stdcells() { enabled_cats |= BIT_STDCELLS_EVAL | BIT_STDCELLS_TRISTATE; } + void setup_stdcells_eval() { enabled_cats |= BIT_STDCELLS_EVAL; } + void setup_stdcells_mem() { enabled_cats |= BIT_STDCELLS_FF; } void clear() { cell_types.clear(); + enabled_cats = 0; } - bool cell_known(RTLIL::IdString type) const + bool builtin_match(size_t idx) const { + if (!enabled_cats || idx >= (size_t)StaticCellTypes::GEN_MAX_CELLS) + return false; + + using namespace StaticCellTypes::GeneratedData; + + if (!is_known[idx]) + return false; + + if (!is_stdcell[idx]) { + if ((enabled_cats & BIT_INTERNALS_EVAL) && is_evaluable[idx]) + return true; + if ((enabled_cats & BIT_INTERNALS_FF) && is_ff[idx]) + return true; + if ((enabled_cats & BIT_INTERNALS_ANYINIT) && is_anyinit[idx]) + return true; + if ((enabled_cats & BIT_INTERNALS_MEM) && is_mem_noff[idx]) + return true; + if (enabled_cats & BIT_INTERNALS_OTHER) { + if (!is_evaluable[idx] && !is_ff[idx] && !is_mem_noff[idx] && !is_anyinit[idx]) + return true; + } + } else { + if ((enabled_cats & BIT_STDCELLS_EVAL) && is_evaluable[idx]) + return true; + if ((enabled_cats & BIT_STDCELLS_TRISTATE) && is_tristate[idx]) + return true; + if ((enabled_cats & BIT_STDCELLS_FF) && is_ff[idx]) + return true; + } + + return false; + } + + bool builtin_is_known(size_t idx) const + { + return enabled_cats && idx < (size_t)StaticCellTypes::GEN_MAX_CELLS && + StaticCellTypes::GeneratedData::is_known[idx]; + } + + bool cell_known(const RTLIL::IdString &type) const + { + if (enabled_cats == BITS_ALL) { + if (builtin_is_known(type.index_)) + return true; + } else if (builtin_match(type.index_)) { + return true; + } + return cell_types.count(type) != 0; } - bool cell_output(RTLIL::IdString type, RTLIL::IdString port) const + bool cell_output(const RTLIL::IdString &type, const RTLIL::IdString &port) const { + size_t idx = type.index_; + bool is_builtin = (enabled_cats == BITS_ALL) ? builtin_is_known(idx) : builtin_match(idx); + if (is_builtin) { + uint32_t target = (uint32_t)port.index_; + uint16_t count = StaticCellTypes::GeneratedData::port_outputs_counts[idx]; + 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; } - bool cell_input(RTLIL::IdString type, RTLIL::IdString port) const + bool cell_input(const RTLIL::IdString &type, const RTLIL::IdString &port) const { + size_t idx = type.index_; + bool is_builtin = (enabled_cats == BITS_ALL) ? builtin_is_known(idx) : builtin_match(idx); + if (is_builtin) { + uint32_t target = (uint32_t)port.index_; + uint16_t count = StaticCellTypes::GeneratedData::port_inputs_counts[idx]; + 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; } RTLIL::PortDir cell_port_dir(RTLIL::IdString type, RTLIL::IdString port) const { + size_t idx = type.index_; + bool is_builtin = (enabled_cats == BITS_ALL) ? builtin_is_known(idx) : builtin_match(idx); + if (is_builtin) { + bool is_in = false, is_out = false; + 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; + } + + 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; + } + + 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_input = it->second.inputs.count(port); - bool is_output = it->second.outputs.count(port); - return RTLIL::PortDir(is_input + is_output * 2); + + bool is_in = it->second.inputs.count(port); + bool is_out = it->second.outputs.count(port); + return RTLIL::PortDir(is_in + is_out * 2); } - bool cell_evaluable(RTLIL::IdString type) const + bool cell_evaluable(const RTLIL::IdString &type) const { + 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]; + auto it = cell_types.find(type); return it != cell_types.end() && it->second.is_evaluable; } @@ -343,10 +245,10 @@ struct CellTypes for (auto bit : v) if (bit == State::S0) bit = State::S1; else if (bit == State::S1) bit = State::S0; + return v; } - // Consider using the ConstEval struct instead if you need named ports and/or multiple outputs static RTLIL::Const eval(RTLIL::IdString type, const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len, bool *errp = nullptr) { if (type == ID($sshr) && !signed1) @@ -429,7 +331,6 @@ struct CellTypes log_abort(); } - // Consider using the ConstEval struct instead if you need named ports and/or multiple outputs static RTLIL::Const eval(RTLIL::Cell *cell, const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool *errp = nullptr) { if (cell->type == ID($slice)) { @@ -445,19 +346,13 @@ struct CellTypes } if (cell->type == ID($bmux)) - { return const_bmux(arg1, arg2); - } if (cell->type == ID($demux)) - { return const_demux(arg1, arg2); - } if (cell->type == ID($bweqx)) - { return const_bweqx(arg1, arg2); - } if (cell->type == ID($lut)) { @@ -466,6 +361,7 @@ 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); @@ -477,7 +373,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; @@ -489,11 +385,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; } @@ -515,7 +411,6 @@ struct CellTypes return eval(cell->type, arg1, arg2, signed_a, signed_b, result_len, errp); } - // Consider using the ConstEval struct instead if you need named ports and/or multiple outputs static RTLIL::Const eval(RTLIL::Cell *cell, const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3, bool *errp = nullptr) { if (cell->type.in(ID($mux), ID($_MUX_))) @@ -535,7 +430,6 @@ struct CellTypes return eval(cell, arg1, arg2, errp); } - // Consider using the ConstEval struct instead if you need named ports and/or multiple outputs static RTLIL::Const eval(RTLIL::Cell *cell, const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3, const RTLIL::Const &arg4, bool *errp = nullptr) { if (cell->type == ID($_AOI4_)) @@ -548,7 +442,6 @@ struct CellTypes } }; -// initialized by yosys_setup() extern CellTypes yosys_celltypes; YOSYS_NAMESPACE_END diff --git a/misc/gen_celltypes.py b/misc/gen_celltypes.py new file mode 100644 index 000000000..1ac03c055 --- /dev/null +++ b/misc/gen_celltypes.py @@ -0,0 +1,579 @@ +#!/usr/bin/env python3 +""" +Generate pre-computed static cell type tables. Outputs: + kernel/gen_celltypes_data.h + kernel/gen_celltypes_data.cc +""" + +import os +import re +import sys +from dataclasses import dataclass + +MAX_CELLS = 300 +MAX_PORTS = 20 + +# Build the IdString index map +def parse_constids(filepath): + index_map = {} + index = 1 # 0 is reserved for empty IdString + with open(filepath, "r") as f: + for line in f: + line = line.strip() + m = re.match(r"^X\((\S+)\)$", line) + if m: + name = m.group(1) + index_map[name] = index + index += 1 + return index_map + + +def resolve_id(index_map, name): + """Resolve a name (e.g. '$and', 'A', '$_BUF_') to its constids index.""" + if name not in index_map: + raise KeyError(f"ID '{name}' not found in constids.inc") + return index_map[name] + + +# Cell type table builder +@dataclass +class Features: + is_evaluable: bool = False + is_combinatorial: bool = False + is_synthesizable: bool = False + is_stdcell: bool = False + is_ff: bool = False + is_mem_noff: bool = False + is_anyinit: bool = False + is_tristate: bool = False + + +@dataclass +class CellInfo: + type_name: str + inputs: list + outputs: list + features: Features + + +def build_cell_table(): + cells = [] + + def setup_type(type_name, inputs, outputs, features): + cells.append(CellInfo(type_name, list(inputs), list(outputs), + Features(**vars(features)))) + + # setup_internals_other + f = Features(is_tristate=True) + setup_type("$tribuf", ["A", "EN"], ["Y"], f) + + f = Features() + setup_type("$assert", ["A", "EN"], [], f) + setup_type("$assume", ["A", "EN"], [], f) + setup_type("$live", ["A", "EN"], [], f) + setup_type("$fair", ["A", "EN"], [], f) + setup_type("$cover", ["A", "EN"], [], f) + setup_type("$initstate", [], ["Y"], f) + setup_type("$anyconst", [], ["Y"], f) + setup_type("$anyseq", [], ["Y"], f) + setup_type("$allconst", [], ["Y"], f) + setup_type("$allseq", [], ["Y"], f) + setup_type("$equiv", ["A", "B"], ["Y"], f) + 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) + setup_type("$print", ["EN", "ARGS", "TRG"], [], f) + setup_type("$check", ["A", "EN", "ARGS", "TRG"], [], f) + setup_type("$set_tag", ["A", "SET", "CLR"], ["Y"], f) + setup_type("$get_tag", ["A"], ["Y"], f) + setup_type("$overwrite_tag", ["A", "SET", "CLR"], [], f) + setup_type("$original_tag", ["A"], ["Y"], f) + setup_type("$future_ff", ["A"], ["Y"], f) + setup_type("$scopeinfo", [], [], f) + setup_type("$input_port", [], ["Y"], f) + setup_type("$connect", ["A", "B"], [], f) + + # setup_internals_eval + f = Features(is_evaluable=True) + + unary_ops = [ + "$not", "$pos", "$buf", "$neg", + "$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_xnor", + "$reduce_bool", + "$logic_not", "$slice", "$lut", "$sop", + ] + binary_ops = [ + "$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", + ] + + for t in unary_ops: + setup_type(t, ["A"], ["Y"], f) + for t in binary_ops: + setup_type(t, ["A", "B"], ["Y"], f) + + for t in ["$mux", "$pmux", "$bwmux"]: + setup_type(t, ["A", "B", "S"], ["Y"], f) + for t in ["$bmux", "$demux"]: + setup_type(t, ["A", "S"], ["Y"], f) + + setup_type("$lcu", ["P", "G", "CI"], ["CO"], f) + setup_type("$alu", ["A", "B", "CI", "BI"], ["X", "Y", "CO"], f) + setup_type("$macc_v2", ["A", "B", "C"], ["Y"], f) + setup_type("$fa", ["A", "B", "C"], ["X", "Y"], f) + + # setup_internals_ff + f = Features(is_ff=True) + setup_type("$sr", ["SET", "CLR"], ["Q"], f) + setup_type("$ff", ["D"], ["Q"], f) + setup_type("$dff", ["CLK", "D"], ["Q"], f) + setup_type("$dffe", ["CLK", "EN", "D"], ["Q"], f) + setup_type("$dffsr", ["CLK", "SET", "CLR", "D"], ["Q"], f) + setup_type("$dffsre", ["CLK", "SET", "CLR", "D", "EN"], ["Q"], f) + setup_type("$adff", ["CLK", "ARST", "D"], ["Q"], f) + setup_type("$adffe", ["CLK", "ARST", "D", "EN"], ["Q"], f) + setup_type("$aldff", ["CLK", "ALOAD", "AD", "D"], ["Q"], f) + setup_type("$aldffe", ["CLK", "ALOAD", "AD", "D", "EN"], ["Q"], f) + setup_type("$sdff", ["CLK", "SRST", "D"], ["Q"], f) + setup_type("$sdffe", ["CLK", "SRST", "D", "EN"], ["Q"], f) + setup_type("$sdffce", ["CLK", "SRST", "D", "EN"], ["Q"], f) + setup_type("$dlatch", ["EN", "D"], ["Q"], f) + setup_type("$adlatch", ["EN", "D", "ARST"], ["Q"], f) + setup_type("$dlatchsr", ["EN", "SET", "CLR", "D"], ["Q"], f) + + # setup_internals_anyinit + f = Features(is_anyinit=True) + setup_type("$anyinit", ["D"], ["Q"], f) + + # setup_internals_mem_noff + f = Features(is_mem_noff=True) + setup_type("$memrd", ["CLK", "EN", "ADDR"], ["DATA"], f) + setup_type("$memrd_v2", ["CLK", "EN", "ARST", "SRST", "ADDR"], + ["DATA"], f) + setup_type("$memwr", ["CLK", "EN", "ADDR", "DATA"], [], f) + setup_type("$memwr_v2", ["CLK", "EN", "ADDR", "DATA"], [], f) + setup_type("$meminit", ["ADDR", "DATA"], [], f) + setup_type("$meminit_v2", ["ADDR", "DATA", "EN"], [], f) + setup_type("$mem", + ["RD_CLK", "RD_EN", "RD_ADDR", + "WR_CLK", "WR_EN", "WR_ADDR", "WR_DATA"], + ["RD_DATA"], f) + setup_type("$mem_v2", + ["RD_CLK", "RD_EN", "RD_ARST", "RD_SRST", "RD_ADDR", + "WR_CLK", "WR_EN", "WR_ADDR", "WR_DATA"], + ["RD_DATA"], f) + setup_type("$fsm", ["CLK", "ARST", "CTRL_IN"], ["CTRL_OUT"], f) + + # setup_stdcells_tristate + f = Features(is_stdcell=True, is_tristate=True) + setup_type("$_TBUF_", ["A", "E"], ["Y"], f) + + # setup_stdcells_eval + f = Features(is_stdcell=True, is_evaluable=True) + setup_type("$_BUF_", ["A"], ["Y"], f) + setup_type("$_NOT_", ["A"], ["Y"], f) + setup_type("$_AND_", ["A", "B"], ["Y"], f) + setup_type("$_NAND_", ["A", "B"], ["Y"], f) + setup_type("$_OR_", ["A", "B"], ["Y"], f) + setup_type("$_NOR_", ["A", "B"], ["Y"], f) + setup_type("$_XOR_", ["A", "B"], ["Y"], f) + setup_type("$_XNOR_", ["A", "B"], ["Y"], f) + setup_type("$_ANDNOT_", ["A", "B"], ["Y"], f) + setup_type("$_ORNOT_", ["A", "B"], ["Y"], f) + setup_type("$_MUX_", ["A", "B", "S"], ["Y"], f) + setup_type("$_NMUX_", ["A", "B", "S"], ["Y"], f) + setup_type("$_MUX4_", ["A", "B", "C", "D", "S", "T"], ["Y"], f) + setup_type("$_MUX8_", + ["A", "B", "C", "D", "E", "F", "G", "H", "S", "T", "U"], + ["Y"], f) + setup_type("$_MUX16_", + ["A", "B", "C", "D", "E", "F", "G", "H", + "I", "J", "K", "L", "M", "N", "O", "P", + "S", "T", "U", "V"], + ["Y"], f) + setup_type("$_AOI3_", ["A", "B", "C"], ["Y"], f) + setup_type("$_OAI3_", ["A", "B", "C"], ["Y"], f) + setup_type("$_AOI4_", ["A", "B", "C", "D"], ["Y"], f) + setup_type("$_OAI4_", ["A", "B", "C", "D"], ["Y"], f) + + # setup_stdcells_ff + f = Features(is_stdcell=True, is_ff=True) + NP = ["N", "P"] + ZO = ["0", "1"] + + for c1 in NP: + for c2 in NP: + setup_type(f"$_SR_{c1}{c2}_", ["S", "R"], ["Q"], f) + + setup_type("$_FF_", ["D"], ["Q"], f) + + for c1 in NP: + setup_type(f"$_DFF_{c1}_", ["C", "D"], ["Q"], f) + + for c1 in NP: + for c2 in NP: + setup_type(f"$_DFFE_{c1}{c2}_", ["C", "D", "E"], ["Q"], f) + + for c1 in NP: + for c2 in NP: + for c3 in ZO: + setup_type(f"$_DFF_{c1}{c2}{c3}_", + ["C", "R", "D"], ["Q"], f) + + for c1 in NP: + for c2 in NP: + for c3 in ZO: + for c4 in NP: + setup_type(f"$_DFFE_{c1}{c2}{c3}{c4}_", + ["C", "R", "D", "E"], ["Q"], f) + + for c1 in NP: + for c2 in NP: + setup_type(f"$_ALDFF_{c1}{c2}_", + ["C", "L", "AD", "D"], ["Q"], f) + + for c1 in NP: + for c2 in NP: + for c3 in NP: + setup_type(f"$_ALDFFE_{c1}{c2}{c3}_", + ["C", "L", "AD", "D", "E"], ["Q"], f) + + for c1 in NP: + for c2 in NP: + for c3 in NP: + setup_type(f"$_DFFSR_{c1}{c2}{c3}_", + ["C", "S", "R", "D"], ["Q"], f) + + for c1 in NP: + for c2 in NP: + for c3 in NP: + for c4 in NP: + setup_type(f"$_DFFSRE_{c1}{c2}{c3}{c4}_", + ["C", "S", "R", "D", "E"], ["Q"], f) + + for c1 in NP: + for c2 in NP: + for c3 in ZO: + setup_type(f"$_SDFF_{c1}{c2}{c3}_", + ["C", "R", "D"], ["Q"], f) + + for c1 in NP: + for c2 in NP: + for c3 in ZO: + for c4 in NP: + setup_type(f"$_SDFFE_{c1}{c2}{c3}{c4}_", + ["C", "R", "D", "E"], ["Q"], f) + + for c1 in NP: + for c2 in NP: + for c3 in ZO: + for c4 in NP: + setup_type(f"$_SDFFCE_{c1}{c2}{c3}{c4}_", + ["C", "R", "D", "E"], ["Q"], f) + + for c1 in NP: + setup_type(f"$_DLATCH_{c1}_", ["E", "D"], ["Q"], f) + + for c1 in NP: + for c2 in NP: + for c3 in ZO: + setup_type(f"$_DLATCH_{c1}{c2}{c3}_", + ["E", "R", "D"], ["Q"], f) + + for c1 in NP: + for c2 in NP: + for c3 in NP: + setup_type(f"$_DLATCHSR_{c1}{c2}{c3}_", + ["E", "S", "R", "D"], ["Q"], f) + + return cells + + +def build_arrays(cells, index_map): + cats = { + "is_known": [0] * MAX_CELLS, + "is_evaluable": [0] * MAX_CELLS, + "is_combinatorial": [0] * MAX_CELLS, + "is_synthesizable": [0] * MAX_CELLS, + "is_stdcell": [0] * MAX_CELLS, + "is_ff": [0] * MAX_CELLS, + "is_mem_noff": [0] * MAX_CELLS, + "is_anyinit": [0] * MAX_CELLS, + "is_tristate": [0] * MAX_CELLS, + } + + input_counts = [0] * MAX_CELLS + input_ports = [[0] * MAX_PORTS for _ in range(MAX_CELLS)] + output_counts = [0] * MAX_CELLS + output_ports = [[0] * MAX_PORTS for _ in range(MAX_CELLS)] + + for cell in cells: + idx = resolve_id(index_map, cell.type_name) + if idx >= MAX_CELLS: + print(f"WARNING: '{cell.type_name}' index {idx} >= MAX_CELLS " + f"({MAX_CELLS}), increase MAX_CELLS", file=sys.stderr) + 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"]: + if getattr(cell.features, feat): + cats[feat][idx] = 1 + + input_counts[idx] = len(cell.inputs) + for j, port in enumerate(cell.inputs): + input_ports[idx][j] = resolve_id(index_map, port) + + output_counts[idx] = len(cell.outputs) + for j, port in enumerate(cell.outputs): + output_ports[idx][j] = resolve_id(index_map, port) + + # Compat derived categories + def bor(a, b): + return [int(a[i] or b[i]) for i in range(MAX_CELLS)] + def band(a, b): + return [int(a[i] and b[i]) for i in range(MAX_CELLS)] + def bnot(a): + return [int(not a[i]) for i in range(MAX_CELLS)] + + mem_ff = bor(cats["is_ff"], cats["is_mem_noff"]) + internals_all = band(cats["is_known"], bnot(cats["is_stdcell"])) + nomem_noff = band(cats["is_known"], bnot(mem_ff)) + + compat = { + "compat_internals_all": internals_all, + "compat_mem_ff": mem_ff, + "compat_nomem_noff": nomem_noff, + "compat_internals_mem_ff": band(internals_all, mem_ff), + "compat_internals_nomem_noff": band(internals_all, nomem_noff), + "compat_stdcells_nomem_noff": band(cats["is_stdcell"], nomem_noff), + "compat_stdcells_mem": band(cats["is_stdcell"], cats["is_mem_noff"]), + } + + return (cats, compat, + input_counts, input_ports, output_counts, output_ports) + + +def fmt_u8(name, data): + lines = [f"const uint8_t {name}[{MAX_CELLS}] = {{"] + for i in range(0, MAX_CELLS, 25): + chunk = data[i:i + 25] + lines.append(" " + ",".join(str(v) for v in chunk) + ",") + lines.append("};") + return "\n".join(lines) + + +def fmt_u16(name, data): + lines = [f"const uint16_t {name}[{MAX_CELLS}] = {{"] + for i in range(0, MAX_CELLS, 25): + chunk = data[i:i + 25] + lines.append(" " + ",".join(str(v) for v in chunk) + ",") + lines.append("};") + return "\n".join(lines) + + +def fmt_u32_2d(name, data): + lines = [f"const uint32_t {name}[{MAX_CELLS}][{MAX_PORTS}] = {{"] + for i in range(MAX_CELLS): + lines.append(" {" + ",".join(str(v) for v in data[i]) + "},") + lines.append("};") + return "\n".join(lines) + + +def generate_cc(cats, compat, in_c, in_p, out_c, out_p): + parts = [ + "// AUTO-GENERATED FILE - DO NOT EDIT", + "// Generated by misc/gen_celltypes.py from kernel/constids.inc", + "// Source of truth for cell definitions: misc/gen_celltypes.py", + "//", + "// Regenerate with: make kernel/gen_celltypes_data.cc", + "", + '#include "kernel/gen_celltypes_data.h"', + "", + "YOSYS_NAMESPACE_BEGIN", + "namespace StaticCellTypes {", + "namespace GeneratedData {", + "", + ] + + for name, data in cats.items(): + parts.append(fmt_u8(name, data)) + parts.append("") + + for name, data in compat.items(): + parts.append(fmt_u8(name, data)) + parts.append("") + + parts.append(fmt_u16("port_inputs_counts", in_c)) + parts.append("") + parts.append(fmt_u32_2d("port_inputs_ports", in_p)) + parts.append("") + parts.append(fmt_u16("port_outputs_counts", out_c)) + parts.append("") + parts.append(fmt_u32_2d("port_outputs_ports", out_p)) + parts.append("") + + parts.extend([ + "} // namespace GeneratedData", + "} // namespace StaticCellTypes", + "YOSYS_NAMESPACE_END", + "", + ]) + return "\n".join(parts) + + +def generate_header(): + return f"""\ +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated by misc/gen_celltypes.py from kernel/constids.inc +// Regenerate with: make kernel/gen_celltypes_data.cc + +#ifndef GEN_CELLTYPES_DATA_H +#define GEN_CELLTYPES_DATA_H + +#include "kernel/yosys.h" +#include + +YOSYS_NAMESPACE_BEGIN +namespace StaticCellTypes {{ + +constexpr int GEN_MAX_CELLS = {MAX_CELLS}; +constexpr int GEN_MAX_PORTS = {MAX_PORTS}; + +namespace GeneratedData {{ + +extern const uint8_t is_known[GEN_MAX_CELLS]; +extern const uint8_t is_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]; +extern const uint8_t is_ff[GEN_MAX_CELLS]; +extern const uint8_t is_mem_noff[GEN_MAX_CELLS]; +extern const uint8_t is_anyinit[GEN_MAX_CELLS]; +extern const uint8_t is_tristate[GEN_MAX_CELLS]; + +extern const uint8_t compat_internals_all[GEN_MAX_CELLS]; +extern const uint8_t compat_mem_ff[GEN_MAX_CELLS]; +extern const uint8_t compat_nomem_noff[GEN_MAX_CELLS]; +extern const uint8_t compat_internals_mem_ff[GEN_MAX_CELLS]; +extern const uint8_t compat_internals_nomem_noff[GEN_MAX_CELLS]; +extern const uint8_t compat_stdcells_nomem_noff[GEN_MAX_CELLS]; +extern const uint8_t compat_stdcells_mem[GEN_MAX_CELLS]; + +extern const uint16_t port_inputs_counts[GEN_MAX_CELLS]; +extern const uint32_t port_inputs_ports[GEN_MAX_CELLS][GEN_MAX_PORTS]; +extern const uint16_t port_outputs_counts[GEN_MAX_CELLS]; +extern const uint32_t port_outputs_ports[GEN_MAX_CELLS][GEN_MAX_PORTS]; + +}} // namespace GeneratedData + +struct GenCategory {{ + const uint8_t* data; + bool operator()(RTLIL::IdString type) const {{ + size_t idx = type.index_; + return idx < GEN_MAX_CELLS && data[idx]; + }} +}}; + +struct GenPortLookup {{ + const uint16_t* counts; + const uint32_t (*ports)[GEN_MAX_PORTS]; + + bool contains(RTLIL::IdString type, RTLIL::IdString port) const {{ + size_t idx = type.index_; + if (idx >= GEN_MAX_CELLS) + return false; + uint16_t count = counts[idx]; + for (uint16_t i = 0; i < count; i++) {{ + if (ports[idx][i] == (uint32_t)port.index_) + return true; + }} + return false; + }} +}}; + +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_compat_internals_all() {{ return {{GeneratedData::compat_internals_all}}; }} +inline GenCategory gen_compat_mem_ff() {{ return {{GeneratedData::compat_mem_ff}}; }} +inline GenCategory gen_compat_nomem_noff() {{ return {{GeneratedData::compat_nomem_noff}}; }} +inline GenCategory gen_compat_internals_mem_ff() {{ return {{GeneratedData::compat_internals_mem_ff}}; }} +inline GenCategory gen_compat_internals_nomem_noff() {{ return {{GeneratedData::compat_internals_nomem_noff}}; }} +inline GenCategory gen_compat_stdcells_nomem_noff() {{ return {{GeneratedData::compat_stdcells_nomem_noff}}; }} +inline GenCategory gen_compat_stdcells_mem() {{ return {{GeneratedData::compat_stdcells_mem}}; }} + +inline GenPortLookup gen_port_inputs() {{ + return {{GeneratedData::port_inputs_counts, GeneratedData::port_inputs_ports}}; +}} + +inline GenPortLookup gen_port_outputs() {{ + return {{GeneratedData::port_outputs_counts, GeneratedData::port_outputs_ports}}; +}} + +}} // namespace StaticCellTypes +YOSYS_NAMESPACE_END + +#endif // GEN_CELLTYPES_DATA_H +""" + + +def main(): + if len(sys.argv) > 1: + yosys_src = sys.argv[1] + else: + yosys_src = os.environ.get("YOSYS_SRC", ".") + + constids_path = os.path.join(yosys_src, "kernel", "constids.inc") + header_out = os.path.join(yosys_src, "kernel", "gen_celltypes_data.h") + data_out = os.path.join(yosys_src, "kernel", "gen_celltypes_data.cc") + + if not os.path.exists(constids_path): + print(f"Error: {constids_path} not found.", file=sys.stderr) + sys.exit(1) + + # Parse + print(f"Parsing {constids_path}...", file=sys.stderr) + index_map = parse_constids(constids_path) + print(f" {len(index_map)} IdString constants", file=sys.stderr) + + # Build tables + cells = build_cell_table() + print(f" {len(cells)} cell types defined", file=sys.stderr) + + try: + result = build_arrays(cells, index_map) + except KeyError as e: + print(f"Error: {e}", file=sys.stderr) + print(f" kernel/constids.inc may be missing entries.", + file=sys.stderr) + sys.exit(1) + + cats, compat, in_c, in_p, out_c, out_p = result + + # Write header + with open(header_out, "w") as f: + f.write(generate_header()) + print(f"Wrote {header_out}", file=sys.stderr) + + # Write data + with open(data_out, "w") as f: + f.write(generate_cc(cats, compat, in_c, in_p, out_c, out_p)) + print(f"Wrote {data_out}", file=sys.stderr) + + +if __name__ == "__main__": + main()