CIRCT 24.0.0git
Loading...
Searching...
No Matches
ImportLiberty.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements the Liberty file import functionality.
10//
11//===----------------------------------------------------------------------===//
12
21#include "mlir/IR/Attributes.h"
22#include "mlir/IR/Builders.h"
23#include "mlir/IR/BuiltinAttributes.h"
24#include "mlir/IR/BuiltinOps.h"
25#include "mlir/IR/BuiltinTypes.h"
26#include "mlir/IR/Diagnostics.h"
27#include "mlir/IR/Location.h"
28#include "mlir/IR/MLIRContext.h"
29#include "mlir/IR/Value.h"
30#include "mlir/Support/FileUtilities.h"
31#include "mlir/Support/LogicalResult.h"
32#include "mlir/Tools/mlir-translate/Translation.h"
33#include "llvm/ADT/STLExtras.h"
34#include "llvm/ADT/StringRef.h"
35#include "llvm/Support/LogicalResult.h"
36#include "llvm/Support/SourceMgr.h"
37
38#define DEBUG_TYPE "import-liberty"
39
40using namespace mlir;
41using namespace circt;
42using namespace circt::hw;
43using namespace circt::comb;
44
45namespace {
46
47/// Liberty token types for lexical analysis
48enum class LibertyTokenKind {
49 // Literals
50 Identifier,
51 String,
52 Number,
53
54 // Punctuation
55 LBrace, // {
56 RBrace, // }
57 LParen, // (
58 RParen, // )
59 Colon, // :
60 Semi, // ;
61 Comma, // ,
62
63 // Special
64 EndOfFile,
65 Error
66};
67
68StringRef stringifyTokenKind(LibertyTokenKind kind) {
69 switch (kind) {
70 case LibertyTokenKind::Identifier:
71 return "identifier";
72 case LibertyTokenKind::String:
73 return "string";
74 case LibertyTokenKind::Number:
75 return "number";
76 case LibertyTokenKind::LBrace:
77 return "'{'";
78 case LibertyTokenKind::RBrace:
79 return "'}'";
80 case LibertyTokenKind::LParen:
81 return "'('";
82 case LibertyTokenKind::RParen:
83 return "')'";
84 case LibertyTokenKind::Colon:
85 return "':'";
86 case LibertyTokenKind::Semi:
87 return "';'";
88 case LibertyTokenKind::Comma:
89 return "','";
90
91 case LibertyTokenKind::EndOfFile:
92 return "end of file";
93 case LibertyTokenKind::Error:
94 return "error";
95 }
96 return "unknown";
97}
98
99struct LibertyToken {
100 LibertyTokenKind kind;
101 StringRef spelling;
102 SMLoc location;
103
104 LibertyToken(LibertyTokenKind kind, StringRef spelling, SMLoc location)
105 : kind(kind), spelling(spelling), location(location) {}
106
107 bool is(LibertyTokenKind k) const { return kind == k; }
108};
109
110class LibertyLexer {
111public:
112 LibertyLexer(const llvm::SourceMgr &sourceMgr, MLIRContext *context)
113 : sourceMgr(sourceMgr), context(context),
114 curBuffer(
115 sourceMgr.getMemoryBuffer(sourceMgr.getMainFileID())->getBuffer()),
116 curPtr(curBuffer.begin()) {}
117
118 LibertyToken nextToken();
119 LibertyToken peekToken();
120
121 SMLoc getCurrentLoc() const { return SMLoc::getFromPointer(curPtr); }
122
123 Location translateLocation(llvm::SMLoc loc) const {
124 unsigned mainFileID = sourceMgr.getMainFileID();
125 auto lineAndColumn = sourceMgr.getLineAndColumn(loc, mainFileID);
126 return FileLineColLoc::get(
127 StringAttr::get(
128 context,
129 sourceMgr.getMemoryBuffer(mainFileID)->getBufferIdentifier()),
130 lineAndColumn.first, lineAndColumn.second);
131 }
132
133 bool isAtEnd() const { return curPtr >= curBuffer.end(); }
134
135private:
136 const llvm::SourceMgr &sourceMgr;
137 MLIRContext *context;
138 StringRef curBuffer;
139 const char *curPtr;
140
141 void skipWhitespaceAndComments();
142 LibertyToken lexIdentifier();
143 LibertyToken lexString();
144 LibertyToken lexNumber();
145 LibertyToken makeToken(LibertyTokenKind kind, const char *start);
146};
147
148/// Helper class to parse boolean expressions from Liberty function attributes.
149///
150/// This parser implements a recursive-descent parser for Liberty boolean
151/// expressions with the following operator precedence (highest to lowest):
152/// 1. NOT (!, ') - Unary negation (prefix or postfix)
153/// 2. AND (*, &) - Conjunction
154/// 3. XOR (^) - Exclusive OR
155/// 4. OR (+, |) - Disjunction
156///
157/// Parentheses can be used to override precedence. The grammar is:
158/// OrExpr -> XorExpr { ('+'|'|') XorExpr }
159/// XorExpr -> AndExpr { '^' AndExpr }
160/// AndExpr -> UnaryExpr { ('*'|'&') UnaryExpr }
161/// UnaryExpr -> '!' UnaryExpr | '(' OrExpr ')' ['\''] | ID ['\']
162class ExpressionParser {
163public:
164 ExpressionParser(LibertyLexer &lexer, OpBuilder &builder, StringRef expr,
165 const llvm::StringMap<Value> &values, SMLoc baseLoc);
166
167 ParseResult parse(Value &result);
168
169private:
170 enum class TokenKind {
171 ID,
172 AND,
173 OR,
174 XOR,
175 PREFIX_NOT, // !
176 POSTFIX_NOT, // '
177 LPAREN,
178 RPAREN,
179 END
180 };
181
182 struct Token {
183 TokenKind kind;
184 StringRef spelling;
185 SMLoc loc;
186 };
187
188 LibertyLexer &lexer;
189 OpBuilder &builder;
190 const llvm::StringMap<Value> &values;
191 SMLoc baseLoc;
192 const char *exprStart;
193 SmallVector<Token> tokens;
194 size_t pos = 0;
195 Value trueVal = nullptr;
196
197 SMLoc getLoc(const char *ptr);
198 void tokenize(StringRef expr);
199 Token peek() const;
200 Token consume();
201 Value createNot(Value val, SMLoc loc);
202
203 ParseResult parseOrExpr(Value &result);
204 ParseResult parseXorExpr(Value &result);
205 ParseResult parseAndExpr(Value &result);
206 ParseResult parseUnaryExpr(Value &result);
207
208 InFlightDiagnostic emitError(llvm::SMLoc loc, const Twine &message) const;
209};
210
211//===----------------------------------------------------------------------===//
212// ExpressionParser Implementation
213//===----------------------------------------------------------------------===//
214
215ExpressionParser::ExpressionParser(LibertyLexer &lexer, OpBuilder &builder,
216 StringRef expr,
217 const llvm::StringMap<Value> &values,
218 SMLoc baseLoc)
219 : lexer(lexer), builder(builder), values(values), baseLoc(baseLoc),
220 exprStart(expr.begin()) {
221 tokenize(expr);
222}
223
224ParseResult ExpressionParser::parse(Value &result) {
225 return parseOrExpr(result);
226}
227
228SMLoc ExpressionParser::getLoc(const char *ptr) {
229 size_t offset = ptr - exprStart;
230 return SMLoc::getFromPointer(baseLoc.getPointer() + 1 + offset);
231}
232
233void ExpressionParser::tokenize(StringRef expr) {
234 const char *ptr = expr.begin();
235 const char *end = expr.end();
236
237 while (ptr < end) {
238 // Skip whitespace
239 if (isspace(*ptr)) {
240 ++ptr;
241 continue;
242 }
243
244 SMLoc loc = getLoc(ptr);
245
246 // Identifier
247 if (isalnum(*ptr) || *ptr == '_') {
248 const char *start = ptr;
249 while (ptr < end && (isalnum(*ptr) || *ptr == '_'))
250 ++ptr;
251 tokens.push_back({TokenKind::ID, StringRef(start, ptr - start), loc});
252 continue;
253 }
254
255 // Operators and punctuation
256 switch (*ptr) {
257 case '*':
258 case '&':
259 tokens.push_back({TokenKind::AND, StringRef(ptr, 1), loc});
260 break;
261 case '+':
262 case '|':
263 tokens.push_back({TokenKind::OR, StringRef(ptr, 1), loc});
264 break;
265 case '^':
266 tokens.push_back({TokenKind::XOR, StringRef(ptr, 1), loc});
267 break;
268 case '!':
269 tokens.push_back({TokenKind::PREFIX_NOT, StringRef(ptr, 1), loc});
270 break;
271 case '\'':
272 tokens.push_back({TokenKind::POSTFIX_NOT, StringRef(ptr, 1), loc});
273 break;
274 case '(':
275 tokens.push_back({TokenKind::LPAREN, StringRef(ptr, 1), loc});
276 break;
277 case ')':
278 tokens.push_back({TokenKind::RPAREN, StringRef(ptr, 1), loc});
279 break;
280 }
281 ++ptr;
282 }
283
284 tokens.push_back({TokenKind::END, "", getLoc(ptr)});
285}
286
287ExpressionParser::Token ExpressionParser::peek() const { return tokens[pos]; }
288
289ExpressionParser::Token ExpressionParser::consume() { return tokens[pos++]; }
290
291Value ExpressionParser::createNot(Value val, SMLoc loc) {
292 if (!trueVal)
293 trueVal = hw::ConstantOp::create(builder, lexer.translateLocation(loc),
294 builder.getI1Type(), 1);
295 return comb::XorOp::create(builder, lexer.translateLocation(loc), val,
296 trueVal);
297}
298
299/// Parse OR expressions with lowest precedence.
300/// OrExpr -> XorExpr { ('+'|'|') XorExpr }
301/// This implements left-associative parsing: A + B + C becomes (A + B) + C
302ParseResult ExpressionParser::parseOrExpr(Value &result) {
303 Value lhs;
304 if (parseXorExpr(lhs))
305 return failure();
306 while (peek().kind == TokenKind::OR) {
307 auto loc = consume().loc;
308 Value rhs;
309 if (parseXorExpr(rhs))
310 return failure();
311 lhs = comb::OrOp::create(builder, lexer.translateLocation(loc), lhs, rhs);
312 }
313 result = lhs;
314 return success();
315}
316
317/// Parse XOR expressions with medium precedence.
318/// XorExpr -> AndExpr { '^' AndExpr }
319/// This implements left-associative parsing: A ^ B ^ C becomes (A ^ B) ^ C
320ParseResult ExpressionParser::parseXorExpr(Value &result) {
321 Value lhs;
322 if (parseAndExpr(lhs))
323 return failure();
324 while (peek().kind == TokenKind::XOR) {
325 auto loc = consume().loc;
326 Value rhs;
327 if (parseAndExpr(rhs))
328 return failure();
329 lhs = comb::XorOp::create(builder, lexer.translateLocation(loc), lhs, rhs);
330 }
331 result = lhs;
332 return success();
333}
334
335/// Parse AND expressions with highest precedence.
336/// AndExpr -> UnaryExpr { ('*'|'&'|implicit) UnaryExpr }
337/// This implements left-associative parsing: A * B * C becomes (A * B) * C
338/// Space between expressions is treated as implicit AND.
339ParseResult ExpressionParser::parseAndExpr(Value &result) {
340 Value lhs;
341 if (parseUnaryExpr(lhs))
342 return failure();
343 while (peek().kind == TokenKind::AND ||
344 (peek().kind == TokenKind::ID || peek().kind == TokenKind::LPAREN ||
345 peek().kind == TokenKind::PREFIX_NOT)) {
346 auto loc = peek().loc;
347 if (peek().kind == TokenKind::AND)
348 consume();
349 Value rhs;
350 if (parseUnaryExpr(rhs))
351 return failure();
352 lhs = comb::AndOp::create(builder, lexer.translateLocation(loc), lhs, rhs);
353 }
354 result = lhs;
355 return success();
356}
357
358/// Parse unary expressions and primary expressions.
359/// UnaryExpr -> '!' UnaryExpr | '(' OrExpr ')' ['\''] | ID ['\']
360/// Handles prefix NOT (!), parenthesized expressions, and identifiers.
361/// Postfix NOT (') is only allowed after expressions and identifiers.
362ParseResult ExpressionParser::parseUnaryExpr(Value &result) {
363 // Prefix NOT (!)
364 if (peek().kind == TokenKind::PREFIX_NOT) {
365 auto loc = consume().loc;
366 Value val;
367 if (parseUnaryExpr(val))
368 return failure();
369 result = createNot(val, loc);
370 return success();
371 }
372
373 // Parenthesized expression
374 if (peek().kind == TokenKind::LPAREN) {
375 consume();
376 Value val;
377 if (parseOrExpr(val))
378 return failure();
379 if (peek().kind != TokenKind::RPAREN)
380 return failure();
381 consume();
382 // Postfix NOT (')
383 if (peek().kind == TokenKind::POSTFIX_NOT) {
384 auto loc = consume().loc;
385 val = createNot(val, loc);
386 }
387 result = val;
388 return success();
389 }
390
391 // Identifier
392 if (peek().kind == TokenKind::ID) {
393 auto tok = consume();
394 StringRef name = tok.spelling;
395 auto it = values.find(name);
396 if (it == values.end())
397 return emitError(tok.loc, "variable not found");
398 Value val = it->second;
399 // Postfix NOT (')
400 if (peek().kind == TokenKind::POSTFIX_NOT) {
401 auto loc = consume().loc;
402 val = createNot(val, loc);
403 }
404 result = val;
405 return success();
406 }
407
408 return emitError(peek().loc, "expected expression");
409}
410
411InFlightDiagnostic ExpressionParser::emitError(llvm::SMLoc loc,
412 const Twine &message) const {
413 return mlir::emitError(lexer.translateLocation(loc), message);
414}
415
416// Parsed result of a Liberty group
417struct LibertyGroup {
418 StringRef name;
419 SMLoc loc;
420 SmallVector<Attribute> args;
421 struct AttrEntry {
422 StringRef name;
423 Attribute value;
424 SMLoc loc;
425 AttrEntry(StringRef name, Attribute value, SMLoc loc)
426 : name(name), value(value), loc(loc) {}
427 };
428 SmallVector<AttrEntry> attrs;
429 SmallVector<std::unique_ptr<LibertyGroup>> subGroups;
430
431 // Helper to find an attribute by name
432 std::pair<Attribute, SMLoc> getAttribute(StringRef name) const {
433 for (const auto &attr : attrs)
434 if (attr.name == name)
435 return {attr.value, attr.loc};
436 return {};
437 }
438
439 void eraseSubGroup(llvm::function_ref<bool(const LibertyGroup &)> pred) {
440 subGroups.erase(
441 llvm::remove_if(subGroups,
442 [pred](const auto &group) { return pred(*group); }),
443 subGroups.end());
444 }
445
446 LogicalResult checkArgs(
447 size_t n,
448 llvm::function_ref<LogicalResult(size_t idx, Attribute)> pred) const {
449 if (args.size() != n)
450 return failure();
451 for (size_t i = 0; i < n; ++i)
452 if (failed(pred(i, args[i])))
453 return failure();
454 return success();
455 }
456};
457
458class LibertyParser {
459public:
460 LibertyParser(const llvm::SourceMgr &sourceMgr, MLIRContext *context,
461 ModuleOp module)
462 : lexer(sourceMgr, context), module(module),
463 builder(module.getBodyRegion()) {}
464
465 ParseResult parse();
466
467private:
468 LibertyLexer lexer;
469 ModuleOp module;
470 OpBuilder builder;
471
472 // Specific group parsers
473 ParseResult parseLibrary();
474 ParseResult parseGroupBody(LibertyGroup &group);
475 ParseResult parseStatement(LibertyGroup &parent);
476
477 // Lowering methods
478 ParseResult
479 lowerCell(const LibertyGroup &group, StringAttr &cellNameAttr,
480 const llvm::StringMap<const LibertyGroup *> &tableTemplates);
481
482 ParseResult
483 parseNLDMTable(const LibertyGroup &group,
484 const llvm::StringMap<const LibertyGroup *> &tableTemplates,
485 synth::NLDMTableAttr &table);
486
487 LogicalResult
488 buildTimingArcs(const LibertyGroup &pinGroup,
489 const llvm::StringMap<const LibertyGroup *> &tableTemplates,
490 SmallVector<Attribute> &timingArcs);
491
492 LogicalResult
493 collectPorts(const LibertyGroup &cellGroup,
494 const llvm::StringMap<const LibertyGroup *> &tableTemplates,
495 SmallVector<hw::PortInfo> &ports,
496 SmallVector<const LibertyGroup *> &pinGroups,
497 SmallVector<Attribute> &timingArcs,
498 size_t &numOutputPinMissingFunction);
499
500 LogicalResult buildCellLogic(hw::HWModuleOp moduleOp,
501 ArrayRef<hw::PortInfo> ports,
502 ArrayRef<const LibertyGroup *> pinGroups);
503
504 LogicalResult parseLibertyFloatList(StringRef value, SMLoc loc,
505 SmallVectorImpl<double> &result) const;
506
507 //===--------------------------------------------------------------------===//
508 // Parser for Subgroup of "cell" group.
509 //===--------------------------------------------------------------------===//
510
511 Attribute convertGroupToAttr(const LibertyGroup &group);
512
513 // Attribute parsing
514 ParseResult parseAttribute(Attribute &result);
515
516 // Expression parsing
517 ParseResult parseExpression(Value &result, StringRef expr,
518 const llvm::StringMap<Value> &values, SMLoc loc);
519
520 InFlightDiagnostic emitError(llvm::SMLoc loc, const Twine &message) const {
521 return mlir::emitError(lexer.translateLocation(loc), message);
522 }
523
524 InFlightDiagnostic emitWarning(llvm::SMLoc loc, const Twine &message) const {
525 return mlir::emitWarning(lexer.translateLocation(loc), message);
526 }
527
528 ParseResult consume(LibertyTokenKind kind, const Twine &msg) {
529 if (lexer.nextToken().is(kind))
530 return success();
531 return emitError(lexer.getCurrentLoc(), msg);
532 }
533
534 ParseResult consumeUntil(LibertyTokenKind kind) {
535 while (!lexer.peekToken().is(kind) &&
536 lexer.peekToken().kind != LibertyTokenKind::EndOfFile)
537 lexer.nextToken();
538 if (lexer.peekToken().is(kind))
539 return success();
540 return emitError(lexer.getCurrentLoc(),
541 " expected " + stringifyTokenKind(kind));
542 }
543
544 ParseResult consume(LibertyTokenKind kind) {
545 if (lexer.nextToken().is(kind))
546 return success();
547 return emitError(lexer.getCurrentLoc(),
548 " expected " + stringifyTokenKind(kind));
549 }
550
551 ParseResult expect(LibertyTokenKind kind) {
552 if (!lexer.peekToken().is(kind))
553 return emitError(lexer.getCurrentLoc(),
554 " expected " + stringifyTokenKind(kind));
555
556 return success();
557 }
558
559 StringRef getTokenSpelling(const LibertyToken &token) {
560 StringRef str = token.spelling;
561 if (token.kind == LibertyTokenKind::String)
562 str = str.drop_front().drop_back();
563 return str;
564 }
565};
566
567//===----------------------------------------------------------------------===//
568// LibertyLexer Implementation
569//===----------------------------------------------------------------------===//
570
571void LibertyLexer::skipWhitespaceAndComments() {
572 while (curPtr < curBuffer.end()) {
573 if (isspace(*curPtr)) {
574 ++curPtr;
575 continue;
576 }
577
578 // Comments
579 if (*curPtr == '/' && curPtr + 1 < curBuffer.end()) {
580 if (*(curPtr + 1) == '*') { // /* ... */
581 curPtr += 2;
582 while (curPtr + 1 < curBuffer.end() &&
583 (*curPtr != '*' || *(curPtr + 1) != '/'))
584 ++curPtr;
585 if (curPtr + 1 < curBuffer.end())
586 curPtr += 2;
587 continue;
588 }
589 if (*(curPtr + 1) == '/') { // // ...
590 while (curPtr < curBuffer.end() && *curPtr != '\n')
591 ++curPtr;
592 continue;
593 }
594 }
595
596 // Backslash newline continuation
597 if (*curPtr == '\\' && curPtr + 1 < curBuffer.end() &&
598 *(curPtr + 1) == '\n') {
599 curPtr += 2;
600 continue;
601 }
602
603 break;
604 }
605}
606
607LibertyToken LibertyLexer::lexIdentifier() {
608 const char *start = curPtr;
609 while (curPtr < curBuffer.end() &&
610 (isalnum(*curPtr) || *curPtr == '_' || *curPtr == '.'))
611 ++curPtr;
612 return makeToken(LibertyTokenKind::Identifier, start);
613}
614
615LibertyToken LibertyLexer::lexString() {
616 const char *start = curPtr;
617 ++curPtr; // skip opening quote
618 while (curPtr < curBuffer.end() && *curPtr != '"') {
619 if (*curPtr == '\\' && curPtr + 1 < curBuffer.end())
620 ++curPtr; // skip escaped char
621 ++curPtr;
622 }
623 if (curPtr < curBuffer.end())
624 ++curPtr; // skip closing quote
625 return makeToken(LibertyTokenKind::String, start);
626}
627
628LibertyToken LibertyLexer::lexNumber() {
629 const char *start = curPtr;
630 bool seenDot = false;
631 while (curPtr < curBuffer.end()) {
632 if (isdigit(*curPtr)) {
633 ++curPtr;
634 } else if (*curPtr == '.') {
635 if (seenDot) {
636 mlir::emitError(translateLocation(SMLoc::getFromPointer(curPtr)),
637 "multiple decimal points in number");
638 return makeToken(LibertyTokenKind::Error, start);
639 }
640 seenDot = true;
641 ++curPtr;
642 } else {
643 break;
644 }
645 }
646 return makeToken(LibertyTokenKind::Number, start);
647}
648
649LibertyToken LibertyLexer::makeToken(LibertyTokenKind kind, const char *start) {
650 return LibertyToken(kind, StringRef(start, curPtr - start),
651 SMLoc::getFromPointer(start));
652}
653
654LibertyToken LibertyLexer::nextToken() {
655 skipWhitespaceAndComments();
656
657 if (curPtr >= curBuffer.end())
658 return makeToken(LibertyTokenKind::EndOfFile, curPtr);
659
660 const char *start = curPtr;
661 char c = *curPtr;
662
663 if (isalpha(c) || c == '_')
664 return lexIdentifier();
665
666 if (isdigit(c) ||
667 (c == '.' && curPtr + 1 < curBuffer.end() && isdigit(*(curPtr + 1))))
668 return lexNumber();
669
670 if (c == '"')
671 return lexString();
672
673 ++curPtr;
674 switch (c) {
675 case '{':
676 return makeToken(LibertyTokenKind::LBrace, start);
677 case '}':
678 return makeToken(LibertyTokenKind::RBrace, start);
679 case '(':
680 return makeToken(LibertyTokenKind::LParen, start);
681 case ')':
682 return makeToken(LibertyTokenKind::RParen, start);
683 case ':':
684 return makeToken(LibertyTokenKind::Colon, start);
685 case ';':
686 return makeToken(LibertyTokenKind::Semi, start);
687 case ',':
688 return makeToken(LibertyTokenKind::Comma, start);
689
690 default:
691 return makeToken(LibertyTokenKind::Error, start);
692 }
693}
694
695LibertyToken LibertyLexer::peekToken() {
696 const char *savedPtr = curPtr;
697 LibertyToken token = nextToken();
698 curPtr = savedPtr;
699 return token;
700}
701
702//===----------------------------------------------------------------------===//
703// LibertyParser Implementation
704//===----------------------------------------------------------------------===//
705
706ParseResult LibertyParser::parse() {
707 while (lexer.peekToken().kind != LibertyTokenKind::EndOfFile) {
708 auto token = lexer.peekToken();
709 // Skip any stray tokens that aren't valid group starts
710 if (token.kind != LibertyTokenKind::Identifier ||
711 token.spelling != "library") {
712 return emitError(token.location, "expected `library` keyword");
713 }
714 lexer.nextToken();
715 if (parseLibrary())
716 return failure();
717 }
718 return success();
719}
720
721ParseResult LibertyParser::parseLibrary() {
722 LibertyGroup libertyLib;
723 if (parseGroupBody(libertyLib))
724 return failure();
725
726 // First Pass: collect lu_table_template groups by name so they can be
727 // referenced during NLDM table parsing in lowerCell. Liberty tables
728 // reference templates by name for their index axes e.g.
729 // cell_rise(delay_template_7x7) where the template defines index_1/index_2.
730
731 llvm::StringMap<const LibertyGroup *> tableTemplates;
732 for (const auto &stmt : libertyLib.subGroups)
733 if (stmt->name == "lu_table_template")
734 if (!stmt->args.empty())
735 if (auto name = dyn_cast<StringAttr>(stmt->args[0]))
736 tableTemplates[name.getValue()] = stmt.get();
737
738 // Second Pass: lower cells, passing the template map through for index
739 // lookup.
740 DenseSet<StringAttr> seenCells;
741 for (auto &stmt : libertyLib.subGroups) {
742 if (stmt->name != "cell")
743 continue;
744 StringAttr cellNameAttr;
745 if (lowerCell(*stmt, cellNameAttr, tableTemplates))
746 return failure();
747 if (!seenCells.insert(cellNameAttr).second)
748 return emitError(stmt->loc, "redefinition of cell '" +
749 cellNameAttr.getValue() + "'");
750 }
751
752 libertyLib.eraseSubGroup(
753 [](const LibertyGroup &group) { return group.name == "cell"; });
754 auto attr = convertGroupToAttr(libertyLib);
755 module->setAttr("synth.liberty.library", attr);
756 return success();
757}
758
759// Parse a template like: lu_table_template (delay_template_6x6) { variable_1:
760// total_output_net_capacitance; variable_2: input_net_transition; }
761Attribute LibertyParser::convertGroupToAttr(const LibertyGroup &group) {
762 SmallVector<NamedAttribute> attrs;
763 if (!group.args.empty())
764 attrs.push_back(
765 builder.getNamedAttr("args", builder.getArrayAttr(group.args)));
766
767 for (const auto &attr : group.attrs)
768 attrs.push_back(builder.getNamedAttr(attr.name, attr.value));
769
770 llvm::StringMap<SmallVector<Attribute>> subGroups;
771 for (const auto &sub : group.subGroups)
772 subGroups[sub->name].push_back(convertGroupToAttr(*sub));
773
774 for (auto &it : subGroups)
775 attrs.push_back(
776 builder.getNamedAttr(it.getKey(), builder.getArrayAttr(it.getValue())));
777
778 return builder.getDictionaryAttr(attrs);
779}
780
781LogicalResult
782LibertyParser::parseLibertyFloatList(StringRef value, SMLoc loc,
783 SmallVectorImpl<double> &result) const {
784 SmallVector<StringRef> parts;
785 value.split(parts, ',');
786 for (auto part : parts) {
787 part = part.trim();
788 if (part.empty())
789 continue;
790 double val;
791 if (part.getAsDouble(val))
792 return emitError(loc, "expected floating point value in NLDM table");
793 result.push_back(val);
794 }
795 return success();
796}
797
798ParseResult LibertyParser::parseNLDMTable(
799 const LibertyGroup &group,
800 const llvm::StringMap<const LibertyGroup *> &tableTemplates,
801 synth::NLDMTableAttr &table) {
802 SmallVector<double> index1, index2, values;
803
804 auto parseList = [&](const LibertyGroup &source, StringRef name,
805 SmallVectorImpl<double> &result) -> ParseResult {
806 if (auto [attr, loc] = source.getAttribute(name); attr) {
807 auto stringAttr = dyn_cast<StringAttr>(attr);
808 if (!stringAttr)
809 return emitError(loc, "expected string value for NLDM table entry");
810 if (failed(parseLibertyFloatList(stringAttr.getValue(), loc, result)))
811 return failure();
812 return success();
813 }
814
815 for (const auto &subGroup : source.subGroups) {
816 if (subGroup->name != name)
817 continue;
818 if (subGroup->args.empty())
819 return emitError(subGroup->loc, "NLDM table entry missing value");
820 for (auto arg : subGroup->args) {
821 auto stringAttr = dyn_cast<StringAttr>(arg);
822 if (!stringAttr)
823 return emitError(subGroup->loc,
824 "expected string value for NLDM table entry");
825 if (failed(parseLibertyFloatList(stringAttr.getValue(), subGroup->loc,
826 result)))
827 return failure();
828 }
829 return success();
830 }
831
832 return success();
833 };
834
835 // Table-local indices override template indices when present.
836 if (parseList(group, "index_1", index1) ||
837 parseList(group, "index_2", index2) || parseList(group, "values", values))
838 return failure();
839
840 if (!group.args.empty()) {
841 auto templateName = dyn_cast<StringAttr>(group.args[0]);
842 if (!templateName)
843 return emitError(group.loc, "NLDM table template name must be a string");
844
845 auto it = tableTemplates.find(templateName.getValue());
846 if (it == tableTemplates.end())
847 return emitError(group.loc, "unknown NLDM table template '")
848 << templateName.getValue() << "'";
849
850 if (index1.empty() && parseList(*it->second, "index_1", index1))
851 return failure();
852 if (index2.empty() && parseList(*it->second, "index_2", index2))
853 return failure();
854 }
855
856 if (index1.empty())
857 return emitError(group.loc, "NLDM table missing index_1");
858 if (index2.empty())
859 return emitError(group.loc, "NLDM table missing index_2");
860 if (values.empty())
861 return emitError(group.loc, "NLDM table missing values");
862
863 if (values.size() != index1.size() * index2.size()) {
864 return emitError(group.loc, "NLDM table has ")
865 << values.size() << " values, expected "
866 << index1.size() * index2.size() << " for " << index1.size() << " x "
867 << index2.size() << " indices";
868 }
869
870 table = synth::NLDMTableAttr::get(
871 builder.getContext(),
872 DenseF64ArrayAttr::get(builder.getContext(), index1),
873 DenseF64ArrayAttr::get(builder.getContext(), index2),
874 DenseF64ArrayAttr::get(builder.getContext(), values));
875 return success();
876}
877
878static bool isNLDMTableGroup(StringRef name) {
879 return name == "cell_rise" || name == "cell_fall" ||
880 name == "rise_transition" || name == "fall_transition";
881}
882
883LogicalResult LibertyParser::buildTimingArcs(
884 const LibertyGroup &pinGroup,
885 const llvm::StringMap<const LibertyGroup *> &tableTemplates,
886 SmallVector<Attribute> &timingArcs) {
887 auto pinName = cast<StringAttr>(pinGroup.args[0]);
888
889 for (const auto &child : pinGroup.subGroups) {
890 if (child->name != "timing")
891 continue;
892
893 auto relatedPin =
894 dyn_cast_or_null<StringAttr>(child->getAttribute("related_pin").first);
895 auto timingSense =
896 dyn_cast_or_null<StringAttr>(child->getAttribute("timing_sense").first);
897 if (!relatedPin || !timingSense)
898 return emitError(child->loc,
899 "timing related_pin and timing_sense must be strings");
900
901 synth::NLDMTableAttr cellRise, cellFall, riseTransition, fallTransition;
902 for (const auto &table : child->subGroups) {
903 if (!isNLDMTableGroup(table->name))
904 continue;
905
906 synth::NLDMTableAttr parsedTable;
907 if (parseNLDMTable(*table, tableTemplates, parsedTable))
908 return failure();
909
910 if (table->name == "cell_rise")
911 cellRise = parsedTable;
912 else if (table->name == "cell_fall")
913 cellFall = parsedTable;
914 else if (table->name == "rise_transition")
915 riseTransition = parsedTable;
916 else if (table->name == "fall_transition")
917 fallTransition = parsedTable;
918 }
919
920 if (!cellRise || !cellFall || !riseTransition || !fallTransition)
921 return emitError(child->loc,
922 "NLDM timing arc requires cell_rise, cell_fall, "
923 "rise_transition, and fall_transition tables");
924
925 timingArcs.push_back(synth::NLDMTimingArcAttr::get(
926 builder.getContext(), pinName, relatedPin, timingSense, cellRise,
927 cellFall, riseTransition, fallTransition));
928 }
929 return success();
930}
931
932LogicalResult LibertyParser::collectPorts(
933 const LibertyGroup &cellGroup,
934 const llvm::StringMap<const LibertyGroup *> &tableTemplates,
935 SmallVector<hw::PortInfo> &ports,
936 SmallVector<const LibertyGroup *> &pinGroups,
937 SmallVector<Attribute> &timingArcs, size_t &numOutputPinMissingFunction) {
938
939 llvm::DenseSet<StringAttr> seenPins;
940
941 // Gather ports and count output pins without "function" attribute.
942 for (const auto &sub : cellGroup.subGroups) {
943 if (sub->name != "pin")
944 continue;
945
946 pinGroups.push_back(sub.get());
947 if (sub->args.empty())
948 return emitError(sub->loc, "pin missing name");
949
950 StringAttr pinName = dyn_cast<StringAttr>(sub->args[0]);
951 if (!seenPins.insert(pinName).second)
952 return emitError(sub->loc,
953 "redefinition of pin '" + pinName.getValue() + "'");
954
955 std::optional<hw::ModulePort::Direction> dir;
956 SmallVector<NamedAttribute> pinAttrs;
957 bool functionExist = false;
958
959 // Track if this pin has a "function" attribute (only relevant for outputs).
960 bool isInOut = false;
961 for (const auto &attr : sub->attrs) {
962 if (attr.name == "direction") {
963 auto val = dyn_cast<StringAttr>(attr.value);
964 if (!val)
965 return emitError(sub->loc, "pin direction must be a string");
966 if (val.getValue() == "input")
968 else if (val.getValue() == "output")
970 else if (val.getValue() == "inout") {
972 isInOut = true;
973 } else
974 return emitError(sub->loc,
975 "pin direction must be input, output, or inout");
976 continue;
977 }
978 if (attr.name == "function")
979 functionExist = true;
980 pinAttrs.push_back(builder.getNamedAttr(attr.name, attr.value));
981 }
982
983 if (!dir)
984 return emitError(sub->loc, "pin direction must be specified");
985
986 if (dir == hw::ModulePort::Direction::Output && !functionExist)
987 ++numOutputPinMissingFunction;
988
989 // Build timing arcs for this pin's timing subgroups.
990 if (failed(buildTimingArcs(*sub, tableTemplates, timingArcs)))
991 return failure();
992
993 // Collect non-timing subgroups as generic attrs
994 llvm::StringMap<SmallVector<Attribute>> subGroupAttrs;
995 for (const auto &child : sub->subGroups)
996 if (child->name != "timing")
997 subGroupAttrs[child->name].push_back(convertGroupToAttr(*child));
998
999 for (auto &it : subGroupAttrs)
1000 pinAttrs.push_back(builder.getNamedAttr(
1001 it.getKey(), builder.getArrayAttr(it.getValue())));
1002
1003 auto libertyAttrs = builder.getDictionaryAttr(pinAttrs);
1004 auto attrs = builder.getDictionaryAttr(
1005 builder.getNamedAttr("synth.liberty.pin", libertyAttrs));
1006 mlir::Type portType = builder.getI1Type();
1007 if (isInOut)
1008 portType = sv::NetType::get(portType);
1009
1010 hw::PortInfo port;
1011 port.name = pinName;
1012 port.type = portType;
1013 port.dir = *dir;
1014 port.attrs = attrs;
1015 ports.push_back(port);
1016 }
1017 return success();
1018}
1019
1020LogicalResult
1021LibertyParser::buildCellLogic(hw::HWModuleOp moduleOp,
1022 ArrayRef<hw::PortInfo> ports,
1023 ArrayRef<const LibertyGroup *> pinGroups) {
1024
1025 // All outputs have "function": emit logic into hw.module body.
1026 OpBuilder::InsertionGuard guard(builder);
1027 builder.setInsertionPointToStart(moduleOp.getBodyBlock());
1028
1029 llvm::StringMap<Value> portValues;
1030 for (const auto &port : ports)
1031 if (port.dir == hw::ModulePort::Direction::Input)
1032 portValues[port.name.getValue()] =
1033 moduleOp.getBodyBlock()->getArgument(port.argNum);
1034
1035 SmallVector<Value> outputs;
1036 for (const auto &port : ports) {
1037 if (port.dir != hw::ModulePort::Direction::Output)
1038 continue;
1039
1040 auto *it = llvm::find_if(pinGroups, [&](const LibertyGroup *g) {
1041 assert(g->name == "pin" && "expected pin group");
1042 // First arg is the pin name
1043 return cast<StringAttr>(g->args[0]) == port.name;
1044 });
1045
1046 if (!it)
1047 continue;
1048
1049 const LibertyGroup *pg = *it;
1050 auto attrPair = pg->getAttribute("function");
1051 assert(attrPair.first && "output pin missing function attribute");
1052
1053 Value val;
1054 if (parseExpression(val, cast<StringAttr>(attrPair.first).getValue(),
1055 portValues, attrPair.second))
1056 return failure();
1057 outputs.push_back(val);
1058 }
1059
1060 moduleOp.getBodyBlock()->getTerminator()->setOperands(outputs);
1061 return success();
1062}
1063
1064ParseResult LibertyParser::lowerCell(
1065 const LibertyGroup &group, StringAttr &cellNameAttr,
1066 const llvm::StringMap<const LibertyGroup *> &tableTemplates) {
1067
1068 if (group.args.empty())
1069 return emitError(group.loc, "cell missing name");
1070 cellNameAttr = dyn_cast<StringAttr>(group.args[0]);
1071 if (!cellNameAttr)
1072 return emitError(group.loc, "cell name must be a string");
1073
1074 SmallVector<hw::PortInfo> ports;
1075 SmallVector<const LibertyGroup *> pinGroups;
1076 SmallVector<Attribute> timingArcs;
1077 size_t numOutputPinMissingFunction = 0;
1078
1079 if (failed(collectPorts(group, tableTemplates, ports, pinGroups, timingArcs,
1080 numOutputPinMissingFunction)))
1081 return failure();
1082
1083 // Fix up argNum for inputs
1084 int inputIdx = 0;
1085 for (auto &p : ports)
1087 p.argNum = inputIdx++;
1088 else
1089 p.argNum = 0;
1090
1091 auto loc = lexer.translateLocation(group.loc);
1092 auto numOutput = ports.size() - inputIdx;
1093
1094 // Decide whether to create hw.module (with logic) or hw.module.extern (black
1095 // box) based on whether output pins have "function" attributes.
1096 // All outputs must either have "function" or all must lack it.
1097
1098 // Mixed case: some outputs have "function", others don't - error.
1099 if (numOutputPinMissingFunction != 0 &&
1100 numOutputPinMissingFunction != numOutput)
1101 return emitError(group.loc, "cell '")
1102 << cellNameAttr.getValue()
1103 << "' has mixed output pins with and without 'function' attribute";
1104
1105 // All outputs lack "function": emit as hw.module.extern (black box).
1106 if (numOutputPinMissingFunction == numOutput) {
1107 auto externOp =
1108 hw::HWModuleExternOp::create(builder, loc, cellNameAttr, ports);
1109 if (!timingArcs.empty())
1110 externOp->setAttr("synth.timing.nldm", builder.getArrayAttr(timingArcs));
1111 return success();
1112 }
1113
1114 // All outputs have "function": emit as hw.module with logic.
1115 auto moduleOp = hw::HWModuleOp::create(builder, loc, cellNameAttr, ports);
1116
1117 if (failed(buildCellLogic(moduleOp, ports, pinGroups)))
1118 return failure();
1119
1120 if (!timingArcs.empty())
1121 moduleOp->setAttr("synth.timing.nldm", builder.getArrayAttr(timingArcs));
1122
1123 return success();
1124}
1125
1126ParseResult LibertyParser::parseGroupBody(LibertyGroup &group) {
1127 // Parse args: ( arg1, arg2 )
1128 if (lexer.peekToken().kind == LibertyTokenKind::LParen) {
1129 lexer.nextToken(); // (
1130 while (lexer.peekToken().kind != LibertyTokenKind::RParen) {
1131 Attribute arg;
1132 if (parseAttribute(arg))
1133 return failure();
1134 group.args.push_back(arg);
1135 if (lexer.peekToken().kind == LibertyTokenKind::Comma)
1136 lexer.nextToken();
1137 }
1138 if (consume(LibertyTokenKind::RParen, "expected ')'"))
1139 return failure();
1140 }
1141
1142 // Parse body: { ... }
1143 if (lexer.peekToken().kind == LibertyTokenKind::LBrace) {
1144 lexer.nextToken(); // {
1145 while (lexer.peekToken().kind != LibertyTokenKind::RBrace &&
1146 lexer.peekToken().kind != LibertyTokenKind::EndOfFile) {
1147 if (parseStatement(group))
1148 return failure();
1149 }
1150 if (consume(LibertyTokenKind::RBrace, "expected '}'"))
1151 return failure();
1152 } else {
1153 // Optional semicolon if no body
1154 if (lexer.peekToken().kind == LibertyTokenKind::Semi)
1155 lexer.nextToken();
1156 }
1157 return success();
1158}
1159
1160// Parse group, attribute, or define statements
1161ParseResult LibertyParser::parseStatement(LibertyGroup &parent) {
1162 auto nameTok = lexer.nextToken();
1163 if (nameTok.kind != LibertyTokenKind::Identifier)
1164 return emitError(nameTok.location, "expected identifier");
1165 StringRef name = nameTok.spelling;
1166
1167 // Attribute statement.
1168 if (lexer.peekToken().kind == LibertyTokenKind::Colon) {
1169 lexer.nextToken(); // :
1170 Attribute val;
1171 auto loc = lexer.peekToken().location;
1172 if (parseAttribute(val))
1173 return failure();
1174 parent.attrs.emplace_back(name, val, loc);
1175 return consume(LibertyTokenKind::Semi, "expected ';'");
1176 }
1177
1178 // Group statement.
1179 if (lexer.peekToken().kind == LibertyTokenKind::LParen) {
1180 auto subGroup = std::make_unique<LibertyGroup>();
1181 subGroup->name = name;
1182 subGroup->loc = nameTok.location;
1183 if (parseGroupBody(*subGroup))
1184 return failure();
1185 parent.subGroups.push_back(std::move(subGroup));
1186 return success();
1187 }
1188
1189 // TODO: Support define.
1190
1191 return emitError(nameTok.location, "expected ':' or '('");
1192}
1193
1194// Parse an attribute value, which can be:
1195// - A string: "value"
1196// - A number: 1.23
1197// - An identifier: value
1198ParseResult LibertyParser::parseAttribute(Attribute &result) {
1199 auto token = lexer.peekToken();
1200 // Number token
1201 if (token.is(LibertyTokenKind::Number)) {
1202 lexer.nextToken();
1203 StringRef numStr = token.spelling;
1204 double val;
1205 if (!numStr.getAsDouble(val)) {
1206 result = builder.getF64FloatAttr(val);
1207 return success();
1208 }
1209 return emitError(token.location, "expected number value");
1210 }
1211
1212 // Identifier token
1213 if (token.is(LibertyTokenKind::Identifier)) {
1214 lexer.nextToken();
1215 result = builder.getStringAttr(token.spelling);
1216 return success();
1217 }
1218
1219 if (token.is(LibertyTokenKind::String)) {
1220 lexer.nextToken();
1221 result = builder.getStringAttr(getTokenSpelling(token));
1222 return success();
1223 }
1224
1225 return emitError(token.location, "expected attribute value");
1226}
1227
1228// Parse boolean expressions from Liberty function attributes
1229// Supports: *, +, ^, !, (), and identifiers
1230ParseResult LibertyParser::parseExpression(Value &result, StringRef expr,
1231 const llvm::StringMap<Value> &values,
1232 SMLoc loc) {
1233 ExpressionParser parser(lexer, builder, expr, values, loc);
1234 return parser.parse(result);
1235}
1236
1237} // namespace
1238
1239namespace circt::liberty {
1241 TranslateToMLIRRegistration reg(
1242 "import-liberty", "Import Liberty file",
1243 [](llvm::SourceMgr &sourceMgr, MLIRContext *context) {
1244 ModuleOp module = ModuleOp::create(UnknownLoc::get(context));
1245 LibertyParser parser(sourceMgr, context, module);
1246 // Load required dialects
1247 context->loadDialect<hw::HWDialect>();
1248 context->loadDialect<sv::SVDialect>();
1249 context->loadDialect<comb::CombDialect>();
1250 context->loadDialect<synth::SynthDialect>();
1251
1252 if (failed(parser.parse()))
1253 return OwningOpRef<ModuleOp>();
1254 return OwningOpRef<ModuleOp>(module);
1255 },
1256 [](DialectRegistry &registry) {
1257 registry.insert<HWDialect>();
1258 registry.insert<sv::SVDialect>();
1259 registry.insert<comb::CombDialect>();
1260 registry.insert<synth::SynthDialect>();
1261 });
1262}
1263} // namespace circt::liberty
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
#define isalpha(x)
Definition FIRLexer.cpp:27
#define isdigit(x)
Definition FIRLexer.cpp:26
static Location getLoc(DefSlot slot)
Definition Mem2Reg.cpp:193
false return failure()
Parse an optional keyword or string and set instance into 'result'.
return success()
NamedAttrList attrs
create(data_type, value)
Definition hw.py:433
void registerImportLibertyTranslation()
Register the Liberty importer in the translation registry.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
mlir::Type type
Definition HWTypes.h:34
mlir::StringAttr name
Definition HWTypes.h:33
This holds the name, type, direction of a module's ports.
DictionaryAttr attrs
The optional symbol for this port.