17#include "mlir/IR/Attributes.h"
18#include "mlir/IR/DialectImplementation.h"
19#include "llvm/ADT/ArrayRef.h"
20#include "llvm/ADT/DenseMap.h"
21#include "llvm/ADT/TypeSwitch.h"
26AnyType AnyType::get(MLIRContext *
context) {
return Base::get(
context); }
29WindowFieldType::verify(llvm::function_ref<InFlightDiagnostic()> emitError,
30 StringAttr fieldName, uint64_t numItems,
31 uint64_t bulkCountWidth) {
32 if (numItems > 0 && bulkCountWidth > 0)
33 return emitError() <<
"cannot specify both numItems and countWidth for "
35 << fieldName.getValue() <<
"'";
41 case ChannelSignaling::ValidReady:
43 case ChannelSignaling::FIFO:
45 case ChannelSignaling::ValidOnly:
48 llvm_unreachable(
"Unhandled ChannelSignaling");
50std::optional<int64_t> ChannelType::getBitWidth()
const {
60 return llvm::make_filter_range(chan.getUses(), [](
auto &use) {
61 return !isa<SnoopValidReadyOp, SnoopTransactionOp>(use.getOwner());
64SmallVector<std::reference_wrapper<OpOperand>, 4>
65ChannelType::getConsumers(mlir::TypedValue<ChannelType> chan) {
66 return SmallVector<std::reference_wrapper<OpOperand>, 4>(
69bool ChannelType::hasOneConsumer(mlir::TypedValue<ChannelType> chan) {
71 if (consumers.empty())
73 return ++consumers.begin() == consumers.end();
75bool ChannelType::hasNoConsumers(mlir::TypedValue<ChannelType> chan) {
78OpOperand *ChannelType::getSingleConsumer(mlir::TypedValue<ChannelType> chan) {
80 auto iter = consumers.begin();
81 if (iter == consumers.end())
83 OpOperand *result = &*iter;
84 if (++iter != consumers.end())
88LogicalResult ChannelType::verifyChannel(mlir::TypedValue<ChannelType> chan) {
90 if (consumers.empty() || ++consumers.begin() == consumers.end())
92 auto err = chan.getDefiningOp()->emitOpError(
93 "channels must have at most one consumer");
94 for (
auto &consumer : consumers)
95 err.attachNote(consumer.getOwner()->
getLoc()) <<
"channel used here";
99std::optional<int64_t> WindowType::getBitWidth()
const {
100 return hw::getBitWidth(getLoweredType());
104WindowType::verify(llvm::function_ref<InFlightDiagnostic()> emitError,
105 StringAttr name, Type into,
106 ArrayRef<WindowFrameType> frames) {
107 auto structInto = hw::type_dyn_cast<hw::StructType>(into);
109 return emitError() <<
"only windows into structs are currently supported";
113 for (hw::StructType::FieldInfo field : structInto.getElements())
114 fieldTypes[field.name] = field.type;
117 DenseSet<StringAttr> consumedFields;
120 DenseSet<StringAttr> bulkTransferFields;
122 for (
auto frame : frames) {
123 bool encounteredArrayOrListWithNumItems =
false;
125 for (WindowFieldType field : frame.getMembers()) {
126 auto fieldTypeIter = fieldTypes.find(field.getFieldName());
127 if (fieldTypeIter == fieldTypes.end())
128 return emitError() <<
"invalid field name: " << field.getFieldName();
130 Type fieldType = fieldTypeIter->getSecond();
136 bool isBulkTransferHeader = field.getBulkCountWidth() > 0;
137 bool isBulkTransferData =
138 bulkTransferFields.contains(field.getFieldName());
140 if (consumedFields.contains(field.getFieldName())) {
143 return emitError() <<
"field '" << field.getFieldName()
144 <<
"' already consumed by a previous frame";
149 bool isArrayOrListWithNumItems =
150 hw::type_isa<hw::ArrayType, esi::ListType>(fieldType) &&
151 field.getNumItems() > 0;
152 if (isArrayOrListWithNumItems) {
153 if (encounteredArrayOrListWithNumItems)
155 <<
"cannot have two array or list fields with num items (in "
156 << field.getFieldName() <<
")";
157 encounteredArrayOrListWithNumItems =
true;
161 uint64_t numItems = field.getNumItems();
163 if (
auto arrField = hw::type_dyn_cast<hw::ArrayType>(fieldType)) {
164 if (numItems > arrField.getNumElements())
166 <<
"num items is larger than array size in field "
167 << field.getFieldName();
168 }
else if (!hw::type_isa<esi::ListType>(fieldType)) {
169 return emitError() <<
"specification of num items only allowed on "
170 "array or list fields (in "
171 << field.getFieldName() <<
")";
177 uint64_t bulkCountWidth = field.getBulkCountWidth();
178 if (bulkCountWidth > 0) {
179 if (!hw::type_isa<esi::ListType>(fieldType))
180 return emitError() <<
"bulk transfer (countWidth) only allowed on "
182 << field.getFieldName() <<
")";
185 if (bulkTransferFields.contains(field.getFieldName()))
186 return emitError() <<
"field '" << field.getFieldName()
187 <<
"' already has countWidth specified";
189 bulkTransferFields.insert(field.getFieldName());
193 if (isBulkTransferData) {
196 consumedFields.insert(field.getFieldName());
197 }
else if (!isBulkTransferHeader) {
199 consumedFields.insert(field.getFieldName());
206Type WindowType::getLoweredType()
const {
208 auto into = hw::type_cast<hw::StructType>(getInto());
210 for (hw::StructType::FieldInfo field : into.getElements())
211 intoFields[field.name] = field.type;
213 auto getInnerTypeOrSelf = [&](Type t) {
214 return TypeSwitch<Type, Type>(t)
215 .Case<hw::ArrayType>(
216 [](hw::ArrayType
arr) {
return arr.getElementType(); })
217 .Case<esi::ListType>(
219 .Default([&](Type t) {
return t; });
224 auto wrapInTypeAliasIfNeeded = [&](Type loweredType) -> Type {
225 if (
auto intoAlias = dyn_cast<hw::TypeAliasType>(getInto())) {
226 auto intoRef = intoAlias.getRef();
227 std::string aliasName = (Twine(intoRef.getLeafReference().getValue()) +
230 auto newRef = SymbolRefAttr::get(
231 intoRef.getRootReference(),
232 {FlatSymbolRefAttr::get(StringAttr::get(getContext(), aliasName))});
233 return hw::TypeAliasType::get(newRef, loweredType);
240 DenseSet<StringAttr> bulkTransferFields;
241 for (WindowFrameType frame : getFrames()) {
242 for (WindowFieldType field : frame.getMembers()) {
243 if (field.getBulkCountWidth() > 0)
244 bulkTransferFields.insert(field.getFieldName());
249 SmallVector<hw::UnionType::FieldInfo, 4> unionFields;
250 for (WindowFrameType frame : getFrames()) {
253 SmallVector<hw::StructType::FieldInfo, 4> fields;
254 SmallVector<hw::StructType::FieldInfo, 4> leftOverFields;
255 bool hasLeftOver =
false;
256 StringAttr leftOverName;
258 for (WindowFieldType field : frame.getMembers()) {
259 auto fieldTypeIter = intoFields.find(field.getFieldName());
260 assert(fieldTypeIter != intoFields.end());
261 auto fieldType = fieldTypeIter->getSecond();
264 uint64_t bulkCountWidth = field.getBulkCountWidth();
265 if (bulkCountWidth > 0) {
270 {StringAttr::get(getContext(),
271 Twine(field.getFieldName().getValue()) +
"_count"),
272 IntegerType::get(getContext(), bulkCountWidth)});
279 bool isBulkTransferData =
280 bulkTransferFields.contains(field.getFieldName());
283 if (field.getNumItems() == 0) {
286 auto type = getInnerTypeOrSelf(fieldType);
287 fields.push_back({field.getFieldName(), type});
288 leftOverFields.push_back({field.getFieldName(), type});
290 if (hw::type_isa<esi::ListType>(fieldType) && !isBulkTransferData) {
293 auto lastType = IntegerType::get(getContext(), 1);
294 auto lastField = StringAttr::get(getContext(),
"last");
295 fields.push_back({lastField, lastType});
296 leftOverFields.push_back({lastField, lastType});
300 hw::type_dyn_cast<hw::ArrayType>(fieldTypeIter->getSecond())) {
303 {field.getFieldName(), hw::ArrayType::get(array.getElementType(),
304 field.getNumItems())});
308 size_t leftOver = array.getNumElements() % field.getNumItems();
314 leftOverFields.push_back(
315 {field.getFieldName(),
316 hw::ArrayType::get(array.getElementType(), leftOver)});
318 leftOverName = StringAttr::get(
319 getContext(), Twine(frame.getName().getValue(),
"_leftOver"));
321 }
else if (
auto list = hw::type_cast<esi::ListType>(
322 fieldTypeIter->getSecond())) {
325 {field.getFieldName(),
328 if (!isBulkTransferData) {
334 Twine(field.getFieldName().getValue(),
"_size")),
335 IntegerType::get(getContext(),
336 llvm::Log2_64_Ceil(field.getNumItems()))});
338 fields.push_back({StringAttr::get(getContext(),
"last"),
339 IntegerType::get(getContext(), 1)});
342 llvm_unreachable(
"numItems specified on non-array/list field");
349 if (getFrames().size() == 1 && frame.getName().getValue().empty() &&
351 auto loweredStruct = hw::StructType::get(getContext(), fields);
352 return wrapInTypeAliasIfNeeded(loweredStruct);
356 unionFields.push_back(
357 {frame.getName(), hw::StructType::get(getContext(), fields), 0});
360 unionFields.push_back(
361 {leftOverName, hw::StructType::get(getContext(), leftOverFields), 0});
364 auto unionType = hw::UnionType::get(getContext(), unionFields);
365 return wrapInTypeAliasIfNeeded(unionType);
371 static FailureOr<::BundledChannel>
parse(AsmParser &p) {
374 if (p.parseType(type))
376 auto dir = FieldParser<::ChannelDirection>::parse(p);
379 if (p.parseKeywordOrString(&name))
381 return BundledChannel{StringAttr::get(p.getContext(), name), *dir, type};
387inline ::llvm::raw_ostream &
operator<<(::llvm::raw_ostream &p,
394ChannelBundleType ChannelBundleType::getReversed()
const {
395 SmallVector<BundledChannel, 4> reversed;
396 for (
auto channel : getChannels())
397 reversed.push_back({channel.name,
flip(channel.direction), channel.type});
398 return ChannelBundleType::get(getContext(), reversed, getResettable());
401std::optional<int64_t> ChannelBundleType::getBitWidth()
const {
402 int64_t totalWidth = 0;
403 for (
auto channel : getChannels()) {
404 std::optional<int64_t> channelWidth = channel.type.getBitWidth();
407 totalWidth += *channelWidth;
412#define GET_TYPEDEF_CLASSES
413#include "circt/Dialect/ESI/ESITypes.cpp.inc"
415void ESIDialect::registerTypes() {
417#define GET_TYPEDEF_LIST
418#include "circt/Dialect/ESI/ESITypes.cpp.inc"
423 circt::esi::ChannelType chan =
424 dyn_cast_or_null<circt::esi::ChannelType>(type);
426 type = chan.getInner();
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static unsigned getSignalingBitWidth(ChannelSignaling signaling)
static auto getChannelConsumers(mlir::TypedValue< ChannelType > chan)
Get the list of users with snoops filtered out.
static Location getLoc(DefSlot slot)
Lists represent variable-length sequences of elements of a single type.
const Type * getElementType() const
mlir::Type innerType(mlir::Type type)
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
ModulePort::Direction flip(ModulePort::Direction direction)
Flip a port direction.
int64_t getBitWidth(mlir::Type type)
Return the hardware bit width of a type.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
inline ::llvm::raw_ostream & operator<<(::llvm::raw_ostream &p, ::BundledChannel channel)
ChannelDirection direction
static FailureOr<::BundledChannel > parse(AsmParser &p)