CIRCT  18.0.0git
Schema.cpp
Go to the documentation of this file.
1 //===- Schema.cpp - ESI Cap'nProto schema utilities -------------*- 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 //
9 //
10 //===----------------------------------------------------------------------===//
11 
12 #include "ESICapnp.h"
16 #include "circt/Dialect/HW/HWOps.h"
18 #include "circt/Dialect/SV/SVOps.h"
20 #include "mlir/Support/IndentedOstream.h"
21 
22 // NOLINTNEXTLINE(clang-diagnostic-error)
23 #include "capnp/schema-parser.h"
24 #include "mlir/Dialect/Func/IR/FuncOps.h"
25 #include "mlir/IR/Builders.h"
26 #include "mlir/IR/BuiltinAttributes.h"
27 #include "mlir/IR/BuiltinTypes.h"
28 #include "llvm/ADT/IntervalMap.h"
29 #include "llvm/ADT/TypeSwitch.h"
30 #include "llvm/Support/Format.h"
31 
32 #include <initializer_list>
33 #include <string>
34 
35 using namespace circt::esi::capnp::detail;
36 using namespace circt;
37 using circt::comb::ICmpPredicate;
38 
39 namespace {
40 struct GasketComponent;
41 struct Slice;
42 } // anonymous namespace
43 
44 //===----------------------------------------------------------------------===//
45 // CapnpTypeSchema class implementation.
46 //===----------------------------------------------------------------------===//
47 
48 namespace circt {
49 namespace esi {
50 namespace capnp {
51 
52 namespace detail {
53 /// Actual implementation of `CapnpTypeSchema` to keep all the details out of
54 /// the header.
56 public:
59  LogicalResult write(llvm::raw_ostream &os) const;
60 
61  bool operator==(const CapnpTypeSchemaImpl &) const;
62 
63  // Compute the expected size of the capnp message in bits.
64  size_t size() const;
65 
66  /// Build an HW/SV dialect capnp encoder for this type.
67  hw::HWModuleOp buildEncoder(Value clk, Value valid, Value);
68  /// Build an HW/SV dialect capnp decoder for this type.
69  hw::HWModuleOp buildDecoder(Value clk, Value valid, Value);
70 
71 private:
72  ::capnp::ParsedSchema getSchema() const;
73  ::capnp::StructSchema getCapnpTypeSchema() const;
74 
76 
77  ::capnp::SchemaParser parser;
78  mutable ::capnp::ParsedSchema rootSchema;
79  mutable ::capnp::StructSchema typeSchema;
80 };
81 } // namespace detail
82 } // namespace capnp
83 } // namespace esi
84 } // namespace circt
85 
86 /// Return the encoding value for the size of this type (from the encoding
87 /// spec): 0 = 0 bits, 1 = 1 bit, 2 = 1 byte, 3 = 2 bytes, 4 = 4 bytes, 5 = 8
88 /// bytes (non-pointer), 6 = 8 bytes (pointer).
89 static size_t bitsEncoding(::capnp::schema::Type::Reader type) {
90  using ty = ::capnp::schema::Type;
91  switch (type.which()) {
92  case ty::VOID:
93  return 0;
94  case ty::BOOL:
95  return 1;
96  case ty::UINT8:
97  case ty::INT8:
98  return 2;
99  case ty::UINT16:
100  case ty::INT16:
101  return 3;
102  case ty::UINT32:
103  case ty::INT32:
104  return 4;
105  case ty::UINT64:
106  case ty::INT64:
107  return 5;
108  case ty::ANY_POINTER:
109  case ty::DATA:
110  case ty::INTERFACE:
111  case ty::LIST:
112  case ty::STRUCT:
113  case ty::TEXT:
114  return 6;
115  default:
116  llvm_unreachable("Type not yet supported");
117  }
118 }
119 
120 /// Return the number of bits used by a Capnp type.
121 static size_t bits(::capnp::schema::Type::Reader type) {
122  size_t enc = bitsEncoding(type);
123  if (enc <= 1)
124  return enc;
125  if (enc == 6)
126  return 64;
127  return 1 << (enc + 1);
128 }
129 
130 /// Return true if 'type' is capnp pointer.
131 static bool isPointerType(::capnp::schema::Type::Reader type) {
132  using ty = ::capnp::schema::Type;
133  switch (type.which()) {
134  case ty::ANY_POINTER:
135  case ty::DATA:
136  case ty::INTERFACE:
137  case ty::LIST:
138  case ty::STRUCT:
139  case ty::TEXT:
140  return true;
141  default:
142  return false;
143  }
144 }
145 
147 
148 /// Write a valid capnp schema to memory, then parse it out of memory using the
149 /// capnp library. Writing and parsing text within a single process is ugly, but
150 /// this is by far the easiest way to do this. This isn't the use case for which
151 /// Cap'nProto was designed.
152 ::capnp::ParsedSchema CapnpTypeSchemaImpl::getSchema() const {
153  if (rootSchema != ::capnp::ParsedSchema())
154  return rootSchema;
155 
156  // Write the schema to `schemaText`.
157  std::string schemaText;
158  llvm::raw_string_ostream os(schemaText);
159  emitCapnpID(os, 0xFFFFFFFFFFFFFFFF) << ";\n";
160  auto rc = write(os);
161  assert(succeeded(rc) && "Failed schema text output.");
162  (void)rc;
163  os.str();
164 
165  // Write `schemaText` to an in-memory filesystem then parse it. Yes, this is
166  // the only way to do this.
167  kj::Own<kj::Filesystem> fs = kj::newDiskFilesystem();
168  kj::Own<kj::Directory> dir = kj::newInMemoryDirectory(kj::nullClock());
169  kj::Path fakePath = kj::Path::parse("schema.capnp");
170  { // Ensure that 'fakeFile' has flushed.
171  auto fakeFile = dir->openFile(fakePath, kj::WriteMode::CREATE);
172  fakeFile->writeAll(schemaText);
173  }
174  rootSchema = parser.parseFromDirectory(*dir, std::move(fakePath), nullptr);
175  return rootSchema;
176 }
177 
178 /// Find the schema corresponding to `type` and return it.
179 ::capnp::StructSchema CapnpTypeSchemaImpl::getCapnpTypeSchema() const {
180  if (typeSchema != ::capnp::StructSchema())
181  return typeSchema;
182  uint64_t id = base.typeID();
183  for (auto schemaNode : getSchema().getAllNested()) {
184  if (schemaNode.getProto().getId() == id) {
185  typeSchema = schemaNode.asStruct();
186  return typeSchema;
187  }
188  }
189  llvm_unreachable("A node with a matching ID should always be found.");
190 }
191 
192 /// Returns the expected size of an array (capnp list) in 64-bit words.
193 static int64_t size(hw::ArrayType mType, capnp::schema::Field::Reader cField) {
194  assert(cField.isSlot());
195  auto cType = cField.getSlot().getType();
196  assert(cType.isList());
197  size_t elementBits = bits(cType.getList().getElementType());
198  int64_t listBits = mType.getNumElements() * elementBits;
199  return llvm::divideCeil(listBits, 64);
200 }
201 
202 /// Compute the size of a capnp struct, in 64-bit words.
203 static int64_t size(capnp::schema::Node::Struct::Reader cStruct,
204  ArrayRef<hw::StructType::FieldInfo> mFields) {
205  using namespace capnp::schema;
206  int64_t size = (1 + // Header
207  cStruct.getDataWordCount() + cStruct.getPointerCount());
208  auto cFields = cStruct.getFields();
209  for (Field::Reader cField : cFields) {
210  assert(!cField.isGroup() && "Capnp groups are not supported");
211  // Capnp code order is the index in the MLIR fields array.
212  assert(cField.getCodeOrder() < mFields.size());
213 
214  // The size of the thing to which the pointer is pointing, not the size of
215  // the pointer itself.
216  int64_t pointedToSize =
217  TypeSwitch<mlir::Type, int64_t>(mFields[cField.getCodeOrder()].type)
218  .Case([](IntegerType) { return 0; })
219  .Case([cField](hw::ArrayType mType) {
220  return ::size(mType, cField);
221  });
222  size += pointedToSize;
223  }
224  return size; // Convert from 64-bit words to bits.
225 }
226 
227 // Compute the expected size of the capnp message in bits.
229  auto schema = getCapnpTypeSchema();
230  auto structProto = schema.getProto().getStruct();
231  return ::size(structProto, base.getFields()) * 64;
232 }
233 
234 /// Write a valid Capnp type.
235 static void emitCapnpType(Type type, llvm::raw_ostream &os) {
236  llvm::TypeSwitch<Type>(type)
237  .Case([&os](IntegerType intTy) {
238  auto w = intTy.getWidth();
239  if (w == 0) {
240  os << "Void";
241  } else if (w == 1) {
242  os << "Bool";
243  } else {
244  if (intTy.isSigned())
245  os << "Int";
246  else
247  os << "UInt";
248 
249  // Round up.
250  if (w <= 8)
251  os << "8";
252  else if (w <= 16)
253  os << "16";
254  else if (w <= 32)
255  os << "32";
256  else if (w <= 64)
257  os << "64";
258  else
259  assert(false && "Type not supported. Integer too wide. Please "
260  "check support first with isSupported()");
261  }
262  })
263  .Case([&os](hw::ArrayType arrTy) {
264  os << "List(";
265  emitCapnpType(arrTy.getElementType(), os);
266  os << ')';
267  })
268  .Case([](hw::StructType structTy) {
269  assert(false && "Struct containing structs not supported");
270  })
271  .Default([](Type) {
272  assert(false && "Type not supported. Please check support first with "
273  "isSupported()");
274  });
275 }
276 
277 /// This function is essentially a placeholder which only supports ints. It'll
278 /// need to be re-worked when we start supporting structs, arrays, unions,
279 /// enums, etc.
280 LogicalResult CapnpTypeSchemaImpl::write(llvm::raw_ostream &rawOS) const {
281  mlir::raw_indented_ostream os(rawOS);
282 
283  // Since capnp requires messages to be structs, emit a wrapper struct.
284  os << "struct ";
285  base.writeMetadata(rawOS);
286  os << " {\n";
287  os.indent();
288 
289  size_t counter = 0;
290  size_t maxNameLength = 0;
291  for (auto field : base.getFields())
292  maxNameLength = std::max(maxNameLength, field.name.size());
293 
294  for (auto field : base.getFields()) {
295  // Specify the actual type, followed by the capnp field.
296  os << field.name.getValue();
297  std::string padding = std::string(maxNameLength - field.name.size(), ' ');
298  os << padding << " @" << counter++ << " :";
299  emitCapnpType(field.type, os.getOStream());
300  os << "; # Actual type is " << field.type << ".\n";
301  }
302 
303  os.unindent();
304  os << "}\n\n";
305  return success();
306 }
307 
308 //===----------------------------------------------------------------------===//
309 // Helper classes for common operations in the encode / decoders
310 //===----------------------------------------------------------------------===//
311 
312 namespace {
313 /// Provides easy methods to build common operations.
314 struct GasketBuilder {
315 public:
316  GasketBuilder() {} // To satisfy containers.
317  GasketBuilder(OpBuilder &b, Location loc) : builder(&b), location(loc) {}
318 
319  /// Get a zero constant of 'width' bit width.
320  GasketComponent zero(uint64_t width) const;
321  /// Get a constant 'value' of a certain bit width.
322  GasketComponent constant(uint64_t width, uint64_t value) const;
323 
324  /// Get 'p' bits of i1 padding.
325  Slice padding(uint64_t p) const;
326 
327  Location loc() const { return *location; }
328  void setLoc(Location loc) { location = loc; }
329  OpBuilder &b() const { return *builder; }
330  MLIRContext *ctxt() const { return builder->getContext(); }
331 
332 protected:
333  OpBuilder *builder;
334  std::optional<Location> location;
335 };
336 } // anonymous namespace
337 
338 namespace {
339 /// Contains helper methods to assist with naming and casting.
340 struct GasketComponent : GasketBuilder {
341 public:
342  GasketComponent() {} // To satisfy containers.
343  GasketComponent(OpBuilder &b, Value init)
344  : GasketBuilder(b, init.getLoc()), s(init) {}
345  GasketComponent(std::initializer_list<GasketComponent> values) {
346  *this = GasketComponent::concat(values);
347  }
348 
349  /// Set the "name" attribute of a value's op.
350  template <typename T = GasketComponent>
351  T &name(const Twine &name) {
352  std::string nameStr = name.str();
353  if (nameStr.empty())
354  return *(T *)this;
355  auto nameAttr = StringAttr::get(ctxt(), nameStr);
356  s.getDefiningOp()->setAttr("name", nameAttr);
357  return *(T *)this;
358  }
359  template <typename T = GasketComponent>
360  T &name(capnp::Text::Reader fieldName, const Twine &nameSuffix) {
361  return name<T>(StringRef(fieldName.cStr()) + nameSuffix);
362  }
363 
364  StringRef getName() const {
365  auto nameAttr = s.getDefiningOp()->getAttrOfType<StringAttr>("name");
366  if (nameAttr)
367  return nameAttr.getValue();
368  return StringRef();
369  }
370 
371  /// Construct a bitcast.
372  GasketComponent cast(Type t) const {
373  auto dst = builder->create<hw::BitcastOp>(loc(), t, s);
374  auto gc = GasketComponent(*builder, dst);
375  StringRef name = getName();
376  if (name.empty())
377  return gc;
378  return gc.name(name + "_casted");
379  ;
380  }
381 
382  /// Construct a bitcast.
383  Slice castBitArray() const;
384 
385  /// Downcast an int, accounting for signedness.
386  GasketComponent downcast(IntegerType t) const {
387  // Since the HW dialect operators only operate on signless integers, we
388  // have to cast to signless first, then cast the sign back.
389  assert(s.getType().isa<IntegerType>());
390  Value signlessVal = s;
391  if (!signlessVal.getType().isSignlessInteger())
392  signlessVal = builder->create<hw::BitcastOp>(
393  loc(), builder->getIntegerType(s.getType().getIntOrFloatBitWidth()),
394  s);
395 
396  if (!t.isSigned()) {
397  auto extracted =
398  builder->create<comb::ExtractOp>(loc(), t, signlessVal, 0);
399  return GasketComponent(*builder, extracted).cast(t);
400  }
401  auto magnitude = builder->create<comb::ExtractOp>(
402  loc(), builder->getIntegerType(t.getWidth() - 1), signlessVal, 0);
403  auto sign = builder->create<comb::ExtractOp>(
404  loc(), builder->getIntegerType(1), signlessVal, t.getWidth() - 1);
405  auto result = builder->create<comb::ConcatOp>(loc(), sign, magnitude);
406 
407  // We still have to cast to handle signedness.
408  return GasketComponent(*builder, result).cast(t);
409  }
410 
411  /// Pad this value with zeros up to `finalBits`.
412  GasketComponent padTo(uint64_t finalBits) const;
413 
414  /// Returns the bit width of this value.
415  uint64_t size() const { return hw::getBitWidth(s.getType()); }
416 
417  /// Build a component by concatenating some values.
418  static GasketComponent concat(ArrayRef<GasketComponent> concatValues);
419 
420  bool operator==(const GasketComponent &that) { return this->s == that.s; }
421  bool operator!=(const GasketComponent &that) { return this->s != that.s; }
422  Operation *operator->() const { return s.getDefiningOp(); }
423  Value getValue() const { return s; }
424  Type getType() const { return s.getType(); }
425  operator Value() { return s; }
426 
427 protected:
428  Value s;
429 };
430 } // anonymous namespace
431 
432 namespace {
433 /// Holds a 'slice' of an array and is able to construct more slice ops, then
434 /// cast to a type. A sub-slice holds a pointer to the slice which created it,
435 /// so it forms a hierarchy. This is so we can easily track offsets from the
436 /// root message for pointer resolution.
437 ///
438 /// Requirement: any slice which has sub-slices must not be free'd before its
439 /// children slices.
440 struct Slice : public GasketComponent {
441 private:
442  Slice(const Slice *parent, std::optional<int64_t> offset, Value val)
443  : GasketComponent(*parent->builder, val), parent(parent),
444  offsetIntoParent(offset) {
445  type = val.getType().dyn_cast<hw::ArrayType>();
446  assert(type && "Value must be array type");
447  }
448 
449 public:
450  Slice(OpBuilder &b, Value val)
451  : GasketComponent(b, val), parent(nullptr), offsetIntoParent(0) {
452  type = val.getType().dyn_cast<hw::ArrayType>();
453  assert(type && "Value must be array type");
454  }
455  Slice(GasketComponent gc)
456  : GasketComponent(gc), parent(nullptr), offsetIntoParent(0) {
457  type = gc.getValue().getType().dyn_cast<hw::ArrayType>();
458  assert(type && "Value must be array type");
459  }
460 
461  /// Create an op to slice the array from lsb to lsb + size. Return a new slice
462  /// with that op.
463  Slice slice(int64_t lsb, int64_t size) const {
464  hw::ArrayType dstTy = hw::ArrayType::get(type.getElementType(), size);
465  IntegerType idxTy =
466  builder->getIntegerType(llvm::Log2_64_Ceil(type.getNumElements()));
467  Value lsbConst = builder->create<hw::ConstantOp>(loc(), idxTy, lsb);
468  Value newSlice =
469  builder->create<hw::ArraySliceOp>(loc(), dstTy, s, lsbConst);
470  return Slice(this, lsb, newSlice);
471  }
472 
473  /// Create an op to slice the array from lsb to lsb + size. Return a new slice
474  /// with that op. If lsb is greater width thn necessary, lop off the high
475  /// bits.
476  Slice slice(Value lsb, int64_t size) const {
477  assert(lsb.getType().isa<IntegerType>());
478 
479  unsigned expIdxWidth = llvm::Log2_64_Ceil(type.getNumElements());
480  int64_t lsbWidth = lsb.getType().getIntOrFloatBitWidth();
481  if (lsbWidth > expIdxWidth)
482  lsb = builder->create<comb::ExtractOp>(
483  loc(), builder->getIntegerType(expIdxWidth), lsb, 0);
484  else if (lsbWidth < expIdxWidth)
485  assert(false && "LSB Value must not be smaller than expected.");
486  auto dstTy = hw::ArrayType::get(type.getElementType(), size);
487  Value newSlice = builder->create<hw::ArraySliceOp>(loc(), dstTy, s, lsb);
488  return Slice(this, std::nullopt, newSlice);
489  }
490  Slice &name(const Twine &name) { return GasketComponent::name<Slice>(name); }
491  Slice &name(capnp::Text::Reader fieldName, const Twine &nameSuffix) {
492  return GasketComponent::name<Slice>(fieldName.cStr(), nameSuffix);
493  }
494  Slice castToSlice(Type elemTy, size_t size, StringRef name = StringRef(),
495  Twine nameSuffix = Twine()) const {
496  auto arrTy = hw::ArrayType::get(elemTy, size);
497  GasketComponent rawCast =
498  GasketComponent::cast(arrTy).name(name + nameSuffix);
499  return Slice(*builder, rawCast);
500  }
501 
502  GasketComponent operator[](Value idx) const {
503  return GasketComponent(*builder,
504  builder->create<hw::ArrayGetOp>(loc(), s, idx));
505  }
506 
507  GasketComponent operator[](size_t idx) const {
508  IntegerType idxTy =
509  builder->getIntegerType(llvm::Log2_32_Ceil(type.getNumElements()));
510  auto idxVal = builder->create<hw::ConstantOp>(loc(), idxTy, idx);
511  return GasketComponent(*builder,
512  builder->create<hw::ArrayGetOp>(loc(), s, idxVal));
513  }
514 
515  /// Return the root of this slice hierarchy.
516  const Slice &getRootSlice() const {
517  if (parent == nullptr)
518  return *this;
519  return parent->getRootSlice();
520  }
521 
522  std::optional<int64_t> getOffsetFromRoot() const {
523  if (parent == nullptr)
524  return 0;
525  auto parentOffset = parent->getOffsetFromRoot();
526  if (!offsetIntoParent || !parentOffset)
527  return std::nullopt;
528  return *offsetIntoParent + *parentOffset;
529  }
530 
531  uint64_t size() const { return type.getNumElements(); }
532 
533 private:
534  hw::ArrayType type;
535  const Slice *parent;
536  std::optional<int64_t> offsetIntoParent;
537 };
538 } // anonymous namespace
539 
540 // The following methods have to be defined out-of-line because they use types
541 // which aren't yet defined when they are declared.
542 
543 GasketComponent GasketBuilder::zero(uint64_t width) const {
544  return GasketComponent(*builder,
545  builder->create<hw::ConstantOp>(
546  loc(), builder->getIntegerType(width), 0));
547 }
548 GasketComponent GasketBuilder::constant(uint64_t width, uint64_t value) const {
549  return GasketComponent(*builder,
550  builder->create<hw::ConstantOp>(
551  loc(), builder->getIntegerType(width), value));
552 }
553 
554 Slice GasketBuilder::padding(uint64_t p) const {
555  auto zero = GasketBuilder::zero(p);
556  return zero.castBitArray();
557 }
558 
559 Slice GasketComponent::castBitArray() const {
560  auto dstTy =
561  hw::ArrayType::get(builder->getI1Type(), hw::getBitWidth(s.getType()));
562  if (s.getType() == dstTy)
563  return Slice(*builder, s);
564  auto dst = builder->create<hw::BitcastOp>(loc(), dstTy, s);
565  return Slice(*builder, dst);
566 }
567 
568 GasketComponent
569 GasketComponent::concat(ArrayRef<GasketComponent> concatValues) {
570  assert(concatValues.size() > 0);
571  auto builder = concatValues[0].builder;
572  auto loc = concatValues[0].loc();
573  SmallVector<Value, 8> values;
574  for (auto gc : concatValues) {
575  values.push_back(gc.castBitArray());
576  }
577  // Since the "endianness" of `values` is the reverse of ArrayConcat, we must
578  // reverse ourselves.
579  std::reverse(values.begin(), values.end());
580  return GasketComponent(*builder,
581  builder->create<hw::ArrayConcatOp>(loc, values));
582 }
583 namespace {
584 /// Utility class for building sv::AssertOps. Since SV assertions need to be in
585 /// an `always` block (so the simulator knows when to check the assertion), we
586 /// build them all in a region intended for assertions.
587 class AssertBuilder : public OpBuilder {
588 public:
589  AssertBuilder(Location loc, Region &r) : OpBuilder(r), loc(loc) {}
590 
591  void assertPred(GasketComponent veg, ICmpPredicate pred, int64_t expected) {
592  if (veg.getValue().getType().isa<IntegerType>()) {
593  assertPred(veg.getValue(), pred, expected);
594  return;
595  }
596 
597  auto valTy = veg.getValue().getType().dyn_cast<hw::ArrayType>();
598  assert(valTy && valTy.getElementType() == veg.b().getIntegerType(1) &&
599  "Can only compare ints and bit arrays");
600  assertPred(
601  veg.cast(veg.b().getIntegerType(valTy.getNumElements())).getValue(),
602  pred, expected);
603  }
604 
605  void assertEqual(GasketComponent s, int64_t expected) {
606  assertPred(s, ICmpPredicate::eq, expected);
607  }
608 
609 private:
610  void assertPred(Value val, ICmpPredicate pred, int64_t expected) {
611  auto expectedVal = create<hw::ConstantOp>(loc, val.getType(), expected);
612  create<sv::AssertOp>(
613  loc,
614  create<comb::ICmpOp>(loc, getI1Type(), pred, val, expectedVal, false),
615  sv::DeferAssertAttr::get(loc.getContext(), sv::DeferAssert::Immediate));
616  }
617  Location loc;
618 };
619 } // anonymous namespace
620 
621 //===----------------------------------------------------------------------===//
622 // Capnp encode "gasket" HW builders.
623 //
624 // These have the potential to get large and complex as we add more types. The
625 // encoding spec is here: https://capnproto.org/encoding.html
626 //===----------------------------------------------------------------------===//
627 
628 namespace {
629 /// Helps build capnp message DAGs, which are stored in 'segments'. To better
630 /// reason about something which is more memory-like than wire-like, this class
631 /// contains a data structure to efficiently model memory and map it to Values
632 /// (wires).
633 class CapnpSegmentBuilder : public GasketBuilder {
634 public:
635  CapnpSegmentBuilder(OpBuilder &b, Location loc, uint64_t expectedSize)
636  : GasketBuilder(b, loc), segmentValues(allocator), messageSize(0),
637  expectedSize(expectedSize) {}
638  CapnpSegmentBuilder(const CapnpSegmentBuilder &) = delete;
639  ~CapnpSegmentBuilder() {}
640 
641  GasketComponent build(::capnp::schema::Node::Struct::Reader cStruct,
642  ArrayRef<GasketComponent> mFieldValues);
643 
644 private:
645  /// Allocate and build a struct. Return the address of the data section as an
646  /// offset into the 'memory' map.
647  GasketComponent encodeStructAt(uint64_t ptrLoc,
648  ::capnp::schema::Node::Struct::Reader cStruct,
649  ArrayRef<GasketComponent> mFieldValues);
650  /// Build a value from the 'memory' map. Concatenates all the values in the
651  /// 'memory' map, filling in the blank addresses with padding.
652  GasketComponent compile() const;
653 
654  /// Encode 'val' and place the value at the specified 'memory' offset.
655  void encodeFieldAt(uint64_t offset, GasketComponent val,
656  ::capnp::schema::Type::Reader type);
657  /// Allocate and build a list, returning the address which was allocated.
658  uint64_t buildList(Slice val, ::capnp::schema::Type::Reader type);
659 
660  /// Insert 'val' into the 'memory' map.
661  void insert(uint64_t offset, GasketComponent val) {
662  uint64_t valSize = val.size();
663  assert(!segmentValues.overlaps(offset, offset + valSize - 1));
664  assert(offset + valSize - 1 < expectedSize &&
665  "Tried to insert above the max expected size of the message.");
666  segmentValues.insert(offset, offset + valSize - 1, val);
667  }
668 
669  /// This is where the magic lives. An IntervalMap allows us to efficiently
670  /// model segment 'memory' and to place Values at any address. We can then
671  /// manage 'memory allocations' (figuring out where to place pointed to
672  /// objects) separately from the data contained in those values, some of which
673  /// are pointers themselves.
674  llvm::IntervalMap<uint64_t, GasketComponent>::Allocator allocator;
675  llvm::IntervalMap<uint64_t, GasketComponent> segmentValues;
676 
677  /// Track the allocated message size. Increase to 'alloc' more.
678  uint64_t messageSize;
679  uint64_t alloc(size_t bits) {
680  uint64_t ptr = messageSize;
681  messageSize += bits;
682  return ptr;
683  }
684 
685  /// The expected maximum size of the message.
686  uint64_t expectedSize;
687 };
688 } // anonymous namespace
689 
690 void CapnpSegmentBuilder::encodeFieldAt(uint64_t offset, GasketComponent val,
691  ::capnp::schema::Type::Reader type) {
692  TypeSwitch<Type>(val.getValue().getType())
693  .Case([&](IntegerType it) { insert(offset, val); })
694  .Case([&](hw::ArrayType arrTy) {
695  uint64_t listOffset = buildList(Slice(val), type);
696  int32_t relativeOffset = (listOffset - offset - 64) / 64;
697  insert(offset,
699  {constant(2, 1), constant(30, relativeOffset),
700  constant(3, bitsEncoding(type.getList().getElementType())),
701  constant(29, arrTy.getNumElements())}));
702  });
703 }
704 
705 uint64_t CapnpSegmentBuilder::buildList(Slice val,
706  ::capnp::schema::Type::Reader type) {
707  hw::ArrayType arrTy = val.getValue().getType().cast<hw::ArrayType>();
708  auto elemType = type.getList().getElementType();
709  size_t elemWidth = bits(elemType);
710  uint64_t listSize = elemWidth * arrTy.getNumElements();
711  uint64_t m;
712  if ((m = listSize % 64) != 0)
713  listSize += (64 - m);
714  uint64_t listOffset = alloc(listSize);
715 
716  for (size_t i = 0, e = arrTy.getNumElements(); i < e; ++i) {
717  size_t elemNum = e - i - 1;
718  encodeFieldAt(listOffset + (elemNum * elemWidth), val[i], elemType);
719  }
720  return listOffset;
721 }
722 
723 GasketComponent CapnpSegmentBuilder::encodeStructAt(
724  uint64_t ptrLoc, ::capnp::schema::Node::Struct::Reader cStruct,
725  ArrayRef<GasketComponent> mFieldValues) {
726 
727  assert(ptrLoc % 64 == 0);
728  size_t structSize =
729  (cStruct.getDataWordCount() + cStruct.getPointerCount()) * 64;
730  uint64_t structDataSectionOffset = alloc(structSize);
731  uint64_t structPointerSectionOffset =
732  structDataSectionOffset + (cStruct.getDataWordCount() * 64);
733  assert(structDataSectionOffset % 64 == 0);
734  int64_t relativeStructDataOffsetWords =
735  ((structDataSectionOffset - ptrLoc) / 64) -
736  /*offset from end of pointer.*/ 1;
737  GasketComponent structPtr = {constant(2, 0),
738  constant(30, relativeStructDataOffsetWords),
739  constant(16, cStruct.getDataWordCount()),
740  constant(16, cStruct.getPointerCount())};
741 
742  // Loop through data fields.
743  for (auto field : cStruct.getFields()) {
744  uint16_t idx = field.getCodeOrder();
745  assert(idx < mFieldValues.size() &&
746  "Capnp struct longer than fieldValues.");
747  auto cFieldType = field.getSlot().getType();
748  uint64_t fieldOffset =
749  (isPointerType(cFieldType) ? structPointerSectionOffset
750  : structDataSectionOffset) +
751  field.getSlot().getOffset() * bits(cFieldType);
752  encodeFieldAt(fieldOffset, mFieldValues[idx], cFieldType);
753  }
754 
755  return structPtr;
756 }
757 
758 GasketComponent CapnpSegmentBuilder::compile() const {
759  // Fill in missing bits.
760  SmallVector<GasketComponent, 16> segmentValuesPlusPadding;
761  uint64_t lastStop = 0;
762  for (auto it = segmentValues.begin(), e = segmentValues.end(); it != e;
763  ++it) {
764  auto value = it.value();
765  int64_t padBits = it.start() - lastStop;
766  assert(padBits >= 0 && "Overlap not allowed");
767  if (padBits)
768  segmentValuesPlusPadding.push_back(padding(padBits));
769  segmentValuesPlusPadding.push_back(value.castBitArray());
770  // IntervalMap has inclusive ranges, but we want to reason about [,) regions
771  // to make the math work.
772  lastStop = it.stop() + 1;
773  }
774  assert(expectedSize >= lastStop);
775  if (lastStop != expectedSize)
776  segmentValuesPlusPadding.push_back(padding(expectedSize - lastStop));
777 
778  return GasketComponent::concat(segmentValuesPlusPadding);
779 }
780 
781 GasketComponent
782 CapnpSegmentBuilder::build(::capnp::schema::Node::Struct::Reader cStruct,
783  ArrayRef<GasketComponent> mFieldValues) {
784  uint64_t rootPtrLoc = alloc(64);
785  assert(rootPtrLoc == 0);
786  auto rootPtr = encodeStructAt(rootPtrLoc, cStruct, mFieldValues);
787  insert(rootPtrLoc, rootPtr);
788  return compile();
789 }
790 
791 /// Build an HW/SV dialect capnp encoder module for this type. Inputs need to
792 /// be packed and unpadded.
793 hw::HWModuleOp CapnpTypeSchemaImpl::buildEncoder(Value clk, Value valid,
794  Value operandVal) {
795  Location loc = operandVal.getDefiningOp()->getLoc();
796  ModuleOp topMod = operandVal.getDefiningOp()->getParentOfType<ModuleOp>();
797  OpBuilder b = OpBuilder::atBlockEnd(topMod.getBody());
798 
799  SmallString<64> modName;
800  modName.append("encode");
801  modName.append(base.name());
802  SmallVector<hw::PortInfo, 4> ports;
803  ports.push_back(hw::PortInfo{
804  {b.getStringAttr("clk"), clk.getType(), hw::ModulePort::Direction::Input},
805  0});
806  ports.push_back(hw::PortInfo{{b.getStringAttr("valid"), valid.getType(),
808  1});
809  ports.push_back(
810  hw::PortInfo{{b.getStringAttr("unencodedInput"), operandVal.getType(),
812  2});
813  hw::ArrayType modOutputType = hw::ArrayType::get(b.getI1Type(), size());
814  ports.push_back(hw::PortInfo{{b.getStringAttr("encoded"), modOutputType,
816  0});
817  hw::HWModuleOp retMod = b.create<hw::HWModuleOp>(
818  operandVal.getLoc(), b.getStringAttr(modName), ports);
819 
820  Block *innerBlock = retMod.getBodyBlock();
821  b.setInsertionPointToStart(innerBlock);
822  clk = innerBlock->getArgument(0);
823  valid = innerBlock->getArgument(1);
824  GasketComponent operand(b, innerBlock->getArgument(2));
825  operand.setLoc(loc);
826 
827  ::capnp::schema::Node::Reader rootProto = getCapnpTypeSchema().getProto();
828  auto st = rootProto.getStruct();
829  CapnpSegmentBuilder seg(b, loc, size());
830 
831  // The values in the struct we are encoding.
832  SmallVector<GasketComponent, 16> fieldValues;
833  assert(operand.getValue().getType() == base.getType());
834  if (auto structTy = base.getType().dyn_cast<hw::StructType>()) {
835  for (auto field : structTy.getElements()) {
836  fieldValues.push_back(GasketComponent(
837  b, b.create<hw::StructExtractOp>(loc, operand, field)));
838  }
839  } else {
840  fieldValues.push_back(GasketComponent(b, operand));
841  }
842  GasketComponent ret = seg.build(st, fieldValues);
843 
844  innerBlock->getTerminator()->erase();
845  b.setInsertionPointToEnd(innerBlock);
846  b.create<hw::OutputOp>(loc, ValueRange{ret});
847  return retMod;
848 }
849 
850 //===----------------------------------------------------------------------===//
851 // Capnp decode "gasket" HW builders.
852 //
853 // These have the potential to get large and complex as we add more types. The
854 // encoding spec is here: https://capnproto.org/encoding.html
855 //===----------------------------------------------------------------------===//
856 
857 /// Construct the proper operations to decode a capnp list. This only works for
858 /// arrays of ints or bools. Will need to be updated for structs and lists of
859 /// lists.
860 static GasketComponent decodeList(hw::ArrayType type,
861  capnp::schema::Field::Reader field,
862  Slice ptrSection, AssertBuilder &asserts) {
863  capnp::schema::Type::Reader capnpType = field.getSlot().getType();
864  assert(capnpType.isList());
865  assert(capnpType.getList().hasElementType());
866 
867  auto loc = ptrSection.loc();
868  OpBuilder &b = ptrSection.b();
869  GasketBuilder gb(b, loc);
870 
871  // Get the list pointer and break out its parts.
872  auto ptr = ptrSection.slice(field.getSlot().getOffset() * 64, 64)
873  .name(field.getName(), "_ptr");
874  auto ptrType = ptr.slice(0, 2).name(field.getName(), "_ptrType");
875  auto offset = ptr.slice(2, 30)
876  .cast(b.getIntegerType(30))
877  .name(field.getName(), "_offset");
878  auto elemSize = ptr.slice(32, 3).name(field.getName(), "_elemSize");
879  auto length = ptr.slice(35, 29).name(field.getName(), "_listLength");
880 
881  // Assert that ptr type == list type;
882  asserts.assertEqual(ptrType, 1);
883 
884  // Assert that the element size in the message matches our expectation.
885  auto expectedElemSizeBits = bits(capnpType.getList().getElementType());
886  unsigned expectedElemSizeField;
887  switch (expectedElemSizeBits) {
888  case 0:
889  expectedElemSizeField = 0;
890  break;
891  case 1:
892  expectedElemSizeField = 1;
893  break;
894  case 8:
895  expectedElemSizeField = 2;
896  break;
897  case 16:
898  expectedElemSizeField = 3;
899  break;
900  case 32:
901  expectedElemSizeField = 4;
902  break;
903  case 64:
904  expectedElemSizeField = 5;
905  break;
906  default:
907  llvm_unreachable("bits() returned unexpected value");
908  }
909  asserts.assertEqual(elemSize, expectedElemSizeField);
910 
911  // Assert that the length of the list (array) is at most the length of the
912  // array.
913  asserts.assertPred(length, ICmpPredicate::ule, type.getNumElements());
914 
915  // Get the entire message slice, compute the offset into the list, then get
916  // the list data in an ArrayType.
917  auto msg = ptr.getRootSlice();
918  auto ptrOffset = ptr.getOffsetFromRoot();
919  assert(ptrOffset);
920  GasketComponent offsetInBits(
921  b, b.create<comb::ConcatOp>(loc, offset, gb.zero(6)));
922  GasketComponent listOffset(
923  b, b.create<comb::AddOp>(loc, offsetInBits,
924  gb.constant(36, *ptrOffset + 64), false));
925  listOffset.name(field.getName(), "_listOffset");
926  auto listSlice =
927  msg.slice(listOffset, type.getNumElements() * expectedElemSizeBits)
928  .name("list");
929 
930  // Cast to an array of capnp int elements.
931  assert(type.getElementType().isa<IntegerType>() &&
932  "DecodeList() only works on arrays of ints currently");
933  Type capnpElemTy =
934  b.getIntegerType(expectedElemSizeBits, IntegerType::Signless);
935  auto arrayOfElements =
936  listSlice.castToSlice(capnpElemTy, type.getNumElements());
937  if (arrayOfElements.getValue().getType() == type)
938  return arrayOfElements;
939 
940  // Collect the reduced elements.
941  SmallVector<Value, 64> arrayValues;
942  for (size_t i = 0, e = type.getNumElements(); i < e; ++i) {
943  auto capnpElem = arrayOfElements[i].name(field.getName(), "_capnp_elem");
944  auto esiElem = capnpElem.downcast(type.getElementType().cast<IntegerType>())
945  .name(field.getName(), "_elem");
946  arrayValues.push_back(esiElem);
947  }
948  auto array = b.create<hw::ArrayCreateOp>(loc, arrayValues);
949  return GasketComponent(b, array);
950 }
951 
952 /// Construct the proper operations to convert a capnp field to 'type'.
953 static GasketComponent decodeField(Type type,
954  capnp::schema::Field::Reader field,
955  Slice dataSection, Slice ptrSection,
956  AssertBuilder &asserts) {
957  GasketComponent esiValue =
958  TypeSwitch<Type, GasketComponent>(type)
959  .Case([&](IntegerType it) {
960  auto slice = dataSection.slice(field.getSlot().getOffset() *
961  bits(field.getSlot().getType()),
962  it.getWidth());
963  return slice.name(field.getName(), "_bits").cast(type);
964  })
965  .Case([&](hw::ArrayType at) {
966  return decodeList(at, field, ptrSection, asserts);
967  });
968  esiValue.name(field.getName().cStr(), "Value");
969  return esiValue;
970 }
971 
972 /// Build an HW/SV dialect capnp decoder module for this type. Outputs packed
973 /// and unpadded data.
974 hw::HWModuleOp CapnpTypeSchemaImpl::buildDecoder(Value clk, Value valid,
975  Value operandVal) {
976  auto loc = operandVal.getDefiningOp()->getLoc();
977  auto topMod = operandVal.getDefiningOp()->getParentOfType<ModuleOp>();
978  OpBuilder b = OpBuilder::atBlockEnd(topMod.getBody());
979 
980  SmallString<64> modName;
981  modName.append("decode");
982  modName.append(base.name());
983  SmallVector<hw::PortInfo, 4> ports;
984  ports.push_back(hw::PortInfo{
985  {b.getStringAttr("clk"), clk.getType(), hw::ModulePort::Direction::Input},
986  0});
987  ports.push_back(hw::PortInfo{{b.getStringAttr("valid"), valid.getType(),
989  1});
990  ports.push_back(
991  hw::PortInfo{{b.getStringAttr("encodedInput"), operandVal.getType(),
993  2});
994  ports.push_back(hw::PortInfo{{b.getStringAttr("decoded"), base.getType(),
996  0});
997  hw::HWModuleOp retMod = b.create<hw::HWModuleOp>(
998  operandVal.getLoc(), b.getStringAttr(modName), ports);
999 
1000  Block *innerBlock = retMod.getBodyBlock();
1001  b.setInsertionPointToStart(innerBlock);
1002  clk = innerBlock->getArgument(0);
1003  valid = innerBlock->getArgument(1);
1004  operandVal = innerBlock->getArgument(2);
1005 
1006  // Various useful integer types.
1007  auto i16 = b.getIntegerType(16);
1008 
1009  size_t size = this->size();
1010  hw::ArrayType operandType = operandVal.getType().dyn_cast<hw::ArrayType>();
1011  assert(operandType && operandType.getNumElements() == size &&
1012  "Operand type and length must match the type's capnp size.");
1013  (void)size;
1014  (void)operandType;
1015 
1016  Slice operand(b, operandVal);
1017  operand.setLoc(loc);
1018 
1019  auto hwClk = b.create<seq::FromClockOp>(clk.getLoc(), clk).getResult();
1020  auto alwaysAt =
1021  b.create<sv::AlwaysOp>(loc, sv::EventControl::AtPosEdge, hwClk);
1022  auto ifValid =
1023  OpBuilder(alwaysAt.getBodyRegion()).create<sv::IfOp>(loc, valid);
1024  AssertBuilder asserts(loc, ifValid.getBodyRegion());
1025 
1026  // The next 64-bits of a capnp message is the root struct pointer.
1027  ::capnp::schema::Node::Reader rootProto = getCapnpTypeSchema().getProto();
1028  auto ptr = operand.slice(0, 64).name("rootPointer");
1029 
1030  // Since this is the root, we _expect_ the offset to be zero but that's only
1031  // guaranteed to be the case with canonically-encoded messages.
1032  // TODO: support cases where the pointer offset is non-zero.
1033  Slice assertPtr(ptr);
1034  auto typeAndOffset = assertPtr.slice(0, 32).name("typeAndOffset");
1035  if (base.getType().isInteger(0)) {
1036  asserts.assertEqual(typeAndOffset.slice(0, 2), 0);
1037  asserts.assertEqual(typeAndOffset.slice(2, 30), 0x3FFFFFFF);
1038  } else {
1039  asserts.assertEqual(typeAndOffset, 0);
1040  }
1041 
1042  // We expect the data section to be equal to the computed data section size.
1043  auto dataSectionSize =
1044  assertPtr.slice(32, 16).cast(i16).name("dataSectionSize");
1045  asserts.assertEqual(dataSectionSize,
1046  rootProto.getStruct().getDataWordCount());
1047 
1048  // We expect the pointer section to be equal to the computed pointer section
1049  // size.
1050  auto ptrSectionSize =
1051  assertPtr.slice(48, 16).cast(i16).name("ptrSectionSize");
1052  asserts.assertEqual(ptrSectionSize, rootProto.getStruct().getPointerCount());
1053 
1054  // Get pointers to the data and pointer sections.
1055  auto st = rootProto.getStruct();
1056  auto dataSection =
1057  operand.slice(64, st.getDataWordCount() * 64).name("dataSection");
1058  auto ptrSection = operand
1059  .slice(64 + (st.getDataWordCount() * 64),
1060  rootProto.getStruct().getPointerCount() * 64)
1061  .name("ptrSection");
1062 
1063  // Loop through fields.
1064  SmallVector<GasketComponent, 64> fieldValues;
1065  for (auto field : st.getFields()) {
1066  uint16_t idx = field.getCodeOrder();
1067  assert(idx < base.getFields().size() &&
1068  "Capnp struct longer than fieldTypes.");
1069  fieldValues.push_back(decodeField(base.getFields()[idx].type, field,
1070  dataSection, ptrSection, asserts));
1071  }
1072 
1073  // What to return depends on the type. (e.g. structs have to be constructed
1074  // from the field values.)
1075  GasketComponent ret =
1076  TypeSwitch<Type, GasketComponent>(base.getType())
1077  .Case([&fieldValues](IntegerType) { return fieldValues[0]; })
1078  .Case([&fieldValues](hw::ArrayType) { return fieldValues[0]; })
1079  .Case([&](hw::StructType) {
1080  SmallVector<Value, 8> rawValues(llvm::map_range(
1081  fieldValues, [](GasketComponent c) { return c.getValue(); }));
1082  return GasketComponent(b, b.create<hw::StructCreateOp>(
1083  loc, base.getType(), rawValues));
1084  });
1085  ret.name(base.name());
1086 
1087  innerBlock->getTerminator()->erase();
1088  b.setInsertionPointToEnd(innerBlock);
1089  auto outputOp = b.create<hw::OutputOp>(loc, ValueRange{ret.getValue()});
1090  alwaysAt->moveBefore(outputOp);
1091  return retMod;
1092 }
1093 
1094 //===----------------------------------------------------------------------===//
1095 // CapnpTypeSchema wrapper.
1096 //===----------------------------------------------------------------------===//
1097 
1102 
1103 size_t circt::esi::capnp::CapnpTypeSchema::size() const { return s->size(); }
1104 
1106  : circt::esi::ESIAPIType(outerType) {
1107  s = std::make_shared<detail::CapnpTypeSchemaImpl>(*this);
1108 }
1109 
1110 LogicalResult
1111 circt::esi::capnp::CapnpTypeSchema::write(llvm::raw_ostream &os) const {
1112  return s->write(os);
1113 }
1114 
1116  llvm::raw_ostream &os) const {
1117  os << name() << " ";
1118  emitCapnpID(os, typeID());
1119 }
1120 
1122  Value clk, Value valid,
1123  Value rawData) const {
1124  hw::HWModuleOp encImplMod;
1125  auto encImplIT = encImplMods.find(getType());
1126  if (encImplIT == encImplMods.end()) {
1127  encImplMod = s->buildEncoder(clk, valid, rawData);
1128  encImplMods[getType()] = encImplMod;
1129  } else {
1130  encImplMod = encImplIT->second;
1131  }
1132 
1133  SmallString<64> instName;
1134  instName.append("encode");
1135  instName.append(name());
1136  instName.append("Inst");
1137  auto encodeInst =
1138  builder.create<hw::InstanceOp>(rawData.getLoc(), encImplMod, instName,
1139  ArrayRef<Value>{clk, valid, rawData});
1140  return encodeInst.getResult(0);
1141 }
1142 
1144  Value clk, Value valid,
1145  Value capnpData) const {
1146  hw::HWModuleOp decImplMod;
1147  auto decImplIT = decImplMods.find(getType());
1148  if (decImplIT == decImplMods.end()) {
1149  decImplMod = s->buildDecoder(clk, valid, capnpData);
1150  decImplMods[getType()] = decImplMod;
1151  } else {
1152  decImplMod = decImplIT->second;
1153  }
1154 
1155  SmallString<64> instName;
1156  instName.append("decode");
1157  instName.append(name());
1158  instName.append("Inst");
1159  auto decodeInst =
1160  builder.create<hw::InstanceOp>(capnpData.getLoc(), decImplMod, instName,
1161  ArrayRef<Value>{clk, valid, capnpData});
1162  return decodeInst.getResult(0);
1163 }
assert(baseType &&"element must be base type")
static SmallVector< T > concat(const SmallVectorImpl< T > &a, const SmallVectorImpl< T > &b)
Returns a new vector containing the concatenation of vectors a and b.
Definition: CalyxOps.cpp:538
int32_t width
Definition: FIRRTL.cpp:27
@ Input
Definition: HW.h:32
@ Output
Definition: HW.h:32
bool operator!=(const ResetDomain &a, const ResetDomain &b)
Definition: InferResets.cpp:92
bool operator==(const ResetDomain &a, const ResetDomain &b)
Definition: InferResets.cpp:89
static SmallVector< Value > concatValues(ValueRange a, ValueRange b)
Concatenate two value ranges into a larger range.
Definition: LTLFolds.cpp:29
Builder builder
static GasketComponent decodeList(hw::ArrayType type, capnp::schema::Field::Reader field, Slice ptrSection, AssertBuilder &asserts)
Construct the proper operations to decode a capnp list.
Definition: Schema.cpp:860
static bool isPointerType(::capnp::schema::Type::Reader type)
Return true if 'type' is capnp pointer.
Definition: Schema.cpp:131
static size_t bits(::capnp::schema::Type::Reader type)
Return the number of bits used by a Capnp type.
Definition: Schema.cpp:121
static void emitCapnpType(Type type, llvm::raw_ostream &os)
Write a valid Capnp type.
Definition: Schema.cpp:235
static size_t bitsEncoding(::capnp::schema::Type::Reader type)
Return the encoding value for the size of this type (from the encoding spec): 0 = 0 bits,...
Definition: Schema.cpp:89
static int64_t size(hw::ArrayType mType, capnp::schema::Field::Reader cField)
Returns the expected size of an array (capnp list) in 64-bit words.
Definition: Schema.cpp:193
static GasketComponent decodeField(Type type, capnp::schema::Field::Reader field, Slice dataSection, Slice ptrSection, AssertBuilder &asserts)
Construct the proper operations to convert a capnp field to 'type'.
Definition: Schema.cpp:953
StringRef name() const
For now, the name is just the type serialized.
llvm::ArrayRef< FieldInfo > getFields() const
Definition: APIUtilities.h:45
mlir::Type getType() const
Get the type back.
Definition: APIUtilities.h:40
uint64_t typeID() const
Generate and reason about a Cap'nProto schema for a particular MLIR type.
Definition: ESICapnp.h:51
mlir::Value buildEncoder(mlir::OpBuilder &, mlir::Value clk, mlir::Value valid, mlir::Value rawData) const
Build an HW/SV dialect capnp encoder for this type.
Definition: Schema.cpp:1121
static llvm::SmallDenseMap< Type, hw::HWModuleOp > encImplMods
Definition: ESICapnp.h:80
mlir::Value buildDecoder(mlir::OpBuilder &, mlir::Value clk, mlir::Value valid, mlir::Value capnpData) const
Build an HW/SV dialect capnp decoder for this type.
Definition: Schema.cpp:1143
void writeMetadata(llvm::raw_ostream &os) const
Write out the name and ID in capnp schema format.
Definition: Schema.cpp:1115
static llvm::SmallDenseMap< Type, hw::HWModuleOp > decImplMods
Cache of the decode/encode modules;.
Definition: ESICapnp.h:79
size_t size() const
Size in bits of the capnp message.
Definition: Schema.cpp:1103
mlir::LogicalResult write(llvm::raw_ostream &os) const
Write out the schema in its entirety.
Definition: Schema.cpp:1111
std::shared_ptr< detail::CapnpTypeSchemaImpl > s
The implementation of this.
Definition: ESICapnp.h:76
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:53
llvm::raw_ostream & emitCapnpID(llvm::raw_ostream &os, int64_t id)
Emit an ID in capnp format.
Definition: ESICapnp.h:42
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
int64_t getBitWidth(mlir::Type type)
Return the hardware bit width of a type.
Definition: HWTypes.cpp:100
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
Definition: esi.py:1
Actual implementation of CapnpTypeSchema to keep all the details out of the header.
Definition: Schema.cpp:55
hw::HWModuleOp buildEncoder(Value clk, Value valid, Value)
Build an HW/SV dialect capnp encoder for this type.
Definition: Schema.cpp:793
::capnp::ParsedSchema getSchema() const
Write a valid capnp schema to memory, then parse it out of memory using the capnp library.
Definition: Schema.cpp:152
hw::HWModuleOp buildDecoder(Value clk, Value valid, Value)
Build an HW/SV dialect capnp decoder for this type.
Definition: Schema.cpp:974
CapnpTypeSchemaImpl(CapnpTypeSchema &base)
Definition: Schema.cpp:146
CapnpTypeSchemaImpl(const CapnpTypeSchemaImpl &)=delete
::capnp::StructSchema getCapnpTypeSchema() const
Find the schema corresponding to type and return it.
Definition: Schema.cpp:179
bool operator==(const CapnpTypeSchemaImpl &) const
mutable ::capnp::ParsedSchema rootSchema
Definition: Schema.cpp:78
LogicalResult write(llvm::raw_ostream &os) const
This function is essentially a placeholder which only supports ints.
Definition: Schema.cpp:280
mutable ::capnp::StructSchema typeSchema
Definition: Schema.cpp:79
This holds the name, type, direction of a module's ports.