packages feed

retroclash-lib-0.1.0: src/RetroClash/BCD.hs

{-# OPTIONS_GHC -fconstraint-solver-iterations=5 #-}
module RetroClash.BCD
    ( Digit, toDigit
    , BCD
    , fromBCD
    , BCDSize
    , toBCD
    , ShiftAdd, initBCD, stepBCD
    , prop_BCD
    ) where

import Clash.Prelude hiding (shift, add)
import RetroClash.Utils

type Digit = Index 10
type BCD n = Vec n Digit

toDigit :: Unsigned 4 -> Digit
toDigit = bitCoerce

fromBCD :: BCD n -> Integer
fromBCD = foldl (\x d -> x * 10 + fromIntegral d) 0

type BCDSize n = CLog 10 (2 ^ n)
type ShiftAdd n = (Vec (BCDSize n) (Unsigned 4), Unsigned n)

{-# INLINE initBCD #-}
initBCD :: (KnownNat n) => Unsigned n -> ShiftAdd n
initBCD = (,) (repeat 0)

stepBCD :: (KnownNat n) => ShiftAdd n -> ShiftAdd n
stepBCD = shift . add
  where
    shift :: (KnownNat n) => ShiftAdd n -> ShiftAdd n
    shift = bitwise (`shiftL` 1)

    add :: ShiftAdd n -> ShiftAdd n
    add (digits, buf) = (map add3 digits, buf)
      where
        add3 d = if d >= 5 then d + 3 else d

{-# INLINE toBCD #-}
toBCD :: forall n. (KnownNat n) => Unsigned n -> BCD (BCDSize n)
toBCD = map toDigit . fst . last . iterate (SNat @(n + 1)) stepBCD . initBCD

roundtrip :: (KnownNat n) => Unsigned n -> Unsigned n
roundtrip = fromIntegral . fromBCD . map bitCoerce . toBCD

prop_BCD :: (KnownNat n) => Unsigned n -> Bool
prop_BCD x = x == roundtrip x