{-# LANGUAGE CPP #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE Safe #-}
{-# OPTIONS_GHC -fplugin GHC.TypeLits.Normalise #-}
{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
{-# OPTIONS_HADDOCK show-extensions #-}
module Clash.Explicit.Synchronizer
(
dualFlipFlopSynchronizer
, asyncFIFOSynchronizer
)
where
import Control.Applicative (liftA2)
import Data.Bits (complement, shiftR, xor)
import Data.Constraint ((:-)(..), Dict (..))
import Data.Constraint.Nat (leTrans)
import Data.Maybe (isJust)
import GHC.TypeLits (type (+), type (-), type (<=))
import Clash.Class.BitPack (boolToBV)
import Clash.Class.Resize (truncateB)
import Clash.Prelude.BitIndex (slice)
import Clash.Explicit.Mealy (mealyB)
import Clash.Explicit.RAM (asyncRam)
import Clash.Explicit.Signal
(Clock, Reset, Signal, Enable, register, unsafeSynchronizer)
import Clash.Promoted.Nat (SNat (..), pow2SNat)
import Clash.Promoted.Nat.Literals (d0)
import Clash.Signal (mux, KnownDomain)
import Clash.Sized.BitVector (BitVector, (++#))
import Clash.XException (NFDataX)
dualFlipFlopSynchronizer
:: ( NFDataX a
, KnownDomain dom1
, KnownDomain dom2 )
=> Clock dom1
-> Clock dom2
-> Reset dom2
-> Enable dom2
-> a
-> Signal dom1 a
-> Signal dom2 a
dualFlipFlopSynchronizer clk1 clk2 rst en i =
register clk2 rst en i
. register clk2 rst en i
. unsafeSynchronizer clk1 clk2
fifoMem
:: ( KnownDomain wdom
, KnownDomain rdom )
=> Clock wdom
-> Clock rdom
-> Enable wdom
-> SNat addrSize
-> Signal wdom Bool
-> Signal rdom (BitVector addrSize)
-> Signal wdom (Maybe (BitVector addrSize, a))
-> Signal rdom a
fifoMem wclk rclk en addrSize@SNat full raddr writeM =
asyncRam
wclk rclk en
(pow2SNat addrSize)
raddr
(mux full (pure Nothing) writeM)
ptrCompareT
:: SNat addrSize
-> (BitVector (addrSize + 1) -> BitVector (addrSize + 1) -> Bool)
-> ( BitVector (addrSize + 1)
, BitVector (addrSize + 1)
, Bool )
-> ( BitVector (addrSize + 1)
, Bool )
-> ( ( BitVector (addrSize + 1)
, BitVector (addrSize + 1)
, Bool )
, ( Bool
, BitVector addrSize
, BitVector (addrSize + 1)
)
)
ptrCompareT SNat flagGen (bin, ptr, flag) (s_ptr, inc) =
((bin', ptr', flag'), (flag, addr, ptr))
where
bin' = bin + boolToBV (inc && not flag)
ptr' = (bin' `shiftR` 1) `xor` bin'
addr = truncateB bin
flag' = flagGen ptr' s_ptr
isFull
:: forall addrSize
. (2 <= addrSize)
=> SNat addrSize
-> BitVector (addrSize + 1)
-> BitVector (addrSize + 1)
-> Bool
isFull addrSize@SNat ptr s_ptr =
case leTrans @1 @2 @addrSize of
Sub Dict ->
let a1 = SNat @(addrSize - 1)
a2 = SNat @(addrSize - 2)
in ptr == (complement (slice addrSize a1 s_ptr) ++# slice a2 d0 s_ptr)
asyncFIFOSynchronizer
:: ( KnownDomain wdom
, KnownDomain rdom
, 2 <= addrSize )
=> SNat addrSize
-> Clock wdom
-> Clock rdom
-> Reset wdom
-> Reset rdom
-> Enable wdom
-> Enable rdom
-> Signal rdom Bool
-> Signal wdom (Maybe a)
-> (Signal rdom a, Signal rdom Bool, Signal wdom Bool)
asyncFIFOSynchronizer addrSize@SNat wclk rclk wrst rrst wen ren rinc wdataM =
(rdata, rempty, wfull)
where
s_rptr = dualFlipFlopSynchronizer rclk wclk wrst wen 0 rptr
s_wptr = dualFlipFlopSynchronizer wclk rclk rrst ren 0 wptr
rdata =
fifoMem
wclk rclk wen
addrSize wfull raddr
(liftA2 (,) <$> (pure <$> waddr) <*> wdataM)
(rempty, raddr, rptr) =
mealyB
rclk rrst ren
(ptrCompareT addrSize (==))
(0, 0, True)
(s_wptr, rinc)
(wfull, waddr, wptr) =
mealyB
wclk wrst wen
(ptrCompareT addrSize (isFull addrSize))
(0, 0, False)
(s_rptr, isJust <$> wdataM)