@@ -16,6 +16,7 @@ Utilities to deal with resets.
1616
1717{-# OPTIONS_GHC -fplugin=GHC.TypeLits.Normalise #-}
1818{-# OPTIONS_GHC -fplugin=GHC.TypeLits.KnownNat.Solver #-}
19+ {-# OPTIONS_GHC -fplugin=GHC.TypeLits.Extra.Solver #-}
1920
2021module Clash.Explicit.Reset
2122 ( -- Defined in this module
@@ -24,6 +25,7 @@ module Clash.Explicit.Reset
2425 , resetGlitchFilterWithReset
2526 , unsafeResetGlitchFilter
2627 , holdReset
28+ , stretchReset
2729 , convertReset
2830 , noReset
2931 , andReset , unsafeAndReset
@@ -55,11 +57,15 @@ import Clash.Class.Num (satSucc, SaturationMode(SatBound))
5557import Clash.Explicit.Signal
5658import Clash.Explicit.Synchronizer (dualFlipFlopSynchronizer )
5759import Clash.Promoted.Nat
60+ import Clash.Promoted.Nat.Literals
5861import Clash.Signal.Internal
5962import Clash.Sized.Index (Index )
63+ import Clash.Magic (clashCompileError )
6064
6165import GHC.Stack (HasCallStack )
62- import GHC.TypeLits (type (+ ), type (<= ))
66+ import GHC.TypeLits (type (+ ), type (- ), type (<= ))
67+
68+ import GHC.TypeLits.Extra (Max )
6369
6470{- $setup
6571>>> import Clash.Explicit.Prelude
@@ -399,7 +405,15 @@ resetGlitchFilter# SNat reg dffSync rstIn0 =
399405 SActiveHigh -> True
400406 SActiveLow -> False
401407
402- -- | Hold reset for a number of cycles relative to an incoming reset signal.
408+ -- | Hold/stretch reset for a number of cycles relative to an incoming reset
409+ -- signal.
410+ --
411+ -- __NB__: 'holdReset' is unsafe for asynchronous resets. It can cause
412+ -- spurious resets.
413+ -- For synchronous the output is pessimistic since it is driven by combinational
414+ -- logic.
415+ -- You probably want to use 'stretchReset' which is safe but has slightly
416+ -- different timing behaviour.
403417--
404418-- Example:
405419--
@@ -433,6 +447,135 @@ holdReset clk en SNat rst =
433447 counter :: Signal dom (Index (n + 1 ))
434448 counter = register clk rst en 0 (satSucc SatBound <$> counter)
435449
450+ -- | Hold/stretch reset for a number of cycles relative to an incoming reset
451+ -- signal. This is a safe replacement for `holdReset`. There are some
452+ -- differences in behaviour for synchronous resets and asynchronous resets.
453+ --
454+ -- Synchronous resets
455+ --
456+ -- If used with a synchronous reset the assertion of the reset is delayed by 1
457+ -- cycle. If your domain can support initial values the first clock cycle
458+ -- after boot the reset will be asserted. Afterwards the reset keeps being
459+ -- asserted for (n + 1) cycles.
460+ --
461+ -- Examples on a domain with synchronous reset and supporting initial values:
462+ --
463+ -- >>> let sampleWithReset = sampleN 8 . unsafeToActiveHigh
464+ -- >>> let stretchResetBy2 = stretchReset @XilinxSystem clockGen enableGen (SNat @2)
465+ -- >>> sampleWithReset (stretchResetBy2 (resetGenN (SNat @3)))
466+ -- [True,True,True,True,True,True,False,False]
467+ --
468+ -- The first 3 cycles the input reset is asserted and `stretchReset` should
469+ -- stretch it by 2 cycles. That results in a reset that is asserted for 6
470+ -- cycles. This additional cycle is due to the first cycle always being reset if
471+ -- initial values are supported. 'stretchReset' also works on intermediate
472+ -- assertions of the reset signal:
473+ --
474+ -- >>> let rst = fromList [False, False, False, True, False, False, False, False]
475+ -- >>> sampleWithReset (stretchResetBy2 (unsafeFromActiveHigh rst))
476+ -- [True,True,True,False,False,True,True,False]
477+ --
478+ -- Here it again shows that on boot the reset is asserted for (n + 1) cycles
479+ -- And then after 4 cycles the input reset is asserted. Notice the delay on
480+ -- the output reset.
481+ --
482+ -- Asynchronous resets
483+ --
484+ -- If used with an asynchronous reset the minimum stretch period is always three
485+ -- cycles. This is due to an additional reset synchronizer that is inserted.
486+ -- The synchronizer itself consumes two cycles and the stretch adds at least
487+ -- one cycle, hence the minimum of three. There is no delay between assertion
488+ -- of the input and the assertion of the output.
489+ --
490+ -- Example with asynchronous reset:
491+ --
492+ -- >>> let sampleWithReset = sampleN 9 . unsafeToActiveHigh
493+ -- >>> let stretchResetBy3 = stretchReset @System clockGen enableGen (SNat @3)
494+ -- >>> sampleWithReset (stretchResetBy3 (resetGenN (SNat @3)))
495+ -- [True,True,True,True,True,True,False,False,False]
496+ --
497+ -- 'stretchReset' holds the reset for an additional 3 clock cycles for a total
498+ -- of 6 clock cycles where the reset is asserted. 'stretchReset' also works on
499+ -- intermediate assertions of the reset signal:
500+ --
501+ -- >>> let rst = fromList [False, False, False, False, True, False, False, False, False]
502+ -- >>> sampleWithReset (stretchResetBy3 (unsafeFromActiveHigh rst))
503+ -- [True,True,True,False,True,True,True,True, False]
504+ --
505+ -- Notice here that on startup it always resets for exactly the specified amount
506+ -- of cycles. In addition it appears to reset for (n + 1) cycles on a trigger.
507+ -- However the first asserted cycle is the asynchronous assertion so we don't
508+ -- count it as a full cycle.
509+ --
510+ stretchReset
511+ :: forall dom n
512+ . KnownDomain dom
513+ => Clock dom
514+ -> Enable dom
515+ -- ^ Global enable
516+ -> SNat n
517+ -- ^ Hold for /n/ cycles
518+ -> Reset dom
519+ -- ^ Reset to extend
520+ -> Reset dom
521+ stretchReset clk en n@ SNat rst = case (resetKind @ dom , compareSNat d1 (SNat @ (Max 2 (Max 1 n - 1 )))) of
522+ (SSynchronous , _) -> stretchResetSync clk en n rst
523+ (SAsynchronous , SNatGT ) -> clashCompileError " stretchReset: Impossible 1 <= Max 2 (Max 1 n - 1) does not hold. Contact clash developers at: https://github.com/clash-lang/clash-compiler/issues"
524+ (SAsynchronous , SNatLE ) -> stretchResetAsync clk en n rst
525+
526+ stretchResetSync
527+ :: forall dom n
528+ . KnownDomain dom
529+ => DomainResetKind dom ~ 'Synchronous
530+ => Clock dom
531+ -> Enable dom
532+ -- ^ Global enable
533+ -> SNat n
534+ -- ^ Hold for /n/ cycles
535+ -> Reset dom
536+ -- ^ Reset to extend
537+ -> Reset dom
538+ stretchResetSync clk en SNat rst = unsafeToReset (delay clk en isActiveHigh rawRst)
539+ where
540+ counter :: Signal dom (Index (n + 1 ))
541+ counter = register clk rst en 0 (satSucc SatBound <$> counter)
542+ rawRst :: Signal dom Bool
543+ rawRst = case resetPolarity @ dom of
544+ SActiveHigh -> (/= maxBound ) <$> counter
545+ SActiveLow -> (== maxBound ) <$> counter
546+
547+ isActiveHigh = case resetPolarity @ dom of { SActiveHigh -> True ; _ -> False }
548+
549+ stretchResetAsync
550+ :: forall dom n
551+ . KnownDomain dom
552+ => DomainResetKind dom ~ 'Asynchronous
553+ => 1 <= Max 2 (Max 1 n - 1 )
554+ => Clock dom
555+ -> Enable dom
556+ -- ^ Global enable
557+ -> SNat n
558+ -- ^ Hold for /n/ cycles, counting from the moment the incoming reset
559+ -- signal becomes deasserted.
560+ -> Reset dom
561+ -- ^ Reset to extend
562+ -> Reset dom
563+ stretchResetAsync clk en SNat rst = rstOut
564+ where
565+ -- This ensures the index at least has one cycle for counting.
566+ counter :: Signal dom (Index ((Max 2 ((Max 1 n ) - 1 ))))
567+ counter = register clk rst en 0 (satSucc SatBound <$> counter)
568+ rawRst :: Signal dom Bool
569+ rawRst = case resetPolarity @ dom of
570+ SActiveHigh -> (/= maxBound ) <$> counter
571+ SActiveLow -> (== maxBound ) <$> counter
572+
573+ isActiveHigh = case resetPolarity @ dom of { SActiveHigh -> True ; _ -> False }
574+ rstOut = unsafeToReset
575+ $ register clk (unsafeToReset rawRst) en isActiveHigh
576+ $ register clk (unsafeToReset rawRst) en isActiveHigh
577+ $ pure (not isActiveHigh)
578+
436579-- | Convert between different types of reset, adding a synchronizer when
437580-- the domains are not the same. See 'resetSynchronizer' for further details
438581-- about reset synchronization.
0 commit comments