1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
|
// See LICENSE for license details.
package firrtlTests
import firrtl.CircuitState
import firrtl.options.Dependency
import firrtl.stage.{Forms, TransformManager}
import firrtl.testutils.LeanTransformSpec
class PadWidthsTests extends LeanTransformSpec(Seq(Dependency(firrtl.passes.PadWidths))) {
behavior.of("PadWidths pass")
it should "pad widths inside a mux" in {
val input =
"""circuit Top :
| module Top :
| input a : UInt<32>
| input b : UInt<20>
| input pred : UInt<1>
| output c : UInt<32>
| c <= mux(pred,a,b)""".stripMargin
val check = Seq("c <= mux(pred, a, pad(b, 32))")
executeTest(input, check)
}
it should "pad widths of connects" in {
val input =
"""circuit Top :
| module Top :
| output a : UInt<32>
| input b : UInt<20>
| a <= b
| """.stripMargin
val check = Seq("a <= pad(b, 32)")
executeTest(input, check)
}
it should "pad widths of register init expressions" in {
val input =
"""circuit Top :
| module Top :
| input clock: Clock
| input reset: AsyncReset
|
| reg r: UInt<8>, clock with:
| reset => (reset, UInt<1>("h1"))
| """.stripMargin
// PadWidths will call into constant prop directly, thus the literal is widened instead of adding a pad
val check = Seq("reset => (reset, UInt<8>(\"h1\"))")
executeTest(input, check)
}
private def testOp(op: String, width: Int, resultWidth: Int): Unit = {
assert(width > 0)
val input =
s"""circuit Top :
| module Top :
| input a : UInt<32>
| input b : UInt<$width>
| output c : UInt<$resultWidth>
| c <= $op(a,b)""".stripMargin
val check = if (width < 32) {
Seq(s"c <= $op(a, pad(b, 32))")
} else if (width == 32) {
Seq(s"c <= $op(a, b)")
} else {
Seq(s"c <= $op(pad(a, $width), b)")
}
executeTest(input, check)
}
it should "pad widths of the arguments to add and sub" in {
// add and sub have the same width inference rule: max(w_1, w_2) + 1
testOp("add", 2, 33)
testOp("add", 32, 33)
testOp("add", 35, 36)
testOp("sub", 2, 33)
testOp("sub", 32, 33)
testOp("sub", 35, 36)
}
it should "pad widths of the arguments to and, or and xor" in {
// and, or and xor have the same width inference rule: max(w_1, w_2)
testOp("and", 2, 32)
testOp("and", 32, 32)
testOp("and", 35, 35)
testOp("or", 2, 32)
testOp("or", 32, 32)
testOp("or", 35, 35)
testOp("xor", 2, 32)
testOp("xor", 32, 32)
testOp("xor", 35, 35)
}
it should "pad widths of the arguments to lt, leq, gt, geq, eq and neq" in {
// lt, leq, gt, geq, eq and ne have the same width inference rule: 1
testOp("lt", 2, 1)
testOp("lt", 32, 1)
testOp("lt", 35, 1)
testOp("leq", 2, 1)
testOp("leq", 32, 1)
testOp("leq", 35, 1)
testOp("gt", 2, 1)
testOp("gt", 32, 1)
testOp("gt", 35, 1)
testOp("geq", 2, 1)
testOp("geq", 32, 1)
testOp("geq", 35, 1)
testOp("eq", 2, 1)
testOp("eq", 32, 1)
testOp("eq", 35, 1)
testOp("neq", 2, 1)
testOp("neq", 32, 1)
testOp("neq", 35, 1)
}
private val resolvedCompiler = new TransformManager(Forms.Resolved)
private def checkWidthsAfterPadWidths(input: String, op: String): Unit = {
val result = compile(input)
// we serialize the result in order to rerun width inference
val resultFir = firrtl.Parser.parse(result.circuit.serialize)
val newWidths = resolvedCompiler.runTransform(CircuitState(resultFir, Seq()))
// the newly loaded circuit should look the same in serialized form (if this fails, the test has a bug)
assert(newWidths.circuit.serialize == result.circuit.serialize)
// we compare the widths produced by PadWidths with the widths that would normally be inferred
assert(newWidths.circuit.modules.head == result.circuit.modules.head, s"failed with op `$op`")
}
it should "always generate valid firrtl" in {
// an older version of PadWidths would generate ill types firrtl for mul, div, rem and dshl
def input(op: String): String =
s"""circuit Top:
| module Top:
| input a: UInt<3>
| input b: UInt<1>
| output c: UInt
| c <= $op(a, b)
|""".stripMargin
def test(op: String): Unit = checkWidthsAfterPadWidths(input(op), op)
// This was never broken, but we want to make sure that the test works.
test("add")
test("mul")
test("div")
test("rem")
test("dshl")
}
private def executeTest(input: String, expected: Seq[String]): Unit = {
val result = compile(input)
val lines = result.circuit.serialize.split("\n").map(normalized)
expected.map(normalized).foreach { e =>
assert(lines.contains(e), f"Failed to find $e in ${lines.mkString("\n")}")
}
}
}
|