aboutsummaryrefslogtreecommitdiff
path: root/src/test/scala/firrtlTests/AsyncResetSpec.scala
blob: 911094edc58e6c4a2b407a6cbcb9b3c27f422824 (plain)
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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
// SPDX-License-Identifier: Apache-2.0

package firrtlTests

import firrtl._
import firrtl.testutils._
import FirrtlCheckers._

class AsyncResetSpec extends VerilogTransformSpec {
  def compileBody(body: String) = {
    val str = """
                |circuit Test :
                |  module Test :
                |""".stripMargin + body.split("\n").mkString("    ", "\n    ", "")
    compile(str)
  }

  "AsyncReset" should "generate async-reset always blocks" in {
    val result = compileBody(s"""
                                |input clock : Clock
                                |input reset : AsyncReset
                                |input x : UInt<8>
                                |output z : UInt<8>
                                |reg r : UInt<8>, clock with : (reset => (reset, UInt(123)))
                                |r <= x
                                |z <= r""".stripMargin)
    result should containLine("always @(posedge clock or posedge reset) begin")
  }

  it should "work in nested and flipped aggregates with regular and partial connect" in {
    val result = compileBody(s"""
                                |output fizz : { flip foo : { a : AsyncReset, flip b: AsyncReset }[2], bar : { a : AsyncReset, flip b: AsyncReset }[2] }
                                |output buzz : { flip foo : { a : AsyncReset, flip b: AsyncReset }[2], bar : { a : AsyncReset, flip b: AsyncReset }[2] }
                                |fizz.bar <= fizz.foo
                                |buzz.bar <- buzz.foo
                                |""".stripMargin)

    result should containLine("assign fizz_foo_0_b = fizz_bar_0_b;")
    result should containLine("assign fizz_foo_1_b = fizz_bar_1_b;")
    result should containLine("assign fizz_bar_0_a = fizz_foo_0_a;")
    result should containLine("assign fizz_bar_1_a = fizz_foo_1_a;")
    result should containLine("assign buzz_foo_0_b = buzz_bar_0_b;")
    result should containLine("assign buzz_foo_1_b = buzz_bar_1_b;")
    result should containLine("assign buzz_bar_0_a = buzz_foo_0_a;")
    result should containLine("assign buzz_bar_1_a = buzz_foo_1_a;")
  }

  it should "support casting to other types" in {
    val result = compileBody(s"""
                                |input a : AsyncReset
                                |output u : Interval[0, 1].0
                                |output v : UInt<1>
                                |output w : SInt<1>
                                |output x : Clock
                                |output y : Fixed<1><<0>>
                                |output z : AsyncReset
                                |u <= asInterval(a, 0, 1, 0)
                                |v <= asUInt(a)
                                |w <= asSInt(a)
                                |x <= asClock(a)
                                |y <= asFixedPoint(a, 0)
                                |z <= asAsyncReset(a)
                                |""".stripMargin)
    result should containLine("assign v = a;")
    result should containLine("assign w = a;")
    result should containLine("assign x = a;")
    result should containLine("assign y = a;")
    result should containLine("assign z = a;")
  }

  "Other types" should "support casting to AsyncReset" in {
    val result = compileBody(s"""
                                |input a : UInt<1>
                                |input b : SInt<1>
                                |input c : Clock
                                |input d : Fixed<1><<0>>
                                |input e : AsyncReset
                                |input f : Interval[0, 0].0
                                |output u : AsyncReset
                                |output v : AsyncReset
                                |output w : AsyncReset
                                |output x : AsyncReset
                                |output y : AsyncReset
                                |output z : AsyncReset
                                |u <= asAsyncReset(a)
                                |v <= asAsyncReset(b)
                                |w <= asAsyncReset(c)
                                |x <= asAsyncReset(d)
                                |y <= asAsyncReset(e)
                                |z <= asAsyncReset(f)""".stripMargin)
    result should containLine("assign u = a;")
    result should containLine("assign v = b;")
    result should containLine("assign w = c;")
    result should containLine("assign x = d;")
    result should containLine("assign y = e;")
    result should containLine("assign z = f;")
  }

  "Non-literals" should "NOT be allowed as reset values for AsyncReset" in {
    an[checks.CheckResets.NonLiteralAsyncResetValueException] shouldBe thrownBy {
      compileBody(s"""
                     |input clock : Clock
                     |input reset : AsyncReset
                     |input x : UInt<8>
                     |input y : UInt<8>
                     |output z : UInt<8>
                     |reg r : UInt<8>, clock with : (reset => (reset, y))
                     |r <= x
                     |z <= r""".stripMargin)
    }
  }

  "Self-inits" should "NOT cause infinite loops in CheckResets" in {
    val result = compileBody(s"""
                                |input clock : Clock
                                |input reset : AsyncReset
                                |input in : UInt<12>
                                |output out : UInt<10>
                                |
                                |reg a : UInt<10>, clock with :
                                |  reset => (reset, a)
                                |out <= UInt<5>("h15")""".stripMargin)
    result should containLine("assign out = 10'h15;")
  }

  "Late non-literals connections" should "NOT be allowed as reset values for AsyncReset" in {
    an[checks.CheckResets.NonLiteralAsyncResetValueException] shouldBe thrownBy {
      compileBody(s"""
                     |input clock : Clock
                     |input reset : AsyncReset
                     |input x : UInt<8>
                     |input y : UInt<8>
                     |output z : UInt<8>
                     |wire a : UInt<8>
                     |reg r : UInt<8>, clock with : (reset => (reset, a))
                     |a <= y
                     |r <= x
                     |z <= r""".stripMargin)
    }
  }

  "Hidden Non-literals" should "NOT be allowed as reset values for AsyncReset" in {
    an[checks.CheckResets.NonLiteralAsyncResetValueException] shouldBe thrownBy {
      compileBody(s"""
                     |input clock : Clock
                     |input reset : AsyncReset
                     |input x : UInt<1>[4]
                     |input y : UInt<1>
                     |output z : UInt<1>[4]
                     |wire literal : UInt<1>[4]
                     |literal[0] <= UInt<1>("h00")
                     |literal[1] <= y
                     |literal[2] <= UInt<1>("h00")
                     |literal[3] <= UInt<1>("h00")
                     |reg r : UInt<1>[4], clock with : (reset => (reset, literal))
                     |r <= x
                     |z <= r""".stripMargin)
    }
  }
  "Wire connected to non-literal" should "NOT be allowed as reset values for AsyncReset" in {
    an[checks.CheckResets.NonLiteralAsyncResetValueException] shouldBe thrownBy {
      compileBody(s"""
                     |input clock : Clock
                     |input reset : AsyncReset
                     |input x : UInt<1>
                     |input y : UInt<1>
                     |input cond : UInt<1>
                     |output z : UInt<1>
                     |wire w : UInt<1>
                     |w <= UInt(1)
                     |when cond :
                     |  w <= y
                     |reg r : UInt<1>, clock with : (reset => (reset, w))
                     |r <= x
                     |z <= r""".stripMargin)
    }
  }

  "Complex literals" should "be allowed as reset values for AsyncReset" in {
    val result = compileBody(s"""
                                |input clock : Clock
                                |input reset : AsyncReset
                                |input x : UInt<1>[4]
                                |output z : UInt<1>[4]
                                |wire literal : UInt<1>[4]
                                |literal[0] <= UInt<1>("h00")
                                |literal[1] <= UInt<1>("h00")
                                |literal[2] <= UInt<1>("h00")
                                |literal[3] <= UInt<1>("h00")
                                |reg r : UInt<1>[4], clock with : (reset => (reset, literal))
                                |r <= x
                                |z <= r""".stripMargin)
    result should containLine("always @(posedge clock or posedge reset) begin")
  }

  "Complex literals of complex literals" should "be allowed as reset values for AsyncReset" in {
    val result = compileBody(s"""
                                |input clock : Clock
                                |input reset : AsyncReset
                                |input x : UInt<1>[4]
                                |output z : UInt<1>[4]
                                |wire literal : UInt<1>[2]
                                |literal[0] <= UInt<1>("h01")
                                |literal[1] <= UInt<1>("h01")
                                |wire complex_literal : UInt<1>[4]
                                |complex_literal[0] <= literal[0]
                                |complex_literal[1] <= literal[1]
                                |complex_literal[2] <= UInt<1>("h00")
                                |complex_literal[3] <= UInt<1>("h00")
                                |reg r : UInt<1>[4], clock with : (reset => (reset, complex_literal))
                                |r <= x
                                |z <= r""".stripMargin)
    result should containLine("always @(posedge clock or posedge reset) begin")
  }
  "Literals of bundle literals" should "be allowed as reset values for AsyncReset" in {
    val result = compileBody(s"""
                                |input clock : Clock
                                |input reset : AsyncReset
                                |input x : UInt<1>[4]
                                |output z : UInt<1>[4]
                                |wire bundle : {a: UInt<1>, b: UInt<1>}
                                |bundle.a <= UInt<1>("h01")
                                |bundle.b <= UInt<1>("h01")
                                |wire complex_literal : UInt<1>[4]
                                |complex_literal[0] <= bundle.a
                                |complex_literal[1] <= bundle.b
                                |complex_literal[2] <= UInt<1>("h00")
                                |complex_literal[3] <= UInt<1>("h00")
                                |reg r : UInt<1>[4], clock with : (reset => (reset, complex_literal))
                                |r <= x
                                |z <= r""".stripMargin)
    result should containLine("always @(posedge clock or posedge reset) begin")
  }

  "Cast literals" should "be allowed as reset values for AsyncReset" in {
    // This also checks that casts can be across wires and nodes
    val sintResult = compileBody(s"""
                                    |input clock : Clock
                                    |input reset : AsyncReset
                                    |input x : SInt<4>
                                    |output y : SInt<4>
                                    |output z : SInt<4>
                                    |reg r : SInt<4>, clock with : (reset => (reset, asSInt(UInt(0))))
                                    |r <= x
                                    |wire w : SInt<4>
                                    |reg r2 : SInt<4>, clock with : (reset => (reset, w))
                                    |r2 <= x
                                    |node n = UInt("hf")
                                    |w <= asSInt(n)
                                    |y <= r2
                                    |z <= r""".stripMargin)
    sintResult should containLine("always @(posedge clock or posedge reset) begin")
    sintResult should containLine("r <= 4'sh0;")
    sintResult should containLine("r2 <= -4'sh1;")

    val fixedResult = compileBody(s"""
                                     |input clock : Clock
                                     |input reset : AsyncReset
                                     |input x : Fixed<2><<0>>
                                     |output z : Fixed<2><<0>>
                                     |reg r : Fixed<2><<0>>, clock with : (reset => (reset, asFixedPoint(UInt(2), 0)))
                                     |r <= x
                                     |z <= r""".stripMargin)
    fixedResult should containLine("always @(posedge clock or posedge reset) begin")
    fixedResult should containLine("r <= 2'sh2;")

    val intervalResult = compileBody(s"""
                                        |input clock : Clock
                                        |input reset : AsyncReset
                                        |input x : Interval[0, 4].0
                                        |output z : Interval[0, 4].0
                                        |reg r : Interval[0, 4].0, clock with : (reset => (reset, asInterval(UInt(0), 0, 0, 0)))
                                        |r <= x
                                        |z <= r""".stripMargin)
    intervalResult should containLine("always @(posedge clock or posedge reset) begin")
    intervalResult should containLine("r <= 4'sh0;")
  }

  "CheckResets" should "NOT raise StackOverflow Exception on Combinational Loops (should be caught by firrtl.transforms.CheckCombLoops)" in {
    an[firrtl.transforms.CheckCombLoops.CombLoopException] shouldBe thrownBy {
      compileBody(s"""
                     |input clock : Clock
                     |input reset : AsyncReset
                     |wire x : UInt<1>
                     |wire y : UInt<2>
                     |x <= UInt<1>("h01")
                     |node ad = add(x, y)
                     |node adt = tail(ad, 1)
                     |y <= adt
                     |reg r : UInt, clock with : (reset => (reset, y))
                     |""".stripMargin)
    }
  }

  "Every async reset reg" should "generate its own always block" in {
    val result = compileBody(s"""
                                |input clock0 : Clock
                                |input clock1 : Clock
                                |input syncReset : UInt<1>
                                |input asyncReset : AsyncReset
                                |input x : UInt<8>[5]
                                |output z : UInt<8>[5]
                                |reg r0 : UInt<8>, clock0 with : (reset => (syncReset, UInt(123)))
                                |reg r1 : UInt<8>, clock1 with : (reset => (syncReset, UInt(123)))
                                |reg r2 : UInt<8>, clock0 with : (reset => (asyncReset, UInt(123)))
                                |reg r3 : UInt<8>, clock0 with : (reset => (asyncReset, UInt(123)))
                                |reg r4 : UInt<8>, clock1 with : (reset => (asyncReset, UInt(123)))
                                |r0 <= x[0]
                                |r1 <= x[1]
                                |r2 <= x[2]
                                |r3 <= x[3]
                                |r4 <= x[4]
                                |z[0] <= r0
                                |z[1] <= r1
                                |z[2] <= r2
                                |z[3] <= r3
                                |z[4] <= r4""".stripMargin)
    result should containLines(
      "always @(posedge clock0) begin",
      "if (syncReset) begin",
      "r0 <= 8'h7b;",
      "end else begin",
      "r0 <= x_0;",
      "end",
      "end"
    )
    result should containLines(
      "always @(posedge clock1) begin",
      "if (syncReset) begin",
      "r1 <= 8'h7b;",
      "end else begin",
      "r1 <= x_1;",
      "end",
      "end"
    )
    result should containLines(
      "always @(posedge clock0 or posedge asyncReset) begin",
      "if (asyncReset) begin",
      "r2 <= 8'h7b;",
      "end else begin",
      "r2 <= x_2;",
      "end",
      "end"
    )
    result should containLines(
      "always @(posedge clock0 or posedge asyncReset) begin",
      "if (asyncReset) begin",
      "r3 <= 8'h7b;",
      "end else begin",
      "r3 <= x_3;",
      "end",
      "end"
    )
    result should containLines(
      "always @(posedge clock1 or posedge asyncReset) begin",
      "if (asyncReset) begin",
      "r4 <= 8'h7b;",
      "end else begin",
      "r4 <= x_4;",
      "end",
      "end"
    )
  }

  "Unassigned asyncronously reset registers" should "properly constantprop" in {
    val result = compileBody(
      s"""
         |input clock : Clock
         |input reset : AsyncReset
         |output z : UInt<1>[4]
         |wire literal : UInt<1>[2]
         |literal[0] <= UInt<1>("h01")
         |literal[1] <= UInt<1>("h01")
         |wire complex_literal : UInt<1>[4]
         |complex_literal[0] <= literal[0]
         |complex_literal[1] <= literal[1]
         |complex_literal[2] <= UInt<1>("h00")
         |complex_literal[3] <= UInt<1>("h00")
         |reg r : UInt<1>[4], clock with : (reset => (reset, complex_literal))
         |z <= r""".stripMargin
    )
    result shouldNot containLine("always @(posedge clock or posedge reset) begin")
  }

  "Constantly assigned asynchronously reset registers" should "properly constantprop" in {
    val result = compileBody(
      s"""
         |input clock : Clock
         |input reset : AsyncReset
         |output z : UInt<1>
         |reg r : UInt<1>, clock with : (reset => (reset, r))
         |r <= UInt(0)
         |z <= r""".stripMargin
    )
    result shouldNot containLine("always @(posedge clock or posedge reset) begin")
  }

  "Constantly assigned and initialized asynchronously reset registers" should "properly constantprop" in {
    val result = compileBody(
      s"""
         |input clock : Clock
         |input reset : AsyncReset
         |output z : UInt<1>
         |reg r : UInt<1>, clock with : (reset => (reset, UInt(0)))
         |r <= UInt(0)
         |z <= r""".stripMargin
    )
    result shouldNot containLine("always @(posedge clock or posedge reset) begin")
  }

  "AsyncReset registers" should "emit 'else' case for reset even for trivial valued registers" in {
    val withDontTouch = s"""
                           |circuit m :
                           |  module m :
                           |    input clock : Clock
                           |    input reset : AsyncReset
                           |    input x : UInt<8>
                           |    reg r : UInt<8>, clock with : (reset => (reset, UInt(123)))
                           |""".stripMargin
    val annos = Seq(dontTouch("m.r")) // dontTouch prevents ConstantPropagation from fixing this problem
    val result = (new VerilogCompiler).compileAndEmit(CircuitState(parse(withDontTouch), ChirrtlForm, annos))
    result should containLines(
      "always @(posedge clock or posedge reset) begin",
      "if (reset) begin",
      "r <= 8'h7b;",
      "end else begin",
      "r <= 8'h7b;",
      "end",
      "end"
    )
  }
}

class AsyncResetExecutionTest extends ExecutionTest("AsyncResetTester", "/features")