[VerilogQuality] Remove muxes feeding registers their old value (#965)

When a mux feeds an assign to a register and one branch of the mux is the register's old value, we can turn that into a conditional assign. This results in much cleaner verilog output.
This commit is contained in:
Andrew Lenharth 2021-04-27 10:43:52 -05:00 committed by GitHub
parent 72c81189c7
commit 2e11065461
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 101 additions and 2 deletions

View File

@ -366,6 +366,7 @@ def PAssignOp : SVOp<"passign", [InOutTypeConstraint<"src", "dest">,
These occur in initial, always, task, and function blocks. The statement
can be scheduled without blocking procedural flow. See SV Spec 10.4.2.
}];
let hasCanonicalizeMethod = true;
let arguments = (ins InOutType:$dest, InOutElementType:$src);
let results = (outs);
let assemblyFormat = "$dest `,` $src attr-dict `:` type($src)";

View File

@ -874,6 +874,64 @@ static LogicalResult verifyAliasOp(AliasOp op) {
return success();
}
//===----------------------------------------------------------------------===//
// PAssignOp
//===----------------------------------------------------------------------===//
// reg s <= cond ? val : s simplification.
// Don't assign a register's value to itself, conditionally assign the new value
// instead.
LogicalResult PAssignOp::canonicalize(PAssignOp op, PatternRewriter &rewriter) {
auto mux = op.src().getDefiningOp<comb::MuxOp>();
if (!mux)
return failure();
auto reg = dyn_cast<sv::RegOp>(op.dest().getDefiningOp());
if (!reg)
return failure();
bool trueBranch; // did we find the register on the true branch?
auto tvread = mux.trueValue().getDefiningOp<sv::ReadInOutOp>();
auto fvread = mux.falseValue().getDefiningOp<sv::ReadInOutOp>();
if (tvread && reg == tvread.input().getDefiningOp<sv::RegOp>())
trueBranch = true;
else if (fvread && reg == fvread.input().getDefiningOp<sv::RegOp>())
trueBranch = false;
else
return failure();
// Check that this is the only write of the register
for (auto &use : reg->getUses()) {
if (isa<ReadInOutOp>(use.getOwner()))
continue;
if (use.getOwner() == op)
continue;
return failure();
}
// Replace a non-blocking procedural assign in a procedural region with a
// conditional procedural assign. We've ensured that this is the only write
// of the register.
if (trueBranch) {
auto one = rewriter.create<rtl::ConstantOp>(mux.getLoc(),
mux.cond().getType(), -1);
Value ops[] = {mux.cond(), one};
auto cond = rewriter.createOrFold<comb::XorOp>(mux.getLoc(),
mux.cond().getType(), ops);
rewriter.create<sv::IfOp>(mux.getLoc(), cond, [&]() {
rewriter.create<PAssignOp>(op.getLoc(), reg, mux.falseValue());
});
} else {
rewriter.create<sv::IfOp>(mux.getLoc(), mux.cond(), [&]() {
rewriter.create<PAssignOp>(op.getLoc(), reg, mux.trueValue());
});
}
// Remove the wire.
rewriter.eraseOp(op);
return success();
}
//===----------------------------------------------------------------------===//
// TableGen generated logic.
//===----------------------------------------------------------------------===//

View File

@ -85,3 +85,43 @@ func @invert_if(%arg0: i1) {
}
return
}
// CHECK-LABEL: func @mux_to_cond_assign_f
// CHECK-NEXT: %r = sv.reg : !rtl.inout<i2>
// CHECK-NEXT: sv.alwaysff(posedge %arg0) {
// CHECK-NEXT: sv.if %arg1 {
// CHECK-NEXT: sv.passign %r, %arg2 : i2
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: return
// CHECK-NEXT: }
func @mux_to_cond_assign_f(%clock: i1, %c: i1, %data: i2) {
%r = sv.reg : !rtl.inout<i2>
%1 = sv.read_inout %r : !rtl.inout<i2>
%0 = comb.mux %c, %data, %1 : i2
sv.alwaysff(posedge %clock) {
sv.passign %r, %0 : i2
}
return
}
// CHECK-LABEL: func @mux_to_cond_assign_t
// CHECK-NEXT: %true = rtl.constant true
// CHECK-NEXT: %r = sv.reg : !rtl.inout<i2>
// CHECK-NEXT: sv.alwaysff(posedge %arg0) {
// CHECK-NEXT: %0 = comb.xor %arg1, %true : i1
// CHECK-NEXT: sv.if %0 {
// CHECK-NEXT: sv.passign %r, %arg2 : i2
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: return
// CHECK-NEXT: }
func @mux_to_cond_assign_t(%clock: i1, %c: i1, %data: i2) {
%r = sv.reg : !rtl.inout<i2>
%1 = sv.read_inout %r : !rtl.inout<i2>
%0 = comb.mux %c, %1, %data : i2
sv.alwaysff(posedge %clock) {
sv.passign %r, %0 : i2
}
return
}

View File

@ -36,8 +36,8 @@ yosys -q -p "
opt -purge
equiv_make top1 top2 equiv
hierarchy -top equiv
equiv_simple
equiv_induct
equiv_simple -undef
equiv_induct -undef
equiv_status -assert
"
if [ $? -eq 0 ]