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