circus-0.1.0.0: src/Circus/Types.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
module Circus.Types where
import Control.Applicative (empty)
import Data.Aeson
import Data.Aeson.TH
import Data.Aeson.Types (toJSONKeyText)
import Data.ByteString.Lazy (ByteString)
import qualified Data.ByteString.Lazy.Char8 as BS
import Data.Char
import Data.Data (Data)
import Data.Map (Map)
import qualified Data.Map as M
import Data.String (IsString)
import Data.Text (Text)
import qualified Data.Text as T
import GHC.Generics (Generic)
------------------------------------------------------------------------------
-- | A collection of modules.
newtype Schema = Schema
{ schemaModules :: Map ModuleName Module
}
deriving stock (Eq, Show, Data)
deriving newtype (Semigroup, Monoid)
data Module = Module
{ -- | Inputs and outputs
modulePorts :: Map PortName Port
-- | Components
, moduleCells :: Map CellName Cell
}
deriving stock (Eq, Show, Data)
instance Semigroup Module where
(<>) (Module p1 c1) (Module p2 c2) = Module
{ modulePorts = p1 <> p2
, moduleCells = c1 <> c2
}
instance Monoid Module where
mempty = Module {modulePorts = mempty, moduleCells = mempty}
newtype ModuleName = ModuleName { getModuleName :: Text }
deriving stock (Data)
deriving newtype (Eq, Ord, Show, IsString, ToJSONKey, FromJSONKey, FromJSON, ToJSON)
newtype PortName = PortName { getPortName :: Text }
deriving stock (Data)
deriving newtype (Eq, Ord, Show, IsString, ToJSONKey, FromJSONKey, FromJSON, ToJSON)
newtype CellName = CellName { getCellName :: Text }
deriving stock (Data)
deriving newtype (Eq, Ord, Show, IsString, ToJSONKey, FromJSONKey, FromJSON, ToJSON)
data Port = Port
{ -- | Whether this port is an input or an output
portDirection :: Direction
-- | The individual wires connected to this port. They are numbered in the
-- same order they are described here.
, portBits :: [Bit]
}
deriving stock (Eq, Show, Data)
------------------------------------------------------------------------------
-- | A single wire. Bits are defined implicitly by a unique ID. Every component
-- that references the bit will be connected with a common node.
newtype Bit = Bit { getBit :: Int }
deriving stock (Eq, Ord, Show, Data)
deriving newtype (Num, ToJSON, FromJSON)
data Cell = Cell
{ -- | The symbol to use when drawing this cell.
cellType :: CellType
, cellParameters :: Map Parameter Int
, cellAttributes :: Map Text Value
-- | Which ports are inputs and outputs.
, cellPortDirections :: Map PortName Direction
-- | What are the ports connected to? Each port may connect to several
-- bits, but make sure you set the 'Width' cell 'Parameter' if this is the
-- case.
, cellConnections :: Map PortName [Bit]
}
deriving stock (Eq, Show, Data)
data Parameter
= -- | How many bits wide is the given 'Port'?
Width PortName
-- | Is the given 'Port' signed?
| Signed PortName
deriving stock (Eq, Ord, Show, Generic, Data)
deriving anyclass (FromJSON, ToJSON)
instance ToJSONKey Parameter where
toJSONKey = toJSONKeyText $ \case
Width pn -> getPortName pn <> "_WIDTH"
Signed pn -> getPortName pn <> "_SIGNED"
toJSONKeyList = error "toJSONKeyList called on Parameter"
instance FromJSONKey Parameter where
fromJSONKey = undefined
data Direction
= Input
| Output
deriving stock (Eq, Ord, Show, Read, Enum, Bounded, Data)
instance ToJSON Direction where
toJSON Input = String "input"
toJSON Output = String "output"
instance FromJSON Direction where
parseJSON = withText "Direction" $ \case
"input" -> pure Input
"output" -> pure Output
_ -> empty
------------------------------------------------------------------------------
-- | Master list of cells, and their associated names is available here:
--
-- https://raw.githubusercontent.com/nturley/netlistsvg/master/lib/default.svg?sanitize=true
data CellType = CellGeneric Text
deriving stock (Eq, Ord, Show, Data)
pattern CellMux :: CellType
pattern CellMux = CellGeneric "$mux"
pattern CellMuxBus :: CellType
pattern CellMuxBus = CellGeneric "$mux-bus"
pattern CellTribuf :: CellType
pattern CellTribuf = CellGeneric "$tribuf"
pattern CellAnd :: CellType
pattern CellAnd = CellGeneric "$and"
pattern CellOr :: CellType
pattern CellOr = CellGeneric "$or"
pattern CellNand :: CellType
pattern CellNand = CellGeneric "$nand"
pattern CellNor :: CellType
pattern CellNor = CellGeneric "$nor"
pattern CellXor :: CellType
pattern CellXor = CellGeneric "$xor"
pattern CellXnor :: CellType
pattern CellXnor = CellGeneric "$xnor"
pattern CellNot :: CellType
pattern CellNot = CellGeneric "$not"
pattern CellAdd :: CellType
pattern CellAdd = CellGeneric "$add"
pattern CellEq :: CellType
pattern CellEq = CellGeneric "$eq"
pattern CellDff :: CellType
pattern CellDff = CellGeneric "$dff"
pattern CellDffn :: CellType
pattern CellDffn = CellGeneric "$dffn-bus"
pattern CellLt :: CellType
pattern CellLt = CellGeneric "$lt"
pattern CellGe :: CellType
pattern CellGe = CellGeneric "$ge"
pattern CellConstant :: CellType
pattern CellConstant = CellGeneric "$_constant_"
instance ToJSON CellType where
toJSON (CellGeneric t) = String t
instance FromJSON CellType where
parseJSON = withText "CellType" $ pure . CellGeneric
$(deriveJSON
defaultOptions
{ fieldLabelModifier = camelTo2 '_' . drop 4
, constructorTagModifier = map toLower
}
''Cell
)
$(deriveJSON
defaultOptions
{ fieldLabelModifier = camelTo2 '_' . drop 4
, constructorTagModifier = map toLower
}
''Port
)
$(deriveJSON
defaultOptions
{ fieldLabelModifier = camelTo2 '_' . drop 6
, constructorTagModifier = map toLower
}
''Module
)
$(deriveJSON
defaultOptions
{ fieldLabelModifier = camelTo2 '_' . drop 6
, constructorTagModifier = map toLower
}
''Schema
)
renderModuleBS :: Module -> ByteString
renderModuleBS
= encode
. Schema
. M.singleton "module"
renderModuleString :: Module -> String
renderModuleString
= BS.unpack
. renderModuleBS
------------------------------------------------------------------------------
-- | Helper function for constructing 'Cell's.
mkCell
:: CellType
-> M.Map PortName (Direction, [Bit])
-> Cell
mkCell = flip mkCell' mempty
------------------------------------------------------------------------------
-- | Helper function for constructing 'Cell's with explicit attributes.
mkCell'
:: CellType
-> M.Map T.Text Value -- ^ Attributes
-> M.Map PortName (Direction, [Bit])
-> Cell
mkCell' ty as m =
Cell
ty
(M.fromList $ fmap (\(pn, bs) -> (Width pn, length bs)) $ M.toList $ fmap snd m)
as
(fmap fst m)
(fmap snd m)