mirror of
https://github.com/YosysHQ/yosys
synced 2025-12-09 21:33:26 +00:00
Merge pull request #5496 from YosysHQ/emil/liberty-flop-loops
read_liberty: support loopy retention cells
This commit is contained in:
commit
9871e9b17e
10 changed files with 415 additions and 99 deletions
|
|
@ -40,14 +40,14 @@ static RTLIL::SigSpec parse_func_identifier(RTLIL::Module *module, const char *&
|
|||
expr[id_len] == '_' || expr[id_len] == '[' || expr[id_len] == ']') id_len++;
|
||||
|
||||
if (id_len == 0)
|
||||
log_error("Expected identifier at `%s'.\n", expr);
|
||||
log_error("Expected identifier at `%s' in %s.\n", expr, RTLIL::unescape_id(module->name));
|
||||
|
||||
if (id_len == 1 && (*expr == '0' || *expr == '1'))
|
||||
return *(expr++) == '0' ? RTLIL::State::S0 : RTLIL::State::S1;
|
||||
|
||||
std::string id = RTLIL::escape_id(std::string(expr, id_len));
|
||||
if (!module->wires_.count(id))
|
||||
log_error("Can't resolve wire name %s.\n", RTLIL::unescape_id(id));
|
||||
log_error("Can't resolve wire name %s in %s.\n", RTLIL::unescape_id(id), RTLIL::unescape_id(module->name));
|
||||
|
||||
expr += id_len;
|
||||
return module->wires_.at(id);
|
||||
|
|
@ -174,7 +174,7 @@ static RTLIL::SigSpec parse_func_expr(RTLIL::Module *module, const char *expr)
|
|||
#endif
|
||||
|
||||
if (stack.size() != 1 || stack.back().type != 3)
|
||||
log_error("Parser error in function expr `%s'.\n", orig_expr);
|
||||
log_error("Parser error in function expr `%s'in %s.\n", orig_expr, RTLIL::unescape_id(module->name));
|
||||
|
||||
return stack.back().sig;
|
||||
}
|
||||
|
|
@ -191,11 +191,23 @@ static RTLIL::SigSpec create_tristate(RTLIL::Module *module, RTLIL::SigSpec func
|
|||
return cell->getPort(ID::Y);
|
||||
}
|
||||
|
||||
static void create_latch_ff_wires(RTLIL::Module *module, const LibertyAst *node)
|
||||
{
|
||||
module->addWire(RTLIL::escape_id(node->args.at(0)));
|
||||
module->addWire(RTLIL::escape_id(node->args.at(1)));
|
||||
}
|
||||
|
||||
static std::pair<RTLIL::SigSpec, RTLIL::SigSpec> find_latch_ff_wires(RTLIL::Module *module, const LibertyAst *node)
|
||||
{
|
||||
auto* iq_wire = module->wire(RTLIL::escape_id(node->args.at(0)));
|
||||
auto* iqn_wire = module->wire(RTLIL::escape_id(node->args.at(1)));
|
||||
log_assert(iq_wire && iqn_wire);
|
||||
return std::make_pair(iq_wire, iqn_wire);
|
||||
}
|
||||
|
||||
static void create_ff(RTLIL::Module *module, const LibertyAst *node)
|
||||
{
|
||||
RTLIL::SigSpec iq_sig(module->addWire(RTLIL::escape_id(node->args.at(0))));
|
||||
RTLIL::SigSpec iqn_sig(module->addWire(RTLIL::escape_id(node->args.at(1))));
|
||||
|
||||
auto [iq_sig, iqn_sig] = find_latch_ff_wires(module, node);
|
||||
RTLIL::SigSpec clk_sig, data_sig, clear_sig, preset_sig;
|
||||
bool clk_polarity = true, clear_polarity = true, preset_polarity = true;
|
||||
|
||||
|
|
@ -211,7 +223,7 @@ static void create_ff(RTLIL::Module *module, const LibertyAst *node)
|
|||
}
|
||||
|
||||
if (clk_sig.size() == 0 || data_sig.size() == 0)
|
||||
log_error("FF cell %s has no next_state and/or clocked_on attribute.\n", log_id(module->name));
|
||||
log_error("FF cell %s has no next_state and/or clocked_on attribute.\n", RTLIL::unescape_id(module->name));
|
||||
|
||||
for (bool rerun_invert_rollback = true; rerun_invert_rollback;)
|
||||
{
|
||||
|
|
@ -270,9 +282,7 @@ static void create_ff(RTLIL::Module *module, const LibertyAst *node)
|
|||
|
||||
static bool create_latch(RTLIL::Module *module, const LibertyAst *node, bool flag_ignore_miss_data_latch)
|
||||
{
|
||||
RTLIL::SigSpec iq_sig(module->addWire(RTLIL::escape_id(node->args.at(0))));
|
||||
RTLIL::SigSpec iqn_sig(module->addWire(RTLIL::escape_id(node->args.at(1))));
|
||||
|
||||
auto [iq_sig, iqn_sig] = find_latch_ff_wires(module, node);
|
||||
RTLIL::SigSpec enable_sig, data_sig, clear_sig, preset_sig;
|
||||
bool enable_polarity = true, clear_polarity = true, preset_polarity = true;
|
||||
|
||||
|
|
@ -289,9 +299,9 @@ static bool create_latch(RTLIL::Module *module, const LibertyAst *node, bool fla
|
|||
|
||||
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", log_id(module->name));
|
||||
log_error("Latch cell %s has no data_in and/or enable attribute.\n", RTLIL::unescape_id(module->name));
|
||||
else
|
||||
log("Ignored latch cell %s with no data_in and/or enable attribute.\n", log_id(module->name));
|
||||
log("Ignored latch cell %s with no data_in and/or enable attribute.\n", RTLIL::unescape_id(module->name));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -582,9 +592,9 @@ struct LibertyFrontend : public Frontend {
|
|||
{
|
||||
if (!flag_ignore_miss_dir)
|
||||
{
|
||||
log_error("Missing or invalid direction for pin %s on cell %s.\n", node->args.at(0), log_id(module->name));
|
||||
log_error("Missing or invalid direction for pin %s on cell %s.\n", node->args.at(0), RTLIL::unescape_id(module->name));
|
||||
} else {
|
||||
log("Ignoring cell %s with missing or invalid direction for pin %s.\n", log_id(module->name), node->args.at(0));
|
||||
log("Ignoring cell %s with missing or invalid direction for pin %s.\n", RTLIL::unescape_id(module->name), node->args.at(0));
|
||||
delete module;
|
||||
goto skip_cell;
|
||||
}
|
||||
|
|
@ -596,13 +606,13 @@ struct LibertyFrontend : public Frontend {
|
|||
if (node->id == "bus" && node->args.size() == 1)
|
||||
{
|
||||
if (flag_ignore_buses) {
|
||||
log("Ignoring cell %s with a bus interface %s.\n", log_id(module->name), node->args.at(0));
|
||||
log("Ignoring cell %s with a bus interface %s.\n", RTLIL::unescape_id(module->name), node->args.at(0));
|
||||
delete module;
|
||||
goto skip_cell;
|
||||
}
|
||||
|
||||
if (!flag_lib)
|
||||
log_error("Error in cell %s: bus interfaces are only supported in -lib mode.\n", log_id(cell_name));
|
||||
log_error("Error in cell %s: bus interfaces are only supported in -lib mode.\n", RTLIL::unescape_id(cell_name));
|
||||
|
||||
const LibertyAst *dir = node->find("direction");
|
||||
|
||||
|
|
@ -613,7 +623,7 @@ struct LibertyFrontend : public Frontend {
|
|||
}
|
||||
|
||||
if (!dir || (dir->value != "input" && dir->value != "output" && dir->value != "inout" && dir->value != "internal"))
|
||||
log_error("Missing or invalid direction for bus %s on cell %s.\n", node->args.at(0), log_id(module->name));
|
||||
log_error("Missing or invalid direction for bus %s on cell %s.\n", node->args.at(0), RTLIL::unescape_id(module->name));
|
||||
|
||||
simple_comb_cell = false;
|
||||
|
||||
|
|
@ -624,7 +634,7 @@ struct LibertyFrontend : public Frontend {
|
|||
|
||||
if (!bus_type_node || !type_map.count(bus_type_node->value))
|
||||
log_error("Unknown or unsupported type for bus interface %s on cell %s.\n",
|
||||
node->args.at(0).c_str(), log_id(cell_name));
|
||||
node->args.at(0).c_str(), RTLIL::unescape_id(cell_name));
|
||||
|
||||
int bus_type_width = std::get<0>(type_map.at(bus_type_node->value));
|
||||
int bus_type_offset = std::get<1>(type_map.at(bus_type_node->value));
|
||||
|
|
@ -646,6 +656,13 @@ struct LibertyFrontend : public Frontend {
|
|||
{
|
||||
// some liberty files do not put ff/latch at the beginning of a cell
|
||||
// try to find "ff" or "latch" and create FF/latch _before_ processing all other nodes
|
||||
// but first, in case of balloon retention cells, we need all ff/latch output wires
|
||||
// defined before we add ff/latch cells
|
||||
for (auto node : cell->children)
|
||||
{
|
||||
if ((node->id == "ff" && node->args.size() == 2) || (node->id == "latch" && node->args.size() == 2))
|
||||
create_latch_ff_wires(module, node);
|
||||
}
|
||||
for (auto node : cell->children)
|
||||
{
|
||||
if (node->id == "ff" && node->args.size() == 2)
|
||||
|
|
@ -701,9 +718,9 @@ struct LibertyFrontend : public Frontend {
|
|||
if (dir->value != "inout") { // allow inout with missing function, can be used for power pins
|
||||
if (!flag_ignore_miss_func)
|
||||
{
|
||||
log_error("Missing function on output %s of cell %s.\n", log_id(wire->name), log_id(module->name));
|
||||
log_error("Missing function on output %s of cell %s.\n", RTLIL::unescape_id(wire->name), RTLIL::unescape_id(module->name));
|
||||
} else {
|
||||
log("Ignoring cell %s with missing function on output %s.\n", log_id(module->name), log_id(wire->name));
|
||||
log("Ignoring cell %s with missing function on output %s.\n", RTLIL::unescape_id(module->name), RTLIL::unescape_id(wire->name));
|
||||
delete module;
|
||||
goto skip_cell;
|
||||
}
|
||||
|
|
@ -757,13 +774,13 @@ struct LibertyFrontend : public Frontend {
|
|||
if (design->has(cell_name)) {
|
||||
Module *existing_mod = design->module(cell_name);
|
||||
if (!flag_nooverwrite && !flag_overwrite && !existing_mod->get_bool_attribute(ID::blackbox)) {
|
||||
log_error("Re-definition of cell/module %s!\n", log_id(cell_name));
|
||||
log_error("Re-definition of cell/module %s!\n", RTLIL::unescape_id(cell_name));
|
||||
} else if (flag_nooverwrite) {
|
||||
log("Ignoring re-definition of module %s.\n", log_id(cell_name));
|
||||
log("Ignoring re-definition of module %s.\n", RTLIL::unescape_id(cell_name));
|
||||
delete module;
|
||||
goto skip_cell;
|
||||
} else {
|
||||
log("Replacing existing%s module %s.\n", existing_mod->get_bool_attribute(ID::blackbox) ? " blackbox" : "", log_id(cell_name));
|
||||
log("Replacing existing%s module %s.\n", existing_mod->get_bool_attribute(ID::blackbox) ? " blackbox" : "", RTLIL::unescape_id(cell_name));
|
||||
design->remove(existing_mod);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -827,6 +827,17 @@ std::string vlog_identifier(std::string str)
|
|||
return str;
|
||||
}
|
||||
|
||||
void event2vl_wire(std::string &edge, LibertyExpression& parsed, const std::string& wire)
|
||||
{
|
||||
edge.clear();
|
||||
if (parsed.kind == LibertyExpression::Kind::NOT) {
|
||||
edge = "negedge " + wire;
|
||||
// parsed = parsed.children[0];
|
||||
} else {
|
||||
edge = "posedge " + wire;
|
||||
}
|
||||
}
|
||||
|
||||
void event2vl(const LibertyAst *ast, std::string &edge, std::string &expr)
|
||||
{
|
||||
edge.clear();
|
||||
|
|
@ -843,33 +854,160 @@ void event2vl(const LibertyAst *ast, std::string &edge, std::string &expr)
|
|||
}
|
||||
}
|
||||
|
||||
void clear_preset_var(std::string var, std::string type)
|
||||
enum ClearPresetVar {
|
||||
Error,
|
||||
L,
|
||||
H,
|
||||
T,
|
||||
X,
|
||||
};
|
||||
|
||||
ClearPresetVar clear_preset_var(std::string type)
|
||||
{
|
||||
if (type.find('L') != std::string::npos) {
|
||||
return ClearPresetVar::L;
|
||||
}
|
||||
if (type.find('H') != std::string::npos) {
|
||||
return ClearPresetVar::H;
|
||||
}
|
||||
if (type.find('T') != std::string::npos) {
|
||||
return ClearPresetVar::T;
|
||||
}
|
||||
if (type.find('X') != std::string::npos) {
|
||||
return ClearPresetVar::X;
|
||||
}
|
||||
return ClearPresetVar::X;
|
||||
}
|
||||
|
||||
void print_clear_preset_var(std::string var, ClearPresetVar type)
|
||||
{
|
||||
if (type == ClearPresetVar::L) {
|
||||
printf(" %s <= 0;\n", var.c_str());
|
||||
return;
|
||||
}
|
||||
if (type.find('H') != std::string::npos) {
|
||||
if (type == ClearPresetVar::H) {
|
||||
printf(" %s <= 1;\n", var.c_str());
|
||||
return;
|
||||
}
|
||||
if (type.find('T') != std::string::npos) {
|
||||
if (type == ClearPresetVar::T) {
|
||||
printf(" %s <= ~%s;\n", var.c_str(), var.c_str());
|
||||
return;
|
||||
}
|
||||
if (type.find('X') != std::string::npos) {
|
||||
if (type == ClearPresetVar::X) {
|
||||
printf(" %s <= 'bx;\n", var.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
struct FfEdge {
|
||||
std::string edge;
|
||||
std::string expr;
|
||||
};
|
||||
struct FfEdges {
|
||||
FfEdge clock;
|
||||
FfEdge clear;
|
||||
FfEdge preset;
|
||||
std::string edge;
|
||||
void wired(FfEdge& edge, const LibertyAst* ast, const std::string& wire, const char* tag) {
|
||||
auto* found = ast->find(tag);
|
||||
if (!found)
|
||||
return;
|
||||
auto lexer = LibertyExpression::Lexer(found->value);
|
||||
auto expr = LibertyExpression::parse(lexer);
|
||||
event2vl_wire(edge.edge, expr, wire);
|
||||
edge.expr = expr.vlog_str();
|
||||
}
|
||||
FfEdges(LibertyAst* child, const std::string& clear_wire, const std::string& preset_wire) {
|
||||
wired(clear, child, clear_wire, "clear");
|
||||
wired(preset, child, preset_wire, "preset");
|
||||
event2vl(child->find("clocked_on"), clock.edge, clock.expr);
|
||||
edge = "";
|
||||
if (!clock.edge.empty())
|
||||
edge += (edge.empty() ? "" : ", ") + clock.edge;
|
||||
if (!clear.edge.empty())
|
||||
edge += (edge.empty() ? "" : ", ") + clear.edge;
|
||||
if (!preset.edge.empty())
|
||||
edge += (edge.empty() ? "" : ", ") + preset.edge;
|
||||
}
|
||||
};
|
||||
|
||||
struct FfVar {
|
||||
std::string var;
|
||||
std::string edge;
|
||||
FfEdge clear;
|
||||
FfEdge preset;
|
||||
// Value for both asserted
|
||||
const char* clear_preset_var_name;
|
||||
std::string next_state;
|
||||
const char* else_prefix = "";
|
||||
public:
|
||||
void proc_header() {
|
||||
printf(" always @(%s) begin\n", edge.c_str());
|
||||
}
|
||||
void proc_footer() {
|
||||
if (*else_prefix)
|
||||
printf(" end\n");
|
||||
|
||||
printf(" end\n");
|
||||
}
|
||||
void proc_cond(FfEdge& edge, const char* value) {
|
||||
printf(" %sif (%s) begin\n", else_prefix, edge.expr.c_str());
|
||||
printf(" %s <= %s;\n", var.c_str(), value);
|
||||
printf(" end\n");
|
||||
else_prefix = "else ";
|
||||
}
|
||||
void proc_cond_clear() { proc_cond(clear, "0"); }
|
||||
void proc_cond_preset() { proc_cond(preset, "1"); }
|
||||
void proc_next_state() {
|
||||
if (*else_prefix)
|
||||
printf(" %sbegin\n", else_prefix);
|
||||
printf(" %s <= %s;\n", var.c_str(), next_state.c_str());
|
||||
}
|
||||
void proc(LibertyAst* child) {
|
||||
else_prefix = "";
|
||||
proc_header();
|
||||
if (!clear.expr.empty() && !preset.expr.empty()) {
|
||||
ClearPresetVar clear_preset = clear_preset_var(find_non_null(child, clear_preset_var_name)->value);
|
||||
if (clear_preset == ClearPresetVar::L) {
|
||||
proc_cond_clear();
|
||||
proc_cond_preset();
|
||||
proc_next_state();
|
||||
proc_footer();
|
||||
return;
|
||||
} else if (clear_preset == ClearPresetVar::H) {
|
||||
// Notice that preset and clear are swapped
|
||||
proc_cond_preset();
|
||||
proc_cond_clear();
|
||||
proc_next_state();
|
||||
proc_footer();
|
||||
return;
|
||||
} else {
|
||||
// Boo, we have to emit non-synthesizable verilog
|
||||
printf(" %sif ((%s) && (%s)) begin\n", else_prefix, clear.expr.c_str(), preset.expr.c_str());
|
||||
print_clear_preset_var(var, clear_preset);
|
||||
printf(" end\n");
|
||||
else_prefix = "else ";
|
||||
}
|
||||
}
|
||||
if (!clear.expr.empty()) {
|
||||
proc_cond_clear();
|
||||
}
|
||||
if (!preset.expr.empty()) {
|
||||
proc_cond_preset();
|
||||
}
|
||||
proc_next_state();
|
||||
proc_footer();
|
||||
}
|
||||
};
|
||||
|
||||
void gen_verilogsim_cell(const LibertyAst *ast)
|
||||
{
|
||||
if (ast->find("statetable") != NULL)
|
||||
return;
|
||||
|
||||
CHECK_NV(ast->args.size(), == 1);
|
||||
printf("module %s (", vlog_identifier(ast->args[0]).c_str());
|
||||
auto module_name = vlog_identifier(ast->args[0]);
|
||||
printf("module %s (", module_name.c_str());
|
||||
bool first = true;
|
||||
for (auto child : ast->children) {
|
||||
if (child->id != "pin")
|
||||
|
|
@ -883,13 +1021,29 @@ void gen_verilogsim_cell(const LibertyAst *ast)
|
|||
for (auto child : ast->children) {
|
||||
if (child->id != "ff" && child->id != "latch")
|
||||
continue;
|
||||
printf(" reg ");
|
||||
first = true;
|
||||
std::string iq = "";
|
||||
for (auto arg : child->args) {
|
||||
if (first)
|
||||
printf(" reg ");
|
||||
printf("%s%s", first ? "" : ", ", vlog_identifier(arg).c_str());
|
||||
if (first)
|
||||
iq = vlog_identifier(arg);
|
||||
first = false;
|
||||
}
|
||||
printf(";\n");
|
||||
if (!first)
|
||||
printf(";\n");
|
||||
first = true;
|
||||
for (auto gchild : child->children) {
|
||||
if (gchild->id == "clear" || gchild->id == "preset") {
|
||||
if (first)
|
||||
printf(" wire ");
|
||||
printf("%s%s_%s", first ? "" : ", ", iq.c_str(), gchild->id.c_str());
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
if (!first)
|
||||
printf(";\n");
|
||||
}
|
||||
|
||||
for (auto child : ast->children) {
|
||||
|
|
@ -909,63 +1063,45 @@ void gen_verilogsim_cell(const LibertyAst *ast)
|
|||
if (child->id != "ff" || child->args.size() != 2)
|
||||
continue;
|
||||
|
||||
std::string iq_var = vlog_identifier(child->args[0]);
|
||||
std::string iqn_var = vlog_identifier(child->args[1]);
|
||||
auto iq_name = vlog_identifier(child->args[0]);
|
||||
auto clear_wire = iq_name + "_clear";
|
||||
auto preset_wire = iq_name + "_preset";
|
||||
FfEdges edges(child, clear_wire, preset_wire);
|
||||
|
||||
std::string clock_edge, clock_expr;
|
||||
event2vl(child->find("clocked_on"), clock_edge, clock_expr);
|
||||
|
||||
std::string clear_edge, clear_expr;
|
||||
event2vl(child->find("clear"), clear_edge, clear_expr);
|
||||
|
||||
std::string preset_edge, preset_expr;
|
||||
event2vl(child->find("preset"), preset_edge, preset_expr);
|
||||
|
||||
std::string edge = "";
|
||||
if (!clock_edge.empty())
|
||||
edge += (edge.empty() ? "" : ", ") + clock_edge;
|
||||
if (!clear_edge.empty())
|
||||
edge += (edge.empty() ? "" : ", ") + clear_edge;
|
||||
if (!preset_edge.empty())
|
||||
edge += (edge.empty() ? "" : ", ") + preset_edge;
|
||||
|
||||
if (edge.empty())
|
||||
if (edges.edge.empty())
|
||||
continue;
|
||||
|
||||
printf(" always @(%s) begin\n", edge.c_str());
|
||||
std::string next_state = func2vl(find_non_null(child, "next_state")->value);
|
||||
std::string not_next_state = std::string("~(") + next_state + ")";
|
||||
|
||||
const char *else_prefix = "";
|
||||
if (!clear_expr.empty() && !preset_expr.empty()) {
|
||||
printf(" %sif ((%s) && (%s)) begin\n", else_prefix, clear_expr.c_str(), preset_expr.c_str());
|
||||
clear_preset_var(iq_var, find_non_null(child, "clear_preset_var1")->value);
|
||||
clear_preset_var(iqn_var, find_non_null(child, "clear_preset_var2")->value);
|
||||
printf(" end\n");
|
||||
else_prefix = "else ";
|
||||
}
|
||||
if (!clear_expr.empty()) {
|
||||
printf(" %sif (%s) begin\n", else_prefix, clear_expr.c_str());
|
||||
printf(" %s <= 0;\n", iq_var.c_str());
|
||||
printf(" %s <= 1;\n", iqn_var.c_str());
|
||||
printf(" end\n");
|
||||
else_prefix = "else ";
|
||||
}
|
||||
if (!preset_expr.empty()) {
|
||||
printf(" %sif (%s) begin\n", else_prefix, preset_expr.c_str());
|
||||
printf(" %s <= 1;\n", iq_var.c_str());
|
||||
printf(" %s <= 0;\n", iqn_var.c_str());
|
||||
printf(" end\n");
|
||||
else_prefix = "else ";
|
||||
}
|
||||
if (*else_prefix)
|
||||
printf(" %sbegin\n", else_prefix);
|
||||
std::string expr = find_non_null(child, "next_state")->value;
|
||||
printf(" // %s\n", expr.c_str());
|
||||
printf(" %s <= %s;\n", iq_var.c_str(), func2vl(expr).c_str());
|
||||
printf(" %s <= ~(%s);\n", iqn_var.c_str(), func2vl(expr).c_str());
|
||||
if (*else_prefix)
|
||||
printf(" end\n");
|
||||
|
||||
printf(" end\n");
|
||||
if (edges.clear.expr.length())
|
||||
std::swap(clear_wire, edges.clear.expr);
|
||||
if (edges.preset.expr.length())
|
||||
std::swap(preset_wire, edges.preset.expr);
|
||||
auto iq = FfVar {
|
||||
.var = vlog_identifier(child->args[0]),
|
||||
.edge = edges.edge,
|
||||
.clear = edges.clear,
|
||||
.preset = edges.preset,
|
||||
.clear_preset_var_name = "clear_preset_var1",
|
||||
.next_state = next_state,
|
||||
};
|
||||
auto iqn = FfVar {
|
||||
.var = vlog_identifier(child->args[1]),
|
||||
.edge = edges.edge,
|
||||
// Swapped clear and preset
|
||||
.clear = edges.preset,
|
||||
.preset = edges.clear,
|
||||
.clear_preset_var_name = "clear_preset_var2",
|
||||
.next_state = not_next_state,
|
||||
};
|
||||
iq.proc(child);
|
||||
iqn.proc(child);
|
||||
if (edges.clear.expr.length())
|
||||
printf(" assign %s = %s;\n", edges.clear.expr.c_str(), clear_wire.c_str());
|
||||
if (edges.preset.expr.length())
|
||||
printf(" assign %s = %s;\n", edges.preset.expr.c_str(), preset_wire.c_str());
|
||||
}
|
||||
|
||||
for (auto child : ast->children)
|
||||
|
|
@ -990,8 +1126,8 @@ void gen_verilogsim_cell(const LibertyAst *ast)
|
|||
const char *else_prefix = "";
|
||||
if (!clear_expr.empty() && !preset_expr.empty()) {
|
||||
printf(" %sif ((%s) && (%s)) begin\n", else_prefix, clear_expr.c_str(), preset_expr.c_str());
|
||||
clear_preset_var(iq_var, find_non_null(child, "clear_preset_var1")->value);
|
||||
clear_preset_var(iqn_var, find_non_null(child, "clear_preset_var2")->value);
|
||||
print_clear_preset_var(iq_var, clear_preset_var(find_non_null(child, "clear_preset_var1")->value));
|
||||
print_clear_preset_var(iqn_var, clear_preset_var(find_non_null(child, "clear_preset_var2")->value));
|
||||
printf(" end\n");
|
||||
else_prefix = "else ";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,9 @@ module dff (D, CLK, Q);
|
|||
output Q;
|
||||
assign Q = IQ; // IQ
|
||||
always @(posedge CLK) begin
|
||||
// "(D)"
|
||||
IQ <= D;
|
||||
end
|
||||
always @(posedge CLK) begin
|
||||
IQN <= ~(D);
|
||||
end
|
||||
endmodule
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ module imux2 (A, B, S, Y);
|
|||
endmodule
|
||||
module dff (D, CLK, RESET, PRESET, Q, QN);
|
||||
reg IQ, IQN;
|
||||
wire IQ_clear, IQ_preset;
|
||||
input D;
|
||||
input CLK;
|
||||
input RESET;
|
||||
|
|
@ -49,25 +50,30 @@ module dff (D, CLK, RESET, PRESET, Q, QN);
|
|||
assign Q = IQ; // "IQ"
|
||||
output QN;
|
||||
assign QN = IQN; // "IQN"
|
||||
always @(posedge CLK, posedge RESET, posedge PRESET) begin
|
||||
if ((RESET) && (PRESET)) begin
|
||||
always @(posedge CLK, posedge IQ_clear, posedge IQ_preset) begin
|
||||
if (IQ_clear) begin
|
||||
IQ <= 0;
|
||||
IQN <= 0;
|
||||
end
|
||||
else if (RESET) begin
|
||||
IQ <= 0;
|
||||
IQN <= 1;
|
||||
end
|
||||
else if (PRESET) begin
|
||||
else if (IQ_preset) begin
|
||||
IQ <= 1;
|
||||
IQN <= 0;
|
||||
end
|
||||
else begin
|
||||
// "D"
|
||||
IQ <= D;
|
||||
end
|
||||
end
|
||||
always @(posedge CLK, posedge IQ_clear, posedge IQ_preset) begin
|
||||
if (IQ_preset) begin
|
||||
IQN <= 0;
|
||||
end
|
||||
else if (IQ_clear) begin
|
||||
IQN <= 1;
|
||||
end
|
||||
else begin
|
||||
IQN <= ~(D);
|
||||
end
|
||||
end
|
||||
assign IQ_clear = RESET;
|
||||
assign IQ_preset = PRESET;
|
||||
endmodule
|
||||
module latch (D, G, Q, QN);
|
||||
reg IQ, IQN;
|
||||
|
|
|
|||
9
tests/liberty/read_liberty.ys
Normal file
9
tests/liberty/read_liberty.ys
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
read_liberty retention.lib
|
||||
rename retention_cell retention_cell_lib
|
||||
read_verilog retention.lib.verilogsim
|
||||
proc
|
||||
rename retention_cell retention_cell_vlog
|
||||
async2sync
|
||||
equiv_make retention_cell_lib retention_cell_vlog equiv
|
||||
equiv_induct equiv
|
||||
equiv_status -assert equiv
|
||||
57
tests/liberty/retention.lib
Normal file
57
tests/liberty/retention.lib
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
library (retention) {
|
||||
delay_model : table_lookup;
|
||||
voltage_unit : 1V;
|
||||
current_unit : 1mA;
|
||||
leakage_power_unit : 1nW;
|
||||
time_unit : 1ns;
|
||||
capacitive_load_unit (1, pf);
|
||||
pulling_resistance_unit : 1kohm;
|
||||
input_threshold_pct_rise : 50;
|
||||
input_threshold_pct_fall : 50;
|
||||
output_threshold_pct_rise : 50;
|
||||
output_threshold_pct_fall : 50;
|
||||
slew_lower_threshold_pct_rise : 30;
|
||||
slew_upper_threshold_pct_rise : 70;
|
||||
slew_upper_threshold_pct_fall : 70;
|
||||
slew_lower_threshold_pct_fall : 30;
|
||||
cell ("retention_cell") {
|
||||
ff (Q1,QN1) {
|
||||
clocked_on : "CK";
|
||||
next_state : "(D * !SE + SI * SE)";
|
||||
clear : "(((!B2B) * !Q2) + !RD)";
|
||||
preset : "((!B2B) * Q2)";
|
||||
clear_preset_var1 : "L";
|
||||
clear_preset_var2 : "H";
|
||||
}
|
||||
latch (Q2,QN2) {
|
||||
enable : "B1";
|
||||
data_in : "Q1";
|
||||
}
|
||||
pin (B1) {
|
||||
direction : input;
|
||||
}
|
||||
pin (B2B) {
|
||||
direction : input;
|
||||
}
|
||||
pin (CK) {
|
||||
clock : true;
|
||||
direction : input;
|
||||
}
|
||||
pin (D) {
|
||||
direction : input;
|
||||
}
|
||||
pin (Q) {
|
||||
direction : output;
|
||||
function : "Q1";
|
||||
}
|
||||
pin (RD) {
|
||||
direction : input;
|
||||
}
|
||||
pin (SE) {
|
||||
direction : input;
|
||||
}
|
||||
pin (SI) {
|
||||
direction : input;
|
||||
}
|
||||
}
|
||||
}
|
||||
42
tests/liberty/retention.lib.filtered.ok
Normal file
42
tests/liberty/retention.lib.filtered.ok
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
library(retention) {
|
||||
cell("retention_cell") {
|
||||
ff(Q1, QN1) {
|
||||
clocked_on : "CK" ;
|
||||
next_state : "(D * !SE + SI * SE)" ;
|
||||
clear : "(((!B2B) * !Q2) + !RD)" ;
|
||||
preset : "((!B2B) * Q2)" ;
|
||||
clear_preset_var1 : "L" ;
|
||||
clear_preset_var2 : "H" ;
|
||||
}
|
||||
latch(Q2, QN2) {
|
||||
enable : "B1" ;
|
||||
data_in : "Q1" ;
|
||||
}
|
||||
pin(B1) {
|
||||
direction : input ;
|
||||
}
|
||||
pin(B2B) {
|
||||
direction : input ;
|
||||
}
|
||||
pin(CK) {
|
||||
clock : true ;
|
||||
direction : input ;
|
||||
}
|
||||
pin(D) {
|
||||
direction : input ;
|
||||
}
|
||||
pin(Q) {
|
||||
direction : output ;
|
||||
function : "Q1" ;
|
||||
}
|
||||
pin(RD) {
|
||||
direction : input ;
|
||||
}
|
||||
pin(SE) {
|
||||
direction : input ;
|
||||
}
|
||||
pin(SI) {
|
||||
direction : input ;
|
||||
}
|
||||
}
|
||||
}
|
||||
44
tests/liberty/retention.lib.verilogsim.ok
Normal file
44
tests/liberty/retention.lib.verilogsim.ok
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
module retention_cell (B1, B2B, CK, D, Q, RD, SE, SI);
|
||||
reg Q1, QN1;
|
||||
wire Q1_clear, Q1_preset;
|
||||
reg Q2, QN2;
|
||||
input B1;
|
||||
input B2B;
|
||||
input CK;
|
||||
input D;
|
||||
output Q;
|
||||
assign Q = Q1; // "Q1"
|
||||
input RD;
|
||||
input SE;
|
||||
input SI;
|
||||
always @(posedge CK, posedge Q1_clear, posedge Q1_preset) begin
|
||||
if (Q1_clear) begin
|
||||
Q1 <= 0;
|
||||
end
|
||||
else if (Q1_preset) begin
|
||||
Q1 <= 1;
|
||||
end
|
||||
else begin
|
||||
Q1 <= ((D&(~SE))|(SI&SE));
|
||||
end
|
||||
end
|
||||
always @(posedge CK, posedge Q1_clear, posedge Q1_preset) begin
|
||||
if (Q1_clear) begin
|
||||
QN1 <= 1;
|
||||
end
|
||||
else if (Q1_preset) begin
|
||||
QN1 <= 0;
|
||||
end
|
||||
else begin
|
||||
QN1 <= ~(((D&(~SE))|(SI&SE)));
|
||||
end
|
||||
end
|
||||
assign Q1_clear = (((~B2B)&(~Q2))|(~RD));
|
||||
assign Q1_preset = ((~B2B)&Q2);
|
||||
always @* begin
|
||||
if (B1) begin
|
||||
Q2 <= Q1;
|
||||
QN2 <= ~(Q1);
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -4,8 +4,9 @@ module DFF (D, CK, Q);
|
|||
input CK;
|
||||
output Q;
|
||||
always @(posedge CK) begin
|
||||
// "D"
|
||||
IQ <= D;
|
||||
end
|
||||
always @(posedge CK) begin
|
||||
IQN <= ~(D);
|
||||
end
|
||||
endmodule
|
||||
|
|
|
|||
|
|
@ -5,8 +5,9 @@ module dff1 (D, CLK, Q);
|
|||
output Q;
|
||||
assign Q = IQ; // IQ
|
||||
always @(posedge CLK) begin
|
||||
// !D
|
||||
IQ <= (~D);
|
||||
end
|
||||
always @(posedge CLK) begin
|
||||
IQN <= ~((~D));
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -17,8 +18,9 @@ module dff2 (D, CLK, Q);
|
|||
output Q;
|
||||
assign Q = IQ; // "IQ"
|
||||
always @(posedge CLK) begin
|
||||
// D '
|
||||
IQ <= (~D);
|
||||
end
|
||||
always @(posedge CLK) begin
|
||||
IQN <= ~((~D));
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -32,8 +34,9 @@ module dffe (D, EN, CLK, Q, QN);
|
|||
output QN;
|
||||
assign QN = IQN; // "IQN"
|
||||
always @(negedge CLK) begin
|
||||
// ( D & EN ) | ( IQ & ! EN )
|
||||
IQ <= ((D&EN)|(IQ&(~EN)));
|
||||
end
|
||||
always @(negedge CLK) begin
|
||||
IQN <= ~(((D&EN)|(IQ&(~EN))));
|
||||
end
|
||||
endmodule
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue