20 #include "mlir/Support/IndentedOstream.h"
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"
32 #include <initializer_list>
36 using namespace circt;
37 using circt::comb::ICmpPredicate;
40 struct GasketComponent;
59 LogicalResult write(llvm::raw_ostream &os)
const;
67 hw::HWModuleOp buildEncoder(Value clk, Value valid, Value);
69 hw::HWModuleOp buildDecoder(Value clk, Value valid, Value);
72 ::capnp::ParsedSchema getSchema()
const;
73 ::capnp::StructSchema getCapnpTypeSchema()
const;
90 using ty = ::capnp::schema::Type;
91 switch (type.which()) {
108 case ty::ANY_POINTER:
116 llvm_unreachable(
"Type not yet supported");
121 static size_t bits(::capnp::schema::Type::Reader type) {
127 return 1 << (enc + 1);
132 using ty = ::capnp::schema::Type;
133 switch (type.which()) {
134 case ty::ANY_POINTER:
157 std::string schemaText;
158 llvm::raw_string_ostream os(schemaText);
161 assert(succeeded(rc) &&
"Failed schema text output.");
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");
171 auto fakeFile = dir->openFile(fakePath, kj::WriteMode::CREATE);
172 fakeFile->writeAll(schemaText);
183 for (
auto schemaNode :
getSchema().getAllNested()) {
184 if (schemaNode.getProto().getId() ==
id) {
189 llvm_unreachable(
"A node with a matching ID should always be found.");
193 static int64_t
size(hw::ArrayType mType, capnp::schema::Field::Reader cField) {
195 auto cType = cField.getSlot().getType();
197 size_t elementBits =
bits(cType.getList().getElementType());
198 int64_t listBits = mType.getNumElements() * elementBits;
199 return llvm::divideCeil(listBits, 64);
203 static int64_t
size(capnp::schema::Node::Struct::Reader cStruct,
204 ArrayRef<hw::StructType::FieldInfo> mFields) {
205 using namespace capnp::schema;
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");
212 assert(cField.getCodeOrder() < mFields.size());
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) {
222 size += pointedToSize;
230 auto structProto = schema.getProto().getStruct();
236 llvm::TypeSwitch<Type>(type)
237 .Case([&os](IntegerType intTy) {
238 auto w = intTy.getWidth();
244 if (intTy.isSigned())
259 assert(
false &&
"Type not supported. Integer too wide. Please "
260 "check support first with isSupported()");
263 .Case([&os](hw::ArrayType arrTy) {
268 .Case([](hw::StructType structTy) {
269 assert(
false &&
"Struct containing structs not supported");
272 assert(
false &&
"Type not supported. Please check support first with "
281 mlir::raw_indented_ostream os(rawOS);
290 size_t maxNameLength = 0;
292 maxNameLength = std::max(maxNameLength, field.name.size());
296 os << field.name.getValue();
297 std::string padding = std::string(maxNameLength - field.name.size(),
' ');
298 os << padding <<
" @" << counter++ <<
" :";
300 os <<
"; # Actual type is " << field.type <<
".\n";
314 struct GasketBuilder {
317 GasketBuilder(OpBuilder &b, Location loc) :
builder(&b), location(loc) {}
320 GasketComponent zero(uint64_t
width)
const;
322 GasketComponent constant(uint64_t
width, uint64_t value)
const;
325 Slice padding(uint64_t p)
const;
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(); }
334 std::optional<Location> location;
340 struct GasketComponent : GasketBuilder {
343 GasketComponent(OpBuilder &b, Value init)
344 : GasketBuilder(b, init.getLoc()), s(init) {}
345 GasketComponent(std::initializer_list<GasketComponent> values) {
350 template <
typename T = GasketComponent>
351 T &name(
const Twine &name) {
352 std::string nameStr = name.str();
356 s.getDefiningOp()->setAttr(
"name", nameAttr);
359 template <
typename T = GasketComponent>
360 T &name(capnp::Text::Reader fieldName,
const Twine &nameSuffix) {
361 return name<T>(StringRef(fieldName.cStr()) + nameSuffix);
365 auto nameAttr = s.getDefiningOp()->getAttrOfType<StringAttr>(
"name");
367 return nameAttr.getValue();
372 GasketComponent cast(Type t)
const {
373 auto dst =
builder->create<hw::BitcastOp>(loc(), t, s);
374 auto gc = GasketComponent(*
builder, dst);
378 return gc.name(name +
"_casted");
383 Slice castBitArray()
const;
386 GasketComponent downcast(IntegerType t)
const {
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()),
398 builder->create<comb::ExtractOp>(loc(), t, signlessVal, 0);
399 return GasketComponent(*
builder, extracted).cast(t);
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);
408 return GasketComponent(*
builder, result).cast(t);
412 GasketComponent padTo(uint64_t finalBits)
const;
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; }
440 struct Slice :
public GasketComponent {
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");
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");
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");
463 Slice slice(int64_t lsb, int64_t
size)
const {
466 builder->getIntegerType(llvm::Log2_64_Ceil(type.getNumElements()));
467 Value lsbConst =
builder->create<hw::ConstantOp>(loc(), idxTy, lsb);
469 builder->create<hw::ArraySliceOp>(loc(), dstTy, s, lsbConst);
470 return Slice(
this, lsb, newSlice);
476 Slice slice(Value lsb, int64_t
size)
const {
477 assert(lsb.getType().isa<IntegerType>());
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.");
487 Value newSlice =
builder->create<hw::ArraySliceOp>(loc(), dstTy, s, lsb);
488 return Slice(
this, std::nullopt, newSlice);
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);
494 Slice castToSlice(Type elemTy,
size_t size, StringRef name = StringRef(),
495 Twine nameSuffix = Twine())
const {
497 GasketComponent rawCast =
498 GasketComponent::cast(arrTy).name(name + nameSuffix);
499 return Slice(*
builder, rawCast);
502 GasketComponent operator[](Value idx)
const {
503 return GasketComponent(*
builder,
504 builder->create<hw::ArrayGetOp>(loc(), s, idx));
507 GasketComponent operator[](
size_t idx)
const {
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));
516 const Slice &getRootSlice()
const {
517 if (parent ==
nullptr)
519 return parent->getRootSlice();
522 std::optional<int64_t> getOffsetFromRoot()
const {
523 if (parent ==
nullptr)
525 auto parentOffset = parent->getOffsetFromRoot();
526 if (!offsetIntoParent || !parentOffset)
528 return *offsetIntoParent + *parentOffset;
531 uint64_t
size()
const {
return type.getNumElements(); }
536 std::optional<int64_t> offsetIntoParent;
543 GasketComponent GasketBuilder::zero(uint64_t
width)
const {
544 return GasketComponent(*
builder,
545 builder->create<hw::ConstantOp>(
548 GasketComponent GasketBuilder::constant(uint64_t
width, uint64_t value)
const {
549 return GasketComponent(*
builder,
550 builder->create<hw::ConstantOp>(
554 Slice GasketBuilder::padding(uint64_t p)
const {
555 auto zero = GasketBuilder::zero(p);
556 return zero.castBitArray();
559 Slice GasketComponent::castBitArray()
const {
562 if (s.getType() == dstTy)
564 auto dst =
builder->create<hw::BitcastOp>(loc(), dstTy, s);
573 SmallVector<Value, 8> values;
575 values.push_back(gc.castBitArray());
579 std::reverse(values.begin(), values.end());
580 return GasketComponent(*
builder,
581 builder->create<hw::ArrayConcatOp>(loc, values));
587 class AssertBuilder :
public OpBuilder {
589 AssertBuilder(Location loc, Region &r) : OpBuilder(r), loc(loc) {}
591 void assertPred(GasketComponent veg, ICmpPredicate pred, int64_t expected) {
592 if (veg.getValue().getType().isa<IntegerType>()) {
593 assertPred(veg.getValue(), pred, expected);
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");
601 veg.cast(veg.b().getIntegerType(valTy.getNumElements())).getValue(),
605 void assertEqual(GasketComponent s, int64_t expected) {
606 assertPred(s, ICmpPredicate::eq, expected);
610 void assertPred(Value val, ICmpPredicate pred, int64_t expected) {
611 auto expectedVal = create<hw::ConstantOp>(loc, val.getType(), expected);
612 create<sv::AssertOp>(
614 create<comb::ICmpOp>(loc, getI1Type(), pred, val, expectedVal,
false),
633 class CapnpSegmentBuilder :
public GasketBuilder {
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() {}
641 GasketComponent build(::capnp::schema::Node::Struct::Reader cStruct,
642 ArrayRef<GasketComponent> mFieldValues);
647 GasketComponent encodeStructAt(uint64_t ptrLoc,
648 ::capnp::schema::Node::Struct::Reader cStruct,
649 ArrayRef<GasketComponent> mFieldValues);
652 GasketComponent compile()
const;
655 void encodeFieldAt(uint64_t offset, GasketComponent val,
656 ::capnp::schema::Type::Reader type);
658 uint64_t buildList(Slice val, ::capnp::schema::Type::Reader type);
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);
674 llvm::IntervalMap<uint64_t, GasketComponent>::Allocator allocator;
675 llvm::IntervalMap<uint64_t, GasketComponent> segmentValues;
678 uint64_t messageSize;
679 uint64_t alloc(
size_t bits) {
680 uint64_t ptr = messageSize;
686 uint64_t expectedSize;
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;
699 {constant(2, 1), constant(30, relativeOffset),
700 constant(3,
bitsEncoding(type.getList().getElementType())),
701 constant(29, arrTy.getNumElements())}));
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();
712 if ((m = listSize % 64) != 0)
713 listSize += (64 - m);
714 uint64_t listOffset = alloc(listSize);
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);
723 GasketComponent CapnpSegmentBuilder::encodeStructAt(
724 uint64_t ptrLoc, ::capnp::schema::Node::Struct::Reader cStruct,
725 ArrayRef<GasketComponent> mFieldValues) {
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) -
737 GasketComponent structPtr = {constant(2, 0),
738 constant(30, relativeStructDataOffsetWords),
739 constant(16, cStruct.getDataWordCount()),
740 constant(16, cStruct.getPointerCount())};
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 =
750 : structDataSectionOffset) +
751 field.getSlot().getOffset() *
bits(cFieldType);
752 encodeFieldAt(fieldOffset, mFieldValues[idx], cFieldType);
758 GasketComponent CapnpSegmentBuilder::compile()
const {
760 SmallVector<GasketComponent, 16> segmentValuesPlusPadding;
761 uint64_t lastStop = 0;
762 for (
auto it = segmentValues.begin(), e = segmentValues.end(); it != e;
764 auto value = it.value();
765 int64_t padBits = it.start() - lastStop;
766 assert(padBits >= 0 &&
"Overlap not allowed");
768 segmentValuesPlusPadding.push_back(padding(padBits));
769 segmentValuesPlusPadding.push_back(value.castBitArray());
772 lastStop = it.stop() + 1;
774 assert(expectedSize >= lastStop);
775 if (lastStop != expectedSize)
776 segmentValuesPlusPadding.push_back(padding(expectedSize - lastStop));
782 CapnpSegmentBuilder::build(::capnp::schema::Node::Struct::Reader cStruct,
783 ArrayRef<GasketComponent> mFieldValues) {
784 uint64_t rootPtrLoc = alloc(64);
786 auto rootPtr = encodeStructAt(rootPtrLoc, cStruct, mFieldValues);
787 insert(rootPtrLoc, rootPtr);
795 Location loc = operandVal.getDefiningOp()->getLoc();
796 ModuleOp topMod = operandVal.getDefiningOp()->getParentOfType<ModuleOp>();
797 OpBuilder b = OpBuilder::atBlockEnd(topMod.getBody());
799 SmallString<64> modName;
800 modName.append(
"encode");
802 SmallVector<hw::PortInfo, 4> ports;
806 ports.push_back(
hw::PortInfo{{b.getStringAttr(
"valid"), valid.getType(),
810 hw::PortInfo{{b.getStringAttr(
"unencodedInput"), operandVal.getType(),
814 ports.push_back(
hw::PortInfo{{b.getStringAttr(
"encoded"), modOutputType,
817 hw::HWModuleOp retMod = b.create<hw::HWModuleOp>(
818 operandVal.getLoc(), b.getStringAttr(modName), ports);
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));
828 auto st = rootProto.getStruct();
829 CapnpSegmentBuilder seg(b, loc,
size());
832 SmallVector<GasketComponent, 16> fieldValues;
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)));
840 fieldValues.push_back(GasketComponent(b, operand));
842 GasketComponent ret = seg.build(st, fieldValues);
844 innerBlock->getTerminator()->erase();
845 b.setInsertionPointToEnd(innerBlock);
846 b.create<hw::OutputOp>(loc, ValueRange{ret});
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());
867 auto loc = ptrSection.loc();
868 OpBuilder &b = ptrSection.b();
869 GasketBuilder gb(b, loc);
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");
882 asserts.assertEqual(ptrType, 1);
885 auto expectedElemSizeBits =
bits(capnpType.getList().getElementType());
886 unsigned expectedElemSizeField;
887 switch (expectedElemSizeBits) {
889 expectedElemSizeField = 0;
892 expectedElemSizeField = 1;
895 expectedElemSizeField = 2;
898 expectedElemSizeField = 3;
901 expectedElemSizeField = 4;
904 expectedElemSizeField = 5;
907 llvm_unreachable(
"bits() returned unexpected value");
909 asserts.assertEqual(elemSize, expectedElemSizeField);
913 asserts.assertPred(length, ICmpPredicate::ule, type.getNumElements());
917 auto msg = ptr.getRootSlice();
918 auto ptrOffset = ptr.getOffsetFromRoot();
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");
927 msg.slice(listOffset, type.getNumElements() * expectedElemSizeBits)
931 assert(type.getElementType().isa<IntegerType>() &&
932 "DecodeList() only works on arrays of ints currently");
934 b.getIntegerType(expectedElemSizeBits, IntegerType::Signless);
935 auto arrayOfElements =
936 listSlice.castToSlice(capnpElemTy, type.getNumElements());
937 if (arrayOfElements.getValue().getType() == type)
938 return arrayOfElements;
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);
948 auto array = b.create<hw::ArrayCreateOp>(loc, arrayValues);
949 return GasketComponent(b, array);
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()),
963 return slice.name(field.getName(),
"_bits").cast(type);
965 .Case([&](hw::ArrayType at) {
966 return decodeList(at, field, ptrSection, asserts);
968 esiValue.name(field.getName().cStr(),
"Value");
976 auto loc = operandVal.getDefiningOp()->getLoc();
977 auto topMod = operandVal.getDefiningOp()->getParentOfType<ModuleOp>();
978 OpBuilder b = OpBuilder::atBlockEnd(topMod.getBody());
980 SmallString<64> modName;
981 modName.append(
"decode");
983 SmallVector<hw::PortInfo, 4> ports;
987 ports.push_back(
hw::PortInfo{{b.getStringAttr(
"valid"), valid.getType(),
991 hw::PortInfo{{b.getStringAttr(
"encodedInput"), operandVal.getType(),
997 hw::HWModuleOp retMod = b.create<hw::HWModuleOp>(
998 operandVal.getLoc(), b.getStringAttr(modName), ports);
1000 Block *innerBlock = retMod.getBodyBlock();
1001 b.setInsertionPointToStart(innerBlock);
1002 clk = innerBlock->getArgument(0);
1003 valid = innerBlock->getArgument(1);
1004 operandVal = innerBlock->getArgument(2);
1007 auto i16 = b.getIntegerType(16);
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.");
1016 Slice operand(b, operandVal);
1017 operand.setLoc(loc);
1019 auto hwClk = b.create<seq::FromClockOp>(clk.getLoc(), clk).getResult();
1021 b.create<sv::AlwaysOp>(loc, sv::EventControl::AtPosEdge, hwClk);
1023 OpBuilder(alwaysAt.getBodyRegion()).create<sv::IfOp>(loc, valid);
1024 AssertBuilder asserts(loc, ifValid.getBodyRegion());
1028 auto ptr = operand.slice(0, 64).name(
"rootPointer");
1033 Slice assertPtr(ptr);
1034 auto typeAndOffset = assertPtr.slice(0, 32).name(
"typeAndOffset");
1036 asserts.assertEqual(typeAndOffset.slice(0, 2), 0);
1037 asserts.assertEqual(typeAndOffset.slice(2, 30), 0x3FFFFFFF);
1039 asserts.assertEqual(typeAndOffset, 0);
1043 auto dataSectionSize =
1044 assertPtr.slice(32, 16).cast(i16).name(
"dataSectionSize");
1045 asserts.assertEqual(dataSectionSize,
1046 rootProto.getStruct().getDataWordCount());
1050 auto ptrSectionSize =
1051 assertPtr.slice(48, 16).cast(i16).name(
"ptrSectionSize");
1052 asserts.assertEqual(ptrSectionSize, rootProto.getStruct().getPointerCount());
1055 auto st = rootProto.getStruct();
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");
1064 SmallVector<GasketComponent, 64> fieldValues;
1065 for (
auto field : st.getFields()) {
1066 uint16_t idx = field.getCodeOrder();
1068 "Capnp struct longer than fieldTypes.");
1070 dataSection, ptrSection, asserts));
1075 GasketComponent ret =
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>(
1087 innerBlock->getTerminator()->erase();
1088 b.setInsertionPointToEnd(innerBlock);
1089 auto outputOp = b.create<hw::OutputOp>(loc, ValueRange{ret.getValue()});
1090 alwaysAt->moveBefore(outputOp);
1107 s = std::make_shared<detail::CapnpTypeSchemaImpl>(*
this);
1112 return s->write(os);
1116 llvm::raw_ostream &os)
const {
1117 os << name() <<
" ";
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;
1130 encImplMod = encImplIT->second;
1133 SmallString<64> instName;
1134 instName.append(
"encode");
1135 instName.append(name());
1136 instName.append(
"Inst");
1138 builder.create<hw::InstanceOp>(rawData.getLoc(), encImplMod, instName,
1139 ArrayRef<Value>{clk, valid, rawData});
1140 return encodeInst.getResult(0);
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;
1152 decImplMod = decImplIT->second;
1155 SmallString<64> instName;
1156 instName.append(
"decode");
1157 instName.append(name());
1158 instName.append(
"Inst");
1160 builder.create<hw::InstanceOp>(capnpData.getLoc(), decImplMod, instName,
1161 ArrayRef<Value>{clk, valid, capnpData});
1162 return decodeInst.getResult(0);
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.
bool operator!=(const ResetDomain &a, const ResetDomain &b)
bool operator==(const ResetDomain &a, const ResetDomain &b)
static SmallVector< Value > concatValues(ValueRange a, ValueRange b)
Concatenate two value ranges into a larger range.
static GasketComponent decodeList(hw::ArrayType type, capnp::schema::Field::Reader field, Slice ptrSection, AssertBuilder &asserts)
Construct the proper operations to decode a capnp list.
static bool isPointerType(::capnp::schema::Type::Reader type)
Return true if 'type' is capnp pointer.
static size_t bits(::capnp::schema::Type::Reader type)
Return the number of bits used by a Capnp type.
static void emitCapnpType(Type type, llvm::raw_ostream &os)
Write a valid Capnp type.
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,...
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.
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'.
StringRef name() const
For now, the name is just the type serialized.
llvm::ArrayRef< FieldInfo > getFields() const
mlir::Type getType() const
Get the type back.
Generate and reason about a Cap'nProto schema for a particular MLIR type.
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.
static llvm::SmallDenseMap< Type, hw::HWModuleOp > encImplMods
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.
void writeMetadata(llvm::raw_ostream &os) const
Write out the name and ID in capnp schema format.
static llvm::SmallDenseMap< Type, hw::HWModuleOp > decImplMods
Cache of the decode/encode modules;.
size_t size() const
Size in bits of the capnp message.
mlir::LogicalResult write(llvm::raw_ostream &os) const
Write out the schema in its entirety.
std::shared_ptr< detail::CapnpTypeSchemaImpl > s
The implementation of this.
CapnpTypeSchema(mlir::Type)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
llvm::raw_ostream & emitCapnpID(llvm::raw_ostream &os, int64_t id)
Emit an ID in capnp format.
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.
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
Actual implementation of CapnpTypeSchema to keep all the details out of the header.
hw::HWModuleOp buildEncoder(Value clk, Value valid, Value)
Build an HW/SV dialect capnp encoder for this type.
::capnp::ParsedSchema getSchema() const
Write a valid capnp schema to memory, then parse it out of memory using the capnp library.
hw::HWModuleOp buildDecoder(Value clk, Value valid, Value)
Build an HW/SV dialect capnp decoder for this type.
CapnpTypeSchemaImpl(CapnpTypeSchema &base)
CapnpTypeSchemaImpl(const CapnpTypeSchemaImpl &)=delete
::capnp::StructSchema getCapnpTypeSchema() const
Find the schema corresponding to type and return it.
bool operator==(const CapnpTypeSchemaImpl &) const
mutable ::capnp::ParsedSchema rootSchema
LogicalResult write(llvm::raw_ostream &os) const
This function is essentially a placeholder which only supports ints.
mutable ::capnp::StructSchema typeSchema
::capnp::SchemaParser parser
This holds the name, type, direction of a module's ports.