[Calyx] Verify continuously assigned values (#3048)

The verification ensures that:
- At most one continuous assignment exists for any given value
- A continuously assigned wire has no assignments inside groups.
This commit is contained in:
Morten Borup Petersen 2022-05-06 10:45:13 +02:00 committed by GitHub
parent 30dad68da3
commit 4b29e3af87
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 79 additions and 2 deletions

View File

@ -739,6 +739,30 @@ LogicalResult WiresOp::verify() {
return op.emitOpError() << "with name: " << groupName
<< " is unused in the control execution schedule";
}
// Verify that:
// - At most one continuous assignment exists for any given value
// - A continuously assigned wire has no assignments inside groups.
for (auto thisAssignment : getBody()->getOps<AssignOp>()) {
// Always assume guarded assignments will not be driven simultaneously. We
// liberally assume that guards are mutually exclusive (more elaborate
// static and dynamic checking can be performed to validate such cases).
if (thisAssignment.guard())
continue;
Value dest = thisAssignment.dest();
for (Operation *user : dest.getUsers()) {
auto assignUser = dyn_cast<AssignOp>(user);
if (!assignUser || assignUser.dest() != dest ||
assignUser == thisAssignment)
continue;
return user->emitOpError() << "destination is already continuously "
"driven. Other assignment is "
<< thisAssignment;
}
}
return success();
}

View File

@ -821,3 +821,46 @@ calyx.program "main" {
calyx.control {}
}
}
// -----
calyx.program "main" {
calyx.component @main(%go: i1 {go}, %clk: i1 {clk}, %reset: i1 {reset}) -> (%done: i1 {done}) {
%std_lt_0.left, %std_lt_0.right, %std_lt_0.out = calyx.std_lt @std_lt_0 : i32, i32, i1
%c64_i32 = hw.constant 64 : i32
%c42_i32 = hw.constant 42 : i32
calyx.wires {
calyx.assign %std_lt_0.left = %c64_i32 : i32
// expected-error @+1 {{'calyx.assign' op destination is already continuously driven. Other assignment is "calyx.assign"(%0#0, %1) : (i32, i32) -> ()}}
calyx.assign %std_lt_0.left = %c42_i32 : i32
}
calyx.control {
calyx.seq { }
}
}
}
// -----
calyx.program "main" {
calyx.component @main(%go: i1 {go}, %clk: i1 {clk}, %reset: i1 {reset}) -> (%done: i1 {done}) {
%std_lt_0.left, %std_lt_0.right, %std_lt_0.out = calyx.std_lt @std_lt_0 : i32, i32, i1
%c64_i32 = hw.constant 64 : i32
%c42_i32 = hw.constant 42 : i32
%r.in, %r.write_en, %r.clk, %r.reset, %r.out, %r.done = calyx.register @r : i1, i1, i1, i1, i1, i1
calyx.wires {
calyx.assign %std_lt_0.left = %c64_i32 : i32
calyx.group @A {
// expected-error @+1 {{'calyx.assign' op destination is already continuously driven. Other assignment is "calyx.assign"(%0#0, %1) : (i32, i32) -> ()}}
calyx.assign %std_lt_0.left = %c42_i32 : i32
calyx.assign %std_lt_0.right = %c42_i32 : i32
calyx.group_done %r.done : i1
}
}
calyx.control {
calyx.seq {
calyx.enable @A
}
}
}
}

View File

@ -19,6 +19,7 @@ calyx.program "main" {
// CHECK-LABEL: calyx.component @main(%clk: i1 {clk}, %go: i1 {go}, %reset: i1 {reset}) -> (%done: i1 {done}) {
calyx.component @main(%clk: i1 {clk}, %go: i1 {go}, %reset: i1 {reset}) -> (%done: i1 {done}) {
// CHECK: %r.in, %r.write_en, %r.clk, %r.reset, %r.out, %r.done = calyx.register @r : i8, i1, i1, i1, i8, i1
// CHECK-NEXT: %r2.in, %r2.write_en, %r2.clk, %r2.reset, %r2.out, %r2.done = calyx.register @r2 : i1, i1, i1, i1, i1, i1
// CHECK-NEXT: %mu.clk, %mu.reset, %mu.go, %mu.left, %mu.right, %mu.out, %mu.done = calyx.std_mult_pipe @mu : i1, i1, i1, i32, i32, i32, i1
// CHECK-NEXT: %du.clk, %du.reset, %du.go, %du.left, %du.right, %du.out_quotient, %du.out_remainder, %du.done = calyx.std_div_pipe @du : i1, i1, i1, i32, i32, i32, i32, i1
// CHECK-NEXT: %m.addr0, %m.addr1, %m.write_data, %m.write_en, %m.clk, %m.read_data, %m.done = calyx.memory @m <[64, 64] x 8> [6, 6] : i6, i6, i8, i1, i1, i8, i1
@ -29,9 +30,10 @@ calyx.program "main" {
// CHECK-NEXT: %gt.left, %gt.right, %gt.out = calyx.std_gt @gt : i8, i8, i1
// CHECK-NEXT: %pad.in, %pad.out = calyx.std_pad @pad : i8, i9
// CHECK-NEXT: %slice.in, %slice.out = calyx.std_slice @slice : i8, i7
// CHECK-NEXT: %not.in, %not.out = calyx.std_not @not : i8, i8
// CHECK-NEXT: %not.in, %not.out = calyx.std_not @not : i1, i1
// CHECK-NEXT: %wire.in, %wire.out = calyx.std_wire @wire : i8, i8
%r.in, %r.write_en, %r.clk, %r.reset, %r.out, %r.done = calyx.register @r : i8, i1, i1, i1, i8, i1
%r2.in, %r2.write_en, %r2.clk, %r2.reset, %r2.out, %r2.done = calyx.register @r2 : i1, i1, i1, i1, i1, i1
%mu.clk, %mu.reset, %mu.go, %mu.lhs, %mu.rhs, %mu.out, %mu.done = calyx.std_mult_pipe @mu : i1, i1, i1, i32, i32, i32, i1
%du.clk, %du.reset, %du.go, %du.left, %du.right, %du.out_quotient, %du.out_remainder, %du.done = calyx.std_div_pipe @du : i1, i1, i1, i32, i32, i32, i32, i1
%m.addr0, %m.addr1, %m.write_data, %m.write_en, %m.clk, %m.read_data, %m.done = calyx.memory @m <[64, 64] x 8> [6, 6] : i6, i6, i8, i1, i1, i8, i1
@ -42,13 +44,21 @@ calyx.program "main" {
%gt.left, %gt.right, %gt.out = calyx.std_gt @gt : i8, i8, i1
%pad.in, %pad.out = calyx.std_pad @pad : i8, i9
%slice.in, %slice.out = calyx.std_slice @slice : i8, i7
%not.in, %not.out = calyx.std_not @not : i8, i8
%not.in, %not.out = calyx.std_not @not : i1, i1
%wire.in, %wire.out = calyx.std_wire @wire : i8, i8
%c1_i1 = hw.constant 1 : i1
%c0_i1 = hw.constant 0 : i1
%c0_i6 = hw.constant 0 : i6
%c0_i8 = hw.constant 0 : i8
calyx.wires {
// CHECK: calyx.assign %not.in = %r2.out : i1
// CHECK-NEXT: calyx.assign %gt.left = %r2.out ? %adder.out : i8
// CHECK-NEXT: calyx.assign %gt.left = %not.out ? %adder.out : i8
calyx.assign %not.in = %r2.out : i1
calyx.assign %gt.left = %r2.out ? %adder.out : i8
calyx.assign %gt.left = %not.out ? %adder.out : i8
// CHECK: calyx.group @Group1 {
calyx.group @Group1 {
// CHECK: calyx.assign %c1.in = %c0.out : i8