3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2026-08-09 23:51:08 +00:00

frontends/liberty: support all \$_LATCH* type cells (copy/modify from FF code)

This commit is contained in:
Iztok Jeras 2026-06-24 04:22:39 +02:00
parent 24dc1c6dc6
commit 40f1df4c1f

View file

@ -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<char> clear_preset_var1;
std::optional<char> 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;
}