clash-0.1.3.9: CLasH/HardwareTypes.hs
{-# LANGUAGE TemplateHaskell, DeriveDataTypeable, RecordWildCards #-}
module CLasH.HardwareTypes
( module Types
, module Data.Param.Integer
, module Data.Param.Vector
, module Data.Param.Index
, module Data.Param.Signed
, module Data.Param.Unsigned
, module Data.Bits
, module Language.Haskell.TH.Lift
, module Control.Arrow
, module Control.Monad.Fix
, module CLasH.Translator.Annotations
, Bit(..)
, State(..)
, hwand
, hwor
, hwxor
, hwnot
, RAM
, MemState
, blockRAM
, Clock(..)
, pulseLength
, Comp
, simulate
, (^^^)
, comp
, bv2u
, u2bv
, s2bv
, bv2s
, SimulatorSession
, SimulationState
, simulateM
, run
, runWithClock
, setInput
, setAndRun
, getOutput
, showOutput
, assert
, report
) where
import Types
import Data.Param.Integer (HWBits(..))
import Data.Param.Vector
import Data.Param.Index
import Data.Param.Signed
import Data.Param.Unsigned
import Data.Bits hiding (shiftL,shiftR)
import qualified Data.Bits as B
import Language.Haskell.TH.Lift
import Data.Typeable
import Control.Category (Category,(.),id)
import Control.Arrow (Arrow,arr,first,ArrowLoop,loop,(>>>),returnA)
import Control.Monad.Fix (mfix)
import qualified Prelude as P
import Prelude hiding (id, (.))
import qualified Data.Set as Set
import qualified Data.List as L
import qualified Control.Monad.Trans.State.Strict as State
import qualified Data.Accessor.Template
import qualified Data.Accessor.Monad.Trans.StrictState as MonadState
import qualified Control.Monad.Trans.Class as Trans
import CLasH.Translator.Annotations
newtype State s = State s deriving (P.Show)
-- The plain Bit type
data Bit = High | Low
deriving (P.Show, Eq, P.Read, Typeable)
deriveLift ''Bit
hwand :: Bit -> Bit -> Bit
hwor :: Bit -> Bit -> Bit
hwxor :: Bit -> Bit -> Bit
hwnot :: Bit -> Bit
High `hwand` High = High
_ `hwand` _ = Low
Low `hwor` Low = Low
_ `hwor` _ = High
High `hwxor` Low = High
Low `hwxor` High = High
_ `hwxor` _ = Low
hwnot High = Low
hwnot Low = High
type RAM s a = Vector s a
type MemState s a = State (RAM s a)
blockRAM ::
PositiveT s =>
MemState s a ->
a ->
Index s ->
Index s ->
Bool ->
(MemState s a, a )
blockRAM (State mem) data_in rdaddr wraddr wrenable =
((State mem'), data_out)
where
data_out = mem!rdaddr
-- Only write data_in to memory if write is enabled
mem' = if wrenable then
vreplace mem wraddr data_in
else
mem
-- ==============================
-- = Integer/Vector Conversions =
-- ==============================
-- ===============
-- = Conversions =
-- ===============
bv2u :: NaturalT nT => Vector nT Bit -> Unsigned nT
bv2u bv = vfoldl (\a b -> let
a' = B.shiftL a 1
in
if b == High then
a' + 1
else
a'
) 0 bv
bv2s :: NaturalT nT => Vector nT Bit -> Signed nT
bv2s bv = vfoldl (\a b -> let
a' = B.shiftL a 1
in
if b == High then
a' + 1
else
a'
) 0 bv
u2bv :: NaturalT nT => Unsigned nT -> Vector nT Bit
u2bv u = vreverse . (vmap fst) . (vgenerate f) $ (Low,(0,u))
where
f (_,(n,u)) = if testBit u n then (High,(n+1,u)) else (Low,(n+1,u))
s2bv :: NaturalT nT => Signed nT -> Vector nT Bit
s2bv u = vreverse . (vmap fst) . (vgenerate f) $ (Low,(0,u))
where
f (_,(n,u)) = if testBit u n then (High,(n+1,u)) else (Low,(n+1,u))
-- ==========
-- = Clocks =
-- ==========
data Clock = ClockUp Int | ClockDown Int
deriving (Eq,Ord,Show)
pulseLength (ClockUp i) = i
pulseLength (ClockDown i) = i
-- ==================
-- = Automata Arrow =
-- ==================
data Comp i o = C {
domain :: Set.Set Clock
, exec :: Clock -> i -> (o, Comp i o)
}
instance Category Comp where
k@(C { domain = cdA, exec = g}) . (C {domain = cdB, exec = f}) =
C { domain = Set.union cdA cdB
, exec = \clk b -> let (c,f') = f clk b
(d,g') = g clk c
in (d, g'.f')
}
id = arr id
instance Arrow Comp where
arr f = C { domain = Set.empty
, exec = \clk b -> (f b, arr f)
}
first af = af { exec = \clk (b,d) -> let (c,f') = (exec af) clk b
in ((c,d), first f')
}
instance ArrowLoop Comp where
loop af = af { exec = (\clk i -> let ((c,d), f') = (exec af) clk (i, d)
in (c, loop f'))
}
comp :: (State s -> i -> (State s,o)) -> s -> Clock -> Comp i o
comp f initS clk = C { domain = Set.singleton clk
, exec = \clk' i -> let (State s,o) = f (State initS) i
s' | clk == clk' = s
| otherwise = initS
in (o, comp f s' clk)
}
liftS :: s -> (State s -> i -> (State s,o)) -> Comp i o
liftS init f = C {domain = Set.singleton (ClockUp 1), exec = applyS}
where applyS = \clk i -> let (State s,o) = f (State init) i
in (o, liftS s f)
(^^^) :: (State s -> i -> (State s,o)) -> s -> Comp i o
(^^^) f init = liftS init f
simulate :: Comp b c -> [b] -> [c]
simulate af inps = if (Set.size $ domain af) < 2 then
simulate' af (Set.findMin $ domain af) inps
else
error "Use simulateM for components with more than 1 clock"
simulate' :: Comp b c -> Clock -> [b] -> [c]
simulate' af _ [] = []
simulate' (C {exec = f}) clk (i:is) = let (o,f') = f clk i in (o : simulate' f' clk is)
data SimulationState i o = SimulationState {
clockTicks_ :: ([Clock],[Int])
, input_ :: i
, hw_ :: Comp i o
}
Data.Accessor.Template.deriveAccessors ''SimulationState
type SimulatorSession i o a = State.StateT (SimulationState i o) IO a
simulateM :: Comp i o -> SimulatorSession i o () -> IO ()
simulateM hw testbench = State.evalStateT testbench initSession
where
initSession = SimulationState ((Set.toList $ domain hw), (replicate (Set.size $ domain hw) 1)) (error "CLasH.simulateM: initial simulation input not set") hw
run :: Int -> SimulatorSession i o ()
run n = do
(clocks,ticks) <- MonadState.get clockTicks
let (pulses,newTicks) = runClocks (clocks,ticks) n
MonadState.modify clockTicks (\(a,b) -> (a,newTicks))
curInp <- MonadState.get input
MonadState.modify hw (snd . (run' pulses curInp))
runWithClock :: Clock -> Int -> SimulatorSession i o ()
runWithClock clk n = do
curInp <- MonadState.get input
MonadState.modify hw (snd . (run' (replicate n clk) curInp))
run' [] _ arch = ([],arch)
run' (clk:clks) i (C {..}) = let (c,f') = clk `seq` exec clk i
(cs,f'') = f' `seq` run' clks i f'
in f'' `seq` (c:cs,f'')
setInput :: i -> SimulatorSession i o ()
setInput i = MonadState.set input i
setAndRun :: i -> Int -> SimulatorSession i o ()
setAndRun inp n = (setInput inp) >> (run n)
getOutput :: SimulatorSession i o o
getOutput = do
curInp <- MonadState.get input
arch <- MonadState.get hw
return $ head $ fst $ run' [ClockUp (-1)] curInp arch
showOutput :: (Show o) => SimulatorSession i o ()
showOutput = do
outp <- getOutput
Trans.lift $ putStrLn $ show outp
assert :: (o -> Bool) -> String -> SimulatorSession i o ()
assert test msg = do
outp <- getOutput
if (test outp) then return () else Trans.lift $ putStrLn msg
report :: String -> SimulatorSession i o ()
report msg = Trans.lift $ putStrLn msg
runClocks :: ([Clock], [Int]) -> Int -> ([Clock],[Int])
runClocks (clocks, ticks) 0 = ([],ticks)
runClocks (clocks, ticks) delta = ((concat curClocks) ++ nextClocks,nextTicks)
where
(curClocks,curTicks) = unzip $ zipWith clockTick clocks ticks
(nextClocks,nextTicks) = runClocks (clocks,curTicks) (delta-1)
clockTick (ClockUp i) i' = if i == i' then ([ClockUp i] ,1) else ([],i'+1)
clockTick (ClockDown i) i' = if i == i' then ([ClockDown i],1) else ([],i'+1)