mirror of https://github.com/zachjs/sv2v.git
172 lines
6.3 KiB
Haskell
172 lines
6.3 KiB
Haskell
|
|
module Language.Verilog.Simulator
|
||
|
|
( Simulator
|
||
|
|
, SimCommand (..)
|
||
|
|
, SimResponse (..)
|
||
|
|
, simulator
|
||
|
|
) where
|
||
|
|
|
||
|
|
import Control.Monad (when)
|
||
|
|
import Data.Array.IO
|
||
|
|
import Data.Bits
|
||
|
|
import Data.IORef
|
||
|
|
import Data.Monoid
|
||
|
|
import System.IO
|
||
|
|
|
||
|
|
import Data.VCD hiding (Var, step)
|
||
|
|
import qualified Data.VCD as VCD
|
||
|
|
|
||
|
|
import Data.BitVec
|
||
|
|
import Language.Verilog.Netlist
|
||
|
|
|
||
|
|
--check msg = putStrLn msg >> hFlush stdout
|
||
|
|
|
||
|
|
-- | A Simulator executes 'SimCommand's.
|
||
|
|
type Simulator = SimCommand -> IO (Maybe SimResponse)
|
||
|
|
|
||
|
|
-- | Simulation commands.
|
||
|
|
data SimCommand
|
||
|
|
= Init (Maybe FilePath)
|
||
|
|
| Step
|
||
|
|
| GetSignalId Path
|
||
|
|
| GetSignal NetId
|
||
|
|
| Close
|
||
|
|
|
||
|
|
-- | Simulation responses.
|
||
|
|
data SimResponse
|
||
|
|
= Id NetId -- ^ Response to GetSignalId.
|
||
|
|
| Value BitVec -- ^ Response to GetSignal.
|
||
|
|
|
||
|
|
-- | Builds a 'Simulator' given a 'Netlist'.
|
||
|
|
simulator :: Netlist BlackBoxInit -> IO Simulator
|
||
|
|
simulator netlist' = do
|
||
|
|
let netlist = sortTopo netlist'
|
||
|
|
memory <- memory netlist
|
||
|
|
vcd <- newIORef Nothing
|
||
|
|
sample <- newIORef $ return ()
|
||
|
|
step <- newIORef $ return ()
|
||
|
|
return $ \ cmd -> case cmd of
|
||
|
|
Init file -> initialize netlist memory vcd file sample step
|
||
|
|
Step -> readIORef step >>= id >> return Nothing
|
||
|
|
GetSignalId path -> return $ getSignalId netlist path
|
||
|
|
GetSignal id -> readArray memory id >>= return . Just . Value
|
||
|
|
Close -> close vcd sample step >> return Nothing
|
||
|
|
|
||
|
|
getSignalId :: Netlist BlackBoxInit -> Path -> Maybe SimResponse
|
||
|
|
getSignalId netlist path = case lookup path paths' of
|
||
|
|
Nothing -> Nothing
|
||
|
|
Just i -> Just $ Id i
|
||
|
|
where
|
||
|
|
paths = [ (paths, id) | Reg id _ paths _ <- netlist ] ++ [ (paths, id) | Var id _ paths _ <- netlist ]
|
||
|
|
paths' = [ (path, id) | (paths, id) <- paths, path <- paths ]
|
||
|
|
|
||
|
|
type Memory = IOArray Int BitVec
|
||
|
|
|
||
|
|
memory :: Netlist BlackBoxInit -> IO Memory
|
||
|
|
memory netlist
|
||
|
|
| null netlist = error "Empty netlist, nothing to simulate."
|
||
|
|
| otherwise = newArray (0, maximum ids) 0
|
||
|
|
where
|
||
|
|
ids = concatMap f netlist
|
||
|
|
f a = case a of
|
||
|
|
Var a _ _ _ -> [a]
|
||
|
|
Reg a _ _ _ -> [a]
|
||
|
|
BBox _ _ _ -> []
|
||
|
|
|
||
|
|
initialize :: Netlist BlackBoxInit -> Memory -> IORef (Maybe VCDHandle) -> Maybe FilePath -> IORef (IO ()) -> IORef (IO ()) -> IO (Maybe SimResponse)
|
||
|
|
initialize netlist memory vcd file sample step = do
|
||
|
|
close vcd sample step
|
||
|
|
mapM_ (initializeNet memory) netlist
|
||
|
|
case file of
|
||
|
|
Nothing -> return ()
|
||
|
|
Just file -> do
|
||
|
|
h <- openFile file WriteMode
|
||
|
|
vcd' <- newVCD h S
|
||
|
|
writeIORef vcd $ Just vcd'
|
||
|
|
writeIORef sample $ VCD.step vcd' 1
|
||
|
|
mapM_ (f memory vcd' sample) netlist
|
||
|
|
netlist <- mapM initializeBBox netlist
|
||
|
|
initializeStep netlist memory sample step
|
||
|
|
return Nothing
|
||
|
|
where
|
||
|
|
f :: Memory -> VCDHandle -> IORef (IO ()) -> Net BlackBoxInit -> IO ()
|
||
|
|
f memory vcd sample a = case a of
|
||
|
|
BBox _ _ _ -> return ()
|
||
|
|
_ -> mapM_ (\ signal -> do
|
||
|
|
sample' <- var vcd signal $ bitVec width 0
|
||
|
|
modifyIORef sample (>> (readArray memory i >>= sample'))
|
||
|
|
) signals
|
||
|
|
where
|
||
|
|
(i, width, signals) = case a of
|
||
|
|
Reg i w p _ -> (i, w, p)
|
||
|
|
Var i w p _ -> (i, w, p)
|
||
|
|
BBox _ _ _ -> undefined
|
||
|
|
|
||
|
|
initializeNet :: Memory -> Net BlackBoxInit -> IO ()
|
||
|
|
initializeNet memory a = case a of
|
||
|
|
Var i w _ _ -> writeArray memory i $ bitVec w 0
|
||
|
|
Reg i w _ _ -> writeArray memory i $ bitVec w 0
|
||
|
|
BBox _ _ _ -> return ()
|
||
|
|
|
||
|
|
initializeBBox :: Net BlackBoxInit -> IO (Net BlackBoxStep)
|
||
|
|
initializeBBox a = case a of
|
||
|
|
Var a b c d -> return $ Var a b c d
|
||
|
|
Reg a b c d -> return $ Reg a b c d
|
||
|
|
BBox i o init -> init >>= return . BBox i o
|
||
|
|
|
||
|
|
initializeStep :: Netlist BlackBoxStep -> Memory -> IORef (IO ()) -> IORef (IO ()) -> IO ()
|
||
|
|
initializeStep netlist memory sample step = do
|
||
|
|
let steps = map stepNet netlist
|
||
|
|
writeIORef step $ do
|
||
|
|
sequence_ steps
|
||
|
|
readIORef sample >>= id
|
||
|
|
where
|
||
|
|
read = readArray memory
|
||
|
|
write' = writeMemory memory
|
||
|
|
stepNet :: Net BlackBoxStep -> IO ()
|
||
|
|
stepNet a = case a of
|
||
|
|
BBox inputs outputs f -> do
|
||
|
|
outs <- mapM read inputs >>= f
|
||
|
|
sequence_ [ write' a b | (a, b) <- zip outputs outs ]
|
||
|
|
Reg q _ _ d -> read d >>= write' q
|
||
|
|
Var i _ _ expr -> case expr of
|
||
|
|
AInput -> return ()
|
||
|
|
AVar a -> read a >>= write
|
||
|
|
AConst a -> write a
|
||
|
|
ASelect a b c -> do { a <- read a; b <- read b; c <- read c; write $ select a (b, c) }
|
||
|
|
ABWNot a -> read a >>= write . complement
|
||
|
|
ABWAnd a b -> do { a <- read a; b <- read b; write $ a .&. b }
|
||
|
|
ABWXor a b -> do { a <- read a; b <- read b; write $ a `xor` b }
|
||
|
|
ABWOr a b -> do { a <- read a; b <- read b; write $ a .|. b }
|
||
|
|
AMul a b -> do { a <- read a; b <- read b; write $ a * b }
|
||
|
|
AAdd a b -> do { a <- read a; b <- read b; write $ a + b }
|
||
|
|
ASub a b -> do { a <- read a; b <- read b; write $ a - b }
|
||
|
|
AShiftL a b -> do { a <- read a; b <- read b; write $ shiftL a $ fromIntegral $ value b }
|
||
|
|
AShiftR a b -> do { a <- read a; b <- read b; write $ shiftR a $ fromIntegral $ value b }
|
||
|
|
AEq a b -> do { a <- read a; b <- read b; write $ bitVec 1 (if value a == value b then 1 else 0) }
|
||
|
|
ANe a b -> do { a <- read a; b <- read b; write $ bitVec 1 (if value a /= value b then 1 else 0) }
|
||
|
|
ALt a b -> do { a <- read a; b <- read b; write $ bitVec 1 (if value a < value b then 1 else 0) }
|
||
|
|
ALe a b -> do { a <- read a; b <- read b; write $ bitVec 1 (if value a <= value b then 1 else 0) }
|
||
|
|
AGt a b -> do { a <- read a; b <- read b; write $ bitVec 1 (if value a > value b then 1 else 0) }
|
||
|
|
AGe a b -> do { a <- read a; b <- read b; write $ bitVec 1 (if value a >= value b then 1 else 0) }
|
||
|
|
AMux a b c -> do { a <- read a; b <- read b; c <- read c; write (if value a /= 0 then b else c) }
|
||
|
|
AConcat a b -> do { a <- read a; b <- read b; write $ mappend a b }
|
||
|
|
where
|
||
|
|
write = write' i
|
||
|
|
|
||
|
|
writeMemory :: Memory -> Int -> BitVec -> IO ()
|
||
|
|
writeMemory memory i a = do
|
||
|
|
b <- readArray memory i
|
||
|
|
when (width b /= width a) $ error $ "Memory update with different bit-vector width: index: " ++ show i ++ " old: " ++ show b ++ " new: " ++ show a
|
||
|
|
writeArray memory i a
|
||
|
|
|
||
|
|
close :: IORef (Maybe VCDHandle) -> IORef (IO ()) -> IORef (IO ()) -> IO ()
|
||
|
|
close vcd sample step = do
|
||
|
|
vcd' <- readIORef vcd
|
||
|
|
case vcd' of
|
||
|
|
Nothing -> return ()
|
||
|
|
Just vcd -> hClose $ handle vcd
|
||
|
|
writeIORef vcd $ Nothing
|
||
|
|
writeIORef sample $ return ()
|
||
|
|
writeIORef step $ return ()
|
||
|
|
|