CIRCT  18.0.0git
GrandCentral.cpp
Go to the documentation of this file.
1 //===- GrandCentral.cpp - Ingest black box sources --------------*- C++ -*-===//
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 // Implement SiFive's Grand Central transform. Currently, this supports
9 // SystemVerilog Interface generation.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "PassDetails.h"
23 #include "circt/Dialect/HW/HWOps.h"
25 #include "circt/Dialect/SV/SVOps.h"
26 #include "mlir/IR/ImplicitLocOpBuilder.h"
27 #include "llvm/ADT/DepthFirstIterator.h"
28 #include "llvm/ADT/TypeSwitch.h"
29 #include "llvm/Support/Debug.h"
30 #include "llvm/Support/YAMLTraits.h"
31 #include <variant>
32 
33 #define DEBUG_TYPE "gct"
34 
35 using namespace circt;
36 using namespace firrtl;
37 
38 //===----------------------------------------------------------------------===//
39 // Collateral for generating a YAML representation of a SystemVerilog interface
40 //===----------------------------------------------------------------------===//
41 
42 namespace {
43 
44 // These macros are used to provide hard-errors if a user tries to use the YAML
45 // infrastructure improperly. We only implement conversion to YAML and not
46 // conversion from YAML. The LLVM YAML infrastructure doesn't provide the
47 // ability to differentitate this and we don't need it for the purposes of
48 // Grand Central.
49 #define UNIMPLEMENTED_DEFAULT(clazz) \
50  llvm_unreachable("default '" clazz \
51  "' construction is an intentionally *NOT* implemented " \
52  "YAML feature (you should never be using this)");
53 #define UNIMPLEMENTED_DENORM(clazz) \
54  llvm_unreachable("conversion from YAML to a '" clazz \
55  "' is intentionally *NOT* implemented (you should not be " \
56  "converting from YAML to an interface)");
57 
58 // This namespace provides YAML-related collateral that is specific to Grand
59 // Central and should not be placed in the `llvm::yaml` namespace.
60 namespace yaml {
61 
62 /// Context information necessary for YAML generation.
63 struct Context {
64  /// A symbol table consisting of _only_ the interfaces construted by the Grand
65  /// Central pass. This is not a symbol table because we do not have an
66  /// up-to-date symbol table that includes interfaces at the time the Grand
67  /// Central pass finishes. This structure is easier to build up and is only
68  /// the information we need.
69  DenseMap<Attribute, sv::InterfaceOp> &interfaceMap;
70 };
71 
72 /// A representation of an `sv::InterfaceSignalOp` that includes additional
73 /// description information.
74 ///
75 /// TODO: This could be removed if we add `firrtl.DocStringAnnotation` support
76 /// or if FIRRTL dialect included support for ops to specify "comment"
77 /// information.
78 struct DescribedSignal {
79  /// The comment associated with this signal.
80  StringAttr description;
81 
82  /// The signal.
83  sv::InterfaceSignalOp signal;
84 };
85 
86 /// This exist to work around the fact that no interface can be instantiated
87 /// inside another interface. This serves to represent an op like this for the
88 /// purposes of conversion to YAML.
89 ///
90 /// TODO: Fix this once we have a solution for #1464.
91 struct DescribedInstance {
92  StringAttr name;
93 
94  /// A comment associated with the interface instance.
95  StringAttr description;
96 
97  /// The dimensionality of the interface instantiation.
98  ArrayAttr dimensions;
99 
100  /// The symbol associated with the interface.
101  FlatSymbolRefAttr interface;
102 };
103 
104 } // namespace yaml
105 } // namespace
106 
107 // These macros tell the YAML infrastructure that these are types which can
108 // show up in vectors and provides implementations of how to serialize these.
109 // Each of these macros puts the resulting class into the `llvm::yaml` namespace
110 // (which is why these are outside the `llvm::yaml` namespace below).
111 LLVM_YAML_IS_SEQUENCE_VECTOR(::yaml::DescribedSignal)
112 LLVM_YAML_IS_SEQUENCE_VECTOR(::yaml::DescribedInstance)
113 LLVM_YAML_IS_SEQUENCE_VECTOR(sv::InterfaceOp)
114 
115 // This `llvm::yaml` namespace contains implementations of classes that enable
116 // conversion from an `sv::InterfaceOp` to a YAML representation of that
117 // interface using [LLVM's YAML I/O library](https://llvm.org/docs/YamlIO.html).
118 namespace llvm {
119 namespace yaml {
120 
121 using namespace ::yaml;
122 
123 /// Convert newlines and comments to remove the comments. This produces better
124 /// looking YAML output. E.g., this will convert the following:
125 ///
126 /// // foo
127 /// // bar
128 ///
129 /// Into the following:
130 ///
131 /// foo
132 /// bar
133 std::string static stripComment(StringRef str) {
134  std::string descriptionString;
135  llvm::raw_string_ostream stream(descriptionString);
136  SmallVector<StringRef> splits;
137  str.split(splits, "\n");
138  llvm::interleave(
139  splits,
140  [&](auto substr) {
141  substr.consume_front("//");
142  stream << substr.drop_while([](auto c) { return c == ' '; });
143  },
144  [&]() { stream << "\n"; });
145  return descriptionString;
146 }
147 
148 /// Conversion from a `DescribedSignal` to YAML. This is
149 /// implemented using YAML normalization to first convert this to an internal
150 /// `Field` structure which has a one-to-one mapping to the YAML represntation.
151 template <>
152 struct MappingContextTraits<DescribedSignal, Context> {
153  /// A one-to-one representation with a YAML representation of a signal/field.
154  struct Field {
155  /// The name of the field.
156  StringRef name;
157 
158  /// An optional, textual description of what the field is.
159  std::optional<std::string> description;
160 
161  /// The dimensions of the field.
162  SmallVector<unsigned, 2> dimensions;
163 
164  /// The width of the underlying type.
165  unsigned width;
166 
167  /// Construct a `Field` from a `DescribedSignal` (an `sv::InterfaceSignalOp`
168  /// with an optional description).
169  Field(IO &io, DescribedSignal &op)
170  : name(op.signal.getSymNameAttr().getValue()) {
171 
172  // Convert the description from a `StringAttr` (which may be null) to an
173  // `optional<StringRef>`. This aligns exactly with the YAML
174  // representation.
175  if (op.description)
176  description = stripComment(op.description.getValue());
177 
178  // Unwrap the type of the field into an array of dimensions and a width.
179  // By example, this is going from the following hardware type:
180  //
181  // !hw.uarray<1xuarray<2xuarray<3xi8>>>
182  //
183  // To the following representation:
184  //
185  // dimensions: [ 3, 2, 1 ]
186  // width: 8
187  //
188  // Note that the above is equivalenet to the following Verilog
189  // specification.
190  //
191  // wire [7:0] foo [2:0][1:0][0:0]
192  //
193  // Do this by repeatedly unwrapping unpacked array types until you get to
194  // the underlying type. The dimensions need to be reversed as this
195  // unwrapping happens in reverse order of the final representation.
196  auto tpe = op.signal.getType();
197  while (auto vector = tpe.dyn_cast<hw::UnpackedArrayType>()) {
198  dimensions.push_back(vector.getNumElements());
199  tpe = vector.getElementType();
200  }
201  dimensions = SmallVector<unsigned>(llvm::reverse(dimensions));
202 
203  // The final non-array type must be an integer. Leave this as an assert
204  // with a blind cast because we generated this type in this pass (and we
205  // therefore cannot fail this cast).
206  assert(isa<IntegerType>(tpe));
207  width = type_cast<IntegerType>(tpe).getWidth();
208  }
209 
210  /// A no-argument constructor is necessary to work with LLVM's YAML library.
211  Field(IO &io){UNIMPLEMENTED_DEFAULT("Field")}
212 
213  /// This cannot be denomralized back to an interface op.
214  DescribedSignal denormalize(IO &) {
215  UNIMPLEMENTED_DENORM("DescribedSignal")
216  }
217  };
218 
219  static void mapping(IO &io, DescribedSignal &op, Context &ctx) {
220  MappingNormalization<Field, DescribedSignal> keys(io, op);
221  io.mapRequired("name", keys->name);
222  io.mapOptional("description", keys->description);
223  io.mapRequired("dimensions", keys->dimensions);
224  io.mapRequired("width", keys->width);
225  }
226 };
227 
228 /// Conversion from a `DescribedInstance` to YAML. This is implemented using
229 /// YAML normalization to first convert the `DescribedInstance` to an internal
230 /// `Instance` struct which has a one-to-one representation with the final YAML
231 /// representation.
232 template <>
233 struct MappingContextTraits<DescribedInstance, Context> {
234  /// A YAML-serializable representation of an interface instantiation.
235  struct Instance {
236  /// The name of the interface.
237  StringRef name;
238 
239  /// An optional textual description of the interface.
240  std::optional<std::string> description = std::nullopt;
241 
242  /// An array describing the dimnensionality of the interface.
243  SmallVector<int64_t, 2> dimensions;
244 
245  /// The underlying interface.
246  FlatSymbolRefAttr interface;
247 
248  Instance(IO &io, DescribedInstance &op)
249  : name(op.name.getValue()), interface(op.interface) {
250 
251  // Convert the description from a `StringAttr` (which may be null) to an
252  // `optional<StringRef>`. This aligns exactly with the YAML
253  // representation.
254  if (op.description)
255  description = stripComment(op.description.getValue());
256 
257  for (auto &d : op.dimensions) {
258  auto dimension = dyn_cast<IntegerAttr>(d);
259  dimensions.push_back(dimension.getInt());
260  }
261  }
262 
263  Instance(IO &io){UNIMPLEMENTED_DEFAULT("Instance")}
264 
265  DescribedInstance denormalize(IO &) {
266  UNIMPLEMENTED_DENORM("DescribedInstance")
267  }
268  };
269 
270  static void mapping(IO &io, DescribedInstance &op, Context &ctx) {
271  MappingNormalization<Instance, DescribedInstance> keys(io, op);
272  io.mapRequired("name", keys->name);
273  io.mapOptional("description", keys->description);
274  io.mapRequired("dimensions", keys->dimensions);
275  io.mapRequired("interface", ctx.interfaceMap[keys->interface], ctx);
276  }
277 };
278 
279 /// Conversion from an `sv::InterfaceOp` to YAML. This is implemented using
280 /// YAML normalization to first convert the interface to an internal `Interface`
281 /// which reformats the Grand Central-generated interface into the YAML format.
282 template <>
283 struct MappingContextTraits<sv::InterfaceOp, Context> {
284  /// A YAML-serializable representation of an interface. This consists of
285  /// fields (vector or ground types) and nested interfaces.
286  struct Interface {
287  /// The name of the interface.
288  StringRef name;
289 
290  /// All ground or vectors that make up the interface.
291  std::vector<DescribedSignal> fields;
292 
293  /// Instantiations of _other_ interfaces.
294  std::vector<DescribedInstance> instances;
295 
296  /// Construct an `Interface` from an `sv::InterfaceOp`. This is tuned to
297  /// "parse" the structure of an interface that the Grand Central pass
298  /// generates. The structure of `Field`s and `Instance`s is documented
299  /// below.
300  ///
301  /// A field will look like the following. The verbatim description is
302  /// optional:
303  ///
304  /// sv.verbatim "// <description>" {
305  /// firrtl.grandcentral.yaml.type = "description",
306  /// symbols = []}
307  /// sv.interface.signal @<name> : <type>
308  ///
309  /// An interface instanctiation will look like the following. The verbatim
310  /// description is optional.
311  ///
312  /// sv.verbatim "// <description>" {
313  /// firrtl.grandcentral.type = "description",
314  /// symbols = []}
315  /// sv.verbatim "<name> <symbol>();" {
316  /// firrtl.grandcentral.yaml.name = "<name>",
317  /// firrtl.grandcentral.yaml.dimensions = [<first dimension>, ...],
318  /// firrtl.grandcentral.yaml.symbol = @<symbol>,
319  /// firrtl.grandcentral.yaml.type = "instance",
320  /// symbols = []}
321  ///
322  Interface(IO &io, sv::InterfaceOp &op) : name(op.getName()) {
323  // A mutable store of the description. This occurs in the op _before_ the
324  // field or instance, so we need someplace to put it until we use it.
325  StringAttr description = {};
326 
327  for (auto &op : op.getBodyBlock()->getOperations()) {
328  TypeSwitch<Operation *>(&op)
329  // A verbatim op is either a description or an interface
330  // instantiation.
331  .Case<sv::VerbatimOp>([&](sv::VerbatimOp op) {
332  auto tpe = op->getAttrOfType<StringAttr>(
333  "firrtl.grandcentral.yaml.type");
334 
335  // This is a descripton. Update the mutable description and
336  // continue;
337  if (tpe.getValue() == "description") {
338  description = op.getFormatStringAttr();
339  return;
340  }
341 
342  // This is an unsupported construct. Just drop it.
343  if (tpe.getValue() == "unsupported") {
344  description = {};
345  return;
346  }
347 
348  // This is an instance of another interface. Add the symbol to
349  // the vector of instances.
350  auto name = op->getAttrOfType<StringAttr>(
351  "firrtl.grandcentral.yaml.name");
352  auto dimensions = op->getAttrOfType<ArrayAttr>(
353  "firrtl.grandcentral.yaml.dimensions");
354  auto symbol = op->getAttrOfType<FlatSymbolRefAttr>(
355  "firrtl.grandcentral.yaml.symbol");
356  instances.push_back(
357  DescribedInstance({name, description, dimensions, symbol}));
358  description = {};
359  })
360  // An interface signal op is a field.
361  .Case<sv::InterfaceSignalOp>([&](sv::InterfaceSignalOp op) {
362  fields.push_back(DescribedSignal({description, op}));
363  description = {};
364  });
365  }
366  }
367 
368  /// A no-argument constructor is necessary to work with LLVM's YAML library.
369  Interface(IO &io){UNIMPLEMENTED_DEFAULT("Interface")}
370 
371  /// This cannot be denomralized back to an interface op.
372  sv::InterfaceOp denormalize(IO &) {
373  UNIMPLEMENTED_DENORM("sv::InterfaceOp")
374  }
375  };
376 
377  static void mapping(IO &io, sv::InterfaceOp &op, Context &ctx) {
378  MappingNormalization<Interface, sv::InterfaceOp> keys(io, op);
379  io.mapRequired("name", keys->name);
380  io.mapRequired("fields", keys->fields, ctx);
381  io.mapRequired("instances", keys->instances, ctx);
382  }
383 };
384 
385 } // namespace yaml
386 } // namespace llvm
387 
388 //===----------------------------------------------------------------------===//
389 // Pass Implementation
390 //===----------------------------------------------------------------------===//
391 
392 namespace {
393 
394 /// A helper to build verbatim strings with symbol placeholders. Provides a
395 /// mechanism to snapshot the current string and symbols and restore back to
396 /// this state after modifications. These snapshots are particularly useful when
397 /// the string is assembled through hierarchical traversal of some sort, which
398 /// populates the string with a prefix common to all children of a hierarchy
399 /// (like the interface field traversal in the `GrandCentralPass`).
400 ///
401 /// The intended use is as follows:
402 ///
403 /// void baz(VerbatimBuilder &v) {
404 /// foo(v.snapshot().append("bar"));
405 /// }
406 ///
407 /// The function `baz` takes a snapshot of the current verbatim text `v`, adds
408 /// "bar" to it and calls `foo` with that appended verbatim text. After the call
409 /// to `foo` returns, any changes made by `foo` as well as the "bar" are dropped
410 /// from the verbatim text `v`, as the temporary snapshot goes out of scope.
411 struct VerbatimBuilder {
412  struct Base {
413  SmallString<128> string;
414  SmallVector<Attribute> symbols;
415  VerbatimBuilder builder() { return VerbatimBuilder(*this); }
416  operator VerbatimBuilder() { return builder(); }
417  };
418 
419  /// Constructing a builder will snapshot the `Base` which holds the actual
420  /// string and symbols.
421  VerbatimBuilder(Base &base)
422  : base(base), stringBaseSize(base.string.size()),
423  symbolsBaseSize(base.symbols.size()) {}
424 
425  /// Destroying a builder will reset the `Base` to the original string and
426  /// symbols.
427  ~VerbatimBuilder() {
428  base.string.resize(stringBaseSize);
429  base.symbols.resize(symbolsBaseSize);
430  }
431 
432  // Disallow copying.
433  VerbatimBuilder(const VerbatimBuilder &) = delete;
434  VerbatimBuilder &operator=(const VerbatimBuilder &) = delete;
435 
436  /// Take a snapshot of the current string and symbols. This returns a new
437  /// `VerbatimBuilder` that will reset to the current state of the string once
438  /// destroyed.
439  VerbatimBuilder snapshot() { return VerbatimBuilder(base); }
440 
441  /// Get the current string.
442  StringRef getString() const { return base.string; }
443  /// Get the current symbols;
444  ArrayRef<Attribute> getSymbols() const { return base.symbols; }
445 
446  /// Append to the string.
447  VerbatimBuilder &append(char c) {
448  base.string.push_back(c);
449  return *this;
450  }
451 
452  /// Append to the string.
453  VerbatimBuilder &append(const Twine &twine) {
454  twine.toVector(base.string);
455  return *this;
456  }
457 
458  /// Append a placeholder and symbol to the string.
459  VerbatimBuilder &append(Attribute symbol) {
460  unsigned id = base.symbols.size();
461  base.symbols.push_back(symbol);
462  append("{{" + Twine(id) + "}}");
463  return *this;
464  }
465 
466  VerbatimBuilder &operator+=(char c) { return append(c); }
467  VerbatimBuilder &operator+=(const Twine &twine) { return append(twine); }
468  VerbatimBuilder &operator+=(Attribute symbol) { return append(symbol); }
469 
470 private:
471  Base &base;
472  size_t stringBaseSize;
473  size_t symbolsBaseSize;
474 };
475 
476 /// A wrapper around a string that is used to encode a type which cannot be
477 /// represented by an mlir::Type for some reason. This is currently used to
478 /// represent either an interface, a n-dimensional vector of interfaces, or a
479 /// tombstone for an actually unsupported type (e.g., an AugmentedBooleanType).
480 struct VerbatimType {
481  /// The textual representation of the type.
482  std::string str;
483 
484  /// True if this is a type which must be "instatiated" and requires a trailing
485  /// "()".
486  bool instantiation;
487 
488  /// A vector storing the width of each dimension of the type.
489  SmallVector<int32_t, 4> dimensions = {};
490 
491  /// Serialize this type to a string.
492  std::string toStr(StringRef name) {
493  SmallString<64> stringType(str);
494  stringType.append(" ");
495  stringType.append(name);
496  for (auto d : llvm::reverse(dimensions)) {
497  stringType.append("[");
498  stringType.append(Twine(d).str());
499  stringType.append("]");
500  }
501  if (instantiation)
502  stringType.append("()");
503  stringType.append(";");
504  return std::string(stringType);
505  }
506 };
507 
508 /// A sum type representing either a type encoded as a string (VerbatimType)
509 /// or an actual mlir::Type.
510 typedef std::variant<VerbatimType, Type> TypeSum;
511 
512 /// Stores the information content of an ExtractGrandCentralAnnotation.
513 struct ExtractionInfo {
514  /// The directory where Grand Central generated collateral (modules,
515  /// interfaces, etc.) will be written.
516  StringAttr directory = {};
517 
518  /// The name of the file where any binds will be written. This will be placed
519  /// in the same output area as normal compilation output, e.g., output
520  /// Verilog. This has no relation to the `directory` member.
521  StringAttr bindFilename = {};
522 };
523 
524 /// Stores information about the companion module of a GrandCentral view.
525 struct CompanionInfo {
526  StringRef name;
527 
528  FModuleOp companion;
529  bool isNonlocal;
530 };
531 
532 /// Stores a reference to a ground type and an optional NLA associated with
533 /// that field.
534 struct FieldAndNLA {
535  FieldRef field;
536  FlatSymbolRefAttr nlaSym;
537 };
538 
539 /// Stores the arguments required to construct the verbatim xmr assignment.
540 struct VerbatimXMRbuilder {
541  Value val;
542  StringAttr str;
543  ArrayAttr syms;
544  FModuleOp companionMod;
545  VerbatimXMRbuilder(Value val, StringAttr str, ArrayAttr syms,
546  FModuleOp companionMod)
547  : val(val), str(str), syms(syms), companionMod(companionMod) {}
548 };
549 
550 /// Stores the arguments required to construct the InterfaceOps and
551 /// InterfaceSignalOps.
552 struct InterfaceElemsBuilder {
553  StringAttr iFaceName;
554  IntegerAttr id;
555  struct Properties {
556  StringAttr description;
557  StringAttr elemName;
558  TypeSum elemType;
559  Properties(StringAttr des, StringAttr name, TypeSum &elemType)
560  : description(des), elemName(name), elemType(elemType) {}
561  };
562  SmallVector<Properties> elementsList;
563  InterfaceElemsBuilder(StringAttr iFaceName, IntegerAttr id)
564  : iFaceName(iFaceName), id(id) {}
565 };
566 
567 /// Generate SystemVerilog interfaces from Grand Central annotations. This pass
568 /// roughly works in the following three phases:
569 ///
570 /// 1. Extraction information is determined.
571 ///
572 /// 2. The circuit is walked to find all scattered annotations related to Grand
573 /// Central interfaces. These are: (a) the companion module and (b) all
574 /// leaves that are to be connected to the interface.
575 ///
576 /// 3. The circuit-level Grand Central annotation is walked to both generate and
577 /// instantiate interfaces and to generate the "mappings" file that produces
578 /// cross-module references (XMRs) to drive the interface.
579 struct GrandCentralPass : public GrandCentralBase<GrandCentralPass> {
580  using GrandCentralBase::companionMode;
581 
582  void runOnOperation() override;
583 
584 private:
585  /// Optionally build an AugmentedType from an attribute. Return none if the
586  /// attribute is not a dictionary or if it does not match any of the known
587  /// templates for AugmentedTypes.
588  std::optional<Attribute> fromAttr(Attribute attr);
589 
590  /// Mapping of ID to leaf ground type and an optional non-local annotation
591  /// associated with that ID.
592  DenseMap<Attribute, FieldAndNLA> leafMap;
593 
594  /// Mapping of ID to companion module.
595  DenseMap<Attribute, CompanionInfo> companionIDMap;
596 
597  /// An optional prefix applied to all interfaces in the design. This is set
598  /// based on a PrefixInterfacesAnnotation.
599  StringRef interfacePrefix;
600 
601  NLATable *nlaTable;
602 
603  /// The design-under-test (DUT) as determined by the presence of a
604  /// "sifive.enterprise.firrtl.MarkDUTAnnotation". This will be null if no DUT
605  /// was found.
606  FModuleOp dut;
607 
608  /// An optional directory for testbench-related files. This is null if no
609  /// "TestBenchDirAnnotation" is found.
610  StringAttr testbenchDir;
611 
612  /// Return a string containing the name of an interface. Apply correct
613  /// prefixing from the interfacePrefix and module-level prefix parameter.
614  std::string getInterfaceName(StringAttr prefix,
615  AugmentedBundleTypeAttr bundleType) {
616 
617  if (prefix)
618  return (prefix.getValue() + interfacePrefix +
619  bundleType.getDefName().getValue())
620  .str();
621  return (interfacePrefix + bundleType.getDefName().getValue()).str();
622  }
623 
624  /// Recursively examine an AugmentedType to populate the "mappings" file
625  /// (generate XMRs) for this interface. This does not build new interfaces.
626  bool traverseField(Attribute field, IntegerAttr id, VerbatimBuilder &path,
627  SmallVector<VerbatimXMRbuilder> &xmrElems,
628  SmallVector<InterfaceElemsBuilder> &interfaceBuilder);
629 
630  /// Recursively examine an AugmentedType to both build new interfaces and
631  /// populate a "mappings" file (generate XMRs) using `traverseField`. Return
632  /// the type of the field exmained.
633  std::optional<TypeSum>
634  computeField(Attribute field, IntegerAttr id, StringAttr prefix,
635  VerbatimBuilder &path, SmallVector<VerbatimXMRbuilder> &xmrElems,
636  SmallVector<InterfaceElemsBuilder> &interfaceBuilder);
637 
638  /// Recursively examine an AugmentedBundleType to both build new interfaces
639  /// and populate a "mappings" file (generate XMRs). Return none if the
640  /// interface is invalid.
641  std::optional<StringAttr>
642  traverseBundle(AugmentedBundleTypeAttr bundle, IntegerAttr id,
643  StringAttr prefix, VerbatimBuilder &path,
644  SmallVector<VerbatimXMRbuilder> &xmrElems,
645  SmallVector<InterfaceElemsBuilder> &interfaceBuilder);
646 
647  /// Return the module associated with this value.
648  igraph::ModuleOpInterface getEnclosingModule(Value value,
649  FlatSymbolRefAttr sym = {});
650 
651  /// Inforamtion about how the circuit should be extracted. This will be
652  /// non-empty if an extraction annotation is found.
653  std::optional<ExtractionInfo> maybeExtractInfo = std::nullopt;
654 
655  /// A filename describing where to put a YAML representation of the
656  /// interfaces generated by this pass.
657  std::optional<StringAttr> maybeHierarchyFileYAML = std::nullopt;
658 
659  StringAttr getOutputDirectory() {
660  if (maybeExtractInfo)
661  return maybeExtractInfo->directory;
662  return {};
663  }
664 
665  /// Store of an instance paths analysis. This is constructed inside
666  /// `runOnOperation`, to work around the deleted copy constructor of
667  /// `InstancePathCache`'s internal `BumpPtrAllocator`.
668  ///
669  /// TODO: Investigate a way to not use a pointer here like how `getNamespace`
670  /// works below.
671  InstancePathCache *instancePaths = nullptr;
672 
673  /// The namespace associated with the circuit. This is lazily constructed
674  /// using `getNamesapce`.
675  std::optional<CircuitNamespace> circuitNamespace;
676 
677  /// The module namespaces. These are lazily constructed by
678  /// `getModuleNamespace`.
679  DenseMap<Operation *, hw::InnerSymbolNamespace> moduleNamespaces;
680 
681  /// Return a reference to the circuit namespace. This will lazily construct a
682  /// namespace if one does not exist.
683  CircuitNamespace &getNamespace() {
684  if (!circuitNamespace)
685  circuitNamespace = CircuitNamespace(getOperation());
686  return *circuitNamespace;
687  }
688 
689  /// Get the cached namespace for a module.
690  hw::InnerSymbolNamespace &getModuleNamespace(FModuleLike module) {
691  return moduleNamespaces.try_emplace(module, module).first->second;
692  }
693 
694  /// A symbol table associated with the circuit. This is lazily constructed by
695  /// `getSymbolTable`.
696  std::optional<SymbolTable *> symbolTable;
697 
698  /// Return a reference to a circuit-level symbol table. Lazily construct one
699  /// if such a symbol table does not already exist.
700  SymbolTable &getSymbolTable() {
701  if (!symbolTable)
702  symbolTable = &getAnalysis<SymbolTable>();
703  return **symbolTable;
704  }
705 
706  // Utility that acts like emitOpError, but does _not_ include a note. The
707  // note in emitOpError includes the entire op which means the **ENTIRE**
708  // FIRRTL circuit. This doesn't communicate anything useful to the user
709  // other than flooding their terminal.
710  InFlightDiagnostic emitCircuitError(StringRef message = {}) {
711  return emitError(getOperation().getLoc(), "'firrtl.circuit' op " + message);
712  }
713 
714  // Insert comment delimiters ("// ") after newlines in the description string.
715  // This is necessary to prevent introducing invalid verbatim Verilog.
716  //
717  // TODO: Add a comment op and lower the description to that.
718  // TODO: Tracking issue: https://github.com/llvm/circt/issues/1677
719  std::string cleanupDescription(StringRef description) {
720  StringRef head;
721  SmallString<64> out;
722  do {
723  std::tie(head, description) = description.split("\n");
724  out.append(head);
725  if (!description.empty())
726  out.append("\n// ");
727  } while (!description.empty());
728  return std::string(out);
729  }
730 
731  /// A store of the YAML representation of interfaces.
732  DenseMap<Attribute, sv::InterfaceOp> interfaceMap;
733 
734  /// Emit the hierarchy yaml file.
735  void emitHierarchyYamlFile(SmallVectorImpl<sv::InterfaceOp> &intfs);
736 };
737 
738 } // namespace
739 
740 //===----------------------------------------------------------------------===//
741 // Code related to handling Grand Central View annotations
742 //===----------------------------------------------------------------------===//
743 
744 /// Recursively walk a sifive.enterprise.grandcentral.AugmentedType to extract
745 /// any annotations it may contain. This is going to generate two types of
746 /// annotations:
747 /// 1) Annotations necessary to build interfaces and store them at "~"
748 /// 2) Scattered annotations for how components bind to interfaces
749 static std::optional<DictionaryAttr>
750 parseAugmentedType(ApplyState &state, DictionaryAttr augmentedType,
751  DictionaryAttr root, StringRef companion, StringAttr name,
752  StringAttr defName, std::optional<IntegerAttr> id,
753  std::optional<StringAttr> description, Twine clazz,
754  StringAttr companionAttr, Twine path = {}) {
755 
756  auto *context = state.circuit.getContext();
757  auto loc = state.circuit.getLoc();
758 
759  /// Optionally unpack a ReferenceTarget encoded as a DictionaryAttr. Return
760  /// either a pair containing the Target string (up to the reference) and an
761  /// array of components or none if the input is malformed. The input
762  /// DicionaryAttr encoding is a JSON object of a serialized ReferenceTarget
763  /// Scala class. By example, this is converting:
764  /// ~Foo|Foo>a.b[0]
765  /// To:
766  /// {"~Foo|Foo>a", {".b", "[0]"}}
767  /// The format of a ReferenceTarget object like:
768  /// circuit: String
769  /// module: String
770  /// path: Seq[(Instance, OfModule)]
771  /// ref: String
772  /// component: Seq[TargetToken]
773  auto refToTarget =
774  [&](DictionaryAttr refTarget) -> std::optional<std::string> {
775  auto circuitAttr =
776  tryGetAs<StringAttr>(refTarget, refTarget, "circuit", loc, clazz, path);
777  auto moduleAttr =
778  tryGetAs<StringAttr>(refTarget, refTarget, "module", loc, clazz, path);
779  auto pathAttr =
780  tryGetAs<ArrayAttr>(refTarget, refTarget, "path", loc, clazz, path);
781  auto componentAttr = tryGetAs<ArrayAttr>(refTarget, refTarget, "component",
782  loc, clazz, path);
783  if (!circuitAttr || !moduleAttr || !pathAttr || !componentAttr)
784  return {};
785 
786  // Parse non-local annotations.
787  SmallString<32> strpath;
788  for (auto p : pathAttr) {
789  auto dict = dyn_cast_or_null<DictionaryAttr>(p);
790  if (!dict) {
791  mlir::emitError(loc, "annotation '" + clazz +
792  " has invalid type (expected DictionaryAttr)");
793  return {};
794  }
795  auto instHolder =
796  tryGetAs<DictionaryAttr>(dict, dict, "_1", loc, clazz, path);
797  auto modHolder =
798  tryGetAs<DictionaryAttr>(dict, dict, "_2", loc, clazz, path);
799  if (!instHolder || !modHolder) {
800  mlir::emitError(loc, "annotation '" + clazz +
801  " has invalid type (expected DictionaryAttr)");
802  return {};
803  }
804  auto inst = tryGetAs<StringAttr>(instHolder, instHolder, "value", loc,
805  clazz, path);
806  auto mod =
807  tryGetAs<StringAttr>(modHolder, modHolder, "value", loc, clazz, path);
808  if (!inst || !mod) {
809  mlir::emitError(loc, "annotation '" + clazz +
810  " has invalid type (expected DictionaryAttr)");
811  return {};
812  }
813  strpath += "/" + inst.getValue().str() + ":" + mod.getValue().str();
814  }
815 
816  SmallVector<Attribute> componentAttrs;
817  SmallString<32> componentStr;
818  for (size_t i = 0, e = componentAttr.size(); i != e; ++i) {
819  auto cPath = (path + ".component[" + Twine(i) + "]").str();
820  auto component = componentAttr[i];
821  auto dict = dyn_cast_or_null<DictionaryAttr>(component);
822  if (!dict) {
823  mlir::emitError(loc, "annotation '" + clazz + "' with path '" + cPath +
824  " has invalid type (expected DictionaryAttr)");
825  return {};
826  }
827  auto classAttr =
828  tryGetAs<StringAttr>(dict, refTarget, "class", loc, clazz, cPath);
829  if (!classAttr)
830  return {};
831 
832  auto value = dict.get("value");
833 
834  // A subfield like "bar" in "~Foo|Foo>foo.bar".
835  if (auto field = dyn_cast<StringAttr>(value)) {
836  assert(classAttr.getValue() == "firrtl.annotations.TargetToken$Field" &&
837  "A StringAttr target token must be found with a subfield target "
838  "token.");
839  componentStr.append((Twine(".") + field.getValue()).str());
840  continue;
841  }
842 
843  // A subindex like "42" in "~Foo|Foo>foo[42]".
844  if (auto index = dyn_cast<IntegerAttr>(value)) {
845  assert(classAttr.getValue() == "firrtl.annotations.TargetToken$Index" &&
846  "An IntegerAttr target token must be found with a subindex "
847  "target token.");
848  componentStr.append(
849  (Twine("[") + Twine(index.getValue().getZExtValue()) + "]").str());
850  continue;
851  }
852 
853  mlir::emitError(loc,
854  "Annotation '" + clazz + "' with path '" + cPath +
855  ".value has unexpected type (should be StringAttr "
856  "for subfield or IntegerAttr for subindex).")
857  .attachNote()
858  << "The value received was: " << value << "\n";
859  return {};
860  }
861 
862  auto refAttr =
863  tryGetAs<StringAttr>(refTarget, refTarget, "ref", loc, clazz, path);
864 
865  return (Twine("~" + circuitAttr.getValue() + "|" + moduleAttr.getValue() +
866  strpath + ">" + refAttr.getValue()) +
867  componentStr)
868  .str();
869  };
870 
871  auto classAttr =
872  tryGetAs<StringAttr>(augmentedType, root, "class", loc, clazz, path);
873  if (!classAttr)
874  return std::nullopt;
875  StringRef classBase = classAttr.getValue();
876  if (!classBase.consume_front("sifive.enterprise.grandcentral.Augmented")) {
877  mlir::emitError(loc,
878  "the 'class' was expected to start with "
879  "'sifive.enterprise.grandCentral.Augmented*', but was '" +
880  classAttr.getValue() + "' (Did you misspell it?)")
881  .attachNote()
882  << "see annotation: " << augmentedType;
883  return std::nullopt;
884  }
885 
886  // An AugmentedBundleType looks like:
887  // "defName": String
888  // "elements": Seq[AugmentedField]
889  if (classBase == "BundleType") {
890  defName =
891  tryGetAs<StringAttr>(augmentedType, root, "defName", loc, clazz, path);
892  if (!defName)
893  return std::nullopt;
894 
895  // Each element is an AugmentedField with members:
896  // "name": String
897  // "description": Option[String]
898  // "tpe": AugmenetedType
899  SmallVector<Attribute> elements;
900  auto elementsAttr =
901  tryGetAs<ArrayAttr>(augmentedType, root, "elements", loc, clazz, path);
902  if (!elementsAttr)
903  return std::nullopt;
904  for (size_t i = 0, e = elementsAttr.size(); i != e; ++i) {
905  auto field = dyn_cast_or_null<DictionaryAttr>(elementsAttr[i]);
906  if (!field) {
907  mlir::emitError(
908  loc,
909  "Annotation '" + Twine(clazz) + "' with path '.elements[" +
910  Twine(i) +
911  "]' contained an unexpected type (expected a DictionaryAttr).")
912  .attachNote()
913  << "The received element was: " << elementsAttr[i] << "\n";
914  return std::nullopt;
915  }
916  auto ePath = (path + ".elements[" + Twine(i) + "]").str();
917  auto name = tryGetAs<StringAttr>(field, root, "name", loc, clazz, ePath);
918  auto tpe =
919  tryGetAs<DictionaryAttr>(field, root, "tpe", loc, clazz, ePath);
920  std::optional<StringAttr> description;
921  if (auto maybeDescription = field.get("description"))
922  description = cast<StringAttr>(maybeDescription);
923  auto eltAttr = parseAugmentedType(
924  state, tpe, root, companion, name, defName, std::nullopt, description,
925  clazz, companionAttr, path + "_" + name.getValue());
926  if (!name || !tpe || !eltAttr)
927  return std::nullopt;
928 
929  // Collect information necessary to build a module with this view later.
930  // This includes the optional description and name.
931  NamedAttrList attrs;
932  if (auto maybeDescription = field.get("description"))
933  attrs.append("description", cast<StringAttr>(maybeDescription));
934  attrs.append("name", name);
935  attrs.append("tpe", tpe.getAs<StringAttr>("class"));
936  elements.push_back(*eltAttr);
937  }
938  // Add an annotation that stores information necessary to construct the
939  // module for the view. This needs the name of the module (defName) and the
940  // names of the components inside it.
941  NamedAttrList attrs;
942  attrs.append("class", classAttr);
943  attrs.append("defName", defName);
944  if (description)
945  attrs.append("description", *description);
946  attrs.append("elements", ArrayAttr::get(context, elements));
947  if (id)
948  attrs.append("id", *id);
949  attrs.append("name", name);
950  return DictionaryAttr::getWithSorted(context, attrs);
951  }
952 
953  // An AugmentedGroundType looks like:
954  // "ref": ReferenceTarget
955  // "tpe": GroundType
956  // The ReferenceTarget is not serialized to a string. The GroundType will
957  // either be an actual FIRRTL ground type or a GrandCentral uninferred type.
958  // This can be ignored for us.
959  if (classBase == "GroundType") {
960  auto maybeTarget = refToTarget(augmentedType.getAs<DictionaryAttr>("ref"));
961  if (!maybeTarget) {
962  mlir::emitError(loc, "Failed to parse ReferenceTarget").attachNote()
963  << "See the full Annotation here: " << root;
964  return std::nullopt;
965  }
966 
967  auto id = state.newID();
968 
969  auto target = *maybeTarget;
970 
971  NamedAttrList elementIface, elementScattered;
972 
973  // Populate the annotation for the interface element.
974  elementIface.append("class", classAttr);
975  if (description)
976  elementIface.append("description", *description);
977  elementIface.append("id", id);
978  elementIface.append("name", name);
979  // Populate an annotation that will be scattered onto the element.
980  elementScattered.append("class", classAttr);
981  elementScattered.append("id", id);
982  // If there are sub-targets, then add these.
983  auto targetAttr = StringAttr::get(context, target);
984  auto xmrSrcTarget = resolvePath(targetAttr.getValue(), state.circuit,
985  state.symTbl, state.targetCaches);
986  if (!xmrSrcTarget) {
987  mlir::emitError(loc, "Failed to resolve target ") << targetAttr;
988  return std::nullopt;
989  }
990 
991  // Determine the source for this Wiring Problem. The source is the value
992  // that will be eventually by read from, via cross-module reference, to
993  // drive this element of the SystemVerilog Interface.
994  auto sourceRef = xmrSrcTarget->ref;
995  ImplicitLocOpBuilder builder(sourceRef.getOp()->getLoc(), context);
996  std::optional<Value> source =
997  TypeSwitch<Operation *, std::optional<Value>>(sourceRef.getOp())
998  // The target is an external module port. The source is the
999  // instance port of this singly-instantiated external module.
1000  .Case<FExtModuleOp>([&](FExtModuleOp extMod)
1001  -> std::optional<Value> {
1002  auto portNo = sourceRef.getImpl().getPortNo();
1003  if (xmrSrcTarget->instances.empty()) {
1004  auto paths = state.instancePathCache.getAbsolutePaths(extMod);
1005  if (paths.size() > 1) {
1006  extMod.emitError(
1007  "cannot resolve a unique instance path from the "
1008  "external module '")
1009  << targetAttr << "'";
1010  return std::nullopt;
1011  }
1012  auto *it = xmrSrcTarget->instances.begin();
1013  for (auto inst : paths.back()) {
1014  xmrSrcTarget->instances.insert(it, cast<InstanceOp>(inst));
1015  ++it;
1016  }
1017  }
1018  auto lastInst = xmrSrcTarget->instances.pop_back_val();
1019  builder.setInsertionPointAfter(lastInst);
1020  return getValueByFieldID(builder, lastInst.getResult(portNo),
1021  xmrSrcTarget->fieldIdx);
1022  })
1023  // The target is a module port. The source is the port _inside_
1024  // that module.
1025  .Case<FModuleOp>([&](FModuleOp module) -> std::optional<Value> {
1026  builder.setInsertionPointToEnd(module.getBodyBlock());
1027  auto portNum = sourceRef.getImpl().getPortNo();
1028  return getValueByFieldID(builder, module.getArgument(portNum),
1029  xmrSrcTarget->fieldIdx);
1030  })
1031  // The target is something else.
1032  .Default([&](Operation *op) -> std::optional<Value> {
1033  auto module = cast<FModuleOp>(sourceRef.getModule());
1034  builder.setInsertionPointToEnd(module.getBodyBlock());
1035  auto is = dyn_cast<hw::InnerSymbolOpInterface>(op);
1036  // Resolve InnerSymbol references to their target result.
1037  if (is && is.getTargetResult())
1038  return getValueByFieldID(builder, is.getTargetResult(),
1039  xmrSrcTarget->fieldIdx);
1040  if (sourceRef.getOp()->getNumResults() != 1) {
1041  op->emitOpError()
1042  << "cannot be used as a target of the Grand Central View \""
1043  << defName.getValue()
1044  << "\" because it does not have exactly one result";
1045  return std::nullopt;
1046  }
1047  return getValueByFieldID(builder, sourceRef.getOp()->getResult(0),
1048  xmrSrcTarget->fieldIdx);
1049  });
1050 
1051  // Exit if there was an error in the source.
1052  if (!source)
1053  return std::nullopt;
1054 
1055  // Compute the sink of this Wiring Problem. The final sink will eventually
1056  // be a SystemVerilog Interface. However, this cannot exist until the
1057  // GrandCentral pass runs. Create an undriven WireOp and use that as the
1058  // sink. The WireOp will be driven later when the Wiring Problem is
1059  // resolved. Apply the scattered element annotation to this directly to save
1060  // having to reprocess this in LowerAnnotations.
1061  auto companionMod =
1062  cast<FModuleOp>(resolvePath(companionAttr.getValue(), state.circuit,
1063  state.symTbl, state.targetCaches)
1064  ->ref.getOp());
1065  builder.setInsertionPointToEnd(companionMod.getBodyBlock());
1066  auto sink = builder.create<WireOp>(source->getType(), name);
1067  state.targetCaches.insertOp(sink);
1068  AnnotationSet annotations(context);
1069  annotations.addAnnotations(
1070  {DictionaryAttr::getWithSorted(context, elementScattered)});
1071  annotations.applyToOperation(sink);
1072 
1073  // Append this new Wiring Problem to the ApplyState. The Wiring Problem
1074  // will be resolved to bore RefType ports before LowerAnnotations finishes.
1075  state.wiringProblems.push_back({*source, sink.getResult(),
1076  (path + "__bore").str(),
1078 
1079  return DictionaryAttr::getWithSorted(context, elementIface);
1080  }
1081 
1082  // An AugmentedVectorType looks like:
1083  // "elements": Seq[AugmentedType]
1084  if (classBase == "VectorType") {
1085  auto elementsAttr =
1086  tryGetAs<ArrayAttr>(augmentedType, root, "elements", loc, clazz, path);
1087  if (!elementsAttr)
1088  return std::nullopt;
1089  SmallVector<Attribute> elements;
1090  for (auto [i, elt] : llvm::enumerate(elementsAttr)) {
1091  auto eltAttr = parseAugmentedType(
1092  state, cast<DictionaryAttr>(elt), root, companion, name,
1093  StringAttr::get(context, ""), id, std::nullopt, clazz, companionAttr,
1094  path + "_" + Twine(i));
1095  if (!eltAttr)
1096  return std::nullopt;
1097  elements.push_back(*eltAttr);
1098  }
1099  NamedAttrList attrs;
1100  attrs.append("class", classAttr);
1101  if (description)
1102  attrs.append("description", *description);
1103  attrs.append("elements", ArrayAttr::get(context, elements));
1104  attrs.append("name", name);
1105  return DictionaryAttr::getWithSorted(context, attrs);
1106  }
1107 
1108  // Any of the following are known and expected, but are legacy AugmentedTypes
1109  // do not have a target:
1110  // - AugmentedStringType
1111  // - AugmentedBooleanType
1112  // - AugmentedIntegerType
1113  // - AugmentedDoubleType
1114  bool isIgnorable =
1115  llvm::StringSwitch<bool>(classBase)
1116  .Cases("StringType", "BooleanType", "IntegerType", "DoubleType", true)
1117  .Default(false);
1118  if (isIgnorable) {
1119  NamedAttrList attrs;
1120  attrs.append("class", classAttr);
1121  attrs.append("name", name);
1122  auto value =
1123  tryGetAs<Attribute>(augmentedType, root, "value", loc, clazz, path);
1124  if (!value)
1125  return std::nullopt;
1126  attrs.append("value", value);
1127  return DictionaryAttr::getWithSorted(context, attrs);
1128  }
1129 
1130  // Anything else is unexpected or a user error if they manually wrote
1131  // annotations. Print an error and error out.
1132  mlir::emitError(loc, "found unknown AugmentedType '" + classAttr.getValue() +
1133  "' (Did you misspell it?)")
1134  .attachNote()
1135  << "see annotation: " << augmentedType;
1136  return std::nullopt;
1137 }
1138 
1139 LogicalResult circt::firrtl::applyGCTView(const AnnoPathValue &target,
1140  DictionaryAttr anno,
1141  ApplyState &state) {
1142 
1143  auto id = state.newID();
1144  auto *context = state.circuit.getContext();
1145  auto loc = state.circuit.getLoc();
1146  NamedAttrList companionAttrs;
1147  companionAttrs.append("class", StringAttr::get(context, companionAnnoClass));
1148  companionAttrs.append("id", id);
1149  auto viewAttr =
1150  tryGetAs<DictionaryAttr>(anno, anno, "view", loc, viewAnnoClass);
1151  if (!viewAttr)
1152  return failure();
1153  auto name = tryGetAs<StringAttr>(anno, anno, "name", loc, viewAnnoClass);
1154  if (!name)
1155  return failure();
1156  companionAttrs.append("name", name);
1157  auto companionAttr =
1158  tryGetAs<StringAttr>(anno, anno, "companion", loc, viewAnnoClass);
1159  if (!companionAttr)
1160  return failure();
1161  companionAttrs.append("target", companionAttr);
1162  state.addToWorklistFn(DictionaryAttr::get(context, companionAttrs));
1163 
1164  auto prunedAttr =
1165  parseAugmentedType(state, viewAttr, anno, companionAttr.getValue(), name,
1166  {}, id, {}, viewAnnoClass, companionAttr, Twine(name));
1167  if (!prunedAttr)
1168  return failure();
1169 
1170  AnnotationSet annotations(state.circuit);
1171  annotations.addAnnotations({*prunedAttr});
1172  annotations.applyToOperation(state.circuit);
1173 
1174  return success();
1175 }
1176 
1177 //===----------------------------------------------------------------------===//
1178 // GrandCentralPass Implementation
1179 //===----------------------------------------------------------------------===//
1180 
1181 std::optional<Attribute> GrandCentralPass::fromAttr(Attribute attr) {
1182  auto dict = dyn_cast<DictionaryAttr>(attr);
1183  if (!dict) {
1184  emitCircuitError() << "attribute is not a dictionary: " << attr << "\n";
1185  return std::nullopt;
1186  }
1187 
1188  auto clazz = dict.getAs<StringAttr>("class");
1189  if (!clazz) {
1190  emitCircuitError() << "missing 'class' key in " << dict << "\n";
1191  return std::nullopt;
1192  }
1193 
1194  auto classBase = clazz.getValue();
1195  classBase.consume_front("sifive.enterprise.grandcentral.Augmented");
1196 
1197  if (classBase == "BundleType") {
1198  if (dict.getAs<StringAttr>("defName") && dict.getAs<ArrayAttr>("elements"))
1199  return AugmentedBundleTypeAttr::get(&getContext(), dict);
1200  emitCircuitError() << "has an invalid AugmentedBundleType that does not "
1201  "contain 'defName' and 'elements' fields: "
1202  << dict;
1203  } else if (classBase == "VectorType") {
1204  if (dict.getAs<StringAttr>("name") && dict.getAs<ArrayAttr>("elements"))
1205  return AugmentedVectorTypeAttr::get(&getContext(), dict);
1206  emitCircuitError() << "has an invalid AugmentedVectorType that does not "
1207  "contain 'name' and 'elements' fields: "
1208  << dict;
1209  } else if (classBase == "GroundType") {
1210  auto id = dict.getAs<IntegerAttr>("id");
1211  auto name = dict.getAs<StringAttr>("name");
1212  if (id && leafMap.count(id) && name)
1213  return AugmentedGroundTypeAttr::get(&getContext(), dict);
1214  if (!id || !name)
1215  emitCircuitError() << "has an invalid AugmentedGroundType that does not "
1216  "contain 'id' and 'name' fields: "
1217  << dict;
1218  if (id && !leafMap.count(id))
1219  emitCircuitError() << "has an AugmentedGroundType with 'id == "
1220  << id.getValue().getZExtValue()
1221  << "' that does not have a scattered leaf to connect "
1222  "to in the circuit "
1223  "(was the leaf deleted or constant prop'd away?)";
1224  } else if (classBase == "StringType") {
1225  if (auto name = dict.getAs<StringAttr>("name"))
1226  return AugmentedStringTypeAttr::get(&getContext(), dict);
1227  } else if (classBase == "BooleanType") {
1228  if (auto name = dict.getAs<StringAttr>("name"))
1229  return AugmentedBooleanTypeAttr::get(&getContext(), dict);
1230  } else if (classBase == "IntegerType") {
1231  if (auto name = dict.getAs<StringAttr>("name"))
1232  return AugmentedIntegerTypeAttr::get(&getContext(), dict);
1233  } else if (classBase == "DoubleType") {
1234  if (auto name = dict.getAs<StringAttr>("name"))
1235  return AugmentedDoubleTypeAttr::get(&getContext(), dict);
1236  } else if (classBase == "LiteralType") {
1237  if (auto name = dict.getAs<StringAttr>("name"))
1238  return AugmentedLiteralTypeAttr::get(&getContext(), dict);
1239  } else if (classBase == "DeletedType") {
1240  if (auto name = dict.getAs<StringAttr>("name"))
1241  return AugmentedDeletedTypeAttr::get(&getContext(), dict);
1242  } else {
1243  emitCircuitError() << "has an invalid AugmentedType";
1244  }
1245  return std::nullopt;
1246 }
1247 
1248 bool GrandCentralPass::traverseField(
1249  Attribute field, IntegerAttr id, VerbatimBuilder &path,
1250  SmallVector<VerbatimXMRbuilder> &xmrElems,
1251  SmallVector<InterfaceElemsBuilder> &interfaceBuilder) {
1252  return TypeSwitch<Attribute, bool>(field)
1253  .Case<AugmentedGroundTypeAttr>([&](AugmentedGroundTypeAttr ground) {
1254  auto [fieldRef, sym] = leafMap.lookup(ground.getID());
1255  hw::HierPathOp nla;
1256  if (sym)
1257  nla = nlaTable->getNLA(sym.getAttr());
1258  Value leafValue = fieldRef.getValue();
1259  assert(leafValue && "leafValue not found");
1260 
1261  auto companionModule = companionIDMap.lookup(id).companion;
1262  igraph::ModuleOpInterface enclosing =
1263  getEnclosingModule(leafValue, sym);
1264 
1265  auto tpe = type_cast<FIRRTLBaseType>(leafValue.getType());
1266 
1267  // If the type is zero-width then do not emit an XMR.
1268  if (!tpe.getBitWidthOrSentinel())
1269  return true;
1270 
1271  // The leafValue is assumed to conform to a very specific pattern:
1272  //
1273  // 1) The leaf value is in the companion.
1274  // 2) The leaf value is a NodeOp
1275  //
1276  // Anything else means that there is an error or the IR is somehow using
1277  // "old-style" Annotations to encode a Grand Central View. This
1278  // _really_ should be impossible to hit given that LowerAnnotations must
1279  // generate code that conforms to the check here.
1280  auto *nodeOp = leafValue.getDefiningOp();
1281  if (companionModule != enclosing) {
1282  auto diag = companionModule->emitError()
1283  << "Grand Central View \""
1284  << companionIDMap.lookup(id).name
1285  << "\" is invalid because a leaf is not inside the "
1286  "companion module";
1287  diag.attachNote(leafValue.getLoc())
1288  << "the leaf value is declared here";
1289  if (nodeOp) {
1290  auto leafModule = nodeOp->getParentOfType<FModuleOp>();
1291  diag.attachNote(leafModule.getLoc())
1292  << "the leaf value is inside this module";
1293  }
1294  return false;
1295  }
1296 
1297  if (!isa<NodeOp>(nodeOp)) {
1298  emitError(leafValue.getLoc())
1299  << "Grand Central View \"" << companionIDMap.lookup(id).name
1300  << "\" has an invalid leaf value (this must be a node)";
1301  return false;
1302  }
1303 
1304  /// Increment all the indices inside `{{`, `}}` by one. This is to
1305  /// indicate that a value is added to the `substitutions` of the
1306  /// verbatim op, other than the symbols.
1307  auto getStrAndIncrementIds = [&](StringRef base) -> StringAttr {
1308  SmallString<128> replStr;
1309  StringRef begin = "{{";
1310  StringRef end = "}}";
1311  // The replacement string.
1312  size_t from = 0;
1313  while (from < base.size()) {
1314  // Search for the first `{{` and `}}`.
1315  size_t beginAt = base.find(begin, from);
1316  size_t endAt = base.find(end, from);
1317  // If not found, then done.
1318  if (beginAt == StringRef::npos || endAt == StringRef::npos ||
1319  (beginAt > endAt)) {
1320  replStr.append(base.substr(from));
1321  break;
1322  }
1323  // Copy the string as is, until the `{{`.
1324  replStr.append(base.substr(from, beginAt - from));
1325  // Advance `from` to the character after the `}}`.
1326  from = endAt + 2;
1327  auto idChar = base.substr(beginAt + 2, endAt - beginAt - 2);
1328  int idNum;
1329  bool failed = idChar.getAsInteger(10, idNum);
1330  (void)failed;
1331  assert(!failed && "failed to parse integer from verbatim string");
1332  // Now increment the id and append.
1333  replStr.append("{{");
1334  Twine(idNum + 1).toVector(replStr);
1335  replStr.append("}}");
1336  }
1337  return StringAttr::get(&getContext(), "assign " + replStr + ";");
1338  };
1339 
1340  // This is the new style of XMRs using RefTypes. The value subsitution
1341  // index is set to -1, as it will be incremented when generating the
1342  // string.
1343  // Generate the path from the LCA to the module that contains the leaf.
1344  path += " = {{-1}}";
1346  // Assemble the verbatim op.
1347  xmrElems.emplace_back(
1348  nodeOp->getOperand(0), getStrAndIncrementIds(path.getString()),
1349  ArrayAttr::get(&getContext(), path.getSymbols()), companionModule);
1350  return true;
1351  })
1352  .Case<AugmentedVectorTypeAttr>([&](auto vector) {
1353  bool notFailed = true;
1354  auto elements = vector.getElements();
1355  for (size_t i = 0, e = elements.size(); i != e; ++i) {
1356  auto field = fromAttr(elements[i]);
1357  if (!field)
1358  return false;
1359  notFailed &= traverseField(
1360  *field, id, path.snapshot().append("[" + Twine(i) + "]"),
1361  xmrElems, interfaceBuilder);
1362  }
1363  return notFailed;
1364  })
1365  .Case<AugmentedBundleTypeAttr>([&](AugmentedBundleTypeAttr bundle) {
1366  bool anyFailed = true;
1367  for (auto element : bundle.getElements()) {
1368  auto field = fromAttr(element);
1369  if (!field)
1370  return false;
1371  auto name = cast<DictionaryAttr>(element).getAs<StringAttr>("name");
1372  if (!name)
1373  name = cast<DictionaryAttr>(element).getAs<StringAttr>("defName");
1374  anyFailed &= traverseField(
1375  *field, id, path.snapshot().append("." + name.getValue()),
1376  xmrElems, interfaceBuilder);
1377  }
1378 
1379  return anyFailed;
1380  })
1381  .Case<AugmentedStringTypeAttr>([&](auto a) { return false; })
1382  .Case<AugmentedBooleanTypeAttr>([&](auto a) { return false; })
1383  .Case<AugmentedIntegerTypeAttr>([&](auto a) { return false; })
1384  .Case<AugmentedDoubleTypeAttr>([&](auto a) { return false; })
1385  .Case<AugmentedLiteralTypeAttr>([&](auto a) { return false; })
1386  .Case<AugmentedDeletedTypeAttr>([&](auto a) { return false; })
1387  .Default([](auto a) { return true; });
1388 }
1389 
1390 std::optional<TypeSum> GrandCentralPass::computeField(
1391  Attribute field, IntegerAttr id, StringAttr prefix, VerbatimBuilder &path,
1392  SmallVector<VerbatimXMRbuilder> &xmrElems,
1393  SmallVector<InterfaceElemsBuilder> &interfaceBuilder) {
1394 
1395  auto unsupported = [&](StringRef name, StringRef kind) {
1396  return VerbatimType({("// <unsupported " + kind + " type>").str(), false});
1397  };
1398 
1399  return TypeSwitch<Attribute, std::optional<TypeSum>>(field)
1400  .Case<AugmentedGroundTypeAttr>(
1401  [&](AugmentedGroundTypeAttr ground) -> std::optional<TypeSum> {
1402  // Traverse to generate mappings.
1403  if (!traverseField(field, id, path, xmrElems, interfaceBuilder))
1404  return std::nullopt;
1405  FieldRef fieldRef = leafMap.lookup(ground.getID()).field;
1406  auto value = fieldRef.getValue();
1407  auto fieldID = fieldRef.getFieldID();
1408  auto tpe = firrtl::type_cast<FIRRTLBaseType>(
1410  fieldID));
1411  if (!tpe.isGround()) {
1412  value.getDefiningOp()->emitOpError()
1413  << "cannot be added to interface with id '"
1414  << id.getValue().getZExtValue()
1415  << "' because it is not a ground type";
1416  return std::nullopt;
1417  }
1418  return TypeSum(IntegerType::get(getOperation().getContext(),
1419  tpe.getBitWidthOrSentinel()));
1420  })
1421  .Case<AugmentedVectorTypeAttr>(
1422  [&](AugmentedVectorTypeAttr vector) -> std::optional<TypeSum> {
1423  auto elements = vector.getElements();
1424  auto firstElement = fromAttr(elements[0]);
1425  auto elementType =
1426  computeField(*firstElement, id, prefix,
1427  path.snapshot().append("[" + Twine(0) + "]"),
1428  xmrElems, interfaceBuilder);
1429  if (!elementType)
1430  return std::nullopt;
1431 
1432  for (size_t i = 1, e = elements.size(); i != e; ++i) {
1433  auto subField = fromAttr(elements[i]);
1434  if (!subField)
1435  return std::nullopt;
1436  (void)traverseField(*subField, id,
1437  path.snapshot().append("[" + Twine(i) + "]"),
1438  xmrElems, interfaceBuilder);
1439  }
1440 
1441  if (auto *tpe = std::get_if<Type>(&*elementType))
1442  return TypeSum(
1443  hw::UnpackedArrayType::get(*tpe, elements.getValue().size()));
1444  auto str = std::get<VerbatimType>(*elementType);
1445  str.dimensions.push_back(elements.getValue().size());
1446  return TypeSum(str);
1447  })
1448  .Case<AugmentedBundleTypeAttr>(
1449  [&](AugmentedBundleTypeAttr bundle) -> TypeSum {
1450  auto ifaceName = traverseBundle(bundle, id, prefix, path, xmrElems,
1451  interfaceBuilder);
1452  assert(ifaceName && *ifaceName);
1453  return VerbatimType({ifaceName->str(), true});
1454  })
1455  .Case<AugmentedStringTypeAttr>([&](auto field) -> TypeSum {
1456  return unsupported(field.getName().getValue(), "string");
1457  })
1458  .Case<AugmentedBooleanTypeAttr>([&](auto field) -> TypeSum {
1459  return unsupported(field.getName().getValue(), "boolean");
1460  })
1461  .Case<AugmentedIntegerTypeAttr>([&](auto field) -> TypeSum {
1462  return unsupported(field.getName().getValue(), "integer");
1463  })
1464  .Case<AugmentedDoubleTypeAttr>([&](auto field) -> TypeSum {
1465  return unsupported(field.getName().getValue(), "double");
1466  })
1467  .Case<AugmentedLiteralTypeAttr>([&](auto field) -> TypeSum {
1468  return unsupported(field.getName().getValue(), "literal");
1469  })
1470  .Case<AugmentedDeletedTypeAttr>([&](auto field) -> TypeSum {
1471  return unsupported(field.getName().getValue(), "deleted");
1472  });
1473 }
1474 
1475 /// Traverse an Annotation that is an AugmentedBundleType. During traversal,
1476 /// construct any discovered SystemVerilog interfaces. If this is the root
1477 /// interface, instantiate that interface in the companion. Recurse into fields
1478 /// of the AugmentedBundleType to construct nested interfaces and generate
1479 /// stringy-typed SystemVerilog hierarchical references to drive the
1480 /// interface. Returns false on any failure and true on success.
1481 std::optional<StringAttr> GrandCentralPass::traverseBundle(
1482  AugmentedBundleTypeAttr bundle, IntegerAttr id, StringAttr prefix,
1483  VerbatimBuilder &path, SmallVector<VerbatimXMRbuilder> &xmrElems,
1484  SmallVector<InterfaceElemsBuilder> &interfaceBuilder) {
1485 
1486  unsigned lastIndex = interfaceBuilder.size();
1487  auto iFaceName = StringAttr::get(
1488  &getContext(), getNamespace().newName(getInterfaceName(prefix, bundle)));
1489  interfaceBuilder.emplace_back(iFaceName, id);
1490 
1491  for (auto element : bundle.getElements()) {
1492  auto field = fromAttr(element);
1493  if (!field)
1494  return std::nullopt;
1495 
1496  auto name = cast<DictionaryAttr>(element).getAs<StringAttr>("name");
1497  // auto signalSym = hw::InnerRefAttr::get(iface.sym_nameAttr(), name);
1498  // TODO: The `append(name.getValue())` in the following should actually be
1499  // `append(signalSym)`, but this requires that `computeField` and the
1500  // functions it calls always return a type for which we can construct an
1501  // `InterfaceSignalOp`. Since nested interface instances are currently
1502  // busted (due to the interface being a symbol table), this doesn't work at
1503  // the moment. Passing a `name` works most of the time, but can be brittle
1504  // if the interface field requires renaming in the output (e.g. due to
1505  // naming conflicts).
1506  auto elementType = computeField(
1507  *field, id, prefix, path.snapshot().append(".").append(name.getValue()),
1508  xmrElems, interfaceBuilder);
1509  if (!elementType)
1510  return std::nullopt;
1511  StringAttr description =
1512  cast<DictionaryAttr>(element).getAs<StringAttr>("description");
1513  interfaceBuilder[lastIndex].elementsList.emplace_back(description, name,
1514  *elementType);
1515  }
1516  return iFaceName;
1517 }
1518 
1519 /// Return the module that is associated with this value. Use the cached/lazily
1520 /// constructed symbol table to make this fast.
1521 igraph::ModuleOpInterface
1522 GrandCentralPass::getEnclosingModule(Value value, FlatSymbolRefAttr sym) {
1523  if (auto blockArg = dyn_cast<BlockArgument>(value))
1524  return cast<igraph::ModuleOpInterface>(blockArg.getOwner()->getParentOp());
1525 
1526  auto *op = value.getDefiningOp();
1527  if (InstanceOp instance = dyn_cast<InstanceOp>(op))
1528  return getSymbolTable().lookup<igraph::ModuleOpInterface>(
1529  instance.getModuleNameAttr().getValue());
1530 
1531  return op->getParentOfType<igraph::ModuleOpInterface>();
1532 }
1533 
1534 /// This method contains the business logic of this pass.
1535 void GrandCentralPass::runOnOperation() {
1536  LLVM_DEBUG(llvm::dbgs() << "===- Running Grand Central Views/Interface Pass "
1537  "-----------------------------===\n");
1538 
1539  CircuitOp circuitOp = getOperation();
1540 
1541  // Look at the circuit annotaitons to do two things:
1542  //
1543  // 1. Determine extraction information (directory and filename).
1544  // 2. Populate a worklist of all annotations that encode interfaces.
1545  //
1546  // Remove annotations encoding interfaces, but leave extraction information as
1547  // this may be needed by later passes.
1548  SmallVector<Annotation> worklist;
1549  bool removalError = false;
1550  AnnotationSet::removeAnnotations(circuitOp, [&](Annotation anno) {
1551  if (anno.isClass(augmentedBundleTypeClass)) {
1552  // If we are in "Instantiate" companion mode, then we don't need to
1553  // create the interface, so we can skip adding it to the worklist. This
1554  // is a janky hack for situations where you want to synthesize assertion
1555  // logic included in the companion, but don't want to have a dead
1556  // interface hanging around (or have problems with tools understanding
1557  // interfaces).
1558  if (companionMode != CompanionMode::Instantiate)
1559  worklist.push_back(anno);
1560  ++numAnnosRemoved;
1561  return true;
1562  }
1563  if (anno.isClass(extractGrandCentralClass)) {
1564  if (maybeExtractInfo) {
1565  emitCircuitError("more than one 'ExtractGrandCentralAnnotation' was "
1566  "found, but exactly one must be provided");
1567  removalError = true;
1568  return false;
1569  }
1570 
1571  auto directory = anno.getMember<StringAttr>("directory");
1572  auto filename = anno.getMember<StringAttr>("filename");
1573  if (!directory || !filename) {
1574  emitCircuitError()
1575  << "contained an invalid 'ExtractGrandCentralAnnotation' that does "
1576  "not contain 'directory' and 'filename' fields: "
1577  << anno.getDict();
1578  removalError = true;
1579  return false;
1580  }
1581  if (directory.getValue().empty())
1582  directory = StringAttr::get(circuitOp.getContext(), ".");
1583 
1584  maybeExtractInfo = {directory, filename};
1585  // Do not delete this annotation. Extraction info may be needed later.
1586  return false;
1587  }
1589  if (maybeHierarchyFileYAML) {
1590  emitCircuitError("more than one 'GrandCentralHierarchyFileAnnotation' "
1591  "was found, but zero or one may be provided");
1592  removalError = true;
1593  return false;
1594  }
1595 
1596  auto filename = anno.getMember<StringAttr>("filename");
1597  if (!filename) {
1598  emitCircuitError()
1599  << "contained an invalid 'GrandCentralHierarchyFileAnnotation' "
1600  "that does not contain 'directory' and 'filename' fields: "
1601  << anno.getDict();
1602  removalError = true;
1603  return false;
1604  }
1605 
1606  maybeHierarchyFileYAML = filename;
1607  ++numAnnosRemoved;
1608  return true;
1609  }
1610  if (anno.isClass(prefixInterfacesAnnoClass)) {
1611  if (!interfacePrefix.empty()) {
1612  emitCircuitError("more than one 'PrefixInterfacesAnnotation' was "
1613  "found, but zero or one may be provided");
1614  removalError = true;
1615  return false;
1616  }
1617 
1618  auto prefix = anno.getMember<StringAttr>("prefix");
1619  if (!prefix) {
1620  emitCircuitError()
1621  << "contained an invalid 'PrefixInterfacesAnnotation' that does "
1622  "not contain a 'prefix' field: "
1623  << anno.getDict();
1624  removalError = true;
1625  return false;
1626  }
1627 
1628  interfacePrefix = prefix.getValue();
1629  ++numAnnosRemoved;
1630  return true;
1631  }
1632  if (anno.isClass(testBenchDirAnnoClass)) {
1633  testbenchDir = anno.getMember<StringAttr>("dirname");
1634  return false;
1635  }
1636  return false;
1637  });
1638 
1639  // Find the DUT if it exists. This needs to be known before the circuit is
1640  // walked.
1641  for (auto mod : circuitOp.getOps<FModuleOp>()) {
1642  if (failed(extractDUT(mod, dut)))
1643  removalError = true;
1644  }
1645 
1646  if (removalError)
1647  return signalPassFailure();
1648 
1649  LLVM_DEBUG({
1650  llvm::dbgs() << "Extraction Info:\n";
1651  if (maybeExtractInfo)
1652  llvm::dbgs() << " directory: " << maybeExtractInfo->directory << "\n"
1653  << " filename: " << maybeExtractInfo->bindFilename << "\n";
1654  else
1655  llvm::dbgs() << " <none>\n";
1656  llvm::dbgs() << "DUT: ";
1657  if (dut)
1658  llvm::dbgs() << dut.getModuleName() << "\n";
1659  else
1660  llvm::dbgs() << "<none>\n";
1661  llvm::dbgs()
1662  << "Prefix Info (from PrefixInterfacesAnnotation):\n"
1663  << " prefix: " << interfacePrefix << "\n"
1664  << "Hierarchy File Info (from GrandCentralHierarchyFileAnnotation):\n"
1665  << " filename: ";
1666  if (maybeHierarchyFileYAML)
1667  llvm::dbgs() << *maybeHierarchyFileYAML;
1668  else
1669  llvm::dbgs() << "<none>";
1670  llvm::dbgs() << "\n";
1671  });
1672 
1673  // Exit immediately if no annotations indicative of interfaces that need to be
1674  // built exist. However, still generate the YAML file if the annotation for
1675  // this was passed in because some flows expect this.
1676  if (worklist.empty()) {
1677  SmallVector<sv::InterfaceOp, 0> interfaceVec;
1678  emitHierarchyYamlFile(interfaceVec);
1679  return markAllAnalysesPreserved();
1680  }
1681 
1682  // Setup the builder to create ops _inside the FIRRTL circuit_. This is
1683  // necessary because interfaces and interface instances are created.
1684  // Instances link to their definitions via symbols and we don't want to
1685  // break this.
1686  auto builder = OpBuilder::atBlockEnd(circuitOp.getBodyBlock());
1687 
1688  // Maybe get an "id" from an Annotation. Generate error messages on the op if
1689  // no "id" exists.
1690  auto getID = [&](Operation *op,
1691  Annotation annotation) -> std::optional<IntegerAttr> {
1692  auto id = annotation.getMember<IntegerAttr>("id");
1693  if (!id) {
1694  op->emitOpError()
1695  << "contained a malformed "
1696  "'sifive.enterprise.grandcentral.AugmentedGroundType' annotation "
1697  "that did not contain an 'id' field";
1698  removalError = true;
1699  return std::nullopt;
1700  }
1701  return id;
1702  };
1703 
1704  /// TODO: Handle this differently to allow construction of an optionsl
1705  auto instancePathCache = InstancePathCache(getAnalysis<InstanceGraph>());
1706  instancePaths = &instancePathCache;
1707 
1708  /// Contains the set of modules which are instantiated by the DUT, but not a
1709  /// companion or instantiated by a companion. If no DUT exists, treat the top
1710  /// module as if it were the DUT. This works by doing a depth-first walk of
1711  /// the instance graph, starting from the "effective" DUT and stopping the
1712  /// search at any modules which are known companions.
1713  DenseSet<igraph::ModuleOpInterface> dutModules;
1714  FModuleOp effectiveDUT = dut;
1715  if (!effectiveDUT)
1716  effectiveDUT = cast<FModuleOp>(
1717  *instancePaths->instanceGraph.getTopLevelNode()->getModule());
1718  auto dfRange =
1719  llvm::depth_first(instancePaths->instanceGraph.lookup(effectiveDUT));
1720  for (auto i = dfRange.begin(), e = dfRange.end(); i != e;) {
1721  auto module = cast<FModuleLike>(*i->getModule());
1722  if (AnnotationSet(module).hasAnnotation(companionAnnoClass)) {
1723  i.skipChildren();
1724  continue;
1725  }
1726  dutModules.insert(i->getModule<igraph::ModuleOpInterface>());
1727  // Manually increment the iterator to avoid walking off the end from
1728  // skipChildren.
1729  ++i;
1730  }
1731 
1732  // Maybe return the lone instance of a module. Generate errors on the op if
1733  // the module is not instantiated or is multiply instantiated.
1734  auto exactlyOneInstance = [&](FModuleOp op,
1735  StringRef msg) -> std::optional<InstanceOp> {
1736  auto *node = instancePaths->instanceGraph[op];
1737 
1738  switch (node->getNumUses()) {
1739  case 0:
1740  op->emitOpError() << "is marked as a GrandCentral '" << msg
1741  << "', but is never instantiated";
1742  return std::nullopt;
1743  case 1:
1744  return cast<InstanceOp>(*(*node->uses().begin())->getInstance());
1745  default:
1746  auto diag = op->emitOpError()
1747  << "is marked as a GrandCentral '" << msg
1748  << "', but it is instantiated more than once";
1749  for (auto *instance : node->uses())
1750  diag.attachNote(instance->getInstance()->getLoc())
1751  << "it is instantiated here";
1752  return std::nullopt;
1753  }
1754  };
1755 
1756  nlaTable = &getAnalysis<NLATable>();
1757 
1758  /// Walk the circuit and extract all information related to scattered Grand
1759  /// Central annotations. This is used to populate: (1) the companionIDMap and
1760  /// (2) the leafMap. Annotations are removed as they are discovered and if
1761  /// they are not malformed.
1762  DenseSet<Operation *> modulesToDelete;
1763  removalError = false;
1764  circuitOp.walk([&](Operation *op) {
1765  TypeSwitch<Operation *>(op)
1766  .Case<RegOp, RegResetOp, WireOp, NodeOp>([&](auto op) {
1767  AnnotationSet::removeAnnotations(op, [&](Annotation annotation) {
1768  if (!annotation.isClass(augmentedGroundTypeClass))
1769  return false;
1770  auto maybeID = getID(op, annotation);
1771  if (!maybeID)
1772  return false;
1773  auto sym =
1774  annotation.getMember<FlatSymbolRefAttr>("circt.nonlocal");
1775  leafMap[*maybeID] = {{op.getResult(), annotation.getFieldID()},
1776  sym};
1777  ++numAnnosRemoved;
1778  return true;
1779  });
1780  })
1781  // TODO: Figure out what to do with this.
1782  .Case<InstanceOp>([&](auto op) {
1783  AnnotationSet::removePortAnnotations(op, [&](unsigned i,
1784  Annotation annotation) {
1785  if (!annotation.isClass(augmentedGroundTypeClass))
1786  return false;
1787  op.emitOpError()
1788  << "is marked as an interface element, but this should be "
1789  "impossible due to how the Chisel Grand Central API works";
1790  removalError = true;
1791  return false;
1792  });
1793  })
1794  .Case<MemOp>([&](auto op) {
1795  AnnotationSet::removeAnnotations(op, [&](Annotation annotation) {
1796  if (!annotation.isClass(augmentedGroundTypeClass))
1797  return false;
1798  op.emitOpError()
1799  << "is marked as an interface element, but this does not make "
1800  "sense (is there a scattering bug or do you have a "
1801  "malformed hand-crafted MLIR circuit?)";
1802  removalError = true;
1803  return false;
1804  });
1806  op, [&](unsigned i, Annotation annotation) {
1807  if (!annotation.isClass(augmentedGroundTypeClass))
1808  return false;
1809  op.emitOpError()
1810  << "has port '" << i
1811  << "' marked as an interface element, but this does not "
1812  "make sense (is there a scattering bug or do you have a "
1813  "malformed hand-crafted MLIR circuit?)";
1814  removalError = true;
1815  return false;
1816  });
1817  })
1818  .Case<FModuleOp>([&](FModuleOp op) {
1819  // Handle annotations on the ports.
1820  AnnotationSet::removePortAnnotations(op, [&](unsigned i,
1821  Annotation annotation) {
1822  if (!annotation.isClass(augmentedGroundTypeClass))
1823  return false;
1824  auto maybeID = getID(op, annotation);
1825  if (!maybeID)
1826  return false;
1827  auto sym =
1828  annotation.getMember<FlatSymbolRefAttr>("circt.nonlocal");
1829  leafMap[*maybeID] = {{op.getArgument(i), annotation.getFieldID()},
1830  sym};
1831  ++numAnnosRemoved;
1832  return true;
1833  });
1834 
1835  // Handle annotations on the module.
1836  AnnotationSet::removeAnnotations(op, [&](Annotation annotation) {
1837  if (!annotation.getClass().startswith(viewAnnoClass))
1838  return false;
1839  auto isNonlocal = annotation.getMember<FlatSymbolRefAttr>(
1840  "circt.nonlocal") != nullptr;
1841  auto name = annotation.getMember<StringAttr>("name");
1842  auto id = annotation.getMember<IntegerAttr>("id");
1843  if (!id) {
1844  op.emitOpError()
1845  << "has a malformed "
1846  "'sifive.enterprise.grandcentral.ViewAnnotation' that did "
1847  "not contain an 'id' field with an 'IntegerAttr' value";
1848  goto FModuleOp_error;
1849  }
1850  if (!name) {
1851  op.emitOpError()
1852  << "has a malformed "
1853  "'sifive.enterprise.grandcentral.ViewAnnotation' that did "
1854  "not contain a 'name' field with a 'StringAttr' value";
1855  goto FModuleOp_error;
1856  }
1857 
1858  // If this is a companion, then:
1859  // 1. Insert it into the companion map
1860  // 2. Create a new mapping module.
1861  // 3. Instatiate the mapping module in the companion.
1862  // 4. Check that the companion is instantated exactly once.
1863  // 5. Set attributes on that lone instance so it will become a
1864  // bind if extraction information was provided. If a DUT is
1865  // known, then anything in the test harness will not be
1866  // extracted.
1867  if (annotation.getClass() == companionAnnoClass) {
1868  builder.setInsertionPointToEnd(circuitOp.getBodyBlock());
1869 
1870  companionIDMap[id] = {name.getValue(), op, isNonlocal};
1871 
1872  // Assert that the companion is instantiated once and only once.
1873  auto instance = exactlyOneInstance(op, "companion");
1874  if (!instance)
1875  goto FModuleOp_error;
1876 
1877  // Companions are only allowed to take inputs.
1878  for (auto [i, result] : llvm::enumerate(instance->getResults())) {
1879  if (instance->getPortDirection(i) == Direction::In)
1880  continue;
1881  // Do not allow any outputs in the drop mode.
1882  auto ty = result.getType();
1883  if (ty.isa<RefType>() && companionMode != CompanionMode::Drop)
1884  continue;
1885  op.emitOpError()
1886  << "companion instance cannot have output ports";
1887  goto FModuleOp_error;
1888  }
1889 
1890  // If no extraction info was provided, exit. Otherwise, setup the
1891  // lone instance of the companion to be lowered as a bind.
1892  if (!maybeExtractInfo) {
1893  ++numAnnosRemoved;
1894  return true;
1895  }
1896 
1897  // If the companion is instantiated above the DUT, then don't
1898  // extract it.
1899  if (dut && !instancePaths->instanceGraph.isAncestor(op, dut)) {
1900  ++numAnnosRemoved;
1901  return true;
1902  }
1903 
1904  // Look for any modules/extmodules _only_ instantiated by the
1905  // companion. If these have no output file attribute, then mark
1906  // them as being extracted into the Grand Central directory.
1907  InstanceGraphNode *companionNode =
1908  instancePaths->instanceGraph.lookup(op);
1909 
1910  LLVM_DEBUG({
1911  llvm::dbgs()
1912  << "Found companion module: "
1913  << companionNode->getModule().getModuleName() << "\n"
1914  << " submodules exclusively instantiated "
1915  "(including companion):\n";
1916  });
1917 
1918  if (companionMode == CompanionMode::Drop) {
1919  // Delete the instance if companions are disabled.
1920  OpBuilder builder(&getContext());
1921  for (auto port : instance->getResults()) {
1922  builder.setInsertionPointAfterValue(port);
1923  auto wire =
1924  builder.create<WireOp>(port.getLoc(), port.getType());
1925  port.replaceAllUsesWith(wire.getResult());
1926  }
1927  instance->erase();
1928  } else {
1929  // Lower the companion to a bind unless the user told us
1930  // explicitly not to.
1931  if (companionMode == CompanionMode::Bind)
1932  (*instance)->setAttr("lowerToBind", builder.getUnitAttr());
1933 
1934  (*instance)->setAttr(
1935  "output_file",
1936  hw::OutputFileAttr::getFromFilename(
1937  &getContext(),
1938  maybeExtractInfo->bindFilename.getValue(),
1939  /*excludeFromFileList=*/true));
1940  }
1941 
1942  for (auto &node : llvm::depth_first(companionNode)) {
1943  auto mod = node->getModule();
1944 
1945  // Check to see if we should change the output directory of a
1946  // module. Only update in the following conditions:
1947  // 1) The module is the companion.
1948  // 2) The module is NOT instantiated by the effective DUT.
1949  auto *modNode = instancePaths->instanceGraph.lookup(mod);
1950  SmallVector<InstanceRecord *> instances(modNode->uses());
1951  if (modNode != companionNode &&
1952  dutModules.count(
1953  modNode->getModule<igraph::ModuleOpInterface>()))
1954  continue;
1955 
1956  LLVM_DEBUG({
1957  llvm::dbgs()
1958  << " - module: " << mod.getModuleName() << "\n";
1959  });
1960 
1961  if (auto extmodule = dyn_cast<FExtModuleOp>(*mod)) {
1962  for (auto anno : AnnotationSet(extmodule)) {
1963  if (companionMode == CompanionMode::Drop) {
1964  modulesToDelete.insert(mod);
1965  break;
1966  }
1967  if (!anno.isClass(blackBoxInlineAnnoClass) &&
1969  continue;
1970  if (extmodule->hasAttr("output_file"))
1971  break;
1972  extmodule->setAttr(
1973  "output_file",
1974  hw::OutputFileAttr::getAsDirectory(
1975  &getContext(),
1976  maybeExtractInfo->directory.getValue()));
1977  break;
1978  }
1979  continue;
1980  }
1981 
1982  if (companionMode == CompanionMode::Drop) {
1983  modulesToDelete.insert(mod);
1984  } else {
1985  // Move this module under the Grand Central output directory
1986  // if no pre-existing output file information is present.
1987  if (!mod->hasAttr("output_file")) {
1988  mod->setAttr("output_file",
1989  hw::OutputFileAttr::getAsDirectory(
1990  &getContext(),
1991  maybeExtractInfo->directory.getValue(),
1992  /*excludeFromFileList=*/true,
1993  /*includeReplicatedOps=*/true));
1994  mod->setAttr("comment", builder.getStringAttr(
1995  "VCS coverage exclude_file"));
1996  }
1997  }
1998  }
1999 
2000  ++numAnnosRemoved;
2001  return true;
2002  }
2003 
2004  op.emitOpError()
2005  << "unknown annotation class: " << annotation.getDict();
2006 
2007  FModuleOp_error:
2008  removalError = true;
2009  return false;
2010  });
2011  });
2012  });
2013 
2014  if (removalError)
2015  return signalPassFailure();
2016 
2017  if (companionMode == CompanionMode::Drop) {
2018  for (auto *mod : modulesToDelete) {
2019  auto name = cast<FModuleLike>(mod).getModuleNameAttr();
2020 
2021  DenseSet<hw::HierPathOp> nlas;
2022  nlaTable->getNLAsInModule(name, nlas);
2023  nlaTable->removeNLAsfromModule(nlas, name);
2024  for (auto nla : nlas) {
2025  if (nla.root() == name)
2026  nla.erase();
2027  }
2028 
2029  mod->erase();
2030  }
2031 
2032  SmallVector<sv::InterfaceOp, 0> interfaceVec;
2033  emitHierarchyYamlFile(interfaceVec);
2034  return;
2035  }
2036 
2037  LLVM_DEBUG({
2038  // Print out the companion map and all leaf values that were discovered.
2039  // Sort these by their keys before printing to make this easier to read.
2040  SmallVector<IntegerAttr> ids;
2041  auto sort = [&ids]() {
2042  llvm::sort(ids, [](IntegerAttr a, IntegerAttr b) {
2043  return a.getValue().getZExtValue() < b.getValue().getZExtValue();
2044  });
2045  };
2046  for (auto tuple : companionIDMap)
2047  ids.push_back(cast<IntegerAttr>(tuple.first));
2048  sort();
2049  llvm::dbgs() << "companionIDMap:\n";
2050  for (auto id : ids) {
2051  auto value = companionIDMap.lookup(id);
2052  llvm::dbgs() << " - " << id.getValue() << ": "
2053  << value.companion.getName() << " -> " << value.name << "\n";
2054  }
2055  ids.clear();
2056  for (auto tuple : leafMap)
2057  ids.push_back(cast<IntegerAttr>(tuple.first));
2058  sort();
2059  llvm::dbgs() << "leafMap:\n";
2060  for (auto id : ids) {
2061  auto fieldRef = leafMap.lookup(id).field;
2062  auto value = fieldRef.getValue();
2063  auto fieldID = fieldRef.getFieldID();
2064  if (auto blockArg = dyn_cast<BlockArgument>(value)) {
2065  FModuleOp module = cast<FModuleOp>(blockArg.getOwner()->getParentOp());
2066  llvm::dbgs() << " - " << id.getValue() << ": "
2067  << module.getName() + ">" +
2068  module.getPortName(blockArg.getArgNumber());
2069  if (fieldID)
2070  llvm::dbgs() << ", fieldID=" << fieldID;
2071  llvm::dbgs() << "\n";
2072  } else {
2073  llvm::dbgs() << " - " << id.getValue() << ": "
2074  << value.getDefiningOp()
2075  ->getAttr("name")
2076  .cast<StringAttr>()
2077  .getValue();
2078  if (fieldID)
2079  llvm::dbgs() << ", fieldID=" << fieldID;
2080  llvm::dbgs() << "\n";
2081  }
2082  }
2083  });
2084 
2085  // Now, iterate over the worklist of interface-encoding annotations to create
2086  // the interface and all its sub-interfaces (interfaces that it instantiates),
2087  // instantiate the top-level interface, and generate a "mappings file" that
2088  // will use XMRs to drive the interface. If extraction info is available,
2089  // then the top-level instantiate interface will be marked for extraction via
2090  // a SystemVerilog bind.
2091  SmallVector<sv::InterfaceOp, 2> interfaceVec;
2093  companionToInterfaceMap;
2094  auto compareInterfaceSignal = [&](InterfaceElemsBuilder &lhs,
2095  InterfaceElemsBuilder &rhs) {
2096  auto compareProps = [&](InterfaceElemsBuilder::Properties &lhs,
2097  InterfaceElemsBuilder::Properties &rhs) {
2098  // If it's a verbatim op, no need to check the string, because the
2099  // interface names might not match. As long as the signal types match that
2100  // is sufficient.
2101  if (lhs.elemType.index() == 0 && rhs.elemType.index() == 0)
2102  return true;
2103  if (std::get<Type>(lhs.elemType) == std::get<Type>(rhs.elemType))
2104  return true;
2105  return false;
2106  };
2107  return std::equal(lhs.elementsList.begin(), lhs.elementsList.end(),
2108  rhs.elementsList.begin(), compareProps);
2109  };
2110  for (auto anno : worklist) {
2111  auto bundle = AugmentedBundleTypeAttr::get(&getContext(), anno.getDict());
2112 
2113  // The top-level AugmentedBundleType must have a global ID field so that
2114  // this can be linked to the companion.
2115  if (!bundle.isRoot()) {
2116  emitCircuitError() << "missing 'id' in root-level BundleType: "
2117  << anno.getDict() << "\n";
2118  removalError = true;
2119  continue;
2120  }
2121 
2122  if (companionIDMap.count(bundle.getID()) == 0) {
2123  emitCircuitError() << "no companion found with 'id' value '"
2124  << bundle.getID().getValue().getZExtValue() << "'\n";
2125  removalError = true;
2126  continue;
2127  }
2128 
2129  // Decide on a symbol name to use for the interface instance. This is needed
2130  // in `traverseBundle` as a placeholder for the connect operations.
2131  auto companionIter = companionIDMap.lookup(bundle.getID());
2132  auto companionModule = companionIter.companion;
2133  auto symbolName = getNamespace().newName(
2134  "__" + companionIDMap.lookup(bundle.getID()).name + "_" +
2135  getInterfaceName(bundle.getPrefix(), bundle) + "__");
2136 
2137  // Recursively walk the AugmentedBundleType to generate interfaces and XMRs.
2138  // Error out if this returns None (indicating that the annotation is
2139  // malformed in some way). A good error message is generated inside
2140  // `traverseBundle` or the functions it calls.
2141  auto instanceSymbol =
2142  hw::InnerRefAttr::get(SymbolTable::getSymbolName(companionModule),
2143  StringAttr::get(&getContext(), symbolName));
2144  VerbatimBuilder::Base verbatimData;
2145  VerbatimBuilder verbatim(verbatimData);
2146  verbatim += instanceSymbol;
2147  // List of interface elements.
2148 
2149  SmallVector<VerbatimXMRbuilder> xmrElems;
2150  SmallVector<InterfaceElemsBuilder> interfaceBuilder;
2151 
2152  auto ifaceName = traverseBundle(bundle, bundle.getID(), bundle.getPrefix(),
2153  verbatim, xmrElems, interfaceBuilder);
2154  if (!ifaceName) {
2155  removalError = true;
2156  continue;
2157  }
2158 
2159  if (companionIter.isNonlocal) {
2160  // If the companion module has two exactly same ViewAnnotation.companion
2161  // annotations, then add the interface for only one of them. This happens
2162  // when the companion is deduped.
2163  auto viewMapIter = companionToInterfaceMap.find(companionModule);
2164  if (viewMapIter != companionToInterfaceMap.end())
2165  if (std::equal(interfaceBuilder.begin(), interfaceBuilder.end(),
2166  viewMapIter->getSecond().begin(),
2167  compareInterfaceSignal)) {
2168  continue;
2169  }
2170 
2171  companionToInterfaceMap[companionModule] = interfaceBuilder;
2172  }
2173 
2174  if (interfaceBuilder.empty())
2175  continue;
2176  auto companionBuilder =
2177  OpBuilder::atBlockEnd(companionModule.getBodyBlock());
2178 
2179  // Generate gathered XMR's.
2180  for (auto xmrElem : xmrElems) {
2181  auto uloc = companionBuilder.getUnknownLoc();
2182  companionBuilder.create<sv::VerbatimOp>(uloc, xmrElem.str, xmrElem.val,
2183  xmrElem.syms);
2184  }
2185  numXMRs += xmrElems.size();
2186 
2187  sv::InterfaceOp topIface;
2188  for (const auto &ifaceBuilder : interfaceBuilder) {
2189  auto builder = OpBuilder::atBlockEnd(getOperation().getBodyBlock());
2190  auto loc = getOperation().getLoc();
2191  sv::InterfaceOp iface =
2192  builder.create<sv::InterfaceOp>(loc, ifaceBuilder.iFaceName);
2193  if (!topIface)
2194  topIface = iface;
2195  ++numInterfaces;
2196  if (dut &&
2197  !instancePaths->instanceGraph.isAncestor(
2198  companionIDMap[ifaceBuilder.id].companion, dut) &&
2199  testbenchDir)
2200  iface->setAttr("output_file",
2201  hw::OutputFileAttr::getAsDirectory(
2202  &getContext(), testbenchDir.getValue(),
2203  /*excludeFromFileList=*/true));
2204  else if (maybeExtractInfo)
2205  iface->setAttr("output_file",
2206  hw::OutputFileAttr::getAsDirectory(
2207  &getContext(), getOutputDirectory().getValue(),
2208  /*excludeFromFileList=*/true));
2209  iface.setCommentAttr(builder.getStringAttr("VCS coverage exclude_file"));
2210  builder.setInsertionPointToEnd(
2211  cast<sv::InterfaceOp>(iface).getBodyBlock());
2212  interfaceMap[FlatSymbolRefAttr::get(builder.getContext(),
2213  ifaceBuilder.iFaceName)] = iface;
2214  for (auto elem : ifaceBuilder.elementsList) {
2215 
2216  auto uloc = builder.getUnknownLoc();
2217 
2218  auto description = elem.description;
2219 
2220  if (description) {
2221  auto descriptionOp = builder.create<sv::VerbatimOp>(
2222  uloc, ("// " + cleanupDescription(description.getValue())));
2223 
2224  // If we need to generate a YAML representation of this interface,
2225  // then add an attribute indicating that this `sv::VerbatimOp` is
2226  // actually a description.
2227  if (maybeHierarchyFileYAML)
2228  descriptionOp->setAttr("firrtl.grandcentral.yaml.type",
2229  builder.getStringAttr("description"));
2230  }
2231  if (auto *str = std::get_if<VerbatimType>(&elem.elemType)) {
2232  auto instanceOp = builder.create<sv::VerbatimOp>(
2233  uloc, str->toStr(elem.elemName.getValue()));
2234 
2235  // If we need to generate a YAML representation of the interface, then
2236  // add attirbutes that describe what this `sv::VerbatimOp` is.
2237  if (maybeHierarchyFileYAML) {
2238  if (str->instantiation)
2239  instanceOp->setAttr("firrtl.grandcentral.yaml.type",
2240  builder.getStringAttr("instance"));
2241  else
2242  instanceOp->setAttr("firrtl.grandcentral.yaml.type",
2243  builder.getStringAttr("unsupported"));
2244  instanceOp->setAttr("firrtl.grandcentral.yaml.name", elem.elemName);
2245  instanceOp->setAttr("firrtl.grandcentral.yaml.dimensions",
2246  builder.getI32ArrayAttr(str->dimensions));
2247  instanceOp->setAttr(
2248  "firrtl.grandcentral.yaml.symbol",
2249  FlatSymbolRefAttr::get(builder.getContext(), str->str));
2250  }
2251  continue;
2252  }
2253 
2254  auto tpe = std::get<Type>(elem.elemType);
2255  builder.create<sv::InterfaceSignalOp>(uloc, elem.elemName.getValue(),
2256  tpe);
2257  }
2258  }
2259 
2260  ++numViews;
2261 
2262  interfaceVec.push_back(topIface);
2263 
2264  // Instantiate the interface inside the companion.
2265  builder.setInsertionPointToStart(companionModule.getBodyBlock());
2266  builder.create<sv::InterfaceInstanceOp>(
2267  getOperation().getLoc(), topIface.getInterfaceType(),
2268  companionIDMap.lookup(bundle.getID()).name,
2269  hw::InnerSymAttr::get(builder.getStringAttr(symbolName)));
2270 
2271  // If no extraction information was present, then just leave the interface
2272  // instantiated in the companion. Otherwise, make it a bind.
2273  if (!maybeExtractInfo)
2274  continue;
2275 
2276  // If the interface is associated with a companion that is instantiated
2277  // above the DUT (e.g.., in the test harness), then don't extract it.
2278  if (dut && !instancePaths->instanceGraph.isAncestor(
2279  companionIDMap[bundle.getID()].companion, dut))
2280  continue;
2281  }
2282 
2283  emitHierarchyYamlFile(interfaceVec);
2284 
2285  // Signal pass failure if any errors were found while examining circuit
2286  // annotations.
2287  if (removalError)
2288  return signalPassFailure();
2289  markAnalysesPreserved<NLATable>();
2290 }
2291 
2292 void GrandCentralPass::emitHierarchyYamlFile(
2293  SmallVectorImpl<sv::InterfaceOp> &intfs) {
2294  // If a `GrandCentralHierarchyFileAnnotation` was passed in, generate a YAML
2295  // representation of the interfaces that we produced with the filename that
2296  // that annotation provided.
2297  if (!maybeHierarchyFileYAML)
2298  return;
2299 
2300  CircuitOp circuitOp = getOperation();
2301 
2302  std::string yamlString;
2303  llvm::raw_string_ostream stream(yamlString);
2304  ::yaml::Context yamlContext({interfaceMap});
2305  llvm::yaml::Output yout(stream);
2306  yamlize(yout, intfs, true, yamlContext);
2307 
2308  auto builder = OpBuilder::atBlockBegin(circuitOp.getBodyBlock());
2309  builder.create<sv::VerbatimOp>(builder.getUnknownLoc(), yamlString)
2310  ->setAttr("output_file",
2311  hw::OutputFileAttr::getFromFilename(
2312  &getContext(), maybeHierarchyFileYAML->getValue(),
2313  /*excludeFromFileList=*/true));
2314  LLVM_DEBUG({ llvm::dbgs() << "Generated YAML:" << yamlString << "\n"; });
2315 }
2316 
2317 //===----------------------------------------------------------------------===//
2318 // Pass Creation
2319 //===----------------------------------------------------------------------===//
2320 
2321 std::unique_ptr<mlir::Pass>
2323  auto pass = std::make_unique<GrandCentralPass>();
2324  pass->companionMode = companionMode;
2325  return pass;
2326 }
lowerAnnotationsNoRefTypePorts FirtoolPreserveValuesMode value
Definition: Firtool.cpp:95
assert(baseType &&"element must be base type")
MlirType elementType
Definition: CHIRRTL.cpp:25
int32_t width
Definition: FIRRTL.cpp:27
#define UNIMPLEMENTED_DENORM(clazz)
static std::optional< DictionaryAttr > parseAugmentedType(ApplyState &state, DictionaryAttr augmentedType, DictionaryAttr root, StringRef companion, StringAttr name, StringAttr defName, std::optional< IntegerAttr > id, std::optional< StringAttr > description, Twine clazz, StringAttr companionAttr, Twine path={})
Recursively walk a sifive.enterprise.grandcentral.AugmentedType to extract any annotations it may con...
#define UNIMPLEMENTED_DEFAULT(clazz)
@ Output
Definition: HW.h:32
Builder builder
static StringAttr append(StringAttr base, const Twine &suffix)
Return a attribute with the specified suffix appended.
This class represents a reference to a specific field or element of an aggregate value.
Definition: FieldRef.h:28
unsigned getFieldID() const
Get the field ID of this FieldRef, which is a unique identifier mapped to a specific field in a bundl...
Definition: FieldRef.h:59
Value getValue() const
Get the Value which created this location.
Definition: FieldRef.h:37
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
bool applyToOperation(Operation *op) const
Store the annotations in this set in an operation's annotations attribute, overwriting any existing a...
void addAnnotations(ArrayRef< Annotation > annotations)
Add more annotations to this annotation set.
static bool removePortAnnotations(Operation *module, llvm::function_ref< bool(unsigned, Annotation)> predicate)
Remove all port annotations from a module or extmodule for which predicate returns true.
This class provides a read-only projection of an annotation.
DictionaryAttr getDict() const
Get the data dictionary of this attribute.
unsigned getFieldID() const
Get the field id this attribute targets.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
StringRef getClass() const
Return the 'class' that this annotation is representing.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
igraph::InstanceGraphNode * getTopLevelNode() override
Get the node corresponding to the top-level module of a circuit.
This table tracks nlas and what modules participate in them.
Definition: NLATable.h:29
void removeNLAsfromModule(const DenseSet< hw::HierPathOp > &nlas, StringAttr mod)
Remove all the nlas in the set nlas from the module.
Definition: NLATable.h:173
void getNLAsInModule(StringAttr modName, DenseSet< hw::HierPathOp > &nlas)
Get the NLAs that the module modName particiaptes in, and insert them into the DenseSet nlas.
Definition: NLATable.h:94
hw::HierPathOp getNLA(StringAttr name)
Resolve a symbol to an NLA.
Definition: NLATable.cpp:48
This is a Node in the InstanceGraph.
auto getModule()
Get the module that this node is tracking.
InstanceGraphNode * lookup(ModuleOpInterface op)
Look up an InstanceGraphNode for a module.
bool isAncestor(ModuleOpInterface child, ModuleOpInterface parent)
Check if child is instantiated by a parent.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:53
constexpr const char * augmentedBundleTypeClass
igraph::InstancePathCache InstancePathCache
constexpr const char * extractGrandCentralClass
LogicalResult extractDUT(FModuleOp mod, FModuleOp &dut)
Utility that searches for a MarkDUTAnnotation on a specific module, mod, and tries to update a design...
constexpr const char * testBenchDirAnnoClass
constexpr const char * augmentedGroundTypeClass
std::unique_ptr< mlir::Pass > createGrandCentralPass(CompanionMode companionMode=CompanionMode::Bind)
constexpr const char * viewAnnoClass
constexpr const char * blackBoxPathAnnoClass
Value getValueByFieldID(ImplicitLocOpBuilder builder, Value value, unsigned fieldID)
This gets the value targeted by a field id.
constexpr const char * companionAnnoClass
constexpr const char * blackBoxInlineAnnoClass
std::optional< AnnoPathValue > resolvePath(StringRef rawPath, CircuitOp circuit, SymbolTable &symTbl, CircuitTargetCache &cache)
Resolve a string path to a named item inside a circuit.
LogicalResult applyGCTView(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
constexpr const char * prefixInterfacesAnnoClass
constexpr const char * grandCentralHierarchyFileAnnoClass
::mlir::Type getFinalTypeByFieldID(Type type, uint64_t fieldID)
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
Definition: DebugAnalysis.h:21
mlir::raw_indented_ostream & dbgs()
Definition: Utility.h:28
static std::string stripComment(StringRef str)
Convert newlines and comments to remove the comments.
Definition: sv.py:1
State threaded through functions for resolving and applying annotations.
SmallVector< WiringProblem > wiringProblems
InstancePathCache & instancePathCache
The namespace of a CircuitOp, generally inhabited by modules.
Definition: Namespace.h:24
void insertOp(Operation *op)
Add a new op to the target cache.
A data structure that caches and provides absolute paths to module instances in the IR.
ArrayRef< InstancePath > getAbsolutePaths(ModuleOpInterface op)
InstanceGraph & instanceGraph
The instance graph of the IR.
SmallVector< int64_t, 2 > dimensions
An array describing the dimnensionality of the interface.
static void mapping(IO &io, DescribedInstance &op, Context &ctx)
DescribedSignal denormalize(IO &)
This cannot be denomralized back to an interface op.
std::optional< std::string > description
An optional, textual description of what the field is.
Field(IO &io)
A no-argument constructor is necessary to work with LLVM's YAML library.
Field(IO &io, DescribedSignal &op)
Construct a Field from a DescribedSignal (an sv::InterfaceSignalOp with an optional description).
SmallVector< unsigned, 2 > dimensions
The dimensions of the field.
static void mapping(IO &io, DescribedSignal &op, Context &ctx)
Interface(IO &io)
A no-argument constructor is necessary to work with LLVM's YAML library.
std::vector< DescribedInstance > instances
Instantiations of other interfaces.
Interface(IO &io, sv::InterfaceOp &op)
Construct an Interface from an sv::InterfaceOp.
sv::InterfaceOp denormalize(IO &)
This cannot be denomralized back to an interface op.
std::vector< DescribedSignal > fields
All ground or vectors that make up the interface.
static void mapping(IO &io, sv::InterfaceOp &op, Context &ctx)