packages feed

data-svd-0.1.0.0: src/Data/SVD/Dim.hs

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Data.SVD.Dim
  ( expandDevice
  -- * For testing
  , expandCluster
  , expandField
  , expandRegister
  ) where

import Control.Lens ((^.), set, over)
import Data.SVD.Lens
import Data.SVD.Types

-- * Dimension expansion

-- Expand @Cluster@, @Field@, @Register@ into multiples
-- according to its @Dimension@
--
-- If @Dimension@ is nothing return singleton with the original
expandDim
  :: ( HasName a String
     , HasDescription a String
     , HasDimension a (Maybe Dimension)
     )
  => (a -> Int) -- ^ Address offset or bit offset getter
  -> (Int -> a -> a) -- ^ Address offset or bit offset setter
  -> a
  -> [a]
expandDim getOffset setOffset element =
  case element ^. dimension of
    Nothing -> pure element
    Just dim ->
      let ixs = case dim ^. index of
            DimensionIndex_FromTo f t -> map show [f .. t]
            DimensionIndex_List l -> l

          gen z i ix =
            let nameTemplate = z ^. name
                descTemplate = z ^. description
                baseOffset = getOffset z
                template ('%':'s':xs) replacement = replacement ++ xs
                template (x:xs) replacement = x:(template xs replacement)
                template [] _ = mempty
            in
                setOffset
                  (baseOffset + dim ^. increment * i)
              . set
                  name
                  (template nameTemplate ix)
              . set
                  description
                  (template descTemplate ix)
              $ set
                  dimension
                  Nothing
                  z
      in
        [ gen element i ix | (i, ix) <- zip [0..] ixs ]

-- | Expand @Field@ into multiple fields if it has a @Dimension@
-- return just the field if not
expandField :: Field -> [Field]
expandField = expandDim (^. bitOffset) (set bitOffset)

-- | Expand @Cluster@ into multiple cluster if it has a @Dimension@
-- return just the cluster if not
expandCluster :: Cluster -> [Cluster]
expandCluster = expandDim (^. addressOffset) (set addressOffset)

-- | Expand @Register@ into multiple registers if it has a @Dimension@
-- return just the register if not
expandRegister :: Register -> [Register]
expandRegister = expandDim (^. addressOffset) (set addressOffset)

-- | Expand all fields of a register
expandRegFields :: Register -> Register
expandRegFields r =
  set
    fields
    (concatMap expandField (r ^. fields))
    r

-- | Expand all registers of a peripheral
expandPeriphRegisters :: Peripheral -> Peripheral
expandPeriphRegisters p =
  set
    registers
    (concatMap expandRegister (p ^. registers))
    p

-- | Expand all cluster of a peripheral
-- then eliminate all of them into registers
expandPeriphClusters :: Peripheral -> Peripheral
expandPeriphClusters p =
  set
    clusters
    mempty
  $ set
      registers
      (let
           expClusters =
             concatMap
               expandCluster
               (p ^. clusters)
        in
          (p ^. registers)
          ++ concatMap
              eliminateCluster
              expClusters
      )
      p

-- | Turn expanded @Cluster@ into @Register@s
-- adding its addressOffset to each registers addressOffset
eliminateCluster :: Cluster -> [Register]
eliminateCluster c =
  map
    (\r ->
      over
        addressOffset
        (+(c ^. addressOffset))
        r
    )
  $ c ^. registers

-- | Expand all dimensions and clusters
--
-- In order
-- - Expand and eliminate each cluster
-- - Expand fields of each register
-- - Expand each register
expandDevice :: Device -> Device
expandDevice =
    over
      (peripherals . traverse)
      expandPeriphRegisters
  . over
      (peripherals . traverse . registers . traverse)
      expandRegFields
  . over
      (peripherals . traverse)
      expandPeriphClusters