11{-|
2- Copyright : (C) 2020-2023 , QBayLogic B.V.,
2+ Copyright : (C) 2020-2026 , QBayLogic B.V.,
33 2022-2023, Google LLC
44License : BSD2 (see the file LICENSE)
55Maintainer : QBayLogic B.V. <devops@qbaylogic.com>
@@ -24,6 +24,7 @@ module Clash.Explicit.Reset
2424 , resetGlitchFilterWithReset
2525 , unsafeResetGlitchFilter
2626 , holdReset
27+ , stretchReset
2728 , convertReset
2829 , noReset
2930 , andReset , unsafeAndReset
@@ -55,8 +56,10 @@ import Clash.Class.Num (satSucc, SaturationMode(SatBound))
5556import Clash.Explicit.Signal
5657import Clash.Explicit.Synchronizer (dualFlipFlopSynchronizer )
5758import Clash.Promoted.Nat
59+ import Clash.Promoted.Nat.Literals
5860import Clash.Signal.Internal
5961import Clash.Sized.Index (Index )
62+ import Clash.Magic (clashCompileError )
6063
6164import GHC.Stack (HasCallStack )
6265import GHC.TypeLits (type (+ ), type (<= ))
@@ -399,20 +402,28 @@ resetGlitchFilter# SNat reg dffSync rstIn0 =
399402 SActiveHigh -> True
400403 SActiveLow -> False
401404
402- -- | Hold reset for a number of cycles relative to an incoming reset signal.
405+ -- | Hold/stretch reset for a number of cycles relative to an incoming reset
406+ -- signal.
407+ --
408+ -- __NB__: 'holdReset' is unsafe for asynchronous resets. It can cause
409+ -- spurious resets.
410+ -- For synchronous resets the output is pessimistic since it is driven by
411+ -- combinational logic.
412+ -- You probably want to use 'stretchReset' which is safe but has slightly
413+ -- different timing behaviour.
403414--
404415-- Example:
405416--
406- -- >>> let sampleWithReset = sampleN 8 . unsafeToActiveHigh
407- -- >>> sampleWithReset (holdReset @System clockGen enableGen (SNat @2) (resetGenN (SNat @3)))
417+ -- >>> let sampleReset = sampleN 8 . unsafeToActiveHigh
418+ -- >>> sampleReset (holdReset @System clockGen enableGen (SNat @2) (resetGenN (SNat @3)))
408419-- [True,True,True,True,True,False,False,False]
409420--
410421-- 'holdReset' holds the reset for an additional 2 clock cycles for a total
411422-- of 5 clock cycles where the reset is asserted. 'holdReset' also works on
412423-- intermediate assertions of the reset signal:
413424--
414425-- >>> let rst = fromList [True, False, False, False, True, False, False, False]
415- -- >>> sampleWithReset (holdReset @System clockGen enableGen (SNat @2) (unsafeFromActiveHigh rst))
426+ -- >>> sampleReset (holdReset @System clockGen enableGen (SNat @2) (unsafeFromActiveHigh rst))
416427-- [True,True,True,False,True,True,True,False]
417428--
418429holdReset
@@ -433,6 +444,120 @@ holdReset clk en SNat rst =
433444 counter :: Signal dom (Index (n + 1 ))
434445 counter = register clk rst en 0 (satSucc SatBound <$> counter)
435446
447+ -- | Hold/stretch reset for a number of cycles relative to an incoming reset
448+ -- signal. This is a safe replacement for `holdReset`. There are some
449+ -- differences in behaviour for synchronous resets and asynchronous resets.
450+ -- If used with a stretch of 0 the function is equivalent to doing nothing to
451+ -- the reset.
452+ --
453+ -- === Synchronous resets
454+ --
455+ -- If used with a domain on a synchronous reset the assertion of the reset is
456+ -- delayed by one cycle relative to the incoming reset. On startup the reset is
457+ -- asserted for @(n + 1)@ cycles.
458+ --
459+ -- >>> let sampleReset = sampleN 9 . unsafeToActiveHigh
460+ -- >>> let stretchResetBy2 = stretchReset @XilinxSystem clockGen enableGen (SNat @2)
461+ -- >>> sampleReset (stretchResetBy2 (resetGenN (SNat @3)))
462+ -- [True,True,True,True,True,True,False,False,False]
463+ --
464+ -- The reset assertion sequence is:
465+ --
466+ -- - The first cycle the output of the internal register is given.s
467+ -- - The next three cycles the reset is asserted due to the incoming reset
468+ -- being asserted.
469+ -- - The final two assertions are due to the reset being stretched by two cycles.
470+ --
471+ -- >>> let rst = fromList [False, False, False, True, False, False, False, False, False]
472+ -- >>> sampleReset (stretchResetBy2 (unsafeFromActiveHigh rst))
473+ -- [True,True,True,False,True,True,True,False,False]
474+ --
475+ -- Here it shows that on startup the reset is asserted for @(n + 1) = 3@ cycles
476+ -- On the fourth cycle the input reset is asserted. Notice the delay on
477+ -- the output reset assertion and that is stretch by two cycles.
478+ --
479+ -- === Asynchronous reset
480+ --
481+ -- In contrast to the synchronous version there is no single cycle delay from
482+ -- input reset assertion to output reset assertion. On startup it also asserts
483+ -- the reset for exactly the stretch period @n@.
484+ --
485+ -- >>> let rst = fromList [False, False, False, False, True, False, False, False, False]
486+ -- >>> let stretchResetBy2 = stretchReset @System clockGen enableGen (SNat @2)
487+ -- >>> sampleReset (stretchResetBy2 (unsafeFromActiveHigh rst))
488+ -- [True,True,False,False,True,True,True,False,False]
489+ --
490+ stretchReset
491+ :: forall dom n
492+ . KnownDomain dom
493+ => Clock dom
494+ -> Enable dom
495+ -- ^ Global enable
496+ -> SNat n
497+ -- ^ Hold for /n/ cycles
498+ -> Reset dom
499+ -- ^ Reset to extend
500+ -> Reset dom
501+ stretchReset clk en n@ SNat rst = case resetKind @ dom of
502+ SSynchronous -> stretchResetSync clk en n rst
503+ SAsynchronous -> stretchResetAsync clk en n rst
504+
505+ stretchResetSync
506+ :: forall dom n
507+ . KnownDomain dom
508+ => DomainResetKind dom ~ 'Synchronous
509+ => Clock dom
510+ -> Enable dom
511+ -- ^ Global enable
512+ -> SNat n
513+ -- ^ Hold for /n/ cycles
514+ -> Reset dom
515+ -- ^ Reset to extend
516+ -> Reset dom
517+ stretchResetSync clk en sn@ SNat rst = rstOut
518+ where
519+ isActiveHigh = case resetPolarity @ dom of { SActiveHigh -> True ; _ -> False }
520+
521+ rstOut = case compareSNat d1 sn of
522+ SNatGT -> rst
523+ SNatLE -> unsafeToReset (register clk rst en isActiveHigh rawRst)
524+ where
525+ counter :: Signal dom (Index (n + 1 ))
526+ counter = register clk rst en 0 (satSucc SatBound <$> counter)
527+ rawRst :: Signal dom Bool
528+ rawRst = case resetPolarity @ dom of
529+ SActiveHigh -> (/= maxBound ) <$> counter
530+ SActiveLow -> (== maxBound ) <$> counter
531+
532+ stretchResetAsync
533+ :: forall dom n
534+ . KnownDomain dom
535+ => DomainResetKind dom ~ 'Asynchronous
536+ => Clock dom
537+ -> Enable dom
538+ -- ^ Global enable
539+ -> SNat n
540+ -- ^ Hold for /n/ cycles
541+ -> Reset dom
542+ -- ^ Reset to extend
543+ -> Reset dom
544+ stretchResetAsync clk en sn@ SNat rst = rstOut
545+ where
546+ isActiveHigh = case resetPolarity @ dom of { SActiveHigh -> True ; _ -> False }
547+ rstOut = case (snatToInteger sn, compareSNat d2 sn) of
548+ (0 , _) -> rst
549+ (1 , _) -> unsafeToReset (register clk rst en isActiveHigh (pure (not isActiveHigh)))
550+ (_, SNatLE ) -> unsafeToReset (register clk rst en isActiveHigh rawRst)
551+ where
552+ counter :: Signal dom (Index n )
553+ counter = register clk rst en 0 (satSucc SatBound <$> counter)
554+ rawRst :: Signal dom Bool
555+ rawRst = case resetPolarity @ dom of
556+ SActiveHigh -> (/= maxBound ) <$> counter
557+ SActiveLow -> (== maxBound ) <$> counter
558+ (_, SNatGT ) -> clashCompileError " stretchResetAsync: Impossbile! n =! 0 && n != 1 && 2 > n, Please report this at https://github.com/clash-lang/clash-compiler/issues."
559+
560+
436561-- | Convert between different types of reset, adding a synchronizer when
437562-- the domains are not the same. See 'resetSynchronizer' for further details
438563-- about reset synchronization.
0 commit comments