CIRCT 22.0.0git
Loading...
Searching...
No Matches
ExportAIGER.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 AIGER file export functionality. AIGER is a format
10// for representing sequential circuits as AIGs (And-Inverter Graphs). This
11// implementation supports both ASCII and binary AIGER formats.
12//
13//===----------------------------------------------------------------------===//
14
24#include "mlir/Analysis/TopologicalSortUtils.h"
25#include "mlir/IR/Builders.h"
26#include "mlir/IR/BuiltinOps.h"
27#include "mlir/IR/DialectRegistry.h"
28#include "mlir/IR/OperationSupport.h"
29#include "mlir/IR/RegionKindInterface.h"
30#include "mlir/IR/Visitors.h"
31#include "mlir/Support/LogicalResult.h"
32#include "mlir/Tools/mlir-translate/Translation.h"
33#include "llvm/ADT/DenseMap.h"
34#include "llvm/ADT/EquivalenceClasses.h"
35#include "llvm/ADT/SmallVector.h"
36#include "llvm/ADT/StringExtras.h"
37#include "llvm/ADT/StringRef.h"
38#include "llvm/Support/Debug.h"
39#include "llvm/Support/LEB128.h"
40#include "llvm/Support/raw_ostream.h"
41#include <string>
42
43using namespace mlir;
44using namespace circt;
45using namespace circt::hw;
46using namespace circt::synth;
47using namespace circt::seq;
48using namespace circt::aiger;
49
50#define DEBUG_TYPE "export-aiger"
51
52//===----------------------------------------------------------------------===//
53// Helper Functions
54//===----------------------------------------------------------------------===//
55
56/// Returns the bit width of a value.
57static int64_t getBitWidth(Value value) {
58 return hw::getBitWidth(value.getType());
59}
60
61//===----------------------------------------------------------------------===//
62// AIGERExporter Implementation
63//===----------------------------------------------------------------------===//
64
65namespace {
66
67/// Main AIGER exporter class that handles the conversion of MLIR modules to
68/// AIGER format. This class manages the state and provides methods for
69/// analyzing the module and writing the AIGER file.
70class AIGERExporter {
71public:
72 AIGERExporter(hw::HWModuleOp module, llvm::raw_ostream &os,
73 const ExportAIGEROptions &options, ExportAIGERHandler *handler)
74 : module(module), os(os), options(options), handler(handler) {}
75
76 /// Export the module to AIGER format.
77 LogicalResult exportModule();
78
79 // Data type for tracking values and their bit positions
80 using Object = std::pair<Value, int>;
81
82private:
83 hw::HWModuleOp module;
84 llvm::raw_ostream &os;
85 const ExportAIGEROptions &options;
86 ExportAIGERHandler *handler;
87
88 // Map of values to their literals
89 DenseMap<Object, unsigned> valueLiteralMap;
90
91 // AIGER file data
92 unsigned getNumInputs() { return inputs.size(); }
93 unsigned getNumLatches() { return latches.size(); }
94 unsigned getNumOutputs() { return outputs.size(); }
95 unsigned getNumAnds() { return andGates.size(); }
96
97 llvm::EquivalenceClasses<Object> valueEquivalence;
98 SmallVector<std::pair<Object, StringAttr>> inputs;
99 SmallVector<std::tuple<Object, StringAttr, Object>>
100 latches; // current, name, next
101 SmallVector<std::pair<Object, StringAttr>> outputs;
102 SmallVector<std::tuple<Object, Object, Object>> andGates; // lhs, rhs0, rhs1
103
104 std::optional<Value> clock;
105
106 /// Analyze the module and collect information
107 LogicalResult analyzeModule();
108
109 /// Get the index name for an object, handling bit positions
110 StringAttr getIndexName(Object obj, StringAttr name) {
111 if (!options.includeSymbolTable || !name || name.getValue().empty() ||
112 obj.first.getType().isInteger(1))
113 return name;
114
115 return StringAttr::get(name.getContext(),
116 name.getValue() + "[" + Twine(obj.second) + "]");
117 }
118
119 /// Add an input to the AIGER file
120 void addInput(Object obj, StringAttr name = {}) {
121 inputs.push_back({obj, getIndexName(obj, name)});
122 }
123
124 /// Add a latch to the AIGER file
125 void addLatch(Object current, Object next, StringAttr name = {}) {
126 latches.push_back({current, getIndexName(current, name), next});
127 }
128
129 /// Add an output to the AIGER file
130 void addOutput(Object obj, StringAttr name = {}) {
131 outputs.push_back({obj, getIndexName(obj, name)});
132 }
133
134 /// Analyze module ports (inputs/outputs)
135 LogicalResult analyzePorts(hw::HWModuleOp module);
136
137 /// Analyze operations in the module
138 LogicalResult analyzeOperations(hw::HWModuleOp module);
139
140 /// Assign literals to all values
141 LogicalResult assignLiterals();
142
143 /// Writers
144 LogicalResult writeHeader();
145 LogicalResult writeInputs();
146 LogicalResult writeLatches();
147 LogicalResult writeOutputs();
148 LogicalResult writeAndGates();
149 LogicalResult writeSymbolTable();
150 LogicalResult writeComments();
151
152 /// Visit an operation and process it
153 LogicalResult visit(Operation *op);
154
155 /// Visit specific operation types
156 LogicalResult visit(aig::AndInverterOp op);
157 LogicalResult visit(seq::CompRegOp op);
158 LogicalResult visit(comb::ConcatOp op);
159 LogicalResult visit(comb::ExtractOp op);
160 LogicalResult visit(comb::ReplicateOp op);
161
162 /// Get or assign a literal for a value
163 unsigned getLiteral(Object obj, bool inverted = false);
164};
165
166} // anonymous namespace
167
168//===----------------------------------------------------------------------===//
169// AIGERExporter Implementation
170//===----------------------------------------------------------------------===//
171
172LogicalResult AIGERExporter::exportModule() {
173 LLVM_DEBUG(llvm::dbgs() << "Starting AIGER export\n");
174 if (failed(analyzeModule()) || failed(writeHeader()) ||
175 failed(writeInputs()) || failed(writeLatches()) ||
176 failed(writeOutputs()) || failed(writeAndGates()) ||
177 failed(writeSymbolTable()) || failed(writeComments()))
178 return failure();
179
180 LLVM_DEBUG(llvm::dbgs() << "AIGER export completed successfully\n");
181 return success();
182}
183
184LogicalResult AIGERExporter::analyzeModule() {
185 LLVM_DEBUG(llvm::dbgs() << "Analyzing module\n");
186
187 auto topModule = module;
188 LLVM_DEBUG(llvm::dbgs() << "Found top module: " << topModule.getModuleName()
189 << "\n");
190
191 // Analyze module ports
192 if (failed(analyzePorts(topModule)))
193 return failure();
194
195 // Walk through all operations in the module body
196 if (failed(analyzeOperations(topModule)))
197 return failure();
198
199 // Assign literals to all values
200 if (failed(assignLiterals()))
201 return failure();
202
203 LLVM_DEBUG(llvm::dbgs() << "Analysis complete: M="
204 << getNumInputs() + getNumLatches() + getNumAnds()
205 << " I=" << getNumInputs() << " L=" << getNumLatches()
206 << " O=" << getNumOutputs() << " A=" << getNumAnds()
207 << "\n");
208
209 return success();
210}
211
212LogicalResult AIGERExporter::writeHeader() {
213 LLVM_DEBUG(llvm::dbgs() << "Writing AIGER header\n");
214
215 // Write format identifier
216 if (options.binaryFormat)
217 os << "aig";
218 else
219 os << "aag";
220
221 // Write M I L O A
222 os << " " << getNumInputs() + getNumLatches() + getNumAnds() << " "
223 << getNumInputs() << " " << getNumLatches() << " " << getNumOutputs()
224 << " " << getNumAnds() << "\n";
225
226 return success();
227}
228
229LogicalResult AIGERExporter::writeInputs() {
230 LLVM_DEBUG(llvm::dbgs() << "Writing inputs\n");
231
232 if (options.binaryFormat)
233 // In binary format, inputs are implicit
234 return success();
235
236 // Write input literals
237 for (auto [input, name] : inputs)
238 os << getLiteral(input) << "\n";
239
240 return success();
241}
242
243LogicalResult AIGERExporter::writeLatches() {
244 LLVM_DEBUG(llvm::dbgs() << "Writing latches\n");
245
246 // Write latch definitions
247 for (auto [current, currentName, next] : latches) {
248
249 // For next state, we need to handle potential inversions
250 // Check if next state comes from an inverter or has inversions
251 unsigned nextLiteral = getLiteral(next);
252
253 if (options.binaryFormat) {
254 // In binary format, current literal is not needed
255 os << nextLiteral << "\n";
256 } else {
257 os << getLiteral(current) << " " << nextLiteral << "\n";
258 }
259 }
260
261 return success();
262}
263
264LogicalResult AIGERExporter::writeOutputs() {
265 LLVM_DEBUG(llvm::dbgs() << "Writing outputs\n");
266
267 // Write output literals
268 for (auto [output, _] : outputs)
269 os << getLiteral(output) << "\n";
270
271 return success();
272}
273
274LogicalResult AIGERExporter::writeAndGates() {
275 LLVM_DEBUG(llvm::dbgs() << "Writing AND gates\n");
276
277 if (options.binaryFormat) {
278 // Implement binary format encoding for AND gates
279 for (auto [lhs, rhs0, rhs1] : andGates) {
280 unsigned lhsLiteral = getLiteral(lhs);
281
282 // Get the AND-inverter operation to check inversion flags
283 auto andInvOp = lhs.first.getDefiningOp<aig::AndInverterOp>();
284 assert(andInvOp && andInvOp.getInputs().size() == 2);
285
286 // Get operand literals with inversion
287 bool rhs0Inverted = andInvOp.getInverted()[0];
288 bool rhs1Inverted = andInvOp.getInverted()[1];
289
290 unsigned rhs0Literal = getLiteral(rhs0, rhs0Inverted);
291 unsigned rhs1Literal = getLiteral(rhs1, rhs1Inverted);
292
293 // Ensure rhs0 >= rhs1 as required by AIGER format
294 if (rhs0Literal < rhs1Literal) {
295 std::swap(rhs0Literal, rhs1Literal);
296 std::swap(rhs0, rhs1);
297 std::swap(rhs0Inverted, rhs1Inverted);
298 }
299
300 // In binary format, we need to write the delta values
301 // Delta0 = lhs - rhs0
302 // Delta1 = rhs0 - rhs1
303 unsigned delta0 = lhsLiteral - rhs0Literal;
304 unsigned delta1 = rhs0Literal - rhs1Literal;
305
306 LLVM_DEBUG(llvm::dbgs()
307 << "Writing AND gate: " << lhsLiteral << " = " << rhs0Literal
308 << " & " << rhs1Literal << " (deltas: " << delta0 << ", "
309 << delta1 << ")\n");
310 assert(lhsLiteral >= rhs0Literal && "lhsLiteral >= rhs0Literal");
311 assert(rhs0Literal >= rhs1Literal && "rhs0Literal >= rhs1Literal");
312
313 // Write deltas using variable-length encoding
314 encodeULEB128(delta0, os);
315 encodeULEB128(delta1, os);
316 }
317 } else {
318 // ASCII format - no need for structural hashing
319 for (auto [lhs, rhs0, rhs1] : andGates) {
320 unsigned lhsLiteral = getLiteral(lhs);
321
322 // Get the AND-inverter operation to check inversion flags
323 auto andInvOp = lhs.first.getDefiningOp<aig::AndInverterOp>();
324
325 assert(andInvOp && andInvOp.getInputs().size() == 2);
326
327 // Get operand literals with inversion
328 bool rhs0Inverted = andInvOp.getInverted()[0];
329 bool rhs1Inverted = andInvOp.getInverted()[1];
330
331 unsigned rhs0Literal = getLiteral(rhs0, rhs0Inverted);
332 unsigned rhs1Literal = getLiteral(rhs1, rhs1Inverted);
333
334 os << lhsLiteral << " " << rhs0Literal << " " << rhs1Literal << "\n";
335 }
336 }
337
338 return success();
339}
340
341LogicalResult AIGERExporter::writeSymbolTable() {
342 LLVM_DEBUG(llvm::dbgs() << "Writing symbol table\n");
343
344 if (!options.includeSymbolTable)
345 return success();
346
347 for (auto [index, elem] : llvm::enumerate(inputs)) {
348 auto [obj, name] = elem;
349 // Don't write inputs without a name
350 if (!name)
351 continue;
352 os << "i" << index << " " << name.getValue() << "\n";
353 }
354
355 for (auto [index, elem] : llvm::enumerate(latches)) {
356 auto [current, currentName, next] = elem;
357 // Don't write latches without a name
358 if (!currentName)
359 continue;
360 os << "l" << index << " " << currentName.getValue() << "\n";
361 }
362
363 for (auto [index, elem] : llvm::enumerate(outputs)) {
364 auto [obj, name] = elem;
365 // Don't write outputs without a name
366 if (!name)
367 continue;
368 os << "o" << index << " " << name.getValue() << "\n";
369 }
370
371 return success();
372}
373
374LogicalResult AIGERExporter::writeComments() {
375 LLVM_DEBUG(llvm::dbgs() << "Writing comments\n");
376
377 // Write comment section
378 os << "c\n";
379 os << "Generated by " << circt::getCirctVersion() << "\n";
380 os << "module: " << module.getModuleName() << "\n";
381
382 return success();
383}
384
385unsigned AIGERExporter::getLiteral(Object obj, bool inverted) {
386 // Get the leader value from the equivalence class
387 obj = valueEquivalence.getOrInsertLeaderValue(obj);
388
389 // Handle constants
390 auto value = obj.first;
391 auto pos = obj.second;
392 {
393 auto it = valueLiteralMap.find({value, pos});
394 if (it != valueLiteralMap.end()) {
395 unsigned literal = it->second;
396 return inverted ? literal ^ 1 : literal;
397 }
398 }
399
400 if (auto constOp = value.getDefiningOp<hw::ConstantOp>()) {
401 APInt constValue = constOp.getValue();
402 if (constValue[obj.second])
403 // TRUE constant = literal 1, inverted = literal 0 (FALSE)
404 return inverted ? 0 : 1;
405 // FALSE constant = literal 0, inverted = literal 1 (TRUE)
406 return inverted ? 1 : 0;
407 }
408
409 // Handle single-input AND-inverter (pure inverter)
410 if (auto andInvOp = value.getDefiningOp<aig::AndInverterOp>()) {
411 if (andInvOp.getInputs().size() == 1) {
412 // This is a pure inverter - we need to find the input's literal
413 Value input = andInvOp.getInputs()[0];
414 bool inputInverted = andInvOp.getInverted()[0];
415 unsigned inputLiteral = getLiteral({input, pos}, inputInverted);
416 // Apply additional inversion if requested
417 valueLiteralMap[{value, pos}] = inputLiteral;
418 return inverted ? inputLiteral ^ 1 : inputLiteral;
419 }
420 }
421
422 // Concatenation, replication, and extraction
423 assert((!isa_and_nonnull<comb::ConcatOp, comb::ExtractOp, comb::ReplicateOp>(
424 value.getDefiningOp())) &&
425 "Unhandled operation type");
426
427 // Look up in the literal map
428 auto it = valueLiteralMap.find({value, pos});
429 if (it != valueLiteralMap.end()) {
430 unsigned literal = it->second;
431 return inverted ? literal ^ 1 : literal;
432 }
433
434 llvm::errs() << "Unhandled: Value not found in literal map: " << value << "["
435 << pos << "]\n";
436
437 // This should not happen if analysis was done correctly
438 llvm_unreachable("Value not found in literal map");
439}
440
441LogicalResult AIGERExporter::analyzePorts(hw::HWModuleOp hwModule) {
442 LLVM_DEBUG(llvm::dbgs() << "Analyzing module ports\n");
443
444 auto inputNames = hwModule.getInputNames();
445 auto outputNames = hwModule.getOutputNames();
446
447 // Analyze input ports
448 for (auto [index, arg] :
449 llvm::enumerate(hwModule.getBodyBlock()->getArguments())) {
450 // Skip non integer inputs.
451 if (!arg.getType().isInteger())
452 continue;
453
454 // All other inputs should be i1 (1-bit assumption)
455 for (int64_t i = 0; i < getBitWidth(arg); ++i) {
456 if (!handler || handler->valueCallback(arg, i, inputs.size()))
457 addInput({arg, i},
458 llvm::dyn_cast_or_null<StringAttr>(inputNames[index]));
459 }
460
461 LLVM_DEBUG(llvm::dbgs() << " Input " << index << ": " << arg << "\n");
462 }
463
464 // Analyze output ports by looking at hw.output operation
465 auto *outputOp = hwModule.getBodyBlock()->getTerminator();
466 for (auto [operand, name] :
467 llvm::zip(outputOp->getOpOperands(), outputNames)) {
468 for (int64_t i = 0; i < getBitWidth(operand.get()); ++i)
469 if (!handler || handler->operandCallback(operand, i, outputs.size()))
470 addOutput({operand.get(), i}, llvm::dyn_cast_or_null<StringAttr>(name));
471 LLVM_DEBUG(llvm::dbgs() << " Output: " << operand.get() << "\n");
472 }
473
474 LLVM_DEBUG(llvm::dbgs() << "Found " << getNumInputs() << " inputs, "
475 << getNumOutputs() << " outputs from ports\n");
476 return success();
477}
478
479LogicalResult AIGERExporter::analyzeOperations(hw::HWModuleOp hwModule) {
480 LLVM_DEBUG(llvm::dbgs() << "Analyzing operations\n");
481
482 // Sort the operations topologically
483 auto result = hwModule.walk([&](Region *region) {
484 auto regionKindOp = dyn_cast<RegionKindInterface>(region->getParentOp());
485 if (!regionKindOp ||
486 regionKindOp.hasSSADominance(region->getRegionNumber()))
487 return WalkResult::advance();
488
489 // Graph region.
490 for (auto &block : *region) {
491 if (!sortTopologically(&block, [&](Value value, Operation *op) {
492 // Topologically sort AND-inverters and purely dataflow ops. Other
493 // operations can be scheduled.
494 return !(isa<aig::AndInverterOp, comb::ExtractOp, comb::ReplicateOp,
495 comb::ConcatOp>(op));
496 }))
497 return WalkResult::interrupt();
498 }
499 return WalkResult::advance();
500 });
501
502 if (result.wasInterrupted())
503 return mlir::emitError(hwModule.getLoc(),
504 "failed to sort operations topologically");
505
506 // Visit each operation in the module
507 for (Operation &op : hwModule.getBodyBlock()->getOperations()) {
508 if (failed(visit(&op)))
509 return failure();
510 }
511
512 LLVM_DEBUG(llvm::dbgs() << "Found " << getNumAnds() << " AND gates, "
513 << getNumLatches() << " latches\n");
514 return success();
515}
516
517LogicalResult AIGERExporter::visit(Operation *op) {
518 return TypeSwitch<Operation *, LogicalResult>(op)
519 .Case<aig::AndInverterOp>([&](auto op) { return visit(op); })
520 .Case<seq::CompRegOp>([&](auto op) { return visit(op); })
521 .Case<comb::ConcatOp>([&](auto op) { return visit(op); })
522 .Case<comb::ExtractOp>([&](auto op) { return visit(op); })
523 .Case<comb::ReplicateOp>([&](auto op) { return visit(op); })
524 .Case<hw::HWModuleOp, hw::ConstantOp, hw::OutputOp>(
525 [](auto) { return success(); })
526 .Default([&](Operation *op) -> LogicalResult {
527 // Handle unknown operations if handler is set
528 if (!handler)
529 return mlir::emitError(op->getLoc(), "unhandled operation: ") << *op;
530
531 // Handle unknown operations through the handler
532 for (mlir::OpOperand &operand : op->getOpOperands()) {
533 if (operand.get().getType().isInteger())
534 for (int64_t i = 0; i < getBitWidth(operand.get()); ++i) {
535 if (handler->operandCallback(operand, i, outputs.size()))
536 addOutput({operand.get(), i});
537 }
538 }
539
540 for (mlir::OpResult result : op->getOpResults()) {
541 if (!result.getType().isInteger())
542 continue;
543 for (int64_t i = 0; i < getBitWidth(result); ++i) {
544 if (handler->valueCallback(result, i, inputs.size()))
545 addInput({result, i});
546 else
547 // Treat it as a constant
548 valueLiteralMap[{result, i}] = 0;
549 }
550 }
551
552 return success();
553 });
554}
555
556LogicalResult AIGERExporter::visit(aig::AndInverterOp op) {
557 // Handle AIG AND-inverter operations
558 if (op.getInputs().size() == 1) {
559 // Single input = inverter (not counted as AND gate in AIGER)
560 // Inversion flag is looked at in getLiteral()
561 LLVM_DEBUG(llvm::dbgs() << " Found inverter: " << op << "\n");
562 } else if (op.getInputs().size() == 2) {
563 // Two inputs = AND gate
564 for (int64_t i = 0; i < getBitWidth(op); ++i)
565 andGates.push_back({{op.getResult(), i},
566 {op.getInputs()[0], i},
567 {op.getInputs()[1], i}});
568
569 LLVM_DEBUG(llvm::dbgs() << " Found AND gate: " << op << "\n");
570 } else {
571 // Variadic AND gates need to be lowered first
572 return mlir::emitError(op->getLoc(),
573 "variadic AND gates not supported, run "
574 "aig-lower-variadic pass first");
575 }
576 if (handler)
577 handler->notifyEmitted(op);
578 return success();
579}
580
581LogicalResult AIGERExporter::visit(seq::CompRegOp op) {
582 // Handle registers (latches in AIGER)
583 for (int64_t i = 0; i < getBitWidth(op); ++i)
584 addLatch({op.getResult(), i}, {op.getInput(), i}, op.getNameAttr());
585
586 LLVM_DEBUG(llvm::dbgs() << " Found latch: " << op << "\n");
587 if (handler)
588 handler->notifyEmitted(op);
589 return success();
590}
591
592LogicalResult AIGERExporter::visit(comb::ConcatOp op) {
593 size_t pos = 0;
594 for (auto operand : llvm::reverse(op.getInputs()))
595 for (int64_t i = 0, e = getBitWidth(operand); i < e; ++i)
596 valueEquivalence.unionSets({operand, i}, {op.getResult(), pos++});
597 return success();
598}
599
600LogicalResult AIGERExporter::visit(comb::ExtractOp op) {
601 for (int64_t i = 0, e = getBitWidth(op); i < e; ++i)
602 valueEquivalence.unionSets({op.getInput(), op.getLowBit() + i},
603 {op.getResult(), i});
604 return success();
605}
606
607LogicalResult AIGERExporter::visit(comb::ReplicateOp op) {
608 auto operandWidth = getBitWidth(op.getInput());
609 for (int64_t i = 0, e = getBitWidth(op); i < e; ++i)
610 valueEquivalence.unionSets({op.getInput(), i % operandWidth},
611 {op.getResult(), i});
612 return success();
613}
614
615llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
616 const AIGERExporter::Object &obj) {
617 return os << obj.first << "[" << obj.second << "]";
618}
619
620llvm::raw_ostream &
621operator<<(llvm::raw_ostream &os,
622 const std::pair<AIGERExporter::Object, StringAttr> &obj) {
623 return os << obj.first
624 << (obj.second ? " (" + obj.second.getValue() + ")" : "");
625}
626
627LogicalResult AIGERExporter::assignLiterals() {
628 LLVM_DEBUG(llvm::dbgs() << "Assigning literals\n");
629
630 unsigned nextLiteral =
631 2; // Start from 2 (literal 0 = FALSE, literal 1 = TRUE)
632
633 // Assign literals to inputs first
634 for (auto input : inputs) {
635 valueLiteralMap[input.first] = nextLiteral;
636 LLVM_DEBUG(llvm::dbgs()
637 << " Input literal " << nextLiteral << ": " << input << "\n");
638 nextLiteral += 2; // Even literals only (odd = inverted)
639 }
640
641 // Assign literals to latches (current state)
642 for (auto [current, currentName, next] : latches) {
643 valueLiteralMap[current] = nextLiteral;
644 LLVM_DEBUG(llvm::dbgs()
645 << " Latch literal " << nextLiteral << ": " << current << "\n");
646 nextLiteral += 2;
647 auto clocked = current.first.getDefiningOp<seq::Clocked>();
648 assert(clocked);
649 if (!clock)
650 clock = clocked.getClk();
651 else if (clock != clocked.getClk()) {
652 auto diag = mlir::emitError(clocked.getClk().getLoc(),
653 "multiple clocks found in the module");
654 diag.attachNote(clock->getLoc()) << "previous clock is here";
655 return diag;
656 }
657 }
658
659 if (clock && handler)
660 handler->notifyClock(*clock);
661
662 for (auto [lhs, rhs0, rhs1] : andGates) {
663 valueLiteralMap[lhs] = nextLiteral;
664 LLVM_DEBUG(llvm::dbgs()
665 << " AND gate literal " << nextLiteral << ": " << lhs << "\n");
666 nextLiteral += 2;
667 }
668
669 LLVM_DEBUG(llvm::dbgs() << "Assigned " << valueLiteralMap.size()
670 << " literals\n");
671 return success();
672}
673
674//===----------------------------------------------------------------------===//
675// Public API Implementation
676//===----------------------------------------------------------------------===//
677
679 llvm::raw_ostream &os,
680 const ExportAIGEROptions *options,
681 ExportAIGERHandler *handler) {
682 ExportAIGEROptions defaultOptions;
683 if (!options)
684 options = &defaultOptions;
685
686 AIGERExporter exporter(module, os, *options, handler);
687 return exporter.exportModule();
688}
689
691 emitTextFormat("emit-text-format",
692 llvm::cl::desc("Export AIGER in text format"),
693 llvm::cl::init(false));
694
696 includeSymbolTable("exclude-symbol-table",
697 llvm::cl::desc("Exclude symbol table from the output"),
698 llvm::cl::init(false));
699
701 static mlir::TranslateFromMLIRRegistration toAIGER(
702 "export-aiger", "Export AIG to AIGER format",
703 [](mlir::ModuleOp module, llvm::raw_ostream &os) -> LogicalResult {
704 auto ops = module.getOps<hw::HWModuleOp>();
705 if (ops.empty())
706 return module.emitError("no HW module found in the input");
707 if (std::next(ops.begin()) != ops.end())
708 return module.emitError(
709 "multiple HW modules found, expected single top module");
710
711 ExportAIGEROptions options;
712 options.binaryFormat = !emitTextFormat;
714
715 return exportAIGER(*ops.begin(), os, &options);
716 },
717 [](DialectRegistry &registry) {
718 registry.insert<synth::SynthDialect, hw::HWDialect, seq::SeqDialect,
719 comb::CombDialect>();
720 });
721}
assert(baseType &&"element must be base type")
llvm::cl::opt< bool > includeSymbolTable("exclude-symbol-table", llvm::cl::desc("Exclude symbol table from the output"), llvm::cl::init(false))
llvm::cl::opt< bool > emitTextFormat("emit-text-format", llvm::cl::desc("Export AIGER in text format"), llvm::cl::init(false))
static Block * getBodyBlock(FModuleLike mod)
mlir::LogicalResult exportAIGER(hw::HWModuleOp module, llvm::raw_ostream &os, const ExportAIGEROptions *options=nullptr, ExportAIGERHandler *handler=nullptr)
Export an MLIR module containing AIG dialect operations to AIGER format.
void registerExportAIGERTranslation()
Register the export-aiger MLIR translation.
OS & operator<<(OS &os, const InnerSymTarget &target)
Printing InnerSymTarget's.
int64_t getBitWidth(mlir::Type type)
Return the hardware bit width of a type.
Definition HWTypes.cpp:110
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
const char * getCirctVersion()
Handler for AIGER export.
Definition ExportAIGER.h:35
virtual bool valueCallback(Value result, size_t bitPos, size_t inputIndex)
Definition ExportAIGER.h:51
virtual void notifyClock(Value value)
Definition ExportAIGER.h:59
virtual bool operandCallback(mlir::OpOperand &operand, size_t bitPos, size_t outputIndex)
Definition ExportAIGER.h:43
virtual void notifyEmitted(Operation *op)
Definition ExportAIGER.h:56
Options for AIGER export.
Definition ExportAIGER.h:63
bool includeSymbolTable
Whether to include symbol table in the output.
Definition ExportAIGER.h:70
bool binaryFormat
Whether to export in binary format (aig) or ASCII format (aag).
Definition ExportAIGER.h:66