From 66d8fc5c287aa67e13148c3b27ce966155e48975 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Wed, 5 Nov 2025 11:00:25 +0100 Subject: [PATCH 1/8] libparse: quirk-compatibility for unquoted boolean expression strings --- passes/techmap/libparse.cc | 81 +++++++++++++++++++++++++++----------- passes/techmap/libparse.h | 2 + 2 files changed, 61 insertions(+), 22 deletions(-) diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc index 7d4fd77ad..cf1854c87 100644 --- a/passes/techmap/libparse.cc +++ b/passes/techmap/libparse.cc @@ -364,7 +364,7 @@ std::string LibertyExpression::str(int indent) #endif -int LibertyParser::lexer(std::string &str) +int LibertyParser::lexer_inner(std::string &str) { int c; @@ -390,11 +390,9 @@ int LibertyParser::lexer(std::string &str) if (str == "+" || str == "-") { /* Single operator is not an identifier */ - // fprintf(stderr, "LEX: char >>%s<<\n", str.c_str()); return str[0]; } else { - // fprintf(stderr, "LEX: identifier >>%s<<\n", str.c_str()); return 'v'; } } @@ -402,24 +400,25 @@ int LibertyParser::lexer(std::string &str) // if it wasn't an identifer, number of array range, // maybe it's a string? if (c == '"') { + f.consume(1); size_t i = 0; while (true) { c = f.peek(i); line += (c == '\n'); - if (c != '"') + if (c != '"' && c != EOF) i += 1; else break; } str.clear(); -#ifdef FILTERLIB f.unget(); - str.append(f.buffered_data(), f.buffered_data() + i + 2); - f.consume(i + 2); -#else - str.append(f.buffered_data(), f.buffered_data() + i); - f.consume(i + 1); + str.append(f.buffered_data(), f.buffered_data() + i + 1); + // Usage in filterlib is expected to retain quotes + // but yosys expects to get unquoted +#ifdef FILTERLIB + str = "\"" + str + "\""; #endif + f.consume(i + 2); return 'v'; } @@ -442,13 +441,12 @@ int LibertyParser::lexer(std::string &str) return lexer(str); } f.unget(); - // fprintf(stderr, "LEX: char >>/<<\n"); return '/'; // a single '/' charater. } // check for a backslash if (c == '\\') { - c = f.get(); + c = f.get(); if (c == '\r') c = f.get(); if (c == '\n') { @@ -467,14 +465,22 @@ int LibertyParser::lexer(std::string &str) // anything else, such as ';' will get passed // through as literal items. - - // if (c >= 32 && c < 255) - // fprintf(stderr, "LEX: char >>%c<<\n", c); - // else - // fprintf(stderr, "LEX: char %d\n", c); return c; } +int LibertyParser::lexer(std::string &str) +{ + int ret = lexer_inner(str); + // if (ret >= 32 && ret < 255) { + // fprintf(stdout, "LEX: ret >>%c<<\n", ret); + // } else if (ret == 'v') { + // fprintf(stdout, "LEX: ret v str %s\n", str.c_str()); + // } else { + // fprintf(stdout, "LEX: ret %d\n", ret); + // } + return ret; +} + void LibertyParser::report_unexpected_token(int tok) { std::string eReport; @@ -545,6 +551,25 @@ void LibertyParser::parse_vector_range(int tok) } } +// Consume into out_str any string-ish tokens, seperated with spaces +// to cope with abuse of the underdefined spec by real world PDKs +// enabled by proprietary implementations. +// Sorry. +int LibertyParser::consume_wrecked_str(int tok, std::string& out_str) { + std::string str = ""; + while (tok != ';' && tok != EOF && tok != 'n') { + out_str += " "; + if (tok == 'v') + out_str += str; + else + out_str += tok; + tok = lexer(str); + } + if (tok == EOF) + error("wrecked string EOF"); + return tok; +} + LibertyAst *LibertyParser::parse(bool top_level) { std::string str; @@ -591,17 +616,29 @@ LibertyAst *LibertyParser::parse(bool top_level) if (tok == '[') { parse_vector_range(tok); tok = lexer(str); + } else { + // Hack for when an expression string is unquoted + // std::cout << "consume_wrecked_str from :\n"; + // std::cout << " weh " << str << "\n"; + tok = consume_wrecked_str(tok, ast->value); } + } else if (tok == '(') { + // Hack for when an expression string is unquoted and starts with + // parentheses + // tok = ''; + // ast->value = "("; + // std::cout << "consume_wrecked_str from (\n"; + tok = consume_wrecked_str(tok, ast->value); } while (tok == '+' || tok == '-' || tok == '*' || tok == '/' || tok == '!') { ast->value += tok; tok = lexer(str); if (tok != 'v') - error(); + error("one"); ast->value += str; tok = lexer(str); } - + // In a liberty file, all key : value pairs should end in ';' // However, there are some liberty files in the wild that // just have a newline. We'll be kind and accept a newline @@ -609,7 +646,7 @@ LibertyAst *LibertyParser::parse(bool top_level) if ((tok == ';') || (tok == 'n')) break; else - error(); + error("two"); continue; } @@ -621,11 +658,11 @@ LibertyAst *LibertyParser::parse(bool top_level) continue; if (tok == ')') break; - + if (tok == '[') { parse_vector_range(tok); - continue; + continue; } if (tok == 'n') continue; diff --git a/passes/techmap/libparse.h b/passes/techmap/libparse.h index 44b5d3d47..91f06a46c 100644 --- a/passes/techmap/libparse.h +++ b/passes/techmap/libparse.h @@ -170,10 +170,12 @@ namespace Yosys 'n': newline anything else is a single character. */ + int lexer_inner(std::string &str); int lexer(std::string &str); void report_unexpected_token(int tok); void parse_vector_range(int tok); + int consume_wrecked_str(int tok, std::string& out_str); LibertyAst *parse(bool top_level); void error() const; void error(const std::string &str) const; From 547e254a9bcd846232f54512a834c1af561a155e Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Wed, 5 Nov 2025 12:12:30 +0100 Subject: [PATCH 2/8] libparse: parse expressions in filterlib --- passes/techmap/dfflibmap.cc | 4 +- passes/techmap/libparse.cc | 120 +++++++++++++++++++++--------------- passes/techmap/libparse.h | 10 +-- 3 files changed, 79 insertions(+), 55 deletions(-) diff --git a/passes/techmap/dfflibmap.cc b/passes/techmap/dfflibmap.cc index 6d55d1b43..e8fc6fc12 100644 --- a/passes/techmap/dfflibmap.cc +++ b/passes/techmap/dfflibmap.cc @@ -127,7 +127,7 @@ static bool parse_next_state(const LibertyAst *cell, const LibertyAst *attr, std return false; } - auto pin_names = pool{}; + auto pin_names = std::unordered_set{}; tree.get_pin_names(pin_names); // from the `ff` block, we know the flop output signal name for loopback. @@ -156,7 +156,7 @@ static bool parse_next_state(const LibertyAst *cell, const LibertyAst *attr, std auto pins = std::vector(pin_names.begin(), pin_names.end()); int lut = 0; for (int n = 0; n < 8; n++) { - auto values = dict{}; + auto values = std::unordered_map{}; values.insert(std::make_pair(pins[0], (n & 1) == 1)); values.insert(std::make_pair(pins[1], (n & 2) == 2)); values.insert(std::make_pair(ff_output, (n & 4) == 4)); diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc index cf1854c87..50bf2aca6 100644 --- a/passes/techmap/libparse.cc +++ b/passes/techmap/libparse.cc @@ -26,8 +26,20 @@ #include #include -#ifndef FILTERLIB +#ifdef FILTERLIB +#undef log_assert +void log_assert(bool cond) { + if (!cond) + fprintf(stderr, "Unspecified assertion failed\n"); +} +void warn(std::string str) { + std::cerr << str; +} +#else #include "kernel/log.h" +void warn(std::string str) { + Yosys::log_formatted_warning("", str); +} #endif using namespace Yosys; @@ -162,13 +174,15 @@ void LibertyAst::dump(FILE *f, sieve &blacklist, sieve &whitelist, std::string i fprintf(f, " ;\n"); } -#ifndef FILTERLIB - // binary operators excluding ' ' -bool LibertyExpression::is_nice_binop(char c) { +bool LibertyExpression::char_is_nice_binop(char c) { return c == '*' || c == '&' || c == '^' || c == '+' || c == '|'; } +bool LibertyExpression::is_binop() { + return kind == AND || kind == OR || kind == XOR; +} + // https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) { if (s.empty()) @@ -191,7 +205,9 @@ LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) { s.next(); lhs = parse(s); if (s.peek() != ')') { - log_warning("expected ')' instead of '%c' while parsing Liberty expression '%s'\n", s.peek(), s.full_expr()); + std::stringstream ss; + ss << "expected ')' instead of " << s.peek() << " while parsing Liberty expression '" << s.full_expr() << "'\n"; + warn(ss.str()); return lhs; } s.next(); @@ -200,7 +216,9 @@ LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) { lhs.kind = Kind::NOT; lhs.children.push_back(parse(s, 7)); } else { - log_warning("unrecognised character '%c' while parsing Liberty expression '%s'\n", c, s.full_expr()); + std::stringstream ss; + ss << "unrecognised character " << c << " while parsing Liberty expression " << s.full_expr() << "\n"; + warn(ss.str()); return lhs; } @@ -246,7 +264,7 @@ LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) { s.next(); c = s.peek(); } - if (is_nice_binop(c)) { + if (char_is_nice_binop(c)) { // We found a real binop, so this space wasn't an AND // and we just discard it as meaningless whitespace continue; @@ -286,7 +304,7 @@ LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) { return lhs; } -void LibertyExpression::get_pin_names(pool& names) { +void LibertyExpression::get_pin_names(std::unordered_set& names) { if (kind == Kind::PIN) { names.insert(name); } else { @@ -295,7 +313,7 @@ void LibertyExpression::get_pin_names(pool& names) { } } -bool LibertyExpression::eval(dict& values) { +bool LibertyExpression::eval(std::unordered_map& values) { bool result = false; switch (kind) { case Kind::AND: @@ -324,7 +342,7 @@ bool LibertyExpression::eval(dict& values) { return false; } -std::string LibertyExpression::str(int indent) +std::string LibertyExpression::sexpr_str(int indent) { std::string prefix; switch (kind) { @@ -355,14 +373,53 @@ std::string LibertyExpression::str(int indent) if (!first) { prefix += "\n" + std::string(indent + add_indent, ' '); } - prefix += child.str(indent + add_indent); + prefix += child.sexpr_str(indent + add_indent); first = false; } prefix += ")"; return prefix; } -#endif +std::string LibertyExpression::vlog_str() +{ + std::string prefix; + if (kind != PIN) + prefix += "("; + if (is_binop()) { + log_assert(children.size() == 2); + prefix += children[0].vlog_str(); + switch (kind) { + case AND: + prefix += "&"; + break; + case OR: + prefix += "|"; + break; + case XOR: + prefix += "^"; + break; + default: + log_assert(false); + } + prefix += children[1].vlog_str(); + } else { + switch (kind) { + case NOT: + log_assert(children.size() == 1); + prefix += "~"; + prefix += children[0].vlog_str(); + break; + case PIN: + prefix += name; + break; + default: + log_assert(false); + } + } + if (kind != PIN) + prefix += ")"; + return prefix; +} int LibertyParser::lexer_inner(std::string &str) { @@ -764,43 +821,8 @@ const LibertyAst *find_non_null(const LibertyAst *node, const char *name) std::string func2vl(std::string str) { - for (size_t pos = str.find_first_of("\" \t"); pos != std::string::npos; pos = str.find_first_of("\" \t")) { - char c_left = pos > 0 ? str[pos-1] : ' '; - char c_right = pos+1 < str.size() ? str[pos+1] : ' '; - if (std::string("\" \t*+").find(c_left) != std::string::npos) - str.erase(pos, 1); - else if (std::string("\" \t*+").find(c_right) != std::string::npos) - str.erase(pos, 1); - else - str[pos] = '*'; - } - - std::vector group_start; - for (size_t pos = 0; pos < str.size(); pos++) { - if (str[pos] == '(') - group_start.push_back(pos); - if (str[pos] == ')' && group_start.size() > 0) { - if (pos+1 < str.size() && str[pos+1] == '\'') { - std::string group = str.substr(group_start.back(), pos-group_start.back()+1); - str[group_start.back()] = '~'; - str.replace(group_start.back()+1, group.size(), group); - pos++; - } - group_start.pop_back(); - } - if (str[pos] == '\'' && pos > 0) { - size_t start = str.find_last_of("()'*+^&| ", pos-1)+1; - std::string group = str.substr(start, pos-start); - str[start] = '~'; - str.replace(start+1, group.size(), group); - } - if (str[pos] == '*') - str[pos] = '&'; - if (str[pos] == '+') - str[pos] = '|'; - } - - return str; + auto helper = LibertyExpression::Lexer(str); + return LibertyExpression::parse(helper).vlog_str(); } void event2vl(const LibertyAst *ast, std::string &edge, std::string &expr) diff --git a/passes/techmap/libparse.h b/passes/techmap/libparse.h index 91f06a46c..f1138567d 100644 --- a/passes/techmap/libparse.h +++ b/passes/techmap/libparse.h @@ -91,11 +91,13 @@ namespace Yosys LibertyExpression() : kind(Kind::EMPTY) {} static LibertyExpression parse(Lexer &s, int min_prio = 0); - void get_pin_names(pool& names); - bool eval(dict& values); - std::string str(int indent = 0); + void get_pin_names(std::unordered_set& names); + bool eval(std::unordered_map& values); + std::string sexpr_str(int indent = 0); + std::string vlog_str(); private: - static bool is_nice_binop(char c); + static bool char_is_nice_binop(char c); + bool is_binop(); }; class LibertyInputStream { From 4fac7a1b20078ee2a73afc815f8244ba5f345528 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Wed, 5 Nov 2025 12:35:58 +0100 Subject: [PATCH 3/8] libparse: fix space before closing paren in expressions --- passes/techmap/libparse.cc | 6 +++--- tests/unit/techmap/libparseTest.cc | 7 ++++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc index 50bf2aca6..6db204c2e 100644 --- a/passes/techmap/libparse.cc +++ b/passes/techmap/libparse.cc @@ -191,7 +191,7 @@ LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) { char c = s.peek(); auto lhs = LibertyExpression{}; - while (isspace(c)) { + while (isspace(c) || c == '"') { if (s.empty()) return lhs; s.next(); @@ -221,7 +221,6 @@ LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) { warn(ss.str()); return lhs; } - while (true) { if (s.empty()) break; @@ -264,9 +263,10 @@ LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) { s.next(); c = s.peek(); } - if (char_is_nice_binop(c)) { + if (char_is_nice_binop(c) || c == ')') { // We found a real binop, so this space wasn't an AND // and we just discard it as meaningless whitespace + // Closing paren is also always terminating here continue; } } else { diff --git a/tests/unit/techmap/libparseTest.cc b/tests/unit/techmap/libparseTest.cc index b58e55bf2..95070dd04 100644 --- a/tests/unit/techmap/libparseTest.cc +++ b/tests/unit/techmap/libparseTest.cc @@ -13,7 +13,7 @@ namespace RTLIL { void checkAll(std::initializer_list expressions, std::string expected) { for (const auto& e : expressions) { auto helper = LibertyExpression::Lexer(e); - auto tree_s = LibertyExpression::parse(helper).str(); + auto tree_s = LibertyExpression::parse(helper).sexpr_str(); EXPECT_EQ(tree_s, expected); } } @@ -82,6 +82,11 @@ namespace RTLIL { }, "(and (pin \"x\")\n" " (not (pin \"y\")))" ); + checkAll({ + "( D & EN )", + }, "(and (pin \"D\")\n" + " (pin \"EN\"))" + ); } } From bf29f6dc11630a3d97254bfaed3652fe8be35890 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Wed, 5 Nov 2025 12:50:50 +0100 Subject: [PATCH 4/8] libparse: tolerate closing quotes in expression parsing --- passes/techmap/libparse.cc | 4 ++-- passes/techmap/libparse.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc index 6db204c2e..eddc68263 100644 --- a/passes/techmap/libparse.cc +++ b/passes/techmap/libparse.cc @@ -263,10 +263,10 @@ LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) { s.next(); c = s.peek(); } - if (char_is_nice_binop(c) || c == ')') { + if (char_is_nice_binop(c) || c == ')' || c == '\'' || c == '\"') { // We found a real binop, so this space wasn't an AND // and we just discard it as meaningless whitespace - // Closing paren is also always terminating here + // Tail operators also imply this isn't an AND continue; } } else { diff --git a/passes/techmap/libparse.h b/passes/techmap/libparse.h index f1138567d..674484dad 100644 --- a/passes/techmap/libparse.h +++ b/passes/techmap/libparse.h @@ -63,7 +63,7 @@ namespace Yosys } std::string pin() { - auto length = s.find_first_of("\t()'!^*& +|"); + auto length = s.find_first_of("\t()'!^*& +|\""); if (length == std::string::npos) { // nothing found so use size of s length = s.size(); From b0a3d6a3e7b01e57c707af7c575ad243ab16ce9c Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Wed, 5 Nov 2025 13:05:30 +0100 Subject: [PATCH 5/8] libparse: fix up tests since liberty expression parsing now normalizes the form of these expressions --- tests/liberty/XNOR2X1.lib.verilogsim.ok | 2 +- tests/liberty/dff.lib | 4 +- tests/liberty/dff.lib.filtered.ok | 4 +- tests/liberty/dff.lib.verilogsim.ok | 6 +-- tests/liberty/normal.lib.verilogsim.ok | 18 +++---- tests/liberty/unquoted.lib | 60 ++++++++++++++++++++++++ tests/liberty/unquoted.lib.filtered.ok | 60 ++++++++++++++++++++++++ tests/liberty/unquoted.lib.verilogsim.ok | 39 +++++++++++++++ 8 files changed, 176 insertions(+), 17 deletions(-) create mode 100644 tests/liberty/unquoted.lib create mode 100644 tests/liberty/unquoted.lib.filtered.ok create mode 100644 tests/liberty/unquoted.lib.verilogsim.ok diff --git a/tests/liberty/XNOR2X1.lib.verilogsim.ok b/tests/liberty/XNOR2X1.lib.verilogsim.ok index 89e55e8b8..6c9ec26dd 100644 --- a/tests/liberty/XNOR2X1.lib.verilogsim.ok +++ b/tests/liberty/XNOR2X1.lib.verilogsim.ok @@ -2,5 +2,5 @@ module XNOR2X1 (B, A, Y); input B; input A; output Y; - assign Y = !(B&!A|!B&A); // "!(B&!A|!B&A)" + assign Y = (~((B&(~A))|((~B)&A))); // "!(B&!A|!B&A)" endmodule diff --git a/tests/liberty/dff.lib b/tests/liberty/dff.lib index 61f5966f5..b5df36587 100644 --- a/tests/liberty/dff.lib +++ b/tests/liberty/dff.lib @@ -5,7 +5,7 @@ library(dff) { area : 1; ff("IQ", "IQN") { next_state : "(D)"; - clocked_on : "CLK"; + clocked_on : (CLK); } pin(D) { direction : input; @@ -15,7 +15,7 @@ library(dff) { } pin(Q) { direction: output; - function : "IQ"; + function : IQ; } } diff --git a/tests/liberty/dff.lib.filtered.ok b/tests/liberty/dff.lib.filtered.ok index b7dcb96be..2c2804ca1 100644 --- a/tests/liberty/dff.lib.filtered.ok +++ b/tests/liberty/dff.lib.filtered.ok @@ -3,7 +3,7 @@ library(dff) { area : 1 ; ff("IQ", "IQN") { next_state : "(D)" ; - clocked_on : "CLK" ; + clocked_on : ( CLK ) ; } pin(D) { direction : input ; @@ -13,7 +13,7 @@ library(dff) { } pin(Q) { direction : output ; - function : "IQ" ; + function : IQ ; } } } diff --git a/tests/liberty/dff.lib.verilogsim.ok b/tests/liberty/dff.lib.verilogsim.ok index 46441d0fc..4f2a5750c 100644 --- a/tests/liberty/dff.lib.verilogsim.ok +++ b/tests/liberty/dff.lib.verilogsim.ok @@ -3,10 +3,10 @@ module dff (D, CLK, Q); input D; input CLK; output Q; - assign Q = IQ; // "IQ" + assign Q = IQ; // IQ always @(posedge CLK) begin // "(D)" - "IQ" <= (D); - "IQN" <= ~((D)); + "IQ" <= D; + "IQN" <= ~(D); end endmodule diff --git a/tests/liberty/normal.lib.verilogsim.ok b/tests/liberty/normal.lib.verilogsim.ok index 85aed5f4e..190ecd285 100644 --- a/tests/liberty/normal.lib.verilogsim.ok +++ b/tests/liberty/normal.lib.verilogsim.ok @@ -1,13 +1,13 @@ module inv (A, Y); input A; output Y; - assign Y = ~A; // "A'" + assign Y = (~A); // "A'" endmodule module tri_inv (A, S, Z); input A; input S; output Z; - assign Z = ~A; // "A'" + assign Z = (~A); // "A'" endmodule module buffer (A, Y); input A; @@ -18,26 +18,26 @@ module nand2 (A, B, Y); input A; input B; output Y; - assign Y = ~(A&B); // "(A * B)'" + assign Y = (~(A&B)); // "(A * B)'" endmodule module nor2 (A, B, Y); input A; input B; output Y; - assign Y = ~(A|B); // "(A + B)'" + assign Y = (~(A|B)); // "(A + B)'" endmodule module xor2 (A, B, Y); input A; input B; output Y; - assign Y = (A&~B)|(~A&B); // "(A *B') + (A' * B)" + assign Y = ((A&(~B))|((~A)&B)); // "(A *B') + (A' * B)" endmodule module imux2 (A, B, S, Y); input A; input B; input S; output Y; - assign Y = ~(&(A&S)|(B&~S)&); // "( (A * S) + (B * S') )'" + assign Y = (~((A&S)|(B&(~S)))); // "( (A * S) + (B * S') )'" endmodule module dff (D, CLK, RESET, PRESET, Q, QN); reg "IQ", "IQN"; @@ -89,14 +89,14 @@ module aoi211 (A, B, C, Y); input B; input C; output Y; - assign Y = ~((A&B)|C); // "((A * B) + C)'" + assign Y = (~((A&B)|C)); // "((A * B) + C)'" endmodule module oai211 (A, B, C, Y); input A; input B; input C; output Y; - assign Y = ~((A|B)&C); // "((A + B) * C)'" + assign Y = (~((A|B)&C)); // "((A + B) * C)'" endmodule module halfadder (A, B, C, Y); input A; @@ -104,7 +104,7 @@ module halfadder (A, B, C, Y); output C; assign C = (A&B); // "(A * B)" output Y; - assign Y = (A&~B)|(~A&B); // "(A *B') + (A' * B)" + assign Y = ((A&(~B))|((~A)&B)); // "(A *B') + (A' * B)" endmodule module fulladder (A, B, CI, CO, Y); input A; diff --git a/tests/liberty/unquoted.lib b/tests/liberty/unquoted.lib new file mode 100644 index 000000000..c2bf538d2 --- /dev/null +++ b/tests/liberty/unquoted.lib @@ -0,0 +1,60 @@ +library(dff_unquoted) { + cell (dff1) { + area : 1; + ff("IQ", "IQN") { + next_state : !D; + clocked_on : (CLK); + } + pin(D) { + direction : input; + } + pin(CLK) { + direction : input; + } + pin(Q) { + direction: output; + function : IQ; + } + } + cell (dff2) { + area : 1; + ff(IQ, IQN) { + next_state : D'; + clocked_on : CLK; + } + pin(D) { + direction : input; + } + pin(CLK) { + direction : input; + } + pin(Q) { + direction: output; + function : "IQ"; + } + } + cell (dffe) { + area : 6; + ff("IQ", "IQN") { + next_state : (D&EN) | (IQ&!EN); + clocked_on : !CLK; + } + pin(D) { + direction : input; + } + pin(EN) { + direction : input; + } + pin(CLK) { + direction : input; + } + pin(Q) { + direction: output; + function : "IQ"; + } + pin(QN) { + direction: output; + function : "IQN"; + } + } +} diff --git a/tests/liberty/unquoted.lib.filtered.ok b/tests/liberty/unquoted.lib.filtered.ok new file mode 100644 index 000000000..0ffc157b4 --- /dev/null +++ b/tests/liberty/unquoted.lib.filtered.ok @@ -0,0 +1,60 @@ +library(dff_unquoted) { + cell(dff1) { + area : 1 ; + ff("IQ", "IQN") { + next_state : !D ; + clocked_on : ( CLK ) ; + } + pin(D) { + direction : input ; + } + pin(CLK) { + direction : input ; + } + pin(Q) { + direction : output ; + function : IQ ; + } + } + cell(dff2) { + area : 1 ; + ff(IQ, IQN) { + next_state : D ' ; + clocked_on : CLK ; + } + pin(D) { + direction : input ; + } + pin(CLK) { + direction : input ; + } + pin(Q) { + direction : output ; + function : "IQ" ; + } + } + cell(dffe) { + area : 6 ; + ff("IQ", "IQN") { + next_state : ( D & EN ) | ( IQ & ! EN ) ; + clocked_on : !CLK ; + } + pin(D) { + direction : input ; + } + pin(EN) { + direction : input ; + } + pin(CLK) { + direction : input ; + } + pin(Q) { + direction : output ; + function : "IQ" ; + } + pin(QN) { + direction : output ; + function : "IQN" ; + } + } +} diff --git a/tests/liberty/unquoted.lib.verilogsim.ok b/tests/liberty/unquoted.lib.verilogsim.ok new file mode 100644 index 000000000..8706d1773 --- /dev/null +++ b/tests/liberty/unquoted.lib.verilogsim.ok @@ -0,0 +1,39 @@ +module dff1 (D, CLK, Q); + reg "IQ", "IQN"; + input D; + input CLK; + output Q; + assign Q = IQ; // IQ + always @(posedge CLK) begin + // !D + "IQ" <= (~D); + "IQN" <= ~((~D)); + end +endmodule +module dff2 (D, CLK, Q); + reg IQ, IQN; + input D; + input CLK; + output Q; + assign Q = IQ; // "IQ" + always @(posedge CLK) begin + // D ' + IQ <= (~D); + IQN <= ~((~D)); + end +endmodule +module dffe (D, EN, CLK, Q, QN); + reg "IQ", "IQN"; + input D; + input EN; + input CLK; + output Q; + assign Q = IQ; // "IQ" + output QN; + assign QN = IQN; // "IQN" + always @(posedge (~CLK)) begin + // ( D & EN ) | ( IQ & ! EN ) + "IQ" <= ((D&EN)|(IQ&(~EN))); + "IQN" <= ~(((D&EN)|(IQ&(~EN)))); + end +endmodule From 504b668ea64b755bfa8090db78ac024a46d4e0ec Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Wed, 5 Nov 2025 13:49:05 +0100 Subject: [PATCH 6/8] libparse: fix verilogsim negedge --- passes/techmap/libparse.cc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc index eddc68263..da88dfc66 100644 --- a/passes/techmap/libparse.cc +++ b/passes/techmap/libparse.cc @@ -831,11 +831,13 @@ void event2vl(const LibertyAst *ast, std::string &edge, std::string &expr) expr.clear(); if (ast != NULL) { - expr = func2vl(ast->value); - if (expr.size() > 0 && expr[0] == '~') - edge = "negedge " + expr.substr(1); + auto helper = LibertyExpression::Lexer(ast->value); + auto parsed = LibertyExpression::parse(helper); + expr = parsed.vlog_str(); + if (parsed.kind == LibertyExpression::Kind::NOT) + edge = "negedge " + parsed.children[0].vlog_str(); else - edge = "posedge " + expr; + edge = "posedge " + parsed.vlog_str(); } } From 90553267b0a3a98bad21b6892f0e170422fdddad Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Wed, 5 Nov 2025 14:13:58 +0100 Subject: [PATCH 7/8] libparse: fix quoting and negedge in filterlib -verilogsim --- passes/techmap/libparse.cc | 26 ++++++++++++++++-------- tests/liberty/dff.lib.verilogsim.ok | 6 +++--- tests/liberty/normal.lib.verilogsim.ok | 24 +++++++++++----------- tests/liberty/unquoted.lib.verilogsim.ok | 14 ++++++------- 4 files changed, 39 insertions(+), 31 deletions(-) diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc index da88dfc66..2d3f1792a 100644 --- a/passes/techmap/libparse.cc +++ b/passes/techmap/libparse.cc @@ -25,6 +25,7 @@ #include #include #include +#include #ifdef FILTERLIB #undef log_assert @@ -825,6 +826,12 @@ std::string func2vl(std::string str) return LibertyExpression::parse(helper).vlog_str(); } +std::string vlog_identifier(std::string str) +{ + str.erase(std::remove(str.begin(), str.end(), '\"'), str.end()); + return str; +} + void event2vl(const LibertyAst *ast, std::string &edge, std::string &expr) { edge.clear(); @@ -867,13 +874,13 @@ void gen_verilogsim_cell(const LibertyAst *ast) return; CHECK_NV(ast->args.size(), == 1); - printf("module %s (", ast->args[0].c_str()); + printf("module %s (", vlog_identifier(ast->args[0]).c_str()); bool first = true; for (auto child : ast->children) { if (child->id != "pin") continue; CHECK_NV(child->args.size(), == 1); - printf("%s%s", first ? "" : ", ", child->args[0].c_str()); + printf("%s%s", first ? "" : ", ", vlog_identifier(child->args[0]).c_str()); first = false; } printf(");\n"); @@ -884,7 +891,7 @@ void gen_verilogsim_cell(const LibertyAst *ast) printf(" reg "); first = true; for (auto arg : child->args) { - printf("%s%s", first ? "" : ", ", arg.c_str()); + printf("%s%s", first ? "" : ", ", vlog_identifier(arg).c_str()); first = false; } printf(";\n"); @@ -896,9 +903,10 @@ void gen_verilogsim_cell(const LibertyAst *ast) CHECK_NV(child->args.size(), == 1); const LibertyAst *dir = find_non_null(child, "direction"); const LibertyAst *func = child->find("function"); - printf(" %s %s;\n", dir->value.c_str(), child->args[0].c_str()); + std::string var = vlog_identifier(child->args[0]); + printf(" %s %s;\n", dir->value.c_str(), var.c_str()); if (func != NULL) - printf(" assign %s = %s; // %s\n", child->args[0].c_str(), func2vl(func->value).c_str(), func->value.c_str()); + printf(" assign %s = %s; // %s\n", var.c_str(), func2vl(func->value).c_str(), func->value.c_str()); } for (auto child : ast->children) @@ -906,8 +914,8 @@ void gen_verilogsim_cell(const LibertyAst *ast) if (child->id != "ff" || child->args.size() != 2) continue; - std::string iq_var = child->args[0]; - std::string iqn_var = child->args[1]; + std::string iq_var = vlog_identifier(child->args[0]); + std::string iqn_var = vlog_identifier(child->args[1]); std::string clock_edge, clock_expr; event2vl(child->find("clocked_on"), clock_edge, clock_expr); @@ -970,8 +978,8 @@ void gen_verilogsim_cell(const LibertyAst *ast) if (child->id != "latch" || child->args.size() != 2) continue; - std::string iq_var = child->args[0]; - std::string iqn_var = child->args[1]; + std::string iq_var = vlog_identifier(child->args[0]); + std::string iqn_var = vlog_identifier(child->args[1]); std::string enable_edge, enable_expr; event2vl(child->find("enable"), enable_edge, enable_expr); diff --git a/tests/liberty/dff.lib.verilogsim.ok b/tests/liberty/dff.lib.verilogsim.ok index 4f2a5750c..e560df539 100644 --- a/tests/liberty/dff.lib.verilogsim.ok +++ b/tests/liberty/dff.lib.verilogsim.ok @@ -1,12 +1,12 @@ module dff (D, CLK, Q); - reg "IQ", "IQN"; + reg IQ, IQN; input D; input CLK; output Q; assign Q = IQ; // IQ always @(posedge CLK) begin // "(D)" - "IQ" <= D; - "IQN" <= ~(D); + IQ <= D; + IQN <= ~(D); end endmodule diff --git a/tests/liberty/normal.lib.verilogsim.ok b/tests/liberty/normal.lib.verilogsim.ok index 190ecd285..92efbf8aa 100644 --- a/tests/liberty/normal.lib.verilogsim.ok +++ b/tests/liberty/normal.lib.verilogsim.ok @@ -40,7 +40,7 @@ module imux2 (A, B, S, Y); assign Y = (~((A&S)|(B&(~S)))); // "( (A * S) + (B * S') )'" endmodule module dff (D, CLK, RESET, PRESET, Q, QN); - reg "IQ", "IQN"; + reg IQ, IQN; input D; input CLK; input RESET; @@ -51,26 +51,26 @@ module dff (D, CLK, RESET, PRESET, Q, QN); assign QN = IQN; // "IQN" always @(posedge CLK, posedge RESET, posedge PRESET) begin if ((RESET) && (PRESET)) begin - "IQ" <= 0; - "IQN" <= 0; + IQ <= 0; + IQN <= 0; end else if (RESET) begin - "IQ" <= 0; - "IQN" <= 1; + IQ <= 0; + IQN <= 1; end else if (PRESET) begin - "IQ" <= 1; - "IQN" <= 0; + IQ <= 1; + IQN <= 0; end else begin // "D" - "IQ" <= D; - "IQN" <= ~(D); + IQ <= D; + IQN <= ~(D); end end endmodule module latch (D, G, Q, QN); - reg "IQ", "IQN"; + reg IQ, IQN; input D; input G; output Q; @@ -79,8 +79,8 @@ module latch (D, G, Q, QN); assign QN = IQN; // "IQN" always @* begin if (G) begin - "IQ" <= D; - "IQN" <= ~(D); + IQ <= D; + IQN <= ~(D); end end endmodule diff --git a/tests/liberty/unquoted.lib.verilogsim.ok b/tests/liberty/unquoted.lib.verilogsim.ok index 8706d1773..2a2f1d173 100644 --- a/tests/liberty/unquoted.lib.verilogsim.ok +++ b/tests/liberty/unquoted.lib.verilogsim.ok @@ -1,13 +1,13 @@ module dff1 (D, CLK, Q); - reg "IQ", "IQN"; + reg IQ, IQN; input D; input CLK; output Q; assign Q = IQ; // IQ always @(posedge CLK) begin // !D - "IQ" <= (~D); - "IQN" <= ~((~D)); + IQ <= (~D); + IQN <= ~((~D)); end endmodule module dff2 (D, CLK, Q); @@ -23,7 +23,7 @@ module dff2 (D, CLK, Q); end endmodule module dffe (D, EN, CLK, Q, QN); - reg "IQ", "IQN"; + reg IQ, IQN; input D; input EN; input CLK; @@ -31,9 +31,9 @@ module dffe (D, EN, CLK, Q, QN); assign Q = IQ; // "IQ" output QN; assign QN = IQN; // "IQN" - always @(posedge (~CLK)) begin + always @(negedge CLK) begin // ( D & EN ) | ( IQ & ! EN ) - "IQ" <= ((D&EN)|(IQ&(~EN))); - "IQN" <= ~(((D&EN)|(IQ&(~EN)))); + IQ <= ((D&EN)|(IQ&(~EN))); + IQN <= ~(((D&EN)|(IQ&(~EN)))); end endmodule From fdcc4c1507392f722014c77c5847e63113508666 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Thu, 6 Nov 2025 13:30:09 +0100 Subject: [PATCH 8/8] libparse: remove leftover comments --- passes/techmap/libparse.cc | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc index 2d3f1792a..9e2556891 100644 --- a/passes/techmap/libparse.cc +++ b/passes/techmap/libparse.cc @@ -676,23 +676,18 @@ LibertyAst *LibertyParser::parse(bool top_level) tok = lexer(str); } else { // Hack for when an expression string is unquoted - // std::cout << "consume_wrecked_str from :\n"; - // std::cout << " weh " << str << "\n"; tok = consume_wrecked_str(tok, ast->value); } } else if (tok == '(') { // Hack for when an expression string is unquoted and starts with // parentheses - // tok = ''; - // ast->value = "("; - // std::cout << "consume_wrecked_str from (\n"; tok = consume_wrecked_str(tok, ast->value); } while (tok == '+' || tok == '-' || tok == '*' || tok == '/' || tok == '!') { ast->value += tok; tok = lexer(str); if (tok != 'v') - error("one"); + error(); ast->value += str; tok = lexer(str); } @@ -704,7 +699,7 @@ LibertyAst *LibertyParser::parse(bool top_level) if ((tok == ';') || (tok == 'n')) break; else - error("two"); + error(); continue; }