22 #include "mlir/IR/Builders.h"
24 using namespace circt;
31 struct HWLegalizeModulesPass
32 :
public sv::HWLegalizeModulesBase<HWLegalizeModulesPass> {
33 void runOnOperation()
override;
36 void processPostOrder(Block &block);
37 bool tryLoweringPackedArrayOp(Operation &op);
38 Value lowerLookupToCasez(Operation &op, Value input, Value index,
40 SmallVector<Value> caseValues);
41 bool processUsers(Operation &op, Value
value, ArrayRef<Value> mapping);
42 std::optional<std::pair<uint64_t, unsigned>>
43 tryExtractIndexAndBitWidth(Value
value);
57 StringAttr lastParsedOptions;
61 bool HWLegalizeModulesPass::tryLoweringPackedArrayOp(Operation &op) {
62 return TypeSwitch<Operation *, bool>(&op)
63 .Case<hw::AggregateConstantOp>([&](hw::AggregateConstantOp constOp) {
67 for (
auto field : llvm::reverse(constOp.getFields())) {
68 if (
auto intAttr = dyn_cast<IntegerAttr>(field))
73 constOp.getLoc(), constOp.getType(), field.cast<ArrayAttr>()));
75 if (!processUsers(op, constOp.getResult(),
inputs))
83 SmallVector<std::pair<Value, uint64_t>> arrays;
84 for (
auto array : llvm::reverse(concatOp.getInputs())) {
85 auto ty = hw::type_cast<hw::ArrayType>(array.getType());
86 arrays.emplace_back(array, ty.getNumElements());
89 llvm::make_early_inc_range(concatOp.getResult().getUsers())) {
90 if (TypeSwitch<Operation *, bool>(user)
92 if (
auto indexAndBitWidth =
93 tryExtractIndexAndBitWidth(getOp.getIndex())) {
94 auto [indexValue, bitWidth] = *indexAndBitWidth;
96 for (const auto &[array, size] : arrays) {
97 if (indexValue >= size) {
101 OpBuilder builder(getOp);
102 getOp.getInputMutable().set(array);
103 getOp.getIndexMutable().set(
104 builder.createOrFold<hw::ConstantOp>(
105 getOp.getLoc(), APInt(bitWidth, indexValue)));
112 .Default([](
auto op) {
return false; }))
115 op.emitError(
"unsupported packed array expression");
124 SmallVector<Value>
inputs(llvm::reverse(createOp.getInputs()));
125 if (!processUsers(op, createOp.getResult(),
inputs))
133 auto index = getOp.getIndex();
134 if (
auto *definingOp = index.getDefiningOp())
135 if (isa<hw::ConstantOp>(definingOp))
139 auto ty = hw::type_cast<hw::ArrayType>(getOp.getInput().getType());
141 SmallVector<Value> caseValues;
142 for (
size_t i = 0, e = ty.getNumElements(); i < e; i++) {
143 auto loc = op.getLoc();
145 loc, APInt(llvm::Log2_64_Ceil(e), i));
148 caseValues.push_back(element);
152 auto theWire = lowerLookupToCasez(op, getOp.getInput(), index,
153 ty.getElementType(), caseValues);
156 builder.setInsertionPoint(getOp);
159 getOp.getResult().replaceAllUsesWith(readWire);
162 .Case<sv::ArrayIndexInOutOp>([&](sv::ArrayIndexInOutOp indexOp) {
164 auto index = indexOp.getIndex();
165 if (
auto *definingOp = index.getDefiningOp())
166 if (isa<hw::ConstantOp>(definingOp))
170 auto inout = indexOp.getInput().getType();
171 if (hw::type_isa<hw::UnpackedArrayType>(inout.getElementType()))
175 auto ty = hw::type_cast<hw::ArrayType>(inout.getElementType());
177 SmallVector<Value> caseValues;
178 for (
size_t i = 0, e = ty.getNumElements(); i < e; i++) {
179 auto loc = op.getLoc();
181 loc, APInt(llvm::Log2_64_Ceil(e), i));
182 auto element =
builder.create<sv::ArrayIndexInOutOp>(
183 loc, indexOp.getInput(), index);
185 caseValues.push_back(readElement);
189 auto theWire = lowerLookupToCasez(op, indexOp.getInput(), index,
190 ty.getElementType(), caseValues);
193 indexOp.getResult().replaceAllUsesWith(theWire);
196 .Case<sv::PAssignOp>([&](sv::PAssignOp assignOp) {
199 auto inout = assignOp.getDest().getType();
200 auto ty = hw::type_dyn_cast<hw::ArrayType>(inout.getElementType());
205 for (
size_t i = 0, e = ty.getNumElements(); i < e; i++) {
206 auto loc = op.getLoc();
208 loc, APInt(llvm::Log2_64_Ceil(e), i));
209 auto dstElement =
builder.create<sv::ArrayIndexInOutOp>(
210 loc, assignOp.getDest(), index);
213 builder.create<sv::PAssignOp>(loc, dstElement, srcElement);
221 auto ty = hw::type_dyn_cast<hw::ArrayType>(regOp.getElementType());
227 SmallVector<Value> elements;
228 for (
size_t i = 0, e = ty.getNumElements(); i < e; i++) {
229 auto loc = op.getLoc();
231 if (
auto nameAttr = regOp->getAttrOfType<StringAttr>(name)) {
235 elements.push_back(element);
239 if (!processUsers(op, regOp.getResult(), elements))
245 .Default([&](
auto op) {
return false; });
248 Value HWLegalizeModulesPass::lowerLookupToCasez(Operation &op, Value input,
251 SmallVector<Value> caseValues) {
256 builder.getStringAttr(
"casez_tmp"));
257 builder.setInsertionPoint(&op);
259 auto loc = input.getDefiningOp()->getLoc();
263 if (!op.getParentOp()->hasTrait<sv::ProceduralRegion>()) {
264 auto alwaysComb =
builder.create<sv::AlwaysCombOp>(loc);
265 builder.setInsertionPointToEnd(alwaysComb.getBodyBlock());
270 if (1ULL << index.getType().getIntOrFloatBitWidth() != caseValues.size()) {
271 caseValues.push_back(
builder.create<sv::ConstantXOp>(
272 op.getLoc(), op.getResult(0).getType()));
275 APInt caseValue(index.getType().getIntOrFloatBitWidth(), 0);
276 auto *context =
builder.getContext();
280 loc, CaseStmtType::CaseZStmt, index, caseValues.size(),
281 [&](
size_t caseIdx) -> std::unique_ptr<sv::CasePattern> {
286 bool isDefault = caseIdx == caseValues.size() - 1;
287 Value theValue = caseValues[caseIdx];
288 std::unique_ptr<sv::CasePattern> thePattern;
291 thePattern = std::make_unique<sv::CaseDefaultPattern>(context);
293 thePattern = std::make_unique<sv::CaseBitPattern>(caseValue, context);
295 builder.create<sv::BPAssignOp>(loc, theWire, theValue);
302 bool HWLegalizeModulesPass::processUsers(Operation &op, Value
value,
303 ArrayRef<Value> mapping) {
304 for (
auto *user : llvm::make_early_inc_range(
value.getUsers())) {
305 if (TypeSwitch<Operation *, bool>(user)
307 if (
auto indexAndBitWidth =
308 tryExtractIndexAndBitWidth(getOp.getIndex())) {
309 getOp.replaceAllUsesWith(mapping[indexAndBitWidth->first]);
315 .Case<sv::ArrayIndexInOutOp>([&](sv::ArrayIndexInOutOp indexOp) {
316 if (
auto indexAndBitWidth =
317 tryExtractIndexAndBitWidth(indexOp.getIndex())) {
318 indexOp.replaceAllUsesWith(mapping[indexAndBitWidth->first]);
324 .Default([](
auto op) {
return false; })) {
329 user->emitError(
"unsupported packed array expression");
337 std::optional<std::pair<uint64_t, unsigned>>
338 HWLegalizeModulesPass::tryExtractIndexAndBitWidth(Value
value) {
339 if (
auto constantOp = dyn_cast<hw::ConstantOp>(
value.getDefiningOp())) {
340 auto index = constantOp.getValue();
341 return std::make_optional(
342 std::make_pair(index.getZExtValue(), index.getBitWidth()));
347 void HWLegalizeModulesPass::processPostOrder(Block &body) {
352 Block::iterator it = std::prev(body.end());
353 while (it != body.end()) {
358 if (it == body.begin())
363 if (op.getNumRegions()) {
364 for (
auto ®ion : op.getRegions())
365 for (
auto ®ionBlock : region.getBlocks())
366 processPostOrder(regionBlock);
369 if (options.disallowPackedArrays) {
371 if (tryLoweringPackedArrayOp(op)) {
372 it = --Block::iterator(op);
374 anythingChanged =
true;
380 for (
auto value : op.getResults()) {
381 if (
value.getType().isa<hw::ArrayType>()) {
382 op.emitError(
"unsupported packed array expression");
390 void HWLegalizeModulesPass::runOnOperation() {
391 thisHWModule = getOperation();
394 auto optionsAttr = LoweringOptions::getAttributeFrom(
395 cast<ModuleOp>(thisHWModule->getParentOp()));
396 if (optionsAttr != lastParsedOptions) {
399 thisHWModule.emitError(error);
403 lastParsedOptions = optionsAttr;
408 anythingChanged =
false;
411 processPostOrder(*thisHWModule.getBodyBlock());
414 if (!anythingChanged)
415 markAllAnalysesPreserved();
419 return std::make_unique<HWLegalizeModulesPass>();
llvm::SmallVector< StringAttr > inputs
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
std::unique_ptr< mlir::Pass > createHWLegalizeModulesPass()
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
Options which control the emission from CIRCT to Verilog.