mirror of https://github.com/llvm/circt.git
346 lines
12 KiB
MLIR
346 lines
12 KiB
MLIR
// RUN: circt-opt %s --pass-pipeline='builtin.module(any(prepare-for-emission))' --split-input-file -verify-diagnostics | FileCheck %s
|
|
// RUN: circt-opt %s --lower-verif-to-sv -export-verilog -split-input-file
|
|
|
|
// CHECK: @namehint_variadic
|
|
hw.module @namehint_variadic(in %a: i3, out b: i3) {
|
|
// CHECK-NEXT: %0 = comb.add %a, %a : i3
|
|
// CHECK-NEXT: %1 = comb.add %a, %0 {sv.namehint = "bar"} : i3
|
|
// CHECK-NEXT: hw.output %1
|
|
%0 = comb.add %a, %a, %a { sv.namehint = "bar" } : i3
|
|
hw.output %0 : i3
|
|
}
|
|
|
|
// CHECK-LABEL: hw.module @outOfOrderInoutOperations
|
|
hw.module @outOfOrderInoutOperations(in %a: i4, out c: i4) {
|
|
// CHECK: %wire = sv.wire
|
|
// CHECK-NEXT: %0 = sv.array_index_inout %wire[%false]
|
|
// CHECK-NEXT: %1 = sv.array_index_inout %0[%false]
|
|
// CHECK-NEXT: %2 = sv.array_index_inout %1[%false]
|
|
// CHECK-NEXT: %3 = sv.read_inout %2
|
|
%false = hw.constant false
|
|
%0 = sv.read_inout %3 : !hw.inout<i4>
|
|
%3 = sv.array_index_inout %2[%false] : !hw.inout<array<1xi4>>, i1
|
|
%2 = sv.array_index_inout %1[%false] : !hw.inout<array<1xarray<1xi4>>>, i1
|
|
%1 = sv.array_index_inout %wire[%false] : !hw.inout<array<1xarray<1xarray<1xi4>>>>, i1
|
|
%wire = sv.wire : !hw.inout<array<1xarray<1xarray<1xi4>>>>
|
|
hw.output %0: i4
|
|
}
|
|
|
|
// CHECK-LABEL: @twoState_variadic
|
|
hw.module @twoState_variadic(in %a: i1, in %b: i1, in %c: i1, out d:i1){
|
|
// CHECK: %0 = comb.or bin %b, %c : i1
|
|
// CHECK-NEXT: %1 = comb.or bin %a, %0 : i1
|
|
// CHECK-NEXT: hw.output %1 : i1
|
|
%0 = comb.or bin %a, %b, %c: i1
|
|
hw.output %0: i1
|
|
}
|
|
|
|
// CHECK-LABEL: @carryOverWireAttrs
|
|
hw.module @carryOverWireAttrs(in %a: i1, out b: i1){
|
|
// CHECK-NEXT: %foo = sv.wire {magic, sv.attributes = []} : !hw.inout<i1>
|
|
// CHECK-NEXT: sv.assign %foo, %a
|
|
// CHECK-NEXT: [[TMP:%.+]] = sv.read_inout %foo
|
|
// CHECK-NEXT: hw.output [[TMP]] : i1
|
|
%foo = hw.wire %a {magic, sv.attributes = []} : i1
|
|
hw.output %foo : i1
|
|
}
|
|
|
|
// -----
|
|
|
|
module {
|
|
// CHECK-LABEL: hw.module @SpillTemporaryInProceduralRegion
|
|
hw.module @SpillTemporaryInProceduralRegion(in %a: i4, in %b: i4, in %fd: i32) {
|
|
// CHECK-NEXT: %r = sv.reg
|
|
// CHECK-NEXT: sv.initial {
|
|
// CHECK-NEXT: %0 = sv.logic
|
|
// CHECK-NEXT: %1 = comb.add %a, %b
|
|
// CHECK-NEXT: sv.bpassign %0, %1
|
|
// CHECK-NEXT: %2 = sv.read_inout %0
|
|
// CHECK-NEXT: %3 = comb.extract %2 from 3
|
|
// CHECK-NEXT: sv.passign %r, %3
|
|
// CHECK-NEXT: }
|
|
// CHECK-NEXT: hw.output
|
|
%r = sv.reg : !hw.inout<i1>
|
|
sv.initial {
|
|
%0 = comb.add %a, %b : i4
|
|
%1 = comb.extract %0 from 3 : (i4) -> i1
|
|
sv.passign %r, %1: i1
|
|
}
|
|
}
|
|
}
|
|
|
|
// -----
|
|
|
|
module attributes {circt.loweringOptions = "disallowLocalVariables"} {
|
|
// CHECK: @test_hoist
|
|
hw.module @test_hoist(in %a: i3) {
|
|
// CHECK-NEXT: %reg = sv.reg
|
|
%reg = sv.reg : !hw.inout<i3>
|
|
// CHECK-NEXT: %0 = comb.add
|
|
// CHECK-NEXT: sv.initial
|
|
sv.initial {
|
|
%0 = comb.add %a, %a : i3
|
|
sv.passign %reg, %0 : i3
|
|
}
|
|
}
|
|
|
|
// CHECK-LABEL: hw.module @SpillTemporary
|
|
hw.module @SpillTemporary(in %a: i4, in %b: i4, out c: i1) {
|
|
// CHECK-NEXT: %0 = comb.add %a, %b
|
|
// CHECK-NEXT: %[[GEN:.+]] = sv.wire
|
|
// CHECK-NEXT: sv.assign %1, %[[GEN:.+]]
|
|
// CHECK-NEXT: %2 = sv.read_inout %1
|
|
// CHECK-NEXT: %3 = comb.extract %2 from 3
|
|
// CHECK-NEXT: hw.output %3
|
|
%0 = comb.add %a, %b : i4
|
|
%1 = comb.extract %0 from 3 : (i4) -> i1
|
|
hw.output %1 : i1
|
|
}
|
|
|
|
// CHECK-LABEL: hw.module @SpillTemporaryInProceduralRegion
|
|
hw.module @SpillTemporaryInProceduralRegion(in %a: i4, in %b: i4, in %fd: i32) {
|
|
// CHECK-NEXT: %r = sv.reg
|
|
// CHECK-NEXT: %[[VAL:.+]] = comb.add %a, %b
|
|
// CHECK-NEXT: %[[GEN:.+]] = sv.wire
|
|
// CHECK-NEXT: sv.assign %[[GEN]], %[[VAL]]
|
|
// CHECK-NEXT: %2 = sv.read_inout %[[GEN]]
|
|
// CHECK-NEXT: %3 = comb.extract %2 from 3
|
|
// CHECK-NEXT: sv.initial {
|
|
// CHECK-NEXT: sv.passign %r, %3
|
|
// CHECK-NEXT: }
|
|
// CHECK-NEXT: hw.output
|
|
%r = sv.reg : !hw.inout<i1>
|
|
sv.initial {
|
|
%0 = comb.add %a, %b : i4
|
|
%1 = comb.extract %0 from 3 : (i4) -> i1
|
|
sv.passign %r, %1: i1
|
|
}
|
|
}
|
|
|
|
// CHECK-LABEL: @SpillTemporaryWireForMultipleUseExpression
|
|
hw.module @SpillTemporaryWireForMultipleUseExpression(in %a: i4, in %b: i4, out c: i4, out d: i4) {
|
|
// CHECK-NEXT: %[[VAL:.+]] = comb.add %a, %b
|
|
// CHECK-NEXT: %[[GEN:bar]] = sv.wire
|
|
// CHECK-NEXT: sv.assign %[[GEN]], %[[VAL]]
|
|
// CHECK-NEXT: %1 = sv.read_inout %[[GEN]]
|
|
// CHECK-NEXT: %2 = sv.read_inout %[[GEN]]
|
|
// CHECK-NEXT: hw.output %2, %1
|
|
%0 = comb.add %a, %b {sv.namehint = "bar"}: i4
|
|
hw.output %0, %0 : i4, i4
|
|
}
|
|
}
|
|
|
|
// -----
|
|
|
|
module attributes {circt.loweringOptions = "disallowExpressionInliningInPorts"} {
|
|
hw.module.extern @MyExtModule(in %in: i8)
|
|
// CHECK-LABEL: @MoveInstances
|
|
hw.module @MoveInstances(in %a_in: i8){
|
|
// CHECK-NEXT: %_xyz3_in = sv.wire
|
|
// CHECK-NEXT: %0 = comb.add %a_in, %a_in
|
|
// CHECK-NEXT: %1 = sv.read_inout %_xyz3_in
|
|
// CHECK-NEXT: sv.assign %_xyz3_in, %0
|
|
// CHECK-NEXT: hw.instance "xyz3" @MyExtModule(in: %1: i8) -> ()
|
|
%0 = comb.add %a_in, %a_in : i8
|
|
hw.instance "xyz3" @MyExtModule(in: %0: i8) -> ()
|
|
}
|
|
}
|
|
|
|
// -----
|
|
|
|
module attributes {circt.loweringOptions = "disallowExpressionInliningInPorts"} {
|
|
// CHECK-LABEL: @ClockExpr(
|
|
hw.module @ClockExpr(in %clk: i1, in %a: i1, in %b: i1) {
|
|
%clk_xor_b = comb.xor %clk, %b : i1
|
|
|
|
// CHECK: %[[XOR:.+]] = comb.xor
|
|
// CHECK: %[[WIRE:.+]] = sv.wire
|
|
// CHECK: sv.assign %[[WIRE]], %[[XOR]]
|
|
// CHECK: %[[READ:.+]] = sv.read_inout %[[WIRE]]
|
|
// CHECK: ltl.clock %{{.+}} posedge %[[READ]]
|
|
%i0 = ltl.implication %a, %b : i1, i1
|
|
%k0 = ltl.clock %i0, posedge %clk_xor_b : !ltl.property
|
|
|
|
verif.assert %k0 : !ltl.property
|
|
}
|
|
}
|
|
|
|
// -----
|
|
module attributes {circt.loweringOptions =
|
|
"wireSpillingHeuristic=spillLargeTermsWithNamehints,wireSpillingNamehintTermLimit=3"} {
|
|
// CHECK-LABEL: namehints
|
|
hw.module @namehints(in %a: i8, out b: i8) {
|
|
// "foo" should be spilled because it has a meaningful name.
|
|
// CHECK: %foo = sv.wire
|
|
%0 = comb.add %a, %a {sv.namehint = "foo" } : i8
|
|
// "_foo" should not be spilled because it has a name which starts with "_" and the term size is 2.
|
|
// CHECK-NOT: %_foo = sv.wire
|
|
%3 = comb.add %a, %a {sv.namehint = "_foo" } : i8
|
|
// "_bar" should be spilled because the term size is 3.
|
|
// CHECK: %_bar = sv.wire
|
|
%1 = comb.add %a, %a, %a {sv.namehint = "_bar" } : i8
|
|
%2 = comb.add %0, %1, %3 : i8
|
|
hw.output %2 : i8
|
|
}
|
|
}
|
|
|
|
// -----
|
|
module attributes {circt.loweringOptions =
|
|
"disallowMuxInlining"} {
|
|
// CHECK-LABEL: mux
|
|
hw.module @mux(in %c: i1, in %b: i8, in %a: i8, out d: i8, out e: i8) {
|
|
// CHECK: %use_for_mux = sv.wire
|
|
// CHECK-NEXT: sv.assign %use_for_mux, %0 : i8
|
|
// CHECK-NEXT: %[[read:.+]] = sv.read_inout %use_for_mux : !hw.inout<i8>
|
|
// CHECK-NEXT: %[[add:.+]] = comb.add %[[read]], %a : i8
|
|
%0 = comb.mux %c, %a, %b : i8
|
|
%use_for_mux = sv.wire : !hw.inout<i8>
|
|
sv.assign %use_for_mux, %0 : i8
|
|
%1 = comb.add %0, %a : i8
|
|
// CHECK: %[[mux2:.+]] = comb.mux
|
|
%2 = comb.mux %c, %a, %b : i8
|
|
// CHECK: hw.output %[[add]], %[[mux2]]
|
|
hw.output %1, %2 : i8, i8
|
|
}
|
|
}
|
|
|
|
// -----
|
|
// CHECK: "wireSpillingHeuristic=spillLargeTermsWithNamehints,disallowMuxInlining"
|
|
module attributes {circt.loweringOptions =
|
|
"wireSpillingHeuristic=spillLargeTermsWithNamehints,disallowMuxInlining"} {
|
|
hw.module @combine(in %c: i1, in %b: i8, in %a: i8, out d: i8) {
|
|
// Meaningful names should be spilled
|
|
// CHECK: %foo = sv.wire
|
|
// Mux should be spilled
|
|
// CHECK: sv.wire
|
|
%0 = comb.add %a, %a {sv.namehint = "foo" } : i8
|
|
%1 = comb.mux %c, %0, %b : i8
|
|
%2 = comb.add %1, %a : i8
|
|
hw.output %2 : i8
|
|
}
|
|
}
|
|
|
|
// -----
|
|
module attributes {circt.loweringOptions = "maximumNumberOfTermsPerExpression=2"} {
|
|
// CHECK-NOT: sv.wire
|
|
hw.module @Foo(in %in_0: i4, in %in_1: i4, in %in_2: i4, in %in_3: i4, out out: !hw.array<4xi4>) {
|
|
%0 = comb.concat %in_0, %in_1, %in_2, %in_3 : i4, i4, i4, i4
|
|
%1 = hw.bitcast %0 : (i16) -> !hw.array<4xi4>
|
|
hw.output %1 : !hw.array<4xi4>
|
|
}
|
|
}
|
|
|
|
// -----
|
|
|
|
// CHECK: hw.module @packed_struct_assignment(in %[[VAL_0:.*]] : i32, out out : !hw.struct<a: i32>, out out2 : !hw.struct<a: i32>, out out3 : !hw.struct<a: i32>) {
|
|
// CHECK: %[[VAL_1:.*]] = sv.wire
|
|
// CHECK-NEXT: %[[VAL_2:.*]] = sv.struct_field_inout %[[VAL_1]]["a"]
|
|
// CHECK-NEXT: sv.assign %[[VAL_2]], %[[VAL_0]]
|
|
// CHECK-NEXT: %[[VAL_3:.*]] = sv.read_inout %[[VAL_1]]
|
|
// CHECK-NEXT: %[[VAL_4:.*]] = sv.read_inout %[[VAL_1]]
|
|
// CHECK: %[[VAL_5:.*]] = sv.wire
|
|
// CHECK-NEXT: %[[VAL_6:.*]] = sv.struct_field_inout %[[VAL_5]]["a"]
|
|
// CHECK-NEXT: sv.assign %[[VAL_6]], %c1_i32
|
|
// CHECK-NEXT: %[[VAL_7:.*]] = sv.read_inout %[[VAL_5]]
|
|
// CHECK-NEXT: hw.output %[[VAL_4]], %[[VAL_3]], %[[VAL_7]]
|
|
// CHECK: }
|
|
!T = !hw.struct<a: i32>
|
|
module attributes { circt.loweringOptions = "disallowPackedStructAssignments"} {
|
|
hw.module @packed_struct_assignment(in %in : i32, out out: !T, out out2: !T, out out3: !T) {
|
|
%0 = hw.struct_create (%in) : !T
|
|
%1 = hw.aggregate_constant [1: i32] : !T
|
|
hw.output %0, %0, %1 : !T, !T, !T
|
|
}
|
|
}
|
|
|
|
// -----
|
|
// LTL expressions that are used before being defined should not be spilled to
|
|
// wires, where they crash the PrepareForEmission pass. They are always emitted
|
|
// inline, so no need to restructure the IR.
|
|
// CHECK-LABEL: hw.module @Issue5613
|
|
hw.module @Issue5613(in %a: i1, in %b: i1) {
|
|
verif.assert %2 : !ltl.sequence
|
|
%0 = ltl.implication %2, %1 : !ltl.sequence, !ltl.property
|
|
%1 = ltl.or %b, %3 : i1, !ltl.property
|
|
%2 = ltl.and %b, %4 : i1, !ltl.sequence
|
|
%3 = ltl.not %b : i1
|
|
%4 = ltl.delay %a, 42 : i1
|
|
hw.output
|
|
}
|
|
|
|
// -----
|
|
|
|
// If an operation is duplicated because it is always inline, make sure that the
|
|
// recursive inlining of its operands (by splitting) may also duplicate an
|
|
// operand which now needs to be spilled to a wire.
|
|
//
|
|
// See: https://github.com/llvm/circt/issues/5605
|
|
// CHECK-LABEL: hw.module @Issue5605
|
|
hw.module @Issue5605(in %a: i1, in %b: i1, in %clock: i1, in %reset: i1) {
|
|
%0 = comb.concat %a, %b : i1, i1
|
|
// CHECK: %1 = sv.wire
|
|
// CHECK-NEXT: sv.assign %1, %0
|
|
%1 = sv.system.sampled %0 : i2
|
|
// CHECK-NEXT: %2 = sv.read_inout %1
|
|
// CHECK-NEXT: %3 = sv.system.sampled %2
|
|
// CHECK-NEXT: sv.assert.concurrent {{.*}}(%3)
|
|
sv.assert.concurrent posedge %clock, %reset label "assert_0" message "foo"(%1) : i2
|
|
// CHECK-NEXT: %4 = sv.read_inout %1
|
|
// CHECK-NEXT: %5 = sv.system.sampled %4
|
|
// CHECK-NEXT: sv.assert.concurrent {{.*}}(%5)
|
|
sv.assert.concurrent posedge %clock, %reset label "assert_1" message "bar"(%1) : i2
|
|
hw.output
|
|
}
|
|
|
|
// -----
|
|
|
|
// The following use of `sv.xmr.ref` inside a procedural block would trigger an
|
|
// assertion in `PrepareForEmission`, since the op was not marked as side-effect
|
|
// free.
|
|
//
|
|
// CHECK-LABEL: hw.module @Foo
|
|
module attributes {circt.loweringOptions = "disallowLocalVariables"} {
|
|
hw.module @Foo(in %a: i1) {
|
|
hw.wire %a sym @a : i1
|
|
// CHECK: sv.alwayscomb
|
|
sv.alwayscomb {
|
|
// CHECK-NEXT: sv.xmr.ref
|
|
%0 = sv.xmr.ref @xmr : !hw.inout<i1>
|
|
sv.verbatim "{{0}}" (%0) : !hw.inout<i1>
|
|
}
|
|
}
|
|
hw.hierpath @xmr [@Foo::@a]
|
|
}
|
|
|
|
|
|
// -----
|
|
|
|
// CHECK-LABEL: @constantInitRegWithBackEdge
|
|
hw.module @constantInitRegWithBackEdge() {
|
|
// CHECK: %reg = sv.reg init %false : !hw.inout<i1>
|
|
// CHECK-NEXT: %false = hw.constant false
|
|
// CHECK-NEXT: %[[VAL_0:.*]] = sv.read_inout %reg : !hw.inout<i1>
|
|
// CHECK-NEXT: %[[VAL_1:.*]] = comb.or %false, %[[VAL_0]] : i1
|
|
%false = hw.constant false
|
|
%0 = comb.or %false, %1 : i1
|
|
%reg = sv.reg init %false : !hw.inout<i1>
|
|
%1 = sv.read_inout %reg : !hw.inout<i1>
|
|
}
|
|
|
|
// -----
|
|
|
|
// CHECK-LABEL: @temporaryWireForReg
|
|
hw.module @temporaryWireForReg() {
|
|
// CHECK: %[[WIRE:.*]] = sv.wire : !hw.inout<i1>
|
|
// CHECK-NEXT: %[[VAL_0:.*]] = sv.read_inout %[[WIRE]] : !hw.inout<i1>
|
|
// CHECK-NEXT: %b = sv.reg init %[[VAL_0]] : !hw.inout<i1>
|
|
// CHECK-NEXT: %[[VAL_1:.*]] = sv.read_inout %b : !hw.inout<i1>
|
|
// CHECK-NEXT: %a = sv.reg init %[[VAL_1]] : !hw.inout<i1>
|
|
// CHECK-NEXT: %[[VAL_2:.*]] = sv.read_inout %a : !hw.inout<i1>
|
|
// CHECK-NEXT: sv.assign %[[WIRE]], %[[VAL_2]] : i1
|
|
%0 = sv.read_inout %a : !hw.inout<i1>
|
|
%1 = sv.read_inout %b : !hw.inout<i1>
|
|
%b = sv.reg init %0 : !hw.inout<i1>
|
|
%a = sv.reg init %1 : !hw.inout<i1>
|
|
}
|