CIRCT  19.0.0git
SeqToSV.cpp
Go to the documentation of this file.
1 //===- LowerSeqToSV.cpp - Seq to SV lowering ------------------------------===//
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 transform translate Seq ops to SV.
10 //
11 //===----------------------------------------------------------------------===//
12 
14 #include "../PassDetail.h"
15 #include "FirMemLowering.h"
16 #include "FirRegLowering.h"
21 #include "circt/Dialect/HW/HWOps.h"
24 #include "circt/Dialect/SV/SVOps.h"
26 #include "circt/Support/Naming.h"
27 #include "mlir/IR/Builders.h"
28 #include "mlir/IR/DialectImplementation.h"
29 #include "mlir/IR/ImplicitLocOpBuilder.h"
30 #include "mlir/IR/Threading.h"
31 #include "mlir/Pass/Pass.h"
32 #include "mlir/Transforms/DialectConversion.h"
33 #include "llvm/ADT/IntervalMap.h"
34 #include "llvm/ADT/TypeSwitch.h"
35 #include "llvm/Support/Mutex.h"
36 
37 #define DEBUG_TYPE "lower-seq-to-sv"
38 
39 using namespace circt;
40 using namespace seq;
41 using hw::HWModuleOp;
42 using llvm::MapVector;
43 
44 namespace {
45 #define GEN_PASS_DEF_LOWERSEQTOSV
46 #include "circt/Conversion/Passes.h.inc"
47 
48 struct SeqToSVPass : public impl::LowerSeqToSVBase<SeqToSVPass> {
49 
50  void runOnOperation() override;
51 
52  using LowerSeqToSVBase<SeqToSVPass>::lowerToAlwaysFF;
53  using LowerSeqToSVBase<SeqToSVPass>::disableRegRandomization;
54  using LowerSeqToSVBase<SeqToSVPass>::emitSeparateAlwaysBlocks;
55  using LowerSeqToSVBase<SeqToSVPass>::LowerSeqToSVBase;
56  using LowerSeqToSVBase<SeqToSVPass>::numSubaccessRestored;
57 };
58 } // anonymous namespace
59 
60 namespace {
61 /// Lower CompRegOp to `sv.reg` and `sv.alwaysff`. Use a posedge clock and
62 /// synchronous reset.
63 template <typename OpTy>
64 class CompRegLower : public OpConversionPattern<OpTy> {
65 public:
66  CompRegLower(TypeConverter &typeConverter, MLIRContext *context,
67  bool lowerToAlwaysFF)
68  : OpConversionPattern<OpTy>(typeConverter, context),
69  lowerToAlwaysFF(lowerToAlwaysFF) {}
70 
71  using OpAdaptor = typename OpConversionPattern<OpTy>::OpAdaptor;
72 
73  LogicalResult
74  matchAndRewrite(OpTy reg, OpAdaptor adaptor,
75  ConversionPatternRewriter &rewriter) const final {
76  Location loc = reg.getLoc();
77 
78  auto regTy =
79  ConversionPattern::getTypeConverter()->convertType(reg.getType());
80 
81  auto svReg = rewriter.create<sv::RegOp>(loc, regTy, reg.getNameAttr(),
82  reg.getInnerSymAttr(),
83  reg.getPowerOnValue());
84  svReg->setDialectAttrs(reg->getDialectAttrs());
85 
87 
88  auto regVal = rewriter.create<sv::ReadInOutOp>(loc, svReg);
89 
90  auto assignValue = [&] {
91  createAssign(rewriter, reg.getLoc(), svReg, reg);
92  };
93  auto assignReset = [&] {
94  rewriter.create<sv::PAssignOp>(loc, svReg, adaptor.getResetValue());
95  };
96 
97  if (adaptor.getReset() && adaptor.getResetValue()) {
98  if (lowerToAlwaysFF) {
99  rewriter.create<sv::AlwaysFFOp>(
100  loc, sv::EventControl::AtPosEdge, adaptor.getClk(),
101  ResetType::SyncReset, sv::EventControl::AtPosEdge,
102  adaptor.getReset(), assignValue, assignReset);
103  } else {
104  rewriter.create<sv::AlwaysOp>(
105  loc, sv::EventControl::AtPosEdge, adaptor.getClk(), [&] {
106  rewriter.create<sv::IfOp>(loc, adaptor.getReset(), assignReset,
107  assignValue);
108  });
109  }
110  } else {
111  if (lowerToAlwaysFF) {
112  rewriter.create<sv::AlwaysFFOp>(loc, sv::EventControl::AtPosEdge,
113  adaptor.getClk(), assignValue);
114  } else {
115  rewriter.create<sv::AlwaysOp>(loc, sv::EventControl::AtPosEdge,
116  adaptor.getClk(), assignValue);
117  }
118  }
119 
120  rewriter.replaceOp(reg, regVal);
121  return success();
122  }
123 
124  // Helper to create an assignment based on the register type.
125  void createAssign(ConversionPatternRewriter &rewriter, Location loc,
126  sv::RegOp svReg, OpAdaptor reg) const;
127 
128 private:
129  bool lowerToAlwaysFF;
130 };
131 
132 /// Create the assign.
133 template <>
134 void CompRegLower<CompRegOp>::createAssign(ConversionPatternRewriter &rewriter,
135  Location loc, sv::RegOp svReg,
136  OpAdaptor reg) const {
137  rewriter.create<sv::PAssignOp>(loc, svReg, reg.getInput());
138 }
139 /// Create the assign inside of an if block.
140 template <>
141 void CompRegLower<CompRegClockEnabledOp>::createAssign(
142  ConversionPatternRewriter &rewriter, Location loc, sv::RegOp svReg,
143  OpAdaptor reg) const {
144  rewriter.create<sv::IfOp>(loc, reg.getClockEnable(), [&]() {
145  rewriter.create<sv::PAssignOp>(loc, svReg, reg.getInput());
146  });
147 }
148 
149 // Lower seq.clock_gate to a fairly standard clock gate implementation.
150 //
151 class ClockGateLowering : public OpConversionPattern<ClockGateOp> {
152 public:
154  using OpAdaptor = typename OpConversionPattern<ClockGateOp>::OpAdaptor;
155  LogicalResult
156  matchAndRewrite(ClockGateOp clockGate, OpAdaptor adaptor,
157  ConversionPatternRewriter &rewriter) const final {
158  auto loc = clockGate.getLoc();
159  Value clk = adaptor.getInput();
160 
161  // enable in
162  Value enable = adaptor.getEnable();
163  if (auto te = adaptor.getTestEnable())
164  enable = rewriter.create<comb::OrOp>(loc, enable, te);
165 
166  // Enable latch.
167  Value enableLatch = rewriter.create<sv::RegOp>(
168  loc, rewriter.getI1Type(), rewriter.getStringAttr("cg_en_latch"));
169 
170  // Latch the enable signal using an always @* block.
171  rewriter.create<sv::AlwaysOp>(
172  loc, llvm::SmallVector<sv::EventControl>{}, llvm::SmallVector<Value>{},
173  [&]() {
174  rewriter.create<sv::IfOp>(
175  loc, comb::createOrFoldNot(loc, clk, rewriter), [&]() {
176  rewriter.create<sv::PAssignOp>(loc, enableLatch, enable);
177  });
178  });
179 
180  // Create the gated clock signal.
181  rewriter.replaceOpWithNewOp<comb::AndOp>(
182  clockGate, clk, rewriter.create<sv::ReadInOutOp>(loc, enableLatch));
183  return success();
184  }
185 };
186 
187 // Lower seq.clock_inv to a regular inverter.
188 //
189 class ClockInverterLowering : public OpConversionPattern<ClockInverterOp> {
190 public:
192 
193  LogicalResult
194  matchAndRewrite(ClockInverterOp op, OpAdaptor adaptor,
195  ConversionPatternRewriter &rewriter) const final {
196  auto loc = op.getLoc();
197  Value clk = adaptor.getInput();
198 
199  StringAttr name = op->getAttrOfType<StringAttr>("sv.namehint");
200  Value one = rewriter.create<hw::ConstantOp>(loc, APInt(1, 1));
201  auto newOp = rewriter.replaceOpWithNewOp<comb::XorOp>(op, clk, one);
202  if (name)
203  rewriter.modifyOpInPlace(newOp,
204  [&] { newOp->setAttr("sv.namehint", name); });
205  return success();
206  }
207 };
208 
209 // Lower seq.clock_mux to a `comb.mux` op
210 //
211 class ClockMuxLowering : public OpConversionPattern<ClockMuxOp> {
212 public:
215 
216  LogicalResult
217  matchAndRewrite(ClockMuxOp clockMux, OpAdaptor adaptor,
218  ConversionPatternRewriter &rewriter) const final {
219  rewriter.replaceOpWithNewOp<comb::MuxOp>(clockMux, adaptor.getCond(),
220  adaptor.getTrueClock(),
221  adaptor.getFalseClock(), true);
222  return success();
223  }
224 };
225 
226 /// Map `seq.clock` to `i1`.
227 struct SeqToSVTypeConverter : public TypeConverter {
228  SeqToSVTypeConverter() {
229  addConversion([&](Type type) { return type; });
230  addConversion([&](seq::ClockType type) {
231  return IntegerType::get(type.getContext(), 1);
232  });
233  addConversion([&](hw::StructType structTy) {
234  bool changed = false;
235 
236  SmallVector<hw::StructType::FieldInfo> newFields;
237  for (auto field : structTy.getElements()) {
238  auto &newField = newFields.emplace_back();
239  newField.name = field.name;
240  newField.type = convertType(field.type);
241  if (field.type != newField.type)
242  changed = true;
243  }
244 
245  if (!changed)
246  return structTy;
247 
248  return hw::StructType::get(structTy.getContext(), newFields);
249  });
250  addConversion([&](hw::ArrayType arrayTy) {
251  auto elementTy = arrayTy.getElementType();
252  auto newElementTy = convertType(elementTy);
253  if (elementTy != newElementTy)
254  return hw::ArrayType::get(newElementTy, arrayTy.getNumElements());
255  return arrayTy;
256  });
257 
258  addTargetMaterialization(
259  [&](mlir::OpBuilder &builder, mlir::Type resultType,
260  mlir::ValueRange inputs,
261  mlir::Location loc) -> std::optional<mlir::Value> {
262  if (inputs.size() != 1)
263  return std::nullopt;
264  return inputs[0];
265  });
266 
267  addSourceMaterialization(
268  [&](mlir::OpBuilder &builder, mlir::Type resultType,
269  mlir::ValueRange inputs,
270  mlir::Location loc) -> std::optional<mlir::Value> {
271  if (inputs.size() != 1)
272  return std::nullopt;
273  return inputs[0];
274  });
275  }
276 };
277 
278 /// Eliminate no-op clock casts.
279 template <typename T>
280 class ClockCastLowering : public OpConversionPattern<T> {
281 public:
283 
284  LogicalResult
285  matchAndRewrite(T op, typename T::Adaptor adaptor,
286  ConversionPatternRewriter &rewriter) const final {
287  // If the cast had a better name than its input, propagate it.
288  if (Operation *inputOp = adaptor.getInput().getDefiningOp())
289  if (!isa<mlir::UnrealizedConversionCastOp>(inputOp))
290  if (auto name = chooseName(op, inputOp))
291  rewriter.modifyOpInPlace(
292  inputOp, [&] { inputOp->setAttr("sv.namehint", name); });
293 
294  rewriter.replaceOp(op, adaptor.getInput());
295  return success();
296  }
297 };
298 
299 // Lower seq.const_clock to `hw.constant`
300 //
301 class ClockConstLowering : public OpConversionPattern<ConstClockOp> {
302 public:
305 
306  LogicalResult
307  matchAndRewrite(ConstClockOp clockConst, OpAdaptor adaptor,
308  ConversionPatternRewriter &rewriter) const final {
309  rewriter.replaceOpWithNewOp<hw::ConstantOp>(
310  clockConst, APInt(1, clockConst.getValue() == ClockConst::High));
311  return success();
312  }
313 };
314 
315 /// Lower `seq.clock_div` to a behavioural clock divider
316 ///
317 class ClockDividerLowering : public OpConversionPattern<ClockDividerOp> {
318 public:
321 
322  LogicalResult
323  matchAndRewrite(ClockDividerOp clockDiv, OpAdaptor adaptor,
324  ConversionPatternRewriter &rewriter) const final {
325  Location loc = clockDiv.getLoc();
326 
327  Value one;
328  if (clockDiv.getPow2()) {
329  one = rewriter.create<hw::ConstantOp>(loc, APInt(1, 1));
330  }
331 
332  Value output = clockDiv.getInput();
333 
334  SmallVector<Value> regs;
335  for (unsigned i = 0; i < clockDiv.getPow2(); ++i) {
336  Value reg = rewriter.create<sv::RegOp>(
337  loc, rewriter.getI1Type(),
338  rewriter.getStringAttr("clock_out_" + std::to_string(i)));
339  regs.push_back(reg);
340 
341  rewriter.create<sv::AlwaysOp>(
342  loc, sv::EventControl::AtPosEdge, output, [&] {
343  Value outputVal = rewriter.create<sv::ReadInOutOp>(loc, reg);
344  Value inverted = rewriter.create<comb::XorOp>(loc, outputVal, one);
345  rewriter.create<sv::BPAssignOp>(loc, reg, inverted);
346  });
347 
348  output = rewriter.create<sv::ReadInOutOp>(loc, reg);
349  }
350 
351  if (!regs.empty()) {
352  Value zero = rewriter.create<hw::ConstantOp>(loc, APInt(1, 0));
353  rewriter.create<sv::InitialOp>(loc, [&] {
354  for (Value reg : regs) {
355  rewriter.create<sv::BPAssignOp>(loc, reg, zero);
356  }
357  });
358  }
359 
360  rewriter.replaceOp(clockDiv, output);
361  return success();
362  }
363 };
364 
365 } // namespace
366 
367 // NOLINTBEGIN(misc-no-recursion)
368 static bool isLegalType(Type ty) {
369  if (hw::type_isa<ClockType>(ty))
370  return false;
371 
372  if (auto arrayTy = hw::type_dyn_cast<hw::ArrayType>(ty))
373  return isLegalType(arrayTy.getElementType());
374 
375  if (auto structTy = hw::type_dyn_cast<hw::StructType>(ty)) {
376  for (auto field : structTy.getElements())
377  if (!isLegalType(field.type))
378  return false;
379  return true;
380  }
381 
382  return true;
383 }
384 // NOLINTEND(misc-no-recursion)
385 
386 static bool isLegalOp(Operation *op) {
387  if (auto module = dyn_cast<hw::HWModuleLike>(op)) {
388  for (auto port : module.getHWModuleType().getPorts())
389  if (!isLegalType(port.type))
390  return false;
391  return true;
392  }
393  bool allOperandsLowered = llvm::all_of(
394  op->getOperands(), [](auto op) { return isLegalType(op.getType()); });
395  bool allResultsLowered = llvm::all_of(op->getResults(), [](auto result) {
396  return isLegalType(result.getType());
397  });
398  return allOperandsLowered && allResultsLowered;
399 }
400 
401 void SeqToSVPass::runOnOperation() {
402  auto circuit = getOperation();
403  MLIRContext *context = &getContext();
404 
405  auto modules = llvm::to_vector(circuit.getOps<HWModuleOp>());
406 
407  FirMemLowering memLowering(circuit);
408 
409  // Identify memories and group them by module.
410  auto uniqueMems = memLowering.collectMemories(modules);
411  MapVector<HWModuleOp, SmallVector<FirMemLowering::MemoryConfig>> memsByModule;
412  for (auto &[config, memOps] : uniqueMems) {
413  // Create the `HWModuleGeneratedOp`s for each unique configuration.
414  auto genOp = memLowering.createMemoryModule(config, memOps);
415 
416  // Group memories by their parent module for parallelism.
417  for (auto memOp : memOps) {
418  auto parent = memOp->getParentOfType<HWModuleOp>();
419  memsByModule[parent].emplace_back(&config, genOp, memOp);
420  }
421  }
422 
423  // Lower memories and registers in modules in parallel.
424  bool needsRegRandomization = false;
425  bool needsMemRandomization = false;
426 
427  struct FragmentInfo {
428  bool needsRegFragment;
429  bool needsMemFragment;
430  };
431  DenseMap<HWModuleOp, FragmentInfo> moduleFragmentInfo;
432  llvm::sys::SmartMutex<true> fragmentsMutex;
433 
434  mlir::parallelForEach(&getContext(), modules, [&](HWModuleOp module) {
435  SeqToSVTypeConverter typeConverter;
436  FirRegLowering regLowering(typeConverter, module, disableRegRandomization,
437  emitSeparateAlwaysBlocks);
438  regLowering.lower();
439  if (regLowering.needsRegRandomization()) {
440  if (!disableRegRandomization) {
441  llvm::sys::SmartScopedLock<true> lock(fragmentsMutex);
442  moduleFragmentInfo[module].needsRegFragment = true;
443  }
444  needsRegRandomization = true;
445  }
446  numSubaccessRestored += regLowering.numSubaccessRestored;
447 
448  if (auto *it = memsByModule.find(module); it != memsByModule.end()) {
449  memLowering.lowerMemoriesInModule(module, it->second);
450  if (!disableMemRandomization) {
451  llvm::sys::SmartScopedLock<true> lock(fragmentsMutex);
452  moduleFragmentInfo[module].needsMemFragment = true;
453  }
454  needsMemRandomization = true;
455  }
456  });
457 
458  auto randomInitFragmentName =
459  FlatSymbolRefAttr::get(context, "RANDOM_INIT_FRAGMENT");
460  auto randomInitRegFragmentName =
461  FlatSymbolRefAttr::get(context, "RANDOM_INIT_REG_FRAGMENT");
462  auto randomInitMemFragmentName =
463  FlatSymbolRefAttr::get(context, "RANDOM_INIT_MEM_FRAGMENT");
464 
465  for (auto &[module, info] : moduleFragmentInfo) {
466  assert((info.needsRegFragment || info.needsMemFragment) &&
467  "module should use memories or registers");
468 
469  SmallVector<Attribute> fragmentAttrs;
470  if (auto others =
471  module->getAttrOfType<ArrayAttr>(emit::getFragmentsAttrName()))
472  fragmentAttrs = llvm::to_vector(others);
473 
474  if (info.needsRegFragment)
475  fragmentAttrs.push_back(randomInitRegFragmentName);
476  if (info.needsMemFragment)
477  fragmentAttrs.push_back(randomInitMemFragmentName);
478  fragmentAttrs.push_back(randomInitFragmentName);
479 
480  module->setAttr(emit::getFragmentsAttrName(),
481  ArrayAttr::get(context, fragmentAttrs));
482  }
483 
484  // Mark all ops which can have clock types as illegal.
485  SeqToSVTypeConverter typeConverter;
486  ConversionTarget target(*context);
487  target.addIllegalDialect<SeqDialect>();
488  target.markUnknownOpDynamicallyLegal(isLegalOp);
489 
490  RewritePatternSet patterns(context);
491  patterns.add<CompRegLower<CompRegOp>>(typeConverter, context,
492  lowerToAlwaysFF);
493  patterns.add<CompRegLower<CompRegClockEnabledOp>>(typeConverter, context,
494  lowerToAlwaysFF);
495  patterns.add<ClockCastLowering<seq::FromClockOp>>(typeConverter, context);
496  patterns.add<ClockCastLowering<seq::ToClockOp>>(typeConverter, context);
497  patterns.add<ClockGateLowering>(typeConverter, context);
498  patterns.add<ClockInverterLowering>(typeConverter, context);
499  patterns.add<ClockMuxLowering>(typeConverter, context);
500  patterns.add<ClockDividerLowering>(typeConverter, context);
501  patterns.add<ClockConstLowering>(typeConverter, context);
502  patterns.add<TypeConversionPattern>(typeConverter, context);
503 
504  if (failed(applyPartialConversion(circuit, target, std::move(patterns))))
505  signalPassFailure();
506 
507  auto loc = UnknownLoc::get(context);
508  auto b = ImplicitLocOpBuilder::atBlockBegin(loc, circuit.getBody());
509  if (needsRegRandomization || needsMemRandomization) {
510  b.create<sv::MacroDeclOp>("ENABLE_INITIAL_REG_");
511  b.create<sv::MacroDeclOp>("ENABLE_INITIAL_MEM_");
512  if (needsRegRandomization) {
513  b.create<sv::MacroDeclOp>("FIRRTL_BEFORE_INITIAL");
514  b.create<sv::MacroDeclOp>("FIRRTL_AFTER_INITIAL");
515  }
516  if (needsMemRandomization)
517  b.create<sv::MacroDeclOp>("RANDOMIZE_MEM_INIT");
518  b.create<sv::MacroDeclOp>("RANDOMIZE_REG_INIT");
519  b.create<sv::MacroDeclOp>("RANDOMIZE");
520  b.create<sv::MacroDeclOp>("RANDOMIZE_DELAY");
521  b.create<sv::MacroDeclOp>("RANDOM");
522  b.create<sv::MacroDeclOp>("INIT_RANDOM");
523  b.create<sv::MacroDeclOp>("INIT_RANDOM_PROLOG_");
524  }
525 
526  bool hasRegRandomization = needsRegRandomization && !disableRegRandomization;
527  bool hasMemRandomization = needsMemRandomization && !disableMemRandomization;
528  if (!hasRegRandomization && !hasMemRandomization)
529  return;
530 
531  // Build macros for FIRRTL-style register and memory initialization.
532  // Insert them at the start of the module, after any other verbatims.
533  for (Operation &op : *circuit.getBody()) {
534  if (!isa<sv::VerbatimOp, sv::IfDefOp>(&op)) {
535  b.setInsertionPoint(&op);
536  break;
537  }
538  }
539 
540  // Create SYNTHESIS/VERILATOR macros if other passes have not done so already.
541  {
542  StringSet<> symbols;
543  for (auto sym : circuit.getOps<sv::MacroDeclOp>())
544  symbols.insert(sym.getName());
545  if (!symbols.count("SYNTHESIS"))
546  b.create<sv::MacroDeclOp>("SYNTHESIS");
547  if (!symbols.count("VERILATOR"))
548  b.create<sv::MacroDeclOp>("VERILATOR");
549  }
550 
551  // TODO: We could have an operation for macros and uses of them, and
552  // even turn them into symbols so we can DCE unused macro definitions.
553  auto emitGuardedDefine = [&](StringRef guard, StringRef defName,
554  StringRef defineTrue = "",
555  StringRef defineFalse = StringRef()) {
556  if (!defineFalse.data()) {
557  assert(defineTrue.data() && "didn't define anything");
558  b.create<sv::IfDefOp>(
559  guard, [&]() { b.create<sv::MacroDefOp>(defName, defineTrue); });
560  } else {
561  b.create<sv::IfDefOp>(
562  guard,
563  [&]() {
564  if (defineTrue.data())
565  b.create<sv::MacroDefOp>(defName, defineTrue);
566  },
567  [&]() { b.create<sv::MacroDefOp>(defName, defineFalse); });
568  }
569  };
570 
571  // Helper function to emit #ifndef guard.
572  auto emitGuard = [&](const char *guard, llvm::function_ref<void(void)> body) {
573  b.create<sv::IfDefOp>(
574  guard, []() {}, body);
575  };
576 
577  b.create<emit::FragmentOp>(randomInitFragmentName.getAttr(), [&] {
578  b.create<sv::VerbatimOp>(
579  "// Standard header to adapt well known macros for "
580  "register randomization.");
581 
582  b.create<sv::VerbatimOp>(
583  "\n// RANDOM may be set to an expression that produces a 32-bit "
584  "random unsigned value.");
585  emitGuardedDefine("RANDOM", "RANDOM", StringRef(), "$random");
586 
587  b.create<sv::VerbatimOp>(
588  "\n// Users can define INIT_RANDOM as general code that gets "
589  "injected "
590  "into the\n// initializer block for modules with registers.");
591  emitGuardedDefine("INIT_RANDOM", "INIT_RANDOM", StringRef(), "");
592 
593  b.create<sv::VerbatimOp>(
594  "\n// If using random initialization, you can also define "
595  "RANDOMIZE_DELAY to\n// customize the delay used, otherwise 0.002 "
596  "is used.");
597  emitGuardedDefine("RANDOMIZE_DELAY", "RANDOMIZE_DELAY", StringRef(),
598  "0.002");
599 
600  b.create<sv::VerbatimOp>(
601  "\n// Define INIT_RANDOM_PROLOG_ for use in our modules below.");
602  emitGuard("INIT_RANDOM_PROLOG_", [&]() {
603  b.create<sv::IfDefOp>(
604  "RANDOMIZE",
605  [&]() {
606  emitGuardedDefine("VERILATOR", "INIT_RANDOM_PROLOG_",
607  "`INIT_RANDOM",
608  "`INIT_RANDOM #`RANDOMIZE_DELAY begin end");
609  },
610  [&]() { b.create<sv::MacroDefOp>("INIT_RANDOM_PROLOG_", ""); });
611  });
612  });
613 
614  if (hasMemRandomization) {
615  b.create<emit::FragmentOp>(randomInitMemFragmentName.getAttr(), [&] {
616  b.create<sv::VerbatimOp>("\n// Include rmemory initializers in init "
617  "blocks unless synthesis is set");
618  emitGuard("RANDOMIZE", [&]() {
619  emitGuardedDefine("RANDOMIZE_MEM_INIT", "RANDOMIZE");
620  });
621  emitGuard("SYNTHESIS", [&] {
622  emitGuardedDefine("ENABLE_INITIAL_MEM_", "ENABLE_INITIAL_MEM_",
623  StringRef(), "");
624  });
625  b.create<sv::VerbatimOp>("");
626  });
627  }
628 
629  if (hasRegRandomization) {
630  b.create<emit::FragmentOp>(randomInitRegFragmentName.getAttr(), [&] {
631  b.create<sv::VerbatimOp>("\n// Include register initializers in init "
632  "blocks unless synthesis is set");
633  emitGuard("RANDOMIZE", [&]() {
634  emitGuardedDefine("RANDOMIZE_REG_INIT", "RANDOMIZE");
635  });
636  emitGuard("SYNTHESIS", [&] {
637  emitGuardedDefine("ENABLE_INITIAL_REG_", "ENABLE_INITIAL_REG_",
638  StringRef(), "");
639  });
640  b.create<sv::VerbatimOp>("");
641  });
642  }
643 }
644 
645 std::unique_ptr<Pass>
646 circt::createLowerSeqToSVPass(const LowerSeqToSVOptions &options) {
647  return std::make_unique<SeqToSVPass>(options);
648 }
assert(baseType &&"element must be base type")
static FIRRTLBaseType convertType(FIRRTLBaseType type)
Returns null type if no conversion is needed.
Definition: DropConst.cpp:25
llvm::SmallVector< StringAttr > inputs
Builder builder
static bool isLegalType(Type ty)
Definition: SeqToSV.cpp:368
static bool isLegalOp(Operation *op)
Definition: SeqToSV.cpp:386
FIR memory lowering helper.
Lower FirRegOp to sv.reg and sv.always.
def create(data_type, value)
Definition: hw.py:393
Definition: sv.py:15
def create(value)
Definition: sv.py:106
Definition: sv.py:68
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
Value createOrFoldNot(Location loc, Value value, OpBuilder &builder, bool twoState=false)
Create a `‘Not’' gate on a value.
Definition: CombOps.cpp:48
StringRef getFragmentsAttrName()
Return the name of the fragments array attribute.
Definition: EmitOps.h:32
mlir::ArrayAttr getSVAttributes(mlir::Operation *op)
Return all the SV attributes of an operation, or null if there are none.
void setSVAttributes(mlir::Operation *op, mlir::ArrayAttr attrs)
Set the SV attributes of an operation.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
std::unique_ptr< mlir::Pass > createLowerSeqToSVPass(const LowerSeqToSVOptions &options={})
Definition: SeqToSV.cpp:646
StringRef chooseName(StringRef a, StringRef b)
Choose a good name for an item from two options.
Definition: Naming.cpp:47
Definition: seq.py:1
def reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
Definition: seq.py:20
Generic pattern which replaces an operation by one of the same operation name, but with converted att...