{-# 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)
-}