-
Notifications
You must be signed in to change notification settings - Fork 147
/
Reset.hs
476 lines (452 loc) · 17 KB
/
Reset.hs
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
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
{-|
Copyright : (C) 2020-2023, QBayLogic B.V.,
2022-2023, Google LLC
License : BSD2 (see the file LICENSE)
Maintainer : QBayLogic B.V. <devops@qbaylogic.com>
Utilities to deal with resets.
-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TypeFamilies #-}
{-# OPTIONS_GHC -fplugin=GHC.TypeLits.Normalise #-}
{-# OPTIONS_GHC -fplugin=GHC.TypeLits.KnownNat.Solver #-}
module Clash.Explicit.Reset
( -- Defined in this module
resetSynchronizer
, resetGlitchFilter
, resetGlitchFilterWithReset
, unsafeResetGlitchFilter
, holdReset
, convertReset
, noReset
, andReset, unsafeAndReset
, orReset, unsafeOrReset
-- Reexports
, Reset
, resetGen
, resetGenN
, resetKind
, systemResetGen
, unsafeToReset
, unsafeFromReset
, unsafeToActiveHigh
, unsafeToActiveLow
, unsafeFromActiveHigh
, unsafeFromActiveLow
-- * Deprecated
, unsafeFromHighPolarity
, unsafeFromLowPolarity
, unsafeToHighPolarity
, unsafeToLowPolarity
) where
import Data.Type.Equality ((:~:)(Refl))
import Clash.Class.Num (satSucc, SaturationMode(SatBound))
import Clash.Explicit.Signal
import Clash.Explicit.Synchronizer (dualFlipFlopSynchronizer)
import Clash.Promoted.Nat
import Clash.Signal.Internal
import Clash.Sized.Index (Index)
import GHC.Stack (HasCallStack)
import GHC.TypeLits (type (+), type (<=))
{- $setup
>>> import Clash.Explicit.Prelude
-}
-- | A reset that is never asserted
noReset :: KnownDomain dom => Reset dom
noReset = unsafeFromActiveHigh (pure False)
-- | Output reset will be asserted when either one of the input resets is
-- asserted
orReset ::
forall dom .
HasSynchronousReset dom =>
Reset dom ->
Reset dom ->
Reset dom
orReset = unsafeOrReset
-- | Output reset will be asserted when either one of the input resets is
-- asserted. This function is considered unsafe because it can be used on
-- domains with components with asynchronous resets, where use of this function
-- can introduce glitches triggering a reset.
unsafeOrReset :: forall dom. KnownDomain dom => Reset dom -> Reset dom -> Reset dom
unsafeOrReset (unsafeFromReset -> rst0) (unsafeFromReset -> rst1) =
unsafeToReset $
case resetPolarity @dom of
SActiveHigh -> rst0 .||. rst1
SActiveLow -> rst0 .&&. rst1
-- | Output reset will be asserted when both input resets are asserted
andReset ::
forall dom .
HasSynchronousReset dom =>
Reset dom ->
Reset dom ->
Reset dom
andReset = unsafeAndReset
-- | Output reset will be asserted when both input resets are asserted. This
-- function is considered unsafe because it can be used on domains with
-- components with asynchronous resets, where use of this function can introduce
-- glitches triggering a reset.
unsafeAndReset :: forall dom. KnownDomain dom => Reset dom -> Reset dom -> Reset dom
unsafeAndReset (unsafeFromReset -> rst0) (unsafeFromReset -> rst1) =
unsafeToReset $
case resetPolarity @dom of
SActiveHigh -> rst0 .&&. rst1
SActiveLow -> rst0 .||. rst1
-- | The resetSynchronizer will synchronize an incoming reset according to
-- whether the domain is synchronous or asynchronous.
--
-- For asynchronous resets this synchronizer ensures the reset will only
-- be de-asserted synchronously but it can still be asserted asynchronously.
-- The reset assert is immediate, but reset de-assertion is delayed by two
-- cycles.
--
-- Normally, asynchronous resets can be both asynchronously asserted and
-- de-asserted. Asynchronous de-assertion can induce meta-stability in the
-- component which is being reset. To ensure this doesn't happen,
-- 'resetSynchronizer' ensures that de-assertion of a reset happens
-- synchronously. Assertion of the reset remains asynchronous.
--
-- Note that asynchronous assertion does not induce meta-stability in the
-- component whose reset is asserted. However, when a component \"A\" in another
-- clock or reset domain depends on the value of a component \"B\" being
-- reset, then asynchronous assertion of the reset of component \"B"\ can induce
-- meta-stability in component \"A\". To prevent this from happening you need
-- to use a proper synchronizer, for example one of the synchronizers in
-- "Clash.Explicit.Synchronizer".
--
-- For synchronous resets this function ensures that the reset is asserted and
-- de-asserted synchronously. Both the assertion and de-assertion of the reset
-- are delayed by two cycles.
--
-- === __Example 1__
-- The circuit below detects a rising bit (i.e., a transition from 0 to 1) in a
-- given argument. It takes a reset that is not synchronized to any of the other
-- incoming signals and synchronizes it using 'resetSynchronizer'.
--
-- @
-- topEntity
-- :: Clock System
-- -> Reset System
-- -> Signal System Bit
-- -> Signal System (BitVector 8)
-- topEntity clk asyncRst key1 =
-- withClockResetEnable clk rst enableGen leds
-- where
-- rst = 'resetSynchronizer' clk asyncRst
-- key1R = isRising 1 key1
-- leds = mealy blinkerT (1, False, 0) key1R
-- @
--
-- === __Example 2__
-- Similar to /Example 1/ this circuit detects a rising bit (i.e., a transition
-- from 0 to 1) in a given argument. It takes a clock that is not stable yet and
-- a reset signal that is not synchronized to any other signals. It stabilizes
-- the clock and then synchronizes the reset signal.
--
--
-- Note that the function 'Clash.Intel.ClockGen.altpllSync' provides this
-- functionality in a convenient form, obviating the need for
-- @resetSynchronizer@ for this use case.
--
-- @
-- topEntity
-- :: Clock System
-- -> Reset System
-- -> Signal System Bit
-- -> Signal System (BitVector 8)
-- topEntity clk rst key1 =
-- let (pllOut,pllStable) = unsafeAltpll clk rst
-- rstSync = 'resetSynchronizer' pllOut (unsafeFromActiveLow pllStable)
-- in exposeClockResetEnable leds pllOut rstSync enableGen
-- where
-- key1R = isRising 1 key1
-- leds = mealy blinkerT (1, False, 0) key1R
-- @
--
-- === __Implementation details__
-- 'resetSynchronizer' implements the following circuit for asynchronous domains:
--
-- @
-- rst
-- --------------------------------------+
-- | |
-- +----v----+ +----v----+
-- deasserted | | | |
-- ---------------> +-------> +-------->
-- | | | |
-- +---|> | +---|> |
-- | | | | | |
-- | +---------+ | +---------+
-- clk | |
-- -----------------------------+
-- @
--
-- This corresponds to figure 3d at <https://www.embedded.com/asynchronous-reset-synchronization-and-distribution-challenges-and-solutions/>
--
-- For synchronous domains two sequential dflipflops are used:
--
-- @
-- +---------+ +---------+
-- rst | | | |
-- ---------------> +-------> +-------->
-- | | | |
-- +---|> | +---|> |
-- | | | | | |
-- | +---------+ | +---------+
-- clk | |
-- -----------------------------+
-- @
--
resetSynchronizer
:: forall dom
. KnownDomain dom
=> Clock dom
-> Reset dom
-> Reset dom
resetSynchronizer clk rst = rstOut
where
isActiveHigh = case resetPolarity @dom of { SActiveHigh -> True; _ -> False }
rstOut =
case (resetKind @dom) of
SAsynchronous -> unsafeToReset
$ register clk rst enableGen isActiveHigh
$ register clk rst enableGen isActiveHigh
$ pure (not isActiveHigh)
SSynchronous -> unsafeToReset
$ delay clk enableGen isActiveHigh
$ delay clk enableGen isActiveHigh
$ unsafeFromReset rst
-- | Filter glitches from reset signals by only triggering a reset after it has
-- been asserted for /glitchlessPeriod/ cycles. Similarly, it will stay
-- asserted until a /glitchlessPeriod/ number of deasserted cycles have been
-- observed.
--
-- This circuit can only be used on platforms supporting initial values. This
-- restriction can be worked around by using 'unsafeResetGlitchFilter' but this
-- is not recommended.
--
-- On platforms without initial values, you should instead use
-- 'resetGlitchFilterWithReset' with an additional power-on reset, or
-- 'holdReset' if filtering is only needed on deassertion.
--
-- At power-on, the reset will be asserted. If the filtered reset input remains
-- unasserted, the output reset will deassert after /glitchlessPeriod/ clock
-- cycles.
--
-- If @resetGlitchFilter@ is used in a domain with asynchronous resets
-- ('Asynchronous'), @resetGlitchFilter@ will first synchronize the reset input
-- with 'dualFlipFlopSynchronizer'.
--
-- === __Example 1__
-- >>> let sampleResetN n = sampleN n . unsafeToActiveHigh
-- >>> let resetFromList = unsafeFromActiveHigh . fromList
-- >>> let rst = resetFromList [True, True, False, False, True, False, False, True, True, False, True, True]
-- >>> sampleResetN 12 (resetGlitchFilter d2 (clockGen @XilinxSystem) rst)
-- [True,True,True,True,False,False,False,False,False,True,True,True]
resetGlitchFilter
:: forall dom glitchlessPeriod
. ( HasCallStack
, HasDefinedInitialValues dom
, 1 <= glitchlessPeriod
)
=> SNat glitchlessPeriod
-- ^ Consider a reset signal to be properly asserted after having seen the
-- reset asserted for /glitchlessPeriod/ cycles straight.
-> Clock dom
-> Reset dom
-> Reset dom
resetGlitchFilter = unsafeResetGlitchFilter
{-# INLINE resetGlitchFilter #-}
-- | Filter glitches from reset signals by only triggering a reset after it has
-- been asserted for /glitchlessPeriod/ cycles. Similarly, it will stay
-- asserted until a /glitchlessPeriod/ number of deasserted cycles have been
-- observed.
--
-- On platforms without initial values ('Unknown'), 'resetGlitchFilter' cannot
-- be used and you should use 'resetGlitchFilterWithReset' with an additional
-- power-on reset, or 'holdReset' if filtering is only needed on deassertion.
--
-- @unsafeResetGlitchFilter@ allows breaking the requirement of initial values,
-- but by doing so it is possible that the design starts up with a period of up
-- to /2 * glitchlessPeriod/ clock cycles where the reset output is unasserted
-- (or longer in the case of glitches on the filtered reset input). This can
-- cause a number of problems. The outputs\/tri-states of the design might
-- output random things, including coherent but incorrect streams of data. This
-- might have grave repercussions in the design's environment (sending network
-- packets, overwriting non-volatile memory, in extreme cases destroying
-- controlled equipment or causing harm to living beings, ...).
--
-- Without initial values, the synthesized result of @unsafeResetGlitchFilter@
-- eventually correctly outputs a filtered version of the reset input. However,
-- in simulation, it will indefinitely output an undefined value. This happens
-- both in Clash simulation and in HDL simulation. Therefore, simulation should
-- not include the @unsafeResetGlitchFilter@.
--
-- If @unsafeResetGlitchFilter@ is used in a domain with asynchronous resets
-- ('Asynchronous'), @unsafeResetGlitchFilter@ will first synchronize the reset
-- input with 'dualFlipFlopSynchronizer'.
unsafeResetGlitchFilter
:: forall dom glitchlessPeriod
. ( HasCallStack
, KnownDomain dom
, 1 <= glitchlessPeriod
)
=> SNat glitchlessPeriod
-- ^ Consider a reset signal to be properly asserted after having seen the
-- reset asserted for /glitchlessPeriod/ cycles straight.
-> Clock dom
-> Reset dom
-> Reset dom
unsafeResetGlitchFilter glitchlessPeriod clk =
resetGlitchFilter# glitchlessPeriod reg dffSync
where
reg = delay clk enableGen
dffSync = dualFlipFlopSynchronizer clk clk noReset enableGen
{-# INLINE unsafeResetGlitchFilter #-}
-- | Filter glitches from reset signals by only triggering a reset after it has
-- been asserted for /glitchlessPeriod/ cycles. Similarly, it will stay
-- asserted until a /glitchlessPeriod/ number of deasserted cycles have been
-- observed.
--
-- Compared to 'resetGlitchFilter', this function adds an additional power-on
-- reset input. As soon as the power-on reset asserts, the reset output will
-- assert, and after the power-on reset deasserts, the reset output will stay
-- asserted for another /glitchlessPeriod/ clock cycles. This is identical
-- behavior to 'holdReset' where it concerns the power-on reset, and differs
-- from the filtered reset, which will only cause an assertion after
-- /glitchlessPeriod/ cycles.
--
-- If @resetGlitchFilterWithReset@ is used in a domain with asynchronous resets
-- ('Asynchronous'), @resetGlitchFilterWithReset@ will first synchronize the
-- reset input with 'dualFlipFlopSynchronizer'.
resetGlitchFilterWithReset
:: forall dom glitchlessPeriod
. ( HasCallStack
, KnownDomain dom
, 1 <= glitchlessPeriod
)
=> SNat glitchlessPeriod
-- ^ Consider a reset signal to be properly asserted after having seen the
-- reset asserted for /glitchlessPeriod/ cycles straight.
-> Clock dom
-> Reset dom
-- ^ The power-on reset for the glitch filter itself
-> Reset dom
-- ^ The reset that will be filtered
-> Reset dom
resetGlitchFilterWithReset glitchlessPeriod clk ownRst =
resetGlitchFilter# glitchlessPeriod reg dffSync
where
reg = register clk ownRst enableGen
dffSync = dualFlipFlopSynchronizer clk clk ownRst enableGen
{-# INLINE resetGlitchFilterWithReset #-}
resetGlitchFilter#
:: forall dom glitchlessPeriod state
. ( HasCallStack
, KnownDomain dom
, 1 <= glitchlessPeriod
, state ~ (Bool, Index glitchlessPeriod)
)
=> SNat glitchlessPeriod
-> ( state
-> Signal dom state
-> Signal dom state
)
-> ( Bool
-> Signal dom Bool
-> Signal dom Bool
)
-> Reset dom
-> Reset dom
resetGlitchFilter# SNat reg dffSync rstIn0 =
let s' = go <$> s <*> rstIn2
s = reg (asserted, 0) s'
in unsafeToReset $ fst <$> s
where
rstIn1 = unsafeFromReset rstIn0
rstIn2 =
case resetKind @dom of
SAsynchronous -> dffSync asserted rstIn1
SSynchronous -> rstIn1
go :: state -> Bool -> state
go (state, count) reset
| reset == state = (state, 0)
| count == maxBound = (not state, 0)
| otherwise = (state, count + 1)
asserted :: Bool
asserted =
case resetPolarity @dom of
SActiveHigh -> True
SActiveLow -> False
-- | Hold reset for a number of cycles relative to an incoming reset signal.
--
-- Example:
--
-- >>> let sampleWithReset = sampleN 8 . unsafeToActiveHigh
-- >>> sampleWithReset (holdReset @System clockGen enableGen (SNat @2) (resetGenN (SNat @3)))
-- [True,True,True,True,True,False,False,False]
--
-- 'holdReset' holds the reset for an additional 2 clock cycles for a total
-- of 5 clock cycles where the reset is asserted. 'holdReset' also works on
-- intermediate assertions of the reset signal:
--
-- >>> let rst = fromList [True, False, False, False, True, False, False, False]
-- >>> sampleWithReset (holdReset @System clockGen enableGen (SNat @2) (unsafeFromActiveHigh rst))
-- [True,True,True,False,True,True,True,False]
--
holdReset
:: forall dom n
. KnownDomain dom
=> Clock dom
-> Enable dom
-- ^ Global enable
-> SNat n
-- ^ Hold for /n/ cycles, counting from the moment the incoming reset
-- signal becomes deasserted.
-> Reset dom
-- ^ Reset to extend
-> Reset dom
holdReset clk en SNat rst =
unsafeFromActiveHigh ((/=maxBound) <$> counter)
where
counter :: Signal dom (Index (n+1))
counter = register clk rst en 0 (satSucc SatBound <$> counter)
-- | Convert between different types of reset, adding a synchronizer when
-- the domains are not the same. See 'resetSynchronizer' for further details
-- about reset synchronization.
--
-- If @domA@ has 'Synchronous' resets, a flip-flop is inserted in @domA@ to
-- filter glitches. This adds one @domA@ clock cycle delay.
convertReset
:: forall domA domB
. ( KnownDomain domA
, KnownDomain domB
)
=> Clock domA
-> Clock domB
-> Reset domA
-> Reset domB
convertReset clkA clkB rstA0 = rstB1
where
rstA1 = unsafeFromReset rstA0
rstA2 =
case (resetPolarity @domA, resetPolarity @domB) of
(SActiveLow, SActiveLow) -> rstA1
(SActiveHigh, SActiveHigh) -> rstA1
_ -> not <$> rstA1
rstA3 =
case resetKind @domA of
SSynchronous -> delay clkA enableGen assertedA rstA2
_ -> rstA2
rstB0 = unsafeToReset $ unsafeSynchronizer clkA clkB rstA3
rstB1 =
case (sameDomain @domA @domB) of
Just Refl -> rstA0
Nothing -> resetSynchronizer clkB rstB0
assertedA :: Bool
assertedA =
case resetPolarity @domA of
SActiveHigh -> True
SActiveLow -> False