{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
#if !MIN_VERSION_constraints(0,9,0)
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE PolyKinds #-}
#endif
#if MIN_VERSION_constraints(0,9,0)
{-# LANGUAGE Safe #-}
#else
{-# LANGUAGE Trustworthy #-}
#endif
{-# 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 (..))
#if MIN_VERSION_constraints(0,9,0)
import Data.Constraint.Nat (leTrans)
#else
import Unsafe.Coerce
#endif
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, register, unsafeSynchronizer)
import Clash.Promoted.Nat (SNat (..), pow2SNat)
import Clash.Promoted.Nat.Literals (d0)
import Clash.Signal (mux)
import Clash.Sized.BitVector (BitVector, (++#))
dualFlipFlopSynchronizer
:: Clock domain1 gated1
-> Clock domain2 gated2
-> Reset domain2 synchronous
-> a
-> Signal domain1 a
-> Signal domain2 a
dualFlipFlopSynchronizer clk1 clk2 rst i =
register clk2 rst i . register clk2 rst i . unsafeSynchronizer clk1 clk2
fifoMem
:: Clock wdomain wgated
-> Clock rdomain rgated
-> SNat addrSize
-> Signal wdomain Bool
-> Signal rdomain (BitVector addrSize)
-> Signal wdomain (Maybe (BitVector addrSize, a))
-> Signal rdomain a
fifoMem wclk rclk addrSize@SNat full raddr writeM =
asyncRam wclk rclk
(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
:: (2 <= addrSize)
=> SNat addrSize
-> Clock wdomain wgated
-> Clock rdomain rgated
-> Reset wdomain synchronous
-> Reset rdomain synchronous
-> Signal rdomain Bool
-> Signal wdomain (Maybe a)
-> (Signal rdomain a, Signal rdomain Bool, Signal wdomain Bool)
asyncFIFOSynchronizer addrSize@SNat wclk rclk wrst rrst rinc wdataM =
(rdata,rempty,wfull)
where
s_rptr = dualFlipFlopSynchronizer rclk wclk wrst 0 rptr
s_wptr = dualFlipFlopSynchronizer wclk rclk rrst 0 wptr
rdata = fifoMem wclk rclk addrSize wfull raddr
(liftA2 (,) <$> (pure <$> waddr) <*> wdataM)
(rempty,raddr,rptr) = mealyB rclk rrst (ptrCompareT addrSize (==)) (0,0,True)
(s_wptr,rinc)
(wfull,waddr,wptr) = mealyB wclk wrst (ptrCompareT addrSize (isFull addrSize))
(0,0,False) (s_rptr,isJust <$> wdataM)
#if !MIN_VERSION_constraints(0,9,0)
axiom :: forall a b . Dict (a ~ b)
axiom = unsafeCoerce (Dict :: Dict (a ~ a))
axiomLe :: forall a b. Dict (a <= b)
axiomLe = axiom
leTrans :: forall a b c. (b <= c, a <= b) :- (a <= c)
leTrans = Sub (axiomLe @a @c)
#endif