{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeFamilies #-}
module Clash.Class.Counter.Internal where
import Clash.CPP (maxTupleSize)
import Clash.Class.Counter.TH (genTupleInstances)
import Clash.Sized.BitVector (BitVector)
import Clash.Sized.Index (Index)
import Clash.Sized.Signed (Signed)
import Clash.Sized.Unsigned (Unsigned)
import Data.Bifunctor (bimap)
import GHC.TypeLits (KnownNat, type (<=))
-- $setup
-- >>> import Clash.Class.Counter
-- >>> import Clash.Sized.BitVector (BitVector)
-- >>> import Clash.Sized.Index (Index)
-- >>> import Clash.Sized.Signed (Signed)
-- >>> import Clash.Sized.Unsigned (Unsigned)
-- | 'Clash.Class.Counter.Counter' is a class that composes multiple counters
-- into a single one. It is similar to odometers found in olds cars,
-- once all counters reach their maximum they reset to zero - i.e. odometer
-- rollover. See 'Clash.Class.Counter.countSucc' and 'Clash.Class.Counter.countPred'
-- for API usage examples.
--
-- Example use case: when driving a monitor through VGA you would like to keep
-- track at least two counters: one counting a horizontal position, and one
-- vertical. Perhaps a fancy VGA driver would also like to keep track of the
-- number of drawn frames. To do so, the three counters are setup with different
-- types. On each /round/ of the horizontal counter the vertical counter should
-- be increased. On each /round/ of the vertical counter the frame counter should
-- be increased. With this class you could simply use the type:
--
-- @
-- (FrameCount, VerticalCount, HorizontalCount)
-- @
--
-- and have 'Clash.Class.Counter.countSucc' work as described.
--
-- __N.B.__: This class exposes four functions 'countMin', 'countMax',
-- 'countSuccOverflow', and 'countPredOverflow'. These functions are considered
-- an internal API. Users are encouraged to use 'Clash.Class.Counter.countSucc'
-- and 'Clash.Class.Counter.countPred'.
--
class Counter a where
-- | Value counter wraps around to on a 'countSuccOverflow' overflow
countMin :: a
default countMin :: Bounded a => a
countMin = minBound
-- | Value counter wraps around to on a 'countPredOverflow' overflow
countMax :: a
default countMax :: Bounded a => a
countMax = maxBound
-- | Gets the successor of @a@. If it overflows, the left part of the tuple
-- will be set to True.
countSuccOverflow :: a -> (Bool, a)
default countSuccOverflow :: (Eq a, Enum a, Bounded a) => a -> (Bool, a)
countSuccOverflow a
| a == maxBound = (True, countMin)
| otherwise = (False, succ a)
-- | Gets the predecessor of @a@. If it overflows, the left part of the tuple
-- will be set to True.
countPredOverflow :: a -> (Bool, a)
default countPredOverflow :: (Eq a, Enum a, Bounded a) => a -> (Bool, a)
countPredOverflow a
| a == minBound = (True, countMax)
| otherwise = (False, pred a)
instance (1 <= n, KnownNat n) => Counter (Index n)
instance KnownNat n => Counter (Unsigned n)
instance KnownNat n => Counter (Signed n)
instance KnownNat n => Counter (BitVector n)
-- | Counter instance that flip-flops between 'Left' and 'Right'. Examples:
--
-- >>> type T = Either (Index 2) (Unsigned 2)
-- >>> countSucc @T (Left 0)
-- Left 1
-- >>> countSucc @T (Left 1)
-- Right 0
-- >>> countSucc @T (Right 0)
-- Right 1
instance (Counter a, Counter b) => Counter (Either a b) where
countMin = Left countMin
countMax = Right countMax
countSuccOverflow e =
case bimap countSuccOverflow countSuccOverflow e of
Left (overflow, a) -> (False, if overflow then Right countMin else Left a)
Right (overflow, b) -> (overflow, if overflow then Left countMin else Right b)
countPredOverflow e =
case bimap countPredOverflow countPredOverflow e of
Left (overflow, a) -> (overflow, if overflow then Right countMax else Left a)
Right (overflow, b) -> (False, if overflow then Left countMax else Right b)
-- | Counters on tuples increment from right-to-left. This makes sense from the
-- perspective of LSB/MSB; MSB is on the left-hand-side and LSB is on the
-- right-hand-side in other Clash types.
--
-- >>> type T = (Unsigned 2, Index 2, Index 2)
-- >>> countSucc @T (0, 0, 0)
-- (0,0,1)
-- >>> countSucc @T (0, 0, 1)
-- (0,1,0)
-- >>> countSucc @T (0, 1, 0)
-- (0,1,1)
-- >>> countSucc @T (0, 1, 1)
-- (1,0,0)
--
-- __N.B.__: The documentation only shows the instances up to /3/-tuples. By
-- default, instances up to and including /12/-tuples will exist. If the flag
-- @large-tuples@ is set instances up to the GHC imposed limit will exist. The
-- GHC imposed limit is either 62 or 64 depending on the GHC version.
instance (Counter a0, Counter a1) => Counter (a0, a1) where
-- a0/a1 instead of a/b to be consistent with TH generated instances
countMin = (countMin, countMin)
countMax = (countMax, countMax)
countSuccOverflow (a0, b0) =
if overflowB
then (overflowA, (a1, b1))
else (overflowB, (a0, b1))
where
(overflowB, b1) = countSuccOverflow b0
(overflowA, a1) = countSuccOverflow a0
countPredOverflow (a0, b0) =
if overflowB
then (overflowA, (a1, b1))
else (overflowB, (a0, b1))
where
(overflowB, b1) = countPredOverflow b0
(overflowA, a1) = countPredOverflow a0
genTupleInstances maxTupleSize