From 40f1df4c1fb5a0d484550853092c8ae20d951c43 Mon Sep 17 00:00:00 2001 From: Iztok Jeras Date: Wed, 24 Jun 2026 04:22:39 +0200 Subject: [PATCH] frontends/liberty: support all \$_LATCH* type cells (copy/modify from FF code) --- frontends/liberty/liberty.cc | 86 +++++++++++++++++++++++++++++------- 1 file changed, 70 insertions(+), 16 deletions(-) diff --git a/frontends/liberty/liberty.cc b/frontends/liberty/liberty.cc index 76c1ecca3..fed405e17 100644 --- a/frontends/liberty/liberty.cc +++ b/frontends/liberty/liberty.cc @@ -220,13 +220,13 @@ static void create_ff(RTLIL::Module *module, const LibertyAst *node) if (child->id == "preset") preset_sig = parse_func_expr(module, child->value.c_str()); - for (auto& [id, var] : {pair{"clear_preset_var1", &clear_preset_var1}, {"clear_preset_var2", &clear_preset_var2}}) + for (auto& [id, var] : {pair{"clear_preset_var1", &clear_preset_var1}, {"clear_preset_var2", &clear_preset_var2}}) { if (child->id == id) { if (child->value.size() != 1) log_error("Unexpected length of clear_preset_var* value %s in FF cell %s\n", child->value, name); *var = child->value[0]; } - + } } if (clk_sig.size() == 0 || data_sig.size() == 0) @@ -296,7 +296,10 @@ static bool create_latch(RTLIL::Module *module, const LibertyAst *node, bool fla { auto [iq_sig, iqn_sig] = find_latch_ff_wires(module, node); RTLIL::SigSpec enable_sig, data_sig, clear_sig, preset_sig; + const std::string name = module->name.unescape(); + std::optional clear_preset_var1; + std::optional clear_preset_var2; for (auto child : node->children) { if (child->id == "enable") enable_sig = parse_func_expr(module, child->value.c_str()); @@ -306,32 +309,83 @@ static bool create_latch(RTLIL::Module *module, const LibertyAst *node, bool fla clear_sig = parse_func_expr(module, child->value.c_str()); if (child->id == "preset") preset_sig = parse_func_expr(module, child->value.c_str()); + + for (auto& [id, var] : {pair{"clear_preset_var1", &clear_preset_var1}, {"clear_preset_var2", &clear_preset_var2}}) { + if (child->id == id) { + if (child->value.size() != 1) + log_error("Unexpected length of clear_preset_var* value %s in LATCH cell %s\n", child->value, name); + *var = child->value[0]; + } + } } if (enable_sig.size() == 0 || data_sig.size() == 0) { if (!flag_ignore_miss_data_latch) - log_error("Latch cell %s has no data_in and/or enable attribute.\n", module); + log_error("LATCH cell %s has no data_in and/or enable attribute.\n", name); else - log("Ignored latch cell %s with no data_in and/or enable attribute.\n", module); + log("Ignored LATCH cell %s with no data_in and/or enable attribute.\n", name); return false; } - RTLIL::Cell *cell = module->addCell(NEW_ID, ID($_NOT_)); - cell->setPort(ID::A, iq_sig); - cell->setPort(ID::Y, iqn_sig); + for (auto& [out_sig, cp_var, neg] : {tuple{iq_sig, clear_preset_var1, false}, {iqn_sig, clear_preset_var2, true}}) { + SigSpec q_sig = out_sig; + if (neg) { + q_sig = module->addWire(NEW_ID, out_sig.as_wire()); + module->addNotGate(NEW_ID, q_sig, out_sig); + } - if (clear_sig.size() == 1) { - RTLIL::SigSpec clear_negative = module->NotGate(NEW_ID, clear_sig); - data_sig = module->AndGate(NEW_ID, data_sig, clear_negative); - enable_sig = module->OrGate(NEW_ID, enable_sig, clear_sig); - } + RTLIL::Cell* cell = module->addCell(NEW_ID, ""); + cell->setPort(ID::D, data_sig); + cell->setPort(ID::Q, q_sig); + cell->setPort(ID::E, enable_sig); - if (preset_sig.size() == 1) { - data_sig = module->OrGate(NEW_ID, data_sig, preset_sig); - enable_sig = module->OrGate(NEW_ID, enable_sig, preset_sig); + if (clear_sig.size() == 0 && preset_sig.size() == 0) { + cell->type = ID::$_DLATCH_P_; + } + + if (clear_sig.size() == 1 && preset_sig.size() == 0) { + cell->type = ID::$_DLATCH_PP0_; + cell->setPort(ID::R, clear_sig); + } + + if (clear_sig.size() == 0 && preset_sig.size() == 1) { + cell->type = ID::$_DLATCH_PP1_; + cell->setPort(ID::R, preset_sig); + } + + if (clear_sig.size() == 1 && preset_sig.size() == 1) { + cell->type = ID::$_DLATCHSR_PPP_; + + SigBit s_sig = preset_sig; + SigBit r_sig = clear_sig; + if (cp_var && *cp_var != 'X') { + // Either set or reset dominates + bool set_dominates; + if (*cp_var == 'L') { + set_dominates = neg; + } else if (*cp_var == 'H') { + set_dominates = !neg; + } else { + log_error("LATCH cell %s has unsupported clear&preset behavior \'%c\'.\n", name, *cp_var); + } + log_debug("cell %s variable %d cp_var %c set dominates? %d\n", name, (int)neg + 1, *cp_var, set_dominates); + // S&R priority is well-defined now + if (set_dominates) { + r_sig = module->AndnotGate(NEW_ID, r_sig, s_sig); + } else { + s_sig = module->AndnotGate(NEW_ID, s_sig, r_sig); + } + } else { + log_debug("cell %s variable %d undef c&p behavior\n", name, (int)neg + 1); + } + + cell->setPort(ID::S, s_sig); + cell->setPort(ID::R, r_sig); + } + + log_assert(!cell->type.empty()); } - cell = module->addDlatchGate(NEW_ID, enable_sig, data_sig, iq_sig, true); return true; }