kansas-lava-cores-0.1.2: Hardware/KansasLava/Rate.hs
{-# LANGUAGE RankNTypes, TypeFamilies, ScopedTypeVariables #-}
-- | The 'Clock' module provides a utility function for simulating clock rate
-- downsampling.
module Hardware.KansasLava.Rate(rate, powerOfTwoRate, rateP, throttleP) where
import Data.Ratio
import Data.Sized.Unsigned
import Data.Sized.Signed
import Data.Sized.Ix
import Language.KansasLava
-- | 'rate' constructs a stream of enable bits used for clock-rate
-- downsampling. For example, with a rate of n=1/2, every other value in the
-- output stream will be True. If 1/n is not a integer, then the function uses
-- http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm to approximate the
-- given rate.
rate :: forall x clk . (Clock clk, Size x) => Witness x -> Rational -> (Signal clk Bool)
rate Witness n
| step > 2^sz = error $ "bit-size " ++ show sz ++ " too small for punctuate Witness " ++ show n
| n <= 0 = error "can not have rate less than or equal zero"
| n > 1 = error $ "can not have rate greater than 1, requesting " ++ show n
-- for power of two, a simple counter works
| num == 1 && step == 2^sz = runRTL $ do
count <- newReg (0 :: (Unsigned x))
count := reg count + 1
return (reg count .==. 0)
| num == 1 = runRTL $ do
count <- newReg (0 :: (Unsigned x))
CASE [ IF (reg count .<. (fromIntegral step - 1)) $
count := reg count + 1
, OTHERWISE $ do
count := 0
]
return (reg count .==. 0)
-- inexact reciprocal, so use Bresenham's to approximate things.
| otherwise = runRTL $ do
count <- newReg (0 :: (Unsigned x))
cut <- newReg (0 :: (Unsigned x))
err <- newReg (0 :: (Signed x))
CASE [ IF (reg count .<. (fromIntegral step + reg cut - 1)) $
count := reg count + 1
, OTHERWISE $ do
count := 0
CASE [ IF (reg err .>. 0) $ do
cut := 1
err := reg err + fromIntegral nerr
, OTHERWISE $ do
cut := 0
err := reg err + fromIntegral perr
]
]
return (reg count .==. 0)
where sz :: Integer
sz = fromIntegral (size (error "witness" :: x))
num = numerator n
dom = denominator n
step = floor (1 / n)
perr = dom - step * num
nerr = dom - (step + 1) * num
-- | 'powerOfTwoRate' generates a pulse every 2^n cycles, which is often good enough for polling, timeouts, etc.
powerOfTwoRate :: forall x clk . (Clock clk, Size x) => Witness x -> Signal clk Bool
powerOfTwoRate Witness = rate (Witness :: Witness x) (1/(2^(fromIntegral (size (error "Witness" :: x)))))
-- | 'rateP' takes a result from rate, and generates token, one per pulse, with
-- unused tokens being discared.
rateP :: forall c sig . (Clock c, sig ~ Signal c)
=> sig Bool
-> Patch () (sig (Enabled ()))
() (sig Ack)
rateP r = outputP (packEnabled r $ pureS ()) $$ enabledToAckBox
-- | 'throttleP' throttles input based on a given rate counter.
throttleP :: forall sig c a x . (sig ~ Signal c, Clock c, Rep a)
=> sig Bool
-> Patch (sig (Enabled a)) (sig (Enabled a))
(sig Ack) (sig Ack)
throttleP in_pred
= openP $$
(top `stackP` emptyP) $$
zipP $$
mapP (\ ab -> snd (unpack ab))
where
top = outputP (packEnabled in_pred (pureS ())) $$
enabledToAckBox
{-
-- Wrong, omit for this release.
--
-- | 'accurateTo' rounds up/down a number within a range,
-- in an attempt to be a integral reciprical (and therefore cheaper to implement in hardware).
--accurateTo :: Rational -> Rational -> Rational
accurateTo n ac
| diff > (1-ac) = error $ "can not find tolerance for "
++ show n ++ " : need " ++ show (fromRational (1 - diff) :: Float)
| otherwise = nR
where
reci = 1 / n
nR = 1 / (fromInteger $ round reci)
diff = abs (n - nR)
-}