packages feed

emhell-0.1.1.0: src/EmHell/SVD/Manipulation.hs

{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE OverloadedStrings #-}
module EmHell.SVD.Manipulation
  ( setField
  ) where

import Control.Lens ((^.), view)
import Data.Bits (Bits, (.|.), (.&.), complement, shiftL)
import Data.SVD
import Prettyprinter
import Prettyprinter.Render.Terminal (AnsiStyle, Color(..), bold, color)
import Text.Printf (PrintfArg)
import qualified Data.Bits.Pretty
import qualified Data.Char

-- | Set @Field@ of a @Register@ to new value
-- and return new value of a whole register
-- or Left if field is not found or value
-- is too large to fit
setField
  :: ( Integral a
     , Bits a
     , PrintfArg a
     , Pretty a
     )
  => Register
  -> a -- ^ Old register value
  -> String -- ^ Field name
  -> a -- ^ Field value
  -> Either (Doc AnsiStyle) a
setField r oldRegVal fName v =
  case filter ((==fName) . (map Data.Char.toLower) . view name) (r ^. fields) of
    [f] ->
      let maxVal = fromIntegral ((2 :: Int) ^ (f ^. bitWidth) - 1)
      in
        if | v > maxVal ->
              Left $ vsep
                [ annotate
                    (color Red)
                    (   "Value too large for field "
                     <> annotate
                          bold
                          (pretty (f ^. name))
                     <> " with bit width of "
                     <> annotate
                          bold
                          (pretty (f ^. bitWidth))
                    )
                , annotate
                    (color Cyan)
                    (   "HEX "
                     <> pretty (Data.Bits.Pretty.formatHex v)
                     <> " ∉ [0.."
                     <> pretty (Data.Bits.Pretty.formatHex maxVal)
                     <> "]"
                    )
                , annotate
                    (color Green)
                    (   "DEC "
                     <> pretty v
                     <> " ∉ [0.."
                     <> pretty maxVal
                     <> "]"
                    )
                ]
           | r ^. access `elem` [ReadOnly, ReadWriteOnce, WriteOnce] ->
              Left
                $ annotate
                    (color Red)
                    (   "Register is"
                    <+> pretty (showAccessType (r ^. access))
                    )
           | f ^. reserved ->
              Left
                $ annotate (color Red) "Field is reserved"
           | otherwise ->
              pure $
                let pos = f ^. bitOffset
                    val = v `shiftL` pos
                    fmax = 2 ^ (f ^. bitWidth) - 1
                    mask = complement (fmax `shiftL` pos)
                in oldRegVal .&. mask .|. val
    _ -> Left "Field not found"