forsyde-deep (empty) → 0.2.0
raw patch · 81 files changed
+13618/−0 lines, 81 filesdep +HUnitdep +QuickCheckdep +basebuild-type:Customsetup-changed
Dependencies added: HUnit, QuickCheck, base, containers, directory, filepath, forsyde-deep, mtl, parameterized-data, pretty, process, random, regex-posix, syb, template-haskell, type-level
Files
- LICENSE +26/−0
- README.md +48/−0
- Setup.hs +110/−0
- examples/ALU.hs +340/−0
- examples/ALU_Shallow.hs +185/−0
- examples/BitVector.hs +19/−0
- examples/ButtonEncoder.hs +43/−0
- examples/CarrySelectAdder.hs +395/−0
- examples/Counter.hs +20/−0
- examples/DeepShallow.hs +22/−0
- examples/LFSR.hs +58/−0
- examples/Multiplexer.hs +57/−0
- examples/Multiplexer_FSVector.hs +45/−0
- examples/Null.hs +20/−0
- examples/ParAddFour.hs +43/−0
- examples/SeqAddFour.hs +45/−0
- examples/ZipTwist.hs +36/−0
- forsyde-deep.cabal +168/−0
- lib/forsyde.vhd +172/−0
- src/Data/Traversable/GenericZipWith.hs +69/−0
- src/Data/Typeable/FSDTypeRepLib.hs +97/−0
- src/Data/Typeable/TypeRepLib.hs +31/−0
- src/ForSyDe/Deep.hs +38/−0
- src/ForSyDe/Deep/AbsentExt.hs +91/−0
- src/ForSyDe/Deep/Backend.hs +24/−0
- src/ForSyDe/Deep/Backend/GraphML.hs +45/−0
- src/ForSyDe/Deep/Backend/GraphML/AST.hs +44/−0
- src/ForSyDe/Deep/Backend/GraphML/FileIO.hs +27/−0
- src/ForSyDe/Deep/Backend/GraphML/Ppr.hs +325/−0
- src/ForSyDe/Deep/Backend/GraphML/Traverse.hs +139/−0
- src/ForSyDe/Deep/Backend/GraphML/Traverse/GraphMLM.hs +247/−0
- src/ForSyDe/Deep/Backend/Ppr.hs +107/−0
- src/ForSyDe/Deep/Backend/Simulate.hs +248/−0
- src/ForSyDe/Deep/Backend/VHDL.hs +125/−0
- src/ForSyDe/Deep/Backend/VHDL/AST.hs +720/−0
- src/ForSyDe/Deep/Backend/VHDL/Constants.hs +342/−0
- src/ForSyDe/Deep/Backend/VHDL/FileIO.hs +30/−0
- src/ForSyDe/Deep/Backend/VHDL/Generate.hs +721/−0
- src/ForSyDe/Deep/Backend/VHDL/Ghdl.hs +213/−0
- src/ForSyDe/Deep/Backend/VHDL/GlobalNameTable.hs +112/−0
- src/ForSyDe/Deep/Backend/VHDL/GlobalNameTable.hs-boot +7/−0
- src/ForSyDe/Deep/Backend/VHDL/Modelsim.hs +169/−0
- src/ForSyDe/Deep/Backend/VHDL/Ppr.hs +481/−0
- src/ForSyDe/Deep/Backend/VHDL/Quartus.hs +115/−0
- src/ForSyDe/Deep/Backend/VHDL/TestBench.hs +220/−0
- src/ForSyDe/Deep/Backend/VHDL/Translate.hs +921/−0
- src/ForSyDe/Deep/Backend/VHDL/Translate/HigherOrderFunctions.hs +211/−0
- src/ForSyDe/Deep/Backend/VHDL/Traverse.hs +234/−0
- src/ForSyDe/Deep/Backend/VHDL/Traverse/VHDLM.hs +614/−0
- src/ForSyDe/Deep/Bit.hs +175/−0
- src/ForSyDe/Deep/Config.hs +29/−0
- src/ForSyDe/Deep/DFT.hs +79/−0
- src/ForSyDe/Deep/FIR.hs +63/−0
- src/ForSyDe/Deep/ForSyDeErr.hs +465/−0
- src/ForSyDe/Deep/Ids.hs +23/−0
- src/ForSyDe/Deep/Netlist.hs +252/−0
- src/ForSyDe/Deep/Netlist/Traverse.hs +193/−0
- src/ForSyDe/Deep/OSharing.hs +208/−0
- src/ForSyDe/Deep/OSharing/UDynamic.hs +54/−0
- src/ForSyDe/Deep/Process.hs +24/−0
- src/ForSyDe/Deep/Process/Desugar.hs +131/−0
- src/ForSyDe/Deep/Process/Desugar.hs-boot +7/−0
- src/ForSyDe/Deep/Process/ProcFun.hs +168/−0
- src/ForSyDe/Deep/Process/ProcType.hs +178/−0
- src/ForSyDe/Deep/Process/ProcType/Instances.hs +119/−0
- src/ForSyDe/Deep/Process/ProcVal.hs +52/−0
- src/ForSyDe/Deep/Process/SynchProc.hs +899/−0
- src/ForSyDe/Deep/Signal.hs +30/−0
- src/ForSyDe/Deep/System.hs +24/−0
- src/ForSyDe/Deep/System/Instantiate.hs +44/−0
- src/ForSyDe/Deep/System/SysDef.hs +410/−0
- src/ForSyDe/Deep/System/SysDef.hs-boot +21/−0
- src/ForSyDe/Deep/System/SysFun.hs +311/−0
- src/ForSyDe/Deep/System/SysFun/Instances.hs +82/−0
- src/ForSyDe/Deep/Version.hs +35/−0
- src/Language/Haskell/TH/Lift.hs +116/−0
- src/Language/Haskell/TH/LiftInstances.hs +129/−0
- src/Language/Haskell/TH/TypeLib.hs +320/−0
- tests/Install.hs +47/−0
- tests/examples/Main.hs +22/−0
- tests/examples/VHDLBackend.hs +289/−0
+ LICENSE view
@@ -0,0 +1,26 @@+ Copyright (c) 2003-2013, ES Group at the School of Information and + Communication Technology, (Royal Institute of Technology, Stockholm, Sweden)++ All rights reserved.++ Redistribution and use in source and binary forms, with or without+ modification, are permitted provided that the following conditions are met:+ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.+ * Redistributions in binary form must reproduce the above copyright+ notice, this list of conditions and the following disclaimer in the+ documentation and/or other materials provided with the distribution.+ * Neither the name of the SAM Group nor the+ names of its contributors may be used to endorse or promote products+ derived from this software without specific prior written permission.++ THIS SOFTWARE IS PROVIDED BY THE SAM GROUP ``AS IS'' AND ANY+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+ DISCLAIMED. IN NO EVENT SHALL The ForSyDe TEAM BE LIABLE FOR ANY+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,48 @@+ForSyDe's Haskell-embedded Domain Specific Language.+====================================================++DESCRIPTION+-----------++ The ForSyDe (Formal System Design) methodology has been developed+ with the objective to move system design to a higher level of+ abstraction and to bridge the abstraction gap by transformational+ design refinement.+ + This library provides ForSyDe's implementation as a Haskell-embedded+ Domain Specific Language (DSL). ++ For more information, please see ForSyDe's website:+ https://forsyde.github.io/+++INSTALLATION+------------+ +ForSyDe depends on GHC vesions 7.10.3 or 8.0.1 due to the use of+numerous extensions, namely Template Haskell (TH).++It depends on the+[`type-level`](https://github.com/forsyde/type-level) and+[`parameterized-data`](https://github.com/forsyde/parameterized-data)+packages and some others normally bundled with GHC distributions.++For synthesis and simulation of the generated VHDL the Altera toolchain is+supported. Quartus and Modelsim need to be on the PATH for these features to+work. Additionally the open-source VHDL simulator Ghdl is supported from+version ghdl-0.33 onwards.++This package can be installed with:++* Cabal, provided you have installed the right version of GHC and its+ dependent `cabal-install` package using the commands:++ cabal install # --with-ghc=path/to/ghc-version # installs forsyde-deep+ cabal haddock # generates documentation. Needs Haddock > 2.0+ +* Stack, which takes care of all dependencies and installs everything+ (including the compiler) in a sandboxed environment:+ + stack install # installs forsyde-deep+ stack haddock # generates documentation+
+ Setup.hs view
@@ -0,0 +1,110 @@+#! /usr/bin/env runhaskell+module Main (main) where++import Control.Monad (liftM, when)+import Data.List (intersperse)+import Distribution.Simple+import Distribution.Simple.Setup+import Distribution.Simple.LocalBuildInfo+import Distribution.PackageDescription+import System.Process+import System.Exit+import System.Directory+import System.FilePath++main :: IO ()+main = defaultMainWithHooks simpleUserHooks{postInst=forsydePostInst,+ postCopy=forsydePostCopy}++forsydePostInst :: Args -> InstallFlags -> PackageDescription -> + LocalBuildInfo -> IO () +forsydePostInst _ _ = compile_forsyde_vhd NoCopyDest++forsydePostCopy :: Args -> CopyFlags -> PackageDescription -> + LocalBuildInfo -> IO ()+forsydePostCopy _ cf = compile_forsyde_vhd $ removeFlag $ (copyDest cf) where+ removeFlag c = case c of+ Distribution.Simple.Setup.Flag x -> x+ _ -> error "Not a Flag"+++-- NOTE: Most of this code is duplicated from ForSyDe.Backend.VHDL.Modelsim,+-- however, it allows Setup.hs to be selfcontained++-- Compile forsyde.vhd if possible, showing what's going on to the end user+compile_forsyde_vhd :: CopyDest -> PackageDescription -> LocalBuildInfo + -> IO ()+compile_forsyde_vhd cd pd lbi = do+ putStrLn "Compiling ForSyDe's VHDL library with Modelsim ..." + (ifNot isModelsimInstalled + (modelsimError "Modelsim executables could not be found.")) <&&>+ (ifNot (do_compile_forsyde_vhd forsyde_vhd_dir)+ ( modelsimError "Compilation failed.")) <&&>+ (putStrLn "Compilation succeded." >> return True) + return ()+ where + forsyde_vhd_dir = (datadir $ absoluteInstallDirs pd lbi cd) </> + "lib"+ modelsimError err = putStrLn $ + "Warning: " ++ err ++ "\n" +++ " ForSyDe will work, but you will not be able to automatically\n" +++ " compile or simulate the ForSyDe-generated VHDL models with Modelsim\n\n" +++ " In order to fix this, make sure that the Modelsim executables\n" ++ + " can be found in PATH and reinstall ForSyDe"++-- Look for modelsim executables+isModelsimInstalled :: IO Bool+isModelsimInstalled = executablePresent "vlib" <&&> + executablePresent "vmap" <&&> + executablePresent "vcom"+ where executablePresent = (liftM (maybe False (\_-> True))) .findExecutable+ +-- Create a modelsim library for forsyde.vhd +-- in the same directory in which forsyde.vhd was copied+do_compile_forsyde_vhd :: FilePath -- ^ absolute directory which + -- forsyde.vhd was copied into + -> IO Bool+do_compile_forsyde_vhd dir = + (runCommandMsg "vlib" [dir </> "modelsim"]) <&&> + (runCommandMsg+ "vcom" ["-93", "-quiet", "-nologo", "-work", dir </> "modelsim", + dir </> "forsyde.vhd"])+ where runWait :: String -> FilePath -> [String] -> IO Bool+ runWait msg proc args = do+ putStrLn msg + h <- runProcess proc args (Just dir) Nothing Nothing Nothing Nothing+ code <- waitForProcess h+ return $ code == ExitSuccess ++-- | run a command showing what's being run+runCommandMsg :: String -- ^ Command to execute + -> [String] -- ^ Command arguments+ -> IO Bool+runCommandMsg command args = runWait msg command args+ where msg = "Running: " ++ command ++ " " ++ (concat $ intersperse " " args)+++-- | Run a process, previously announcing a message and waiting for it+-- to finnish its execution.+runWait :: String -- ^ message to show+ -> FilePath -- ^ command to execute + -> [String] -- ^ command arguments+ -> IO Bool -- ^ Did the execution end succesfully?+runWait msg proc args = do+ putStrLn msg + h <- runProcess proc args Nothing Nothing Nothing Nothing Nothing+ code <- waitForProcess h+ return $ code == ExitSuccess +++-- | short-circuit and for monads+(<&&>) :: Monad m => m Bool -> m Bool -> m Bool+x <&&> y = do p <- x+ if p then y else return False++-- | execute an action when the argument is False+-- and return the boolean value+ifNot :: Monad m => m Bool -> m () -> m Bool+ifNot x a = do p <- x+ when (not p) a+ return p
+ examples/ALU.hs view
@@ -0,0 +1,340 @@+{-# LANGUAGE TemplateHaskell #-}++-- The following example is designed as tutorial example for the paper.+--+-- It implements a simple ALU, which has the following modes+-- HH: out <- a AND b+-- HL: out <- a OR b+-- LH: (cout, out) <- ADD a b+-- LL: out <- shiftl a 0+--+-- The expressiveness of the compiler could be clearly improved, if the+-- higher-order function 'zipWith' that works on vectors would be synthesizable.+-- Check 'and4BitFun'!+--+-- We could improve the example even further, if we use enumeration types for the+-- multiplexer. So far I have not done it, but should only be a matter of time!++module ALU where++import ForSyDe.Deep+import Data.Bits+import Data.Param.FSVec+import Data.TypeLevel.Num.Reps+import Data.TypeLevel.Num.Aliases+++----- AND - 4 Bit++and4BitFun :: ProcFun (FSVec D4 Bit -> FSVec D4 Bit -> FSVec D4 Bit)+and4BitFun = $(newProcFun [d| and4BitFun :: FSVec D4 Bit+ -> FSVec D4 Bit+ -> FSVec D4 Bit+ --and4BitFun a b = Data.Param.FSVec.zipWith (.&.) a b+ and4BitFun a b = (a!d3 .&. b!d3) +> (a!d2 .&. b!d2) +> (a!d1 .&. b!d1) +> (a!d0 .&. b!d0) +> empty+ |])+++and4BitProc :: Signal (FSVec D4 Bit)+ -> Signal (FSVec D4 Bit)+ -> Signal (FSVec D4 Bit)+and4BitProc = zipWithSY "and4" and4BitFun++and4BitSys :: SysDef (Signal (FSVec D4 Bit)+ -> Signal (FSVec D4 Bit)+ -> Signal (FSVec D4 Bit))+and4BitSys = newSysDef and4BitProc "and4Bit" ["a", "b"] ["y"]++simAnd4Bit :: [FSVec D4 Bit] -> [FSVec D4 Bit] -> [FSVec D4 Bit]+simAnd4Bit = simulate and4BitSys++vhdlAnd4Bit = writeVHDL and4BitSys+++----- OR - 4 Bit++or4BitFun :: ProcFun (FSVec D4 Bit -> FSVec D4 Bit -> FSVec D4 Bit)+or4BitFun = $(newProcFun [d| or4BitFun :: FSVec D4 Bit -> FSVec D4 Bit -> FSVec D4 Bit+ --or4BitFun a b = Data.Param.FSVec.zipWith (.&.) a b+ or4BitFun a b = (a!d3 .|. b!d3) +> (a!d2 .|. b!d2) +> (a!d1 .|. b!d1) +> (a!d0 .|. b!d0) +> empty+ |])+++or4BitProc :: Signal (FSVec D4 Bit)+ -> Signal (FSVec D4 Bit)+ -> Signal (FSVec D4 Bit)+or4BitProc = zipWithSY "or4" or4BitFun++or4BitSys :: SysDef (Signal (FSVec D4 Bit)+ -> Signal (FSVec D4 Bit)+ -> Signal (FSVec D4 Bit))+or4BitSys = newSysDef or4BitProc "or4Bit" ["a", "b"] ["y"]++simOr4Bit :: [FSVec D4 Bit] -> [FSVec D4 Bit] -> [FSVec D4 Bit]+simOr4Bit = simulate or4BitSys++vhdlOr4Bit = writeVHDL or4BitSys++----- Logical Shift Left++lslFun :: ProcFun (FSVec D4 Bit -> FSVec D4 Bit)+lslFun = $(newProcFun [d| lslFun :: FSVec D4 Bit -> FSVec D4 Bit+ lslFun a = shiftl a L+ |])++lslProc :: Signal (FSVec D4 Bit) -> Signal (FSVec D4 Bit)+lslProc = mapSY "lsl" lslFun++lslSys :: SysDef (Signal (FSVec D4 Bit) -> Signal (FSVec D4 Bit))+lslSys = newSysDef lslProc "lsl" ["in"] ["out"]++simLsl :: [FSVec D4 Bit] -> [FSVec D4 Bit]+simLsl = simulate lslSys++vhdlLsl = writeVHDL lslSys++----- Mux 4/1 (no use of FSVec as input, only for selection)++mux41Fun :: ProcFun (FSVec D2 Bit+ -> FSVec D4 Bit+ -> FSVec D4 Bit+ -> FSVec D4 Bit+ -> FSVec D4 Bit+ -> FSVec D4 Bit)+mux41Fun = $(newProcFun [d| mux41Fun :: FSVec D2 Bit+ -> FSVec D4 Bit+ -> FSVec D4 Bit+ -> FSVec D4 Bit+ -> FSVec D4 Bit+ -> FSVec D4 Bit+ mux41Fun sel x3 x2 x1 x0+ = if sel == H +> H +> empty then+ x3+ else+ if sel == H +> L +> empty then+ x2+ else+ if sel == L +> H +> empty then+ x1+ else+ x0+ |])++mux41Proc :: Signal (FSVec D2 Bit) -> Signal (FSVec D4 Bit)+ -> Signal (FSVec D4 Bit) -> Signal (FSVec D4 Bit)+ -> Signal (FSVec D4 Bit) -> Signal (FSVec D4 Bit)+mux41Proc = zipWith5SY "mux41" mux41Fun++mux41Sys :: SysDef (Signal (FSVec D2 Bit) -> Signal (FSVec D4 Bit)+ -> Signal (FSVec D4 Bit) -> Signal (FSVec D4 Bit)+ -> Signal (FSVec D4 Bit) -> Signal (FSVec D4 Bit))+mux41Sys = newSysDef mux41Proc "mux41" ["sel", "d3", "d2", "d1", "d0"] ["out"]++simMux41 :: [FSVec D2 Bit] -> [FSVec D4 Bit] -> [FSVec D4 Bit]+ -> [FSVec D4 Bit] -> [FSVec D4 Bit] -> [FSVec D4 Bit]+simMux41 = simulate mux41Sys++vhdlMux41 = writeVHDL mux41Sys++----- Convert FSVector to Tuple (Size 4)++convFromFSVec4Fun :: ProcFun (FSVec D4 Bit -> (Bit, Bit, Bit, Bit))+convFromFSVec4Fun = $(newProcFun [d| convFromFSVec4Fun :: FSVec D4 Bit -> (Bit, Bit, Bit, Bit)+ convFromFSVec4Fun v+ = (v!d3, v!d2, v!d1, v!d0)+ |])++convFromFSVec4Proc :: Signal (FSVec D4 Bit) -> (Signal Bit, Signal Bit, Signal Bit, Signal Bit)+convFromFSVec4Proc = (unzip4SY "unzip4" . mapSY "conv4" convFromFSVec4Fun)++convFromFSVec4Sys :: SysDef (Signal (FSVec D4 Bit) -> (Signal Bit, Signal Bit, Signal Bit, Signal Bit))+convFromFSVec4Sys = newSysDef convFromFSVec4Proc "convFromFSVec" ["vector4"] ["v3", "v2", "v1", "v0"]++simConvFromFSVec4 :: [FSVec D4 Bit] -> ([Bit], [Bit], [Bit], [Bit])+simConvFromFSVec4 = simulate convFromFSVec4Sys++vhdlConvFromFSVec4 = writeVHDL convFromFSVec4Sys++----- Convert To FSVector (Size 4)++convToFSVec4Fun :: ProcFun (Bit -> Bit -> Bit -> Bit -> FSVec D4 Bit)+convToFSVec4Fun = $(newProcFun [d| convToFSVec4Fun :: Bit -> Bit -> Bit -> Bit -> FSVec D4 Bit+ convToFSVec4Fun x3 x2 x1 x0+ = x3 +> x2 +> x1 +> x0 +> empty |])++convToFSVec4Proc :: Signal Bit -> Signal Bit -> Signal Bit -> Signal Bit -> Signal (FSVec D4 Bit)+convToFSVec4Proc = zipWith4SY "toVector4" convToFSVec4Fun++convToFSVec4Sys :: SysDef (Signal Bit -> Signal Bit -> Signal Bit -> Signal Bit -> Signal (FSVec D4 Bit))+convToFSVec4Sys = newSysDef convToFSVec4Proc "convToFSVec" ["x3", "x2", "x1", "x0"] ["vec4"]++simConvToFSVec4 = simulate convToFSVec4Sys++vhdlConvToFSVec4 = writeVHDL convToFSVec4Sys++------ Full Adder++fullAddFun :: ProcFun (Bit -> Bit -> Bit -> (Bit, Bit))+fullAddFun = $(newProcFun+ [d|fullAddFun :: Bit -> Bit -> Bit -> (Bit, Bit)+ fullAddFun a b c_in = (c_out, sum)+ where c_out :: Bit+ c_out = (a .&. b) .|. (a .&. c_in) .|. (b .&. c_in)+ sum :: Bit+ sum = (a `xor` b) `xor` c_in |])+++--fullAddProc :: Signal Bit -> Signal Bit -> Signal Bit -> Signal (Bit, Bit)+--fullAddProc = zipWith3SY "fulladd" fullAddFun+fullAddProc :: Signal Bit -> Signal Bit -> Signal Bit -> (Signal Bit, Signal Bit)+fullAddProc a b c_in = (unzipSY "unzipSY") $ (zipWith3SY "fulladd" fullAddFun a b c_in)++fullAddSys :: SysDef (Signal Bit -> Signal Bit -> Signal Bit -> (Signal Bit, Signal Bit))+fullAddSys = newSysDef fullAddProc "fullAddSys" ["a", "b", "c_in"] ["cout", "sum"]++simFullAdd :: [Bit] -> [Bit] -> [Bit] -> ([Bit], [Bit])+simFullAdd = simulate fullAddSys++--vhdlFullAdd = writeVHDL fullAddSys++a_in = [L,L,L,L,H,H,H,H]+b_in = [L,H,L,H,L,H,L,H]+c_in = [L,L,H,H,L,L,H,H]++------ 4-bit Adder Chain++fourBitAdder :: Signal Bit -- C_IN+ -> Signal Bit -- A3+ -> Signal Bit -- A2+ -> Signal Bit -- A1+ -> Signal Bit -- A0+ -> Signal Bit -- B3+ -> Signal Bit -- B2+ -> Signal Bit -- B1+ -> Signal Bit -- B0+ -> (Signal Bit, -- C_OUT+ Signal Bit, -- SUM3+ Signal Bit, -- SUM2+ Signal Bit, -- SUM1+ Signal Bit) -- SUM0+fourBitAdder c_in a3 a2 a1 a0 b3 b2 b1 b0+ = (c_out, sum3, sum2, sum1, sum0)+ where (c_out, sum3) = (instantiate "add3" fullAddSys) a3 b3 c2+ (c2, sum2) = (instantiate "add2" fullAddSys) a2 b2 c1+ (c1, sum1) = (instantiate "add1" fullAddSys) a1 b1 c0+ (c0, sum0) = (instantiate "add0" fullAddSys) a0 b0 c_in++fourBitAdderSys :: SysDef (Signal Bit -- C_IN+ -> Signal Bit -- A3+ -> Signal Bit -- A2+ -> Signal Bit -- A1+ -> Signal Bit -- A0+ -> Signal Bit -- B3+ -> Signal Bit -- B2+ -> Signal Bit -- B1+ -> Signal Bit -- B0+ -> (Signal Bit, -- C_OUT+ Signal Bit, -- SUM3+ Signal Bit, -- SUM2+ Signal Bit, -- SUM1+ Signal Bit)) -- SUM0+fourBitAdderSys = newSysDef fourBitAdder "fourBitAdder" ["C_IN", "A3", "A2", "A1", "A0",+ "B3", "B2", "B1", "B0"]+ ["C_OUT", "SUM3", "SUM2", "SUM1", "SUM0"]++simFourBitAdder = simulate fourBitAdderSys++vhdlFourBitAdder = writeVHDL fourBitAdderSys++a0 = [L,L,L,L,L,L,L,L,H,H,H,H,H,H,H,H]+a1 = [L,L,L,L,H,H,H,H,L,L,L,L,H,H,H,H]+a2 = [L,L,H,H,L,L,H,H,L,L,H,H,L,L,H,H]+a3 = [L,H,L,H,L,H,L,H,L,H,L,H,L,H,L,H]+zero = [L,L,L,L,L,L,L,L,L,L,L,L,L,L,L,L]+one = [H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H]+++simAdd_0 = simFourBitAdder zero a3 a2 a1 a0 a3 a2 a1 a0+simAdd_1 = simFourBitAdder one a3 a2 a1 a0 a3 a2 a1 a0+++----- FSVec Adder+add4FSVecProc :: Signal (FSVec D4 Bit)+ -> Signal (FSVec D4 Bit)+ -> (Signal Bit, Signal (FSVec D4 Bit))+add4FSVecProc a b = (cout, sum)+ where+ (a3,a2,a1,a0) = (instantiate "split_a" convFromFSVec4Sys) a+ (b3,b2,b1,b0) = (instantiate "split_b" convFromFSVec4Sys) b+ (cout, s3, s2, s1, s0) = (instantiate "adder" fourBitAdderSys) zero a3 a2 a1 a0 b3 b2 b1 b0+ sum = (instantiate "merge" convToFSVec4Sys) s3 s2 s1 s0+ zero = instantiate "Zero" zeroSys++add4FSVecSys :: SysDef (Signal (FSVec D4 Bit)+ -> Signal (FSVec D4 Bit)+ -> (Signal Bit, Signal (FSVec D4 Bit)))+add4FSVecSys = newSysDef add4FSVecProc "add4FSVec" ["a", "b"] ["cout", "sum"]++simAdd4FSVecSys = simulate add4FSVecSys++vhdlAdd4FSVecSys = writeVHDL add4FSVecSys++----- Constant Signals++-- Constant input 'H' modeled with constSY+oneProc :: Signal Bit+oneProc = constSY "high" H++oneSys :: SysDef (Signal Bit)+oneSys = newSysDef oneProc "one" [] ["one"]++-- Constant input 'L' modeled with constSY+zeroProc :: Signal Bit+zeroProc = constSY "low" L++zeroSys :: SysDef (Signal Bit)+zeroSys = newSysDef zeroProc "zero" [] ["zero"]++----- ALU++aluProc :: Signal(FSVec D2 Bit)+ -> Signal (FSVec D4 Bit)+ -> Signal (FSVec D4 Bit)+ -> (Signal Bit, Signal (FSVec D4 Bit))+aluProc sel a b = (cout, out)+ where+ andOut = (instantiate "and" and4BitSys) a b+ orOut = (instantiate "or" or4BitSys) a b+ (cout, sum) = (instantiate "add" add4FSVecSys) a b+ lslOut = (instantiate "lsl" lslSys) a+ out = (instantiate "mux" mux41Sys) sel andOut orOut sum lslOut+++aluSys :: SysDef (Signal(FSVec D2 Bit)+ -> Signal (FSVec D4 Bit)+ -> Signal (FSVec D4 Bit)+ -> (Signal Bit, Signal (FSVec D4 Bit)))+aluSys = newSysDef aluProc "alu" ["sel", "a", "b"] ["cout", "data"]+++simALU :: [FSVec D2 Bit] -> [FSVec D4 Bit] -> [FSVec D4 Bit] -> ([Bit], [FSVec D4 Bit])+simALU = simulate aluSys++vhdlALU = writeVHDL aluSys++----- Test-Inputs++sel = [reallyUnsafeVector [H,H], -- AND+ reallyUnsafeVector [H,L], -- OR+ reallyUnsafeVector [L,H], -- ADD+ reallyUnsafeVector [L,L]] -- LSL++a = [reallyUnsafeVector [L,H,H,H], -- LSB D1 D2 MSB+ reallyUnsafeVector [L,H,L,H],+ reallyUnsafeVector [H,H,H,H],+ reallyUnsafeVector [L,H,H,H]]++b = [reallyUnsafeVector [H,H,H,H],+ reallyUnsafeVector [H,H,L,H],+ reallyUnsafeVector [H,L,L,L],+ reallyUnsafeVector [H,H,H,H]]
+ examples/ALU_Shallow.hs view
@@ -0,0 +1,185 @@+{-# LANGUAGE TemplateHaskell #-}++-- *** Shallow-Embedded ForSyDe Model ***+--+-- The following example is designed as tutorial example for the paper.+--+-- It implements a simple ALU, which has the following modes+-- HH: out <- a AND b+-- HL: out <- a OR b+-- LH: (cout, out) <- ADD a b+-- LL: out <- shiftl a 0+--++module ALU_Shallow where++import ForSyDe.Shallow+import ForSyDe.Deep.Bit+import Data.Bits+import Data.Param.FSVec+import Data.TypeLevel.Num.Reps+import Data.TypeLevel.Num.Aliases++++----- AND - 4 Bit++and4Bit :: Signal (FSVec D4 Bit)+ -> Signal (FSVec D4 Bit)+ -> Signal (FSVec D4 Bit)+and4Bit = zipWithSY (Data.Param.FSVec.zipWith (.&.))++----- OR - 4 Bit++or4Bit :: Signal (FSVec D4 Bit)+ -> Signal (FSVec D4 Bit)+ -> Signal (FSVec D4 Bit)+or4Bit = zipWithSY (Data.Param.FSVec.zipWith (.|.))++----- Logical Shift Left++++lsl :: Signal (FSVec D4 Bit) -> Signal (FSVec D4 Bit)+lsl = mapSY shiftl_L where+ shiftl_L v = shiftl v L++----- Mux 4/1 (no use of FSVec as input, only for selection)++mux41Fun :: FSVec D2 Bit+ -> FSVec D4 Bit+ -> FSVec D4 Bit+ -> FSVec D4 Bit+ -> FSVec D4 Bit+ -> FSVec D4 Bit+mux41Fun sel x3 x2 x1 x0 = if sel == H +> H +> empty then+ x3+ else+ if sel == H +> L +> empty then+ x2+ else+ if sel == L +> H +> empty then+ x1+ else+ x0+++mux41 :: Signal (FSVec D2 Bit) -> Signal (FSVec D4 Bit)+ -> Signal (FSVec D4 Bit) -> Signal (FSVec D4 Bit)+ -> Signal (FSVec D4 Bit) -> Signal (FSVec D4 Bit)+mux41 = zipWith5SY mux41Fun++----- Convert FSVector to Tuple (Size 4)++convFromFSVec4 :: FSVec D4 a -> (a, a, a, a)+convFromFSVec4 v = (v!d3, v!d2, v!d1, v!d0)++----- Convert To FSVector (Size 4)++convToFSVec4 :: a -> a -> a -> a -> FSVec D4 a+convToFSVec4 x3 x2 x1 x0 = x3 +> x2 +> x1 +> x0 +> empty++----- 4-Bit Adder++------ 4-bit Adder Chain+++fourBitAdder :: Signal Bit -- C_IN+ -> Signal Bit -- A3+ -> Signal Bit -- A2+ -> Signal Bit -- A1+ -> Signal Bit -- A0+ -> Signal Bit -- B3+ -> Signal Bit -- B2+ -> Signal Bit -- B1+ -> Signal Bit -- B0+ -> (Signal Bit, -- C_OUT+ Signal Bit, -- SUM3+ Signal Bit, -- SUM2+ Signal Bit, -- SUM1+ Signal Bit) -- SUM0+fourBitAdder c_in a3 a2 a1 a0 b3 b2 b1 b0+ = (c_out, sum3, sum2, sum1, sum0)+ where (c_out, sum3) = unzipSY $ zipWith3SY fullAdd a3 b3 c2+ (c2, sum2) = unzipSY $ zipWith3SY fullAdd a2 b2 c1+ (c1, sum1) = unzipSY $ zipWith3SY fullAdd a1 b1 c0+ (c0, sum0) = unzipSY $ zipWith3SY fullAdd a0 b0 c_in++fullAdd a b cin = (cout, sum)+ where+ cout = (a .&. b) .|. (a .&. cin) .|. (b .&. cin)+ sum = (a `xor` b) `xor` cin++----- FSVec Adder+add4FSVec :: Signal (FSVec D4 Bit)+ -> Signal (FSVec D4 Bit)+ -> (Signal Bit, Signal (FSVec D4 Bit))+add4FSVec a b = (cout, sum)+ where+ (a3,a2,a1,a0) = unzip4SY $ mapSY convFromFSVec4 a+ (b3,b2,b1,b0) = unzip4SY $ mapSY convFromFSVec4 b+ (cout, s3, s2, s1, s0) = fourBitAdder zero a3 a2 a1 a0 b3 b2 b1 b0+ sum = zipWith4SY convToFSVec4 s3 s2 s1 s0+ zero = sourceSY id L++----- ALU++alu :: Signal(FSVec D2 Bit)+ -> Signal (FSVec D4 Bit)+ -> Signal (FSVec D4 Bit)+ -> (Signal Bit, Signal (FSVec D4 Bit))+alu sel a b = (cout, out)+ where+ andOut = and4Bit a b+ orOut = or4Bit a b+ (cout, sum) = add4FSVec a b+ lslOut = lsl a+ out = mux41 sel andOut orOut sum lslOut++++----- Test-Inputs++sel = signal [reallyUnsafeVector [H,H], -- AND+ reallyUnsafeVector [H,L], -- OR+ reallyUnsafeVector [L,H], -- ADD+ reallyUnsafeVector [L,L]] -- LSL++a = signal [reallyUnsafeVector [L,H,H,H],+ reallyUnsafeVector [L,H,L,H],+ reallyUnsafeVector [L,H,H,H],+ reallyUnsafeVector [L,H,H,H]]++b = signal [reallyUnsafeVector [H,H,H,H],+ reallyUnsafeVector [H,H,L,H],+ reallyUnsafeVector [H,H,H,H],+ reallyUnsafeVector [H,H,H,H]]++c = signal [reallyUnsafeVector [L,L,L,L], -- LSB D1 D2 MSB+ reallyUnsafeVector [H,L,L,L],+ reallyUnsafeVector [L,L,L,H],+ reallyUnsafeVector [L,L,L,H]]++d = signal [reallyUnsafeVector [L,L,L,L],+ reallyUnsafeVector [L,L,L,L],+ reallyUnsafeVector [L,L,L,H],+ reallyUnsafeVector [H,H,H,H]]++x0 = signal [L,L,L,L]+x1 = signal [H,H,H,H]++-- Helper functions+zipWith5SY :: (a -> b -> c -> d -> e -> f)+ -> Signal a+ -> Signal b+ -> Signal c+ -> Signal d+ -> Signal e+ -> Signal f+zipWith5SY _ NullS _ _ _ _ = NullS+zipWith5SY _ _ NullS _ _ _ = NullS+zipWith5SY _ _ _ NullS _ _ = NullS+zipWith5SY _ _ _ _ NullS _ = NullS+zipWith5SY _ _ _ _ _ NullS = NullS+zipWith5SY f (a :- as) (b :- bs) (c :- cs) (d :- ds) (e:-es) =+ f a b c d e :- zipWith5SY f as bs cs ds es
+ examples/BitVector.hs view
@@ -0,0 +1,19 @@+{-# LANGUAGE TemplateHaskell #-}+-- example module to test bit conversion functions+module BitVector where++import ForSyDe.Deep+import Data.Int (Int32)+import Data.Param.FSVec+import Data.TypeLevel.Num (D32)++to32BitFun :: ProcFun (Int32 -> FSVec D32 Bit)+to32BitFun = $(newProcFun+ [d| to32Bit :: Int32 -> FSVec D32 Bit+ to32Bit i = toBitVector32 i |])++to32BitProc :: Signal Int32 -> Signal (FSVec D32 Bit)+to32BitProc = mapSY "to32Proc" to32BitFun ++to32BitSysDef :: SysDef (Signal Int32 -> Signal (FSVec D32 Bit))+to32BitSysDef = newSysDef to32BitProc "to32BitSys" ["in1"] ["out1"]
+ examples/ButtonEncoder.hs view
@@ -0,0 +1,43 @@+{-# LANGUAGE TemplateHaskell, DeriveDataTypeable #-}++module ButtonEncoder where++import ForSyDe.Deep+import Language.Haskell.TH.Lift+import Data.Generics (Data, Typeable)+import Prelude hiding (Either(..))++-- Combinational syncrhonous system to encode the keypresses of+-- an arrow-keypad into directions which are easier to process+-- +-- This example shows how the VHDL backend can process custom Enumerated+-- Algebraic types (ie. Algebraic types whose data constructors have all+-- zero arity)+++data Direction = Up | Down | Left | Right | Unknown+ deriving (Data, Typeable, Show, Eq)++deriveLift1 ''Direction++type ButtonPress = (Bit, -- is left pressed + Bit, -- is right pressed+ Bit, -- is up pressed + Bit) -- is down pressed++buttonEncoder :: Signal ButtonPress -> Signal Direction +buttonEncoder = mapSY "encoder" transFun+ where transFun = + $(newProcFun [d| transFun (left,right,up,down) =+ if left == H then Left else+ if right == H then Right else+ if up == H then Up else+ if down == H then Down else Unknown |])++buttonEncoderSys :: SysDef (Signal ButtonPress -> Signal Direction)+buttonEncoderSys = newSysDef buttonEncoder "buttonEncoder" ["buttonPress"] ["direction"]+++simButtonEncoder :: [ButtonPress] -> [Direction]+simButtonEncoder = simulate buttonEncoderSys+
+ examples/CarrySelectAdder.hs view
@@ -0,0 +1,395 @@+{-# LANGUAGE TemplateHaskell #-}++-- This module models a 16-bit carry select adder+++module CarrySelectAdder where++import ForSyDe.Deep -- Definition of Bit data type+import Data.Bits -- Operations for Bit data type+import Data.Param.FSVec+import Data.TypeLevel.Num.Reps+import Data.TypeLevel.Num.Aliases++-- First we create a four bit carry select adder.+-- It uses eight full adders and five 2/1 muxes.++------ Full Adder++fullAddFun :: ProcFun (Bit -> Bit -> Bit -> (Bit, Bit))+fullAddFun = $(newProcFun [d|fullAddFun :: Bit -> Bit -> Bit -> (Bit, Bit)+ -- I would like to use 'where'-clauses. Is this possible?+ -- fullAddFun a b c_in = (c_out, sum)+ -- where c_out = (a .&. b) .|. (a .&. c_in) .|. (b .&. c_in)+ -- sum = (a `xor` b) `xor` c_in |])+ fullAddFun a b c_in = ((a .&. b) .|. (a .&. c_in) .|. (b .&. c_in),+ (a `xor` b) `xor` c_in) |])++--fullAddProc :: Signal Bit -> Signal Bit -> Signal Bit -> Signal (Bit, Bit)+--fullAddProc = zipWith3SY "fulladd" fullAddFun+fullAddProc :: Signal Bit -> Signal Bit -> Signal Bit -> (Signal Bit, Signal Bit)+fullAddProc a b c_in = (unzipSY "unzipSY") $ (zipWith3SY "fulladd" fullAddFun a b c_in)++fullAddSys :: SysDef (Signal Bit -> Signal Bit -> Signal Bit -> (Signal Bit, Signal Bit))+fullAddSys = newSysDef fullAddProc "fullAdd" ["a", "b", "c_in"] ["cout", "sum"]++simFullAdd :: [Bit] -> [Bit] -> [Bit] -> ([Bit], [Bit])+simFullAdd = simulate fullAddSys++--vhdlFullAdd = writeVHDL fullAddSys++a_in = [L,L,L,L,H,H,H,H]+b_in = [L,H,L,H,L,H,L,H]+c_in = [L,L,H,H,L,L,H,H]+++------ 2/1 Multiplexer++--mux21Fun :: ProcFun (Bit -> Bit -> Bit -> Bit)+--mux21Fun = $(newProcFun [d|mux21Fun :: Bit -> Bit -> Bit -> Bit+-- mux21Fun sel d1 d0 = if (sel == L) then+-- d0+-- else+-- d1 |])++mux21Proc :: Signal Bit -> Signal Bit -> Signal Bit -> Signal Bit+mux21Proc = zipWith3SY "mux21" mux21Fun++mux21Sys :: SysDef (Signal Bit -> Signal Bit -> Signal Bit -> Signal Bit)+mux21Sys = newSysDef mux21Proc "mux21" ["sel", "d1", "d0"] ["out"]++simMux21 :: [Bit] -> [Bit] -> [Bit] -> [Bit]+simMux21 = simulate mux21Sys++vhdlMux21 = writeVHDL mux21Sys++------ 4-bit Adder Chain++fourBitAdder :: Signal Bit -- C_IN+ -> Signal Bit -- A3+ -> Signal Bit -- A2+ -> Signal Bit -- A1+ -> Signal Bit -- A0+ -> Signal Bit -- B3+ -> Signal Bit -- B2+ -> Signal Bit -- B1+ -> Signal Bit -- B0+ -> (Signal Bit, -- C_OUT+ Signal Bit, -- SUM3+ Signal Bit, -- SUM2+ Signal Bit, -- SUM1+ Signal Bit) -- SUM0+fourBitAdder c_in a3 a2 a1 a0 b3 b2 b1 b0+ = (c_out, sum3, sum2, sum1, sum0)+ where (c_out, sum3) = (instantiate "add3" fullAddSys) a3 b3 c2+ (c2, sum2) = (instantiate "add2" fullAddSys) a2 b2 c1+ (c1, sum1) = (instantiate "add1" fullAddSys) a1 b1 c0+ (c0, sum0) = (instantiate "add0" fullAddSys) a0 b0 c_in++fourBitAdderSys :: SysDef (Signal Bit -- C_IN+ -> Signal Bit -- A3+ -> Signal Bit -- A2+ -> Signal Bit -- A1+ -> Signal Bit -- A0+ -> Signal Bit -- B3+ -> Signal Bit -- B2+ -> Signal Bit -- B1+ -> Signal Bit -- B0+ -> (Signal Bit, -- C_OUT+ Signal Bit, -- SUM3+ Signal Bit, -- SUM2+ Signal Bit, -- SUM1+ Signal Bit)) -- SUM0+fourBitAdderSys = newSysDef fourBitAdder "fourBitAdder" ["C_IN", "A3", "A2", "A1", "A0",+ "B3", "B2", "B1", "B0"]+ ["C_OUT", "SUM3", "SUM2", "SUM1", "SUM0"]++simFourBitAdder = simulate fourBitAdderSys++vhdlFourBitAdder = writeVHDL fourBitAdderSys++a0 = [L,L,L,L,L,L,L,L,H,H,H,H,H,H,H,H]+a1 = [L,L,L,L,H,H,H,H,L,L,L,L,H,H,H,H]+a2 = [L,L,H,H,L,L,H,H,L,L,H,H,L,L,H,H]+a3 = [L,H,L,H,L,H,L,H,L,H,L,H,L,H,L,H]+zero = [L,L,L,L,L,L,L,L,L,L,L,L,L,L,L,L]+one = [H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H]+++simAdd_0 = simFourBitAdder zero a3 a2 a1 a0 a3 a2 a1 a0+simAdd_1 = simFourBitAdder one a3 a2 a1 a0 a3 a2 a1 a0++----- fourBitCarrySelectAdder+++fourBitCSAdder :: Signal Bit -- C_IN+ -> Signal Bit -- A3+ -> Signal Bit -- A2+ -> Signal Bit -- A1+ -> Signal Bit -- A0+ -> Signal Bit -- B3+ -> Signal Bit -- B2+ -> Signal Bit -- B1+ -> Signal Bit -- B0+ -> (Signal Bit, -- C_OUT+ Signal Bit, -- SUM3+ Signal Bit, -- SUM2+ Signal Bit, -- SUM1+ Signal Bit) -- SUM0fourBitCSAdder ::+fourBitCSAdder c_in a3 a2 a1 a0 b3 b2 b1 b0+ = (c_out, sum3, sum2, sum1, sum0)+ where c_out = (instantiate "mux4" mux21Sys) c_in c_out_1 c_out_0+ sum3 = (instantiate "mux3" mux21Sys) c_in sum3_1 sum3_0+ sum2 = (instantiate "mux2" mux21Sys) c_in sum2_1 sum2_0+ sum1 = (instantiate "mux1" mux21Sys) c_in sum1_1 sum1_0+ sum0 = (instantiate "mux0" mux21Sys) c_in sum0_1 sum0_0+ (c_out_1, sum3_1, sum2_1, sum1_1, sum0_1)+ = (instantiate "Adder_1" fourBitAdderSys)+ one a3 a2 a1 a0 b3 b2 b1 b0+ (c_out_0, sum3_0, sum2_0, sum1_0, sum0_0)+ = (instantiate "Adder_1" fourBitAdderSys)+ zero a3 a2 a1 a0 b3 b2 b1 b0+ one = instantiate "One" oneSys+ zero = instantiate "Zero" zeroSys++fourBitCSAdderSys = newSysDef fourBitCSAdder "fourBitCSAdder" ["C_IN", "A3", "A2", "A1", "A0",+ "B3", "B2", "B1", "B0"]+ ["C_OUT", "SUM3", "SUM2", "SUM1", "SUM0"]+++simFourBitCSAdder = simulate fourBitCSAdderSys++vhdlFourBitCSAdder = writeVHDL fourBitCSAdderSys++simCSAdd_0 = simFourBitAdder zero a3 a2 a1 a0 a3 a2 a1 a0+simCSAdd_1 = simFourBitAdder one a3 a2 a1 a0 a3 a2 a1 a0++----- Convert Output to FSVec++convertToFSVec5 :: a -> a -> a -> a -> a -> FSVec D5 a+convertToFSVec5 x4 x3 x2 x1 x0 = x4 +> x3 +> x2 +> x1 +> (singleton x0)++convOutput :: ProcFun (Bit -> Bit -> Bit -> Bit -> Bit -> FSVec D5 Bit)+convOutput = $(newProcFun [d|convOutput :: Bit -> Bit -> Bit -> Bit -> Bit -> FSVec D5 Bit+ convOutput x4 x3 x2 x1 x0+ = convertToFSVec5 x4 x3 x2 x1 x0 |])++convOutputProc :: Signal Bit -> Signal Bit -> Signal Bit -> Signal Bit -> Signal Bit -> Signal (FSVec D5 Bit)+convOutputProc = zipWith5SY "toVector5" convOutput++convOutputSys :: SysDef (Signal Bit -> Signal Bit -> Signal Bit -> Signal Bit -> Signal Bit -> Signal (FSVec D5 Bit))+convOutputSys = newSysDef convOutputProc "convOutput" ["x4", "x3", "x2", "x1", "x0"] ["vec5"]++fourBitCSAdder' :: Signal Bit -- C_IN+ -> Signal Bit -- A3+ -> Signal Bit -- A2+ -> Signal Bit -- A1+ -> Signal Bit -- A0+ -> Signal Bit -- B3+ -> Signal Bit -- B2+ -> Signal Bit -- B1+ -> Signal Bit -- B0+ -> Signal (FSVec D5 Bit) -- <C_OUT, SUM3, SUM2, SUM1, SUM0>+fourBitCSAdder' c_in a3 a2 a1 a0 b3 b2 b1 b0 = out5+ where+ out5 = (instantiate "toVector5" convOutputSys) c_out sum3 sum2 sum1 sum0+ (c_out, sum3, sum2, sum1, sum0) = (instantiate "Adder" fourBitCSAdderSys) c_in a3 a2 a1 a0 b3 b2 b1 b0++fourBitCSAdderSys2 :: SysDef (Signal Bit -- C_IN+ -> Signal Bit -- A3+ -> Signal Bit -- A2+ -> Signal Bit -- A1+ -> Signal Bit -- A0+ -> Signal Bit -- B3+ -> Signal Bit -- B2+ -> Signal Bit -- B1+ -> Signal Bit -- B0+ -> Signal (FSVec D5 Bit))+fourBitCSAdderSys2 = newSysDef fourBitCSAdder' "fourBitCSAdder" ["C_IN", "A3", "A2", "A1", "A0",+ "B3", "B2", "B1", "B0"]+ ["vect5"]++simFourBitCSAdder2 = simulate fourBitCSAdderSys2++vhdlFourBitCSAdder2 = writeVHDL fourBitCSAdderSys2++simCSAdd_0' = simFourBitCSAdder2 zero a3 a2 a1 a0 a3 a2 a1 a0+simCSAdd_1' = simFourBitCSAdder2 one a3 a2 a1 a0 a3 a2 a1 a0+++----- Four Bit Adder+--+-- The four bit adder is modelled as a set of equations.+-- It uses fixed-size vectors for the 4-bit input and output values.++adder4BitFun :: ProcFun (Bit -> FSVec D4 Bit -> FSVec D4 Bit -> (Bit, FSVec D4 Bit))+adder4BitFun = $(newProcFun [d|adder4BitFun :: Bit -> FSVec D4 Bit -> FSVec D4 Bit -> (Bit, FSVec D4 Bit)+ adder4BitFun cin a b = (cout, sum) where+ cout :: Bit+ cout = (a!d3 .&. b!d3) .|. (a!d3 .&. c2) .|. (b!d3 .&. c2)+ c2 :: Bit+ c2 = (a!d2 .&. b!d2) .|. (a!d2 .&. c1) .|. (b!d2 .&. c1)+ c1 :: Bit+ c1 = (a!d1 .&. b!d1) .|. (a!d1 .&. c0) .|. (b!d1 .&. c0)+ c0 :: Bit+ c0 = (a!d0 .&. b!d0) .|. (a!d0 .&. cin) .|. (b!d0 .&. cin)+ s3 :: Bit+ s3 = (a!d3 `xor` b!d3) `xor` c2+ s2 :: Bit+ s2 = (a!d2 `xor` b!d2) `xor` c1+ s1 :: Bit+ s1 = (a!d1 `xor` b!d1) `xor` c0+ s0 :: Bit+ s0 = (a!d0 `xor` b!d0) `xor` cin+ sum :: FSVec D4 Bit+ sum = s0 +> s1 +> s2 +> s3 +> empty |])++adder4BitProc :: Signal Bit -> Signal (FSVec D4 Bit) -> Signal (FSVec D4 Bit) -> (Signal Bit, Signal (FSVec D4 Bit))+adder4BitProc cin a b = (unzipSY "unzip") $ (zipWith3SY "4BitAdder" adder4BitFun cin a b)++adder4BitSys :: SysDef (Signal Bit -> Signal (FSVec D4 Bit) -> Signal (FSVec D4 Bit) -> (Signal Bit, Signal (FSVec D4 Bit)))+adder4BitSys = newSysDef adder4BitProc "adder4Bit" ["cin", "a", "b"] ["cout", "sum"]++simAdder4Bit = simulate adder4BitSys++vhdlAdder4Bit = writeVHDL adder4BitSys++----- 4-Bit 2/1 Multiplexer+--+-- The multiplexer uses fixed size vectors for input and output values.++mux21Fun :: ProcFun (Bit -> a -> a -> a)+mux21Fun = $(newProcFun [d|mux21Fun :: Bit -> a -> a -> a+ mux21Fun sel d1 d0 = if (sel == L) then+ d0+ else+ d1 |])++mux21_4BitProc :: Signal Bit -> Signal (FSVec D4 Bit) -> Signal (FSVec D4 Bit) -> Signal (FSVec D4 Bit)+mux21_4BitProc = zipWith3SY "mux21" mux21Fun++mux21_4BitSys :: SysDef (Signal Bit -> Signal ( FSVec D4 Bit) -> Signal (FSVec D4 Bit) -> Signal (FSVec D4 Bit))+mux21_4BitSys = newSysDef mux21_4BitProc "mux21_4Bit" ["sel", "d1", "d0"] ["out"]++simMux21_4Bit :: [Bit] -> [FSVec D4 Bit] -> [FSVec D4 Bit] -> [FSVec D4 Bit]+simMux21_4Bit = simulate mux21_4BitSys++vhdlMux21_4Bit = writeVHDL mux21_4BitSys+++----- 4-Bit Carry Select Adder++-- The carry select adder has two input that are connected to 'H' or 'L' respectively++-- Constant input 'H' modeled with constSY+oneProc :: Signal Bit+oneProc = constSY "high" H++oneSys :: SysDef (Signal Bit)+oneSys = newSysDef oneProc "one" [] ["one"]++-- Constant input 'L' modeled with constSY+zeroProc :: Signal Bit+zeroProc = constSY "low" L++zeroSys :: SysDef (Signal Bit)+zeroSys = newSysDef zeroProc "zero" [] ["zero"]++-- The 4-Bit carry select adder is implemented as a composition of components+csAdder4BitProc :: Signal Bit -> Signal (FSVec D4 Bit) -> Signal (FSVec D4 Bit) -> (Signal Bit, Signal (FSVec D4 Bit))+csAdder4BitProc cin a b = (cout, sum) where+ cout = (instantiate "mux" mux21Sys) cin cout_1 cout_0+ sum = (instantiate "mux4Bit" mux21_4BitSys) cin sum_1 sum_0+ (cout_1, sum_1) = (instantiate "adder1" adder4BitSys) one a b+ (cout_0, sum_0) = (instantiate "adder0" adder4BitSys) zero a b+ one = instantiate "One" oneSys+ zero = instantiate "Zero" zeroSys++csAdder4BitSys :: SysDef (Signal Bit -> Signal (FSVec D4 Bit) -> Signal (FSVec D4 Bit) -> (Signal Bit, Signal (FSVec D4 Bit)))+csAdder4BitSys = newSysDef csAdder4BitProc "csAdder4Bit" ["cin", "a", "b"] ["cout", "sum"]++simCSAdder4Bit = simulate csAdder4BitSys++vhdlCSAdder4Bit = writeVHDL csAdder4BitSys++----- 16 Bit Adder++csAdder16BitProc :: Signal (FSVec D16 Bit) -> Signal (FSVec D16 Bit) -> (Signal Bit, Signal (FSVec D16 Bit))+csAdder16BitProc a b = (cout, sum) where+ sum = zipWith4SY "concat4" concat4Fun sum3_0 sum7_4 sum11_8 sum15_12+ (cout, sum15_12) = (instantiate "csadder1" csAdder4BitSys) c11 a15_12 b15_12+ (c11, sum11_8) = (instantiate "csadder2" csAdder4BitSys) c7 a11_8 b11_8+ (c7, sum7_4) = (instantiate "csadder3" csAdder4BitSys) c3 a7_4 b7_4+ (c3, sum3_0) = (instantiate "csadder4" csAdder4BitSys) zero a3_0 b3_0+ a15_12 = mapSY "a15_12" select15_12Fun a+ a11_8 = mapSY "a11_8" select11_8Fun a+ a7_4 = mapSY "a7_4" select7_4Fun a+ a3_0 = mapSY "a3_0" select3_0Fun a+ b15_12 = mapSY "b15_12" select15_12Fun b+ b11_8 = mapSY "b11_8" select11_8Fun b+ b7_4 = mapSY "b7_4" select7_4Fun b+ b3_0 = mapSY "b3 _0" select3_0Fun b+ zero = instantiate "Zero" zeroSys+++csAdder16BitSys = newSysDef csAdder16BitProc "csAdder16Bit" ["a", "b"] ["cout", "sum"]++simCSAdder16Bit = simulate csAdder16BitSys++vhdlCSAdder16Bit = writeVHDL csAdder16BitSys++-- Helper functions++select15_12Fun :: ProcFun (FSVec D16 Bit -> FSVec D4 Bit)+select15_12Fun = $(newProcFun [d|select15_12 :: FSVec D16 Bit -> FSVec D4 Bit+ select15_12 v = select d12 d1 d4 v |])++select11_8Fun :: ProcFun (FSVec D16 Bit -> FSVec D4 Bit)+select11_8Fun = $(newProcFun [d|select11_8 :: FSVec D16 Bit -> FSVec D4 Bit+ select11_8 v = select d8 d1 d4 v |])++select7_4Fun :: ProcFun (FSVec D16 Bit -> FSVec D4 Bit)+select7_4Fun = $(newProcFun [d|select7_4 :: FSVec D16 Bit -> FSVec D4 Bit+ select7_4 v = select d4 d1 d4 v |])++select3_0Fun :: ProcFun (FSVec D16 Bit -> FSVec D4 Bit)+select3_0Fun = $(newProcFun [d|select3_0 :: FSVec D16 Bit -> FSVec D4 Bit+ select3_0 v = select d0 d1 d4 v |])++concat4Fun :: ProcFun (FSVec D4 Bit -> FSVec D4 Bit -> FSVec D4 Bit -> FSVec D4 Bit -> FSVec D16 Bit)+concat4Fun = $(newProcFun [d|concat4Fun :: FSVec D4 Bit -> FSVec D4 Bit -> FSVec D4 Bit -> FSVec D4 Bit -> FSVec D16 Bit+ concat4Fun v1 v2 v3 v4 = v1 Data.Param.FSVec.+++ v2 Data.Param.FSVec.+++ v3 Data.Param.FSVec.+++ v4 |])++-- 16 Bit Test Values++v_0000 = reallyUnsafeVector [L,L,L,L, L,L,L,L, L,L,L,L, L,L,L,L]+v_8000 = reallyUnsafeVector [L,L,L,L, L,L,L,L, L,L,L,L, L,L,L,H] -- H is MSB+v_0001 = reallyUnsafeVector [H,L,L,L, L,L,L,L, L,L,L,L, L,L,L,L]+v_ffff = reallyUnsafeVector [H,H,H,H, H,H,H,H, H,H,H,H, H,H,H,H]++a16 = v_0000 : v_0000 : v_0001 : v_8000 : v_ffff : v_ffff : []+b16 = v_0000 : v_0001 : v_0001 : v_8000 : v_ffff : v_0001 : []++test16Bit = simCSAdder16Bit a16 b16++-- 4 Bit Test Values++v_0 = reallyUnsafeVector [L,L,L,L]+v_1 = reallyUnsafeVector [H,L,L,L]+v_8 = reallyUnsafeVector [L,L,L,H]+v_f = reallyUnsafeVector [H,H,H,H]++a4 = v_0 : v_0 : v_1 : v_8 : v_f : v_1 : []+b4 = v_0 : v_1 : v_1 : v_8 : v_f : v_f : []++c_low = [L,L,L,L,L,L]+c_high = [H,H,H,H,H,H]++test4Bit = simCSAdder4Bit c_low a4 b4++-- Test FSVector++fv = 0 +> 1 +> 2 +> 3 +> empty -- LSB fv!d0 = 0, MSB fv!d3 = 3
+ examples/Counter.hs view
@@ -0,0 +1,20 @@+{-# LANGUAGE TemplateHaskell #-}++-- A counter, the simplest system with which to test netlist loops++module Counter where++import ForSyDe.Deep+import Data.Int++counter :: Signal Int32+counter = sourceSY "counterSource" add1 (0 :: Int32)+ where add1 = $(newProcFun [d| add1 :: Int32 -> Int32 + add1 a = a + 1 |]) +++counterSys :: SysDef (Signal Int32)+counterSys = $(newSysDefTHName 'counter [] ["countVal"])++simCounter :: [Int32]+simCounter = simulate counterSys
+ examples/DeepShallow.hs view
@@ -0,0 +1,22 @@+-- This example shows how to mix shallow-embedded and+-- deep-embedded signals by building an heterogeneous+-- system which adds five to its input+module DeepShallow where++import SeqAddFour (addFourSys)+import ForSyDe.Deep(simulate)+import ForSyDe.Shallow+import Data.Int++-- addOne using shallow-embedded signals+addOne :: Signal Int32 -> Signal Int32+addOne = mapSY (+1)++-- addFourSys uses deep-embedded signals, but we can transform them to lists+-- using simulate+addFourLists :: [Int32] -> [Int32]+addFourLists = simulate addFourSys++-- addFive uses addOne and addFourLists+addFive :: Signal Int32 -> Signal Int32+addFive = signal.addFourLists.fromSignal.addOne
+ examples/LFSR.hs view
@@ -0,0 +1,58 @@+{-# LANGUAGE TemplateHaskell #-}++-- This modules implements an Linear Feedback Shiftregister++-- Source:+-- Ben Cohen. VHDL Answers to Frequently Answered Questions. Kluwer. 1998++module LFSR where++import ForSyDe.Deep -- Definition of Bit data type+import Data.Bits -- Operations for Bit data type+import Data.List++-- ********************+--+-- Naive Implementation+--+-- ********************++-- Design has one XOR-component+xor2f :: ProcFun (Bit -> Bit -> Bit)+xor2f = $(newProcFun [d|xor2f :: Bit -> Bit -> Bit+ xor2f x1 x0 = xor x1 x0 |])++xor2Proc :: Signal Bit -> Signal Bit -> Signal Bit+xor2Proc = zipWithSY "xor2" xor2f++xor2Sys :: SysDef (Signal Bit -> Signal Bit -> Signal Bit)+xor2Sys = newSysDef xor2Proc "xor2" ["in1", "in0"] ["out0"]+++-- Design has four D-Flip-Flops+dffProc :: Signal Bit -> Signal Bit+dffProc = delaySY "delayDFF" H -- Starting Sequence must not be LL...L++dffSys :: SysDef (Signal Bit -> Signal Bit)+dffSys = newSysDef dffProc "DelayFF" ["in1"] ["in0"]++-- Netlist of LFSR+lfsr :: (Signal Bit, Signal Bit, Signal Bit, Signal Bit)+lfsr = (s1, s2, s3, s4)+ where s5 = instantiate "xor2" xor2Sys s1 s4+ s1 = instantiate "dff1" dffSys s5+ s2 = instantiate "dff2" dffSys s1+ s3 = instantiate "dff3" dffSys s2+ s4 = instantiate "dff4" dffSys s3++lfsrSys :: SysDef ((Signal Bit, Signal Bit, Signal Bit, Signal Bit))+lfsrSys = newSysDef lfsr "lfsr" [] ["s1", "s2", "s3", "s4"]++simlfsr :: ([Bit], [Bit], [Bit], [Bit])+simlfsr = simulate lfsrSys++zip4tuple (a, b, c, d) = zip4 a b c d++createVHDL = writeVHDL lfsrSys++myLFSRtest = (take 20 . zip4tuple) (simlfsr)
+ examples/Multiplexer.hs view
@@ -0,0 +1,57 @@+{-# LANGUAGE TemplateHaskell #-}+-- multiplexer+module Multiplexer where++import ForSyDe.Deep+import Language.Haskell.TH.Lift++-- I used tuples, but a vector of bits is what I would like to have+selectf :: ProcFun((Bit, Bit) -> (Bit, Bit, Bit, Bit) -> Bit)+selectf = $(newProcFun [d| select1 :: (Bit, Bit)+ -> (Bit, Bit, Bit, Bit) -> Bit+ select1 (s1, s0) (x3, x2, x1, x0) =+ if (s1 == L) && (s0 == L) then+ x0+ else+ if (s1 == L) && (s0 == H) then+ x1+ else+ if (s1 == H) && (s0 == L) then+ x2+ else+ x3 |])+++-- System function (or process) which uses 'selectf'+selectProc :: Signal (Bit, Bit) -> Signal (Bit, Bit, Bit, Bit) -> Signal Bit+selectProc = zipWithSY "select1" selectf++-- System definition associated to the system process 'selectProc'+muxSysDef :: SysDef (Signal (Bit, Bit) -> Signal (Bit, Bit, Bit, Bit) -> Signal Bit)+muxSysDef = newSysDef selectProc "muxSys" ["sel", "data"] ["out1"]++-- we simulate the system+simMux :: [(Bit, Bit)] -> [(Bit, Bit, Bit, Bit)] -> [Bit]+simMux = simulate muxSysDef+++selIn = [(L,L),(L,H),(H,L)]+dataIn = [(L,L,L,H), (L,L,L,H), (L,H,L,L)]+++-- translate to VHDL, write output and run in QUartus+vhdlMux :: IO ()+vhdlMux = writeVHDLOps defaultVHDLOps{execQuartus=Just quartusOps} muxSysDef+ where quartusOps = QuartusOps FullCompilation+ (Just 50)+ (Just ("CycloneII", Just "EP2C35F672C6"))+ [("sel.tup_1","PIN_AF14"),+ ("sel.tup_2","PIN_AD13"),+ ("data.tup_1","PIN_N25"),+ ("data.tup_2","PIN_N26"),+ ("data.tup_3","PIN_P25"),+ ("data.tup_4","PIN_AE14"),+ ("out1","PIN_AE23")]+++
+ examples/Multiplexer_FSVector.hs view
@@ -0,0 +1,45 @@+{-# LANGUAGE TemplateHaskell #-}+-- multiplexer+module Multiplexer_FSVector where++import ForSyDe.Deep+import Language.Haskell.TH.Lift+import Data.Param.FSVec+import Data.TypeLevel.Num.Reps+import Data.TypeLevel.Num.Aliases++-- I used tuples, but a vector of bits is what I would like to have+selectf :: ProcFun(FSVec D2 Bit -> FSVec D4 Bit -> Bit)+selectf = $(newProcFun [d| select1 :: FSVec D2 Bit+ -> FSVec D4 Bit -> Bit+ select1 sel input =+ if (sel!d1 == L) && (sel!d0 == L) then+ input!d0+ else+ if (sel!d1 == L) && (sel!d0 == H) then+ input!d1+ else+ if (sel!d1 == H) && (sel!d0 == L) then+ input!d2+ else+ input!d3 |])+++-- System function (or process) which uses 'selectf'+selectProc :: Signal (FSVec D2 Bit) -> Signal (FSVec D4 Bit) -> Signal Bit+selectProc = zipWithSY "select1" selectf++-- System definition associated to the system process 'selectProc'+muxSysDef :: SysDef (Signal (FSVec D2 Bit) -> Signal (FSVec D4 Bit) -> Signal Bit)+muxSysDef = $(newSysDefTHName 'selectProc ["sel", "data"] ["out1"])++-- we simulate the system+simMux :: [FSVec D2 Bit] -> [FSVec D4 Bit] -> [Bit]+simMux = simulate muxSysDef++selIn = [$(vectorTH [L,L]), $(vectorTH [H,L]), $(vectorTH [L,H]) ]+dataIn = [$(vectorTH [L,H,H,H]), $(vectorTH [H,L,H,H]), $(vectorTH [H,H,L,H])]++-- Create VHDL+createVHDL = writeVHDL muxSysDef+
+ examples/Null.hs view
@@ -0,0 +1,20 @@+{-# LANGUAGE TemplateHaskell #-}++-- This module is aimed at testing the, normally useless but possible, +-- null system++module Null where++import ForSyDe.Deep++nullSysF :: ()+nullSysF = ()++nullSysDef :: SysDef ()+nullSysDef = newSysDef nullSysF "null" [] []++nullIns0 :: ()+nullIns0 = instantiate "null0" nullSysDef++simNull :: ()+simNull = simulate nullSysDef
+ examples/ParAddFour.hs view
@@ -0,0 +1,43 @@+{-# LANGUAGE TemplateHaskell #-}+-- Naive example to test instances working in paralell+-- Adds a signal to four signals+module ParAddFour where++import ForSyDe.Deep+import Data.Int+++add :: ProcFun (Int32 -> Int32 -> Int32)+add = $(newProcFun [d| add :: Int32 -> Int32 -> Int32+ add a b = a + b |])++addProc :: Signal Int32 -> Signal Int32 -> Signal Int32+addProc s1 s2 = zipWithSY "zip1" add s1 s2+++addSys :: SysDef (Signal Int32 -> Signal Int32 -> Signal Int32)+addSys = newSysDef addProc "add" ["in1","in2"] ["sum"]++simAdd :: [Int32] -> [Int32] -> [Int32]+simAdd = simulate addSys++parAddFour :: Signal Int32 + -> Signal Int32 -> Signal Int32 -> Signal Int32 -> Signal Int32 + -> (Signal Int32, Signal Int32, Signal Int32, Signal Int32)+parAddFour toAdd s1 s2 s3 s4 = (sum1, sum2, sum3, sum4)+ where sum1 = (instantiate "adder1" addSys) toAdd s1+ sum2 = (instantiate "adder2" addSys) toAdd s2+ sum3 = (instantiate "adder3" addSys) toAdd s3+ sum4 = (instantiate "adder4" addSys) toAdd s4++parAddFourSys :: SysDef (Signal Int32 + -> Signal Int32 -> Signal Int32 -> Signal Int32 -> Signal Int32 + -> (Signal Int32, Signal Int32, Signal Int32, Signal Int32))+parAddFourSys = newSysDef parAddFour "parAddFour" ["toAdd","s1","s2","s3","s4"]+ ["sum1","sum2","sum3","sum4"]+++simParAddFour :: [Int32]+ -> [Int32] -> [Int32] -> [Int32] -> [Int32]+ -> ([Int32], [Int32], [Int32], [Int32]) +simParAddFour = simulate parAddFourSys
+ examples/SeqAddFour.hs view
@@ -0,0 +1,45 @@+{-# LANGUAGE TemplateHaskell #-}++-- This module is aimed at testing a naive use-case of +-- the instantiation mechanism++-- We'll build a system which uses four single-adder components connected+-- sequentially to add four units to the values of the input signal++module SeqAddFour where++import ForSyDe.Deep+import Data.Int++-- A process function which adds one to its input+addOnef :: ProcFun (Int32 -> Int32)+addOnef = $(newProcFun [d|addOnef :: Int32 -> Int32 + addOnef n = n + 1 |])++-- System function (or process) which uses addOnef+addOneProc :: Signal Int32 -> Signal Int32+addOneProc = mapSY "addOne" addOnef+++-- System definition associated to the system function+addOneSysDef :: SysDef (Signal Int32 -> Signal Int32)+addOneSysDef = newSysDef addOneProc "addOne" ["in1"] ["out1"]+++-- Finally, we create the sequential add four system function+addFour :: Signal Int32 -> Signal Int32+addFour = instantiate "addOne3" addOneSysDef .+ instantiate "addOne2" addOneSysDef .+ instantiate "addOne1" addOneSysDef .+ instantiate "addOne0" addOneSysDef++-- We build the system+addFourSys :: SysDef (Signal Int32 -> Signal Int32)+addFourSys = newSysDef addFour "addFour" ["in1"] ["out1"]++-- we simulate the system+simAddFour :: [Int32] -> [Int32]+simAddFour = simulate addFourSys++-- we generate VHDL+createVHDL = writeVHDL addFourSys
+ examples/ZipTwist.hs view
@@ -0,0 +1,36 @@+{-# LANGUAGE TemplateHaskell #-}+-- An identity system of six inputs formed by various zipping and unzipping+-- processes+module ZipTwist where++import ForSyDe.Deep+import Data.Param.FSVec+import Data.TypeLevel.Num.Reps+import Data.Int++zip6xSY :: ProcId -> FSVec D6 (Signal Int32) -> Signal (FSVec D6 Int32)+zip6xSY = zipxSY++zipTwistFun :: Signal Int32 -> Signal Int32 -> Signal Int32 + -> Signal Int32 -> Signal Int32 -> Signal Int32+ -> (Signal Int32, Signal Int32, Signal Int32,+ Signal Int32, Signal Int32, Signal Int32)+zipTwistFun i1 i2 i3 i4 i5 i6 = (o1,o2,o3,o4,o5,o6)+ where [i1',i2',i3',i4',i5',i6'] = (fromVector.unzipxSY "unzipxSY" . zip6xSY "zip6xsy")+ $ reallyUnsafeVector [i1,i2,i3,i4,i5,i6]+ zip13 = zip3SY "zip13" i1' i2' i3'+ zip46 = zip3SY "zip46" i4' i5' i6'+ (o1,o2,o3) = unzip3SY "unzip13" zip13+ (o4,o5,o6) = unzip3SY "unzip46" zip46++zipTwistSys :: SysDef (Signal Int32 -> Signal Int32 -> Signal Int32 + -> Signal Int32 -> Signal Int32 -> Signal Int32+ -> (Signal Int32, Signal Int32, Signal Int32,+ Signal Int32, Signal Int32, Signal Int32))+zipTwistSys = newSysDef zipTwistFun "zipTwist"+ ["in1","in2","in3","in4","in5","in6"]+ ["out1","out2","out3","out4","out5","out6"]++simZipTwist = simulate zipTwistSys++writeVHDLZipTwist = writeVHDL zipTwistSys
+ forsyde-deep.cabal view
@@ -0,0 +1,168 @@+name: forsyde-deep+version: 0.2.0+cabal-version: >= 1.9.2+build-type: Custom+license: BSD3+license-file: LICENSE+author: Alfonso Acosta, Hendrik Woidt+copyright: Copyright (c) 2003-2018 ForSyDe Group, KTH/EECS/ES+maintainer: forsyde-dev@eecs.kth.se+homepage: https://forsyde.github.io/+stability: alpha+synopsis:+ ForSyDe's Haskell-embedded Domain Specific Language.+description:+ The ForSyDe (Formal System Design) methodology has been developed with the objective to move system design to a higher level of abstraction and to bridge the abstraction gap by transformational design refinement.++ This library provides ForSyDe's implementation as a Haskell-embedded Domain Specific Language (DSL). For more information, please see ForSyDe's website: <https://forsyde.github.io/>.++ This library provides the deep implementation of ForSyDe in Haskell.+category: Language, Hardware+tested-with: GHC==7.10.3 GHC==8.0.1+data-files: lib/forsyde.vhd+-- In order to include all this files with sdist+extra-source-files: LICENSE,+ README.md,+ examples/ALU.hs,+ examples/ALU_Shallow.hs,+ examples/BitVector.hs,+ examples/ButtonEncoder.hs,+ examples/CarrySelectAdder.hs,+ examples/Counter.hs,+ examples/DeepShallow.hs,+ examples/LFSR.hs,+ examples/Multiplexer.hs,+ examples/Multiplexer_FSVector.hs,+ examples/Null.hs,+ examples/ParAddFour.hs,+ examples/SeqAddFour.hs,+ examples/ZipTwist.hs,+ tests/Install.hs,+ tests/examples/Main.hs,+ tests/examples/VHDLBackend.hs,+-- due to a bug in cabal 1.2, sdist does not include hs-boot files+ src/ForSyDe/Deep/Backend/VHDL/GlobalNameTable.hs-boot,+ src/ForSyDe/Deep/System/SysDef.hs-boot+++Flag developer+ Description: Shorten build time for more fluent development+ Default: False++Test-Suite examples+ type: exitcode-stdio-1.0+ main-is: Main.hs+ hs-source-dirs: tests/examples,+ examples/+ build-depends: base>= 4.8.2 && < 4.9.2,+ parameterized-data >= 0.1.5,+ type-level,+ forsyde-deep,+ HUnit,+ random,+ syb,+ directory,+ QuickCheck++Library+ build-depends: type-level,+ parameterized-data >= 0.1.5,+ containers,+ base>= 4.8.2 && < 4.9.2,+ regex-posix,+ mtl,+ syb,+ pretty,+ template-haskell,+ process,+ directory,+ filepath,+ random+++ hs-source-dirs: src+ exposed-modules: Language.Haskell.TH.Lift,+ Language.Haskell.TH.LiftInstances,+ ForSyDe.Deep,+ ForSyDe.Deep.Ids,+ ForSyDe.Deep.System,+ ForSyDe.Deep.AbsentExt,+ ForSyDe.Deep.Bit,+ ForSyDe.Deep.DFT,+ ForSyDe.Deep.FIR,+ ForSyDe.Deep.Signal,+ ForSyDe.Deep.Process,+ ForSyDe.Deep.Process.SynchProc,+ ForSyDe.Deep.Backend,+ ForSyDe.Deep.Backend.Simulate,+ ForSyDe.Deep.Backend.VHDL,+ ForSyDe.Deep.Backend.GraphML++ other-modules: Paths_forsyde_deep,+ Language.Haskell.TH.TypeLib,+ Data.Typeable.TypeRepLib,+ Data.Typeable.FSDTypeRepLib,+ Data.Traversable.GenericZipWith,+ ForSyDe.Deep.Config,+ ForSyDe.Deep.Version,+ ForSyDe.Deep.ForSyDeErr,+ ForSyDe.Deep.Netlist,+ ForSyDe.Deep.Netlist.Traverse,+ ForSyDe.Deep.OSharing,+ ForSyDe.Deep.OSharing.UDynamic,+ ForSyDe.Deep.System.SysFun,+ ForSyDe.Deep.System.SysFun.Instances,+ ForSyDe.Deep.System.SysDef,+ ForSyDe.Deep.System.Instantiate,+ ForSyDe.Deep.Process.ProcFun,+ ForSyDe.Deep.Process.Desugar,+ ForSyDe.Deep.Process.ProcVal,+ ForSyDe.Deep.Process.ProcType,+ ForSyDe.Deep.Process.ProcType.Instances,+ ForSyDe.Deep.Backend.Ppr,+ ForSyDe.Deep.Backend.VHDL.GlobalNameTable,+ ForSyDe.Deep.Backend.VHDL.AST,+ ForSyDe.Deep.Backend.VHDL.Ppr,+ ForSyDe.Deep.Backend.VHDL.Constants,+ ForSyDe.Deep.Backend.VHDL.FileIO,+ ForSyDe.Deep.Backend.VHDL.Traverse,+ ForSyDe.Deep.Backend.VHDL.Traverse.VHDLM,+ ForSyDe.Deep.Backend.VHDL.Translate,+ ForSyDe.Deep.Backend.VHDL.Translate.HigherOrderFunctions,+ ForSyDe.Deep.Backend.VHDL.Generate,+ ForSyDe.Deep.Backend.VHDL.TestBench,+ ForSyDe.Deep.Backend.VHDL.Quartus,+ ForSyDe.Deep.Backend.VHDL.Modelsim,+ ForSyDe.Deep.Backend.VHDL.Ghdl,+ ForSyDe.Deep.Backend.GraphML.AST,+ ForSyDe.Deep.Backend.GraphML.Ppr,+ ForSyDe.Deep.Backend.GraphML.FileIO,+ ForSyDe.Deep.Backend.GraphML.Traverse,+ ForSyDe.Deep.Backend.GraphML.Traverse.GraphMLM++ ghc-options: -Wall -fno-warn-name-shadowing -fno-warn-orphans+ if flag(developer)+ cpp-options: -DDEVELOPER++custom-setup+ setup-depends: base >=4.8.2 && <4.9.2,+ Cabal >= 1.22 && < 1.25,+ parameterized-data >= 0.1.5,+ containers,+ base>= 4.8.2 && < 4.9.2,+ regex-posix,+ mtl,+ syb,+ pretty,+ template-haskell,+ process,+ directory,+ filepath,+ random,+ type-level,+ HUnit,+ QuickCheck+ +source-repository head+ type: git+ location: git://github.com/forsyde/forsyde-deep.git
+ lib/forsyde.vhd view
@@ -0,0 +1,172 @@+library ieee;+use ieee.numeric_std.all;+use ieee.std_logic_1164.all;+ +package types is+ + function default return integer;+ function show (i : integer) return string; + +-- Commented out due to representation overflow (modelsim integers+-- are 32bits long)+-- subtype int64 is integer range -(2**(64-1)) to +(2**(64-1)-1); ++-- Commented out due to a overflow in 2**32:+-- subtype int32 is integer range -(2**(32-1)) to +(2**(32-1)-1); ++-- Note the lower bound is not -2147483648 because the LRM doesn't+-- force to include it.+ subtype int32 is integer range -2147483647 to +2147483647; + + subtype int16 is integer range -(2**(16-1)) to +(2**(16-1)-1); ++ subtype int8 is integer range -(2**(8-1)) to +(2**(8-1)-1);++ function default return std_logic;+ function show (s : std_logic) return string;+ + function default return boolean;+ function show (b : boolean) return string;+ + -- Indexes for unconstrained fsvecs:+ -- -1 is used to express the null vector, with bounds (0 to -1)+ subtype fsvec_index is integer range -1 to integer'high;++ -- Unconstrained translation of "FSVec _ Bit"+ -- needed for toBitVector and fromBitVector+ type fsvec_std_logic is array (fsvec_index range <>) of std_logic;++ function toBitVector8 (i : int8) return fsvec_std_logic; + function toBitVector16 (i : int16) return fsvec_std_logic;+ function toBitVector32 (i : int32) return fsvec_std_logic;++ function fromBitVector8 (v : fsvec_std_logic) return int8; + function fromBitVector16 (v : fsvec_std_logic) return int16;+ function fromBitVector32 (v : fsvec_std_logic) return int32;++ function fixmul8 (a : int8; b: int8) return int8;+ +end types;++package body types is++-- Commented out due to representation overflow +-- function default return int64 is+-- begin +-- return 0;+-- end default;+ function fixmul8 (a : int8; b: int8) return int8 is+ variable res : int16;+ variable a16 : int16;+ variable b16 : int16;+ variable result_signed : signed (0 to 15);+ begin+ a16 := a;+ b16 := b;+ res := a16 * b16;+ result_signed := to_signed (res, 16);+ return to_integer(result_signed (8 to 15));+ end fixmul8;++ function default return integer is+ begin + return 0;+ end default;++ function show (i : integer) return string is+ begin+ return integer'image(i);+ end show;++ function default return std_logic is+ begin+ return '0';+ end default;++ function show (s : std_logic) return string is+ begin+ if s = '1' then+ return "H";+ else+ return "L";+ end if;+ end show;++ + function default return boolean is+ begin+ return true;+ end default;++ function show (b : boolean) return string is+ begin+ if b then+ return "True";+ else+ return "False";+ end if;+ end show;++ function toBitVector8 (i : int8) return fsvec_std_logic is+ variable inter : signed (0 to 7) := to_signed (i, 8);+ variable ret : fsvec_std_logic (0 to 7); + begin+ for index in ret'range loop+ ret(index) := inter(index);+ end loop;+ return ret;+ end toBitVector8;+++ function toBitVector16 (i : int16) return fsvec_std_logic is+ variable inter : signed (0 to 15) := to_signed (i, 16);+ variable ret : fsvec_std_logic (0 to 15); + begin+ for index in ret'range loop+ ret(index) := inter(index);+ end loop;+ return ret;+ end toBitVector16;+++ function toBitVector32 (i : int32) return fsvec_std_logic is+ variable inter : signed (0 to 31) := to_signed (i, 32);+ variable ret : fsvec_std_logic (0 to 31); + begin+ for index in ret'range loop+ ret(index) := inter(index);+ end loop;+ return ret;+ end toBitVector32;+++ function fromBitVector8 (v : fsvec_std_logic) return int8 is+ variable inter : signed (0 to 7);+ begin+ for index in inter'range loop+ inter(index) := v(index);+ end loop;+ return to_integer(inter);+ end frombitVector8;++ function fromBitVector16 (v : fsvec_std_logic) return int16 is+ variable inter : signed (0 to 15);+ begin+ for index in inter'range loop+ inter(index) := v(index);+ end loop;+ return to_integer(inter);+ end frombitVector16;+++ function fromBitVector32 (v : fsvec_std_logic) return int32 is+ variable inter : signed (0 to 31);+ begin+ for index in inter'range loop+ inter(index) := v(index);+ end loop;+ return to_integer(inter);+ end frombitVector32;+ ++end types;
+ src/Data/Traversable/GenericZipWith.hs view
@@ -0,0 +1,69 @@+---------------------------------------------------------------------------+-- |+-- Module : Data.Traversable.GenericZipWith+-- Copyright : (c) SAM Group, KTH/ICT/ECS 2007-2008+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- This module provides generalized zipWith functions.+-- +-----------------------------------------------------------------------------+module Data.Traversable.GenericZipWith + (zipWithTF,+ zipTF,+ zipWithTFA,+ zipWithTFM) where+++import Data.Foldable+import qualified Data.Traversable as T+import Control.Monad.State ++-- | The state contains the list of values obtained form the foldable container+-- and a String indicating the name of the function currectly being executed+data ZipState a = ZipState {fName :: String,+ list :: [a]}++-- | State monad containing ZipState+type ZipM l a = State (ZipState l) a++-- | pops the first element of the list inside the state+pop :: ZipM l l+pop = do + st <- get + let xs = list st+ n = fName st+ case xs of+ (a:as) -> do put st{list=as}+ return a+ [] -> error $ n ++ ": insufficient input"++-- | pop a value form the state and supply it to the second +-- argument of a binary function +supplySecond :: (a -> b -> c) -> a -> ZipM b c+supplySecond f a = do b <- pop + return $ f a b++zipWithTFError :: (Traversable t,Foldable f) => + String -> (a -> b -> c) -> t a -> f b -> t c +zipWithTFError str g t f = evalState (T.mapM (supplySecond g) t) + (ZipState str (toList f))+++zipWithTF :: (Traversable t,Foldable f) => (a -> b -> c) -> t a -> f b -> t c+zipWithTF = zipWithTFError "GenericZip.zipWithTF"++zipTF :: (Traversable t, Foldable f) => t a -> f b -> t (a,b)+zipTF = zipWithTFError "GenericZip.zipTF" (,) +++zipWithTFM :: (Traversable t,Foldable f,Monad m) => + (a -> b -> m c) -> t a -> f b -> m (t c)+zipWithTFM g t f = T.sequence (zipWithTFError "GenericZip.zipWithTFM" g t f)+ +zipWithTFA :: (Traversable t,Foldable f,Applicative m) => + (a -> b -> m c) -> t a -> f b -> m (t c)+zipWithTFA g t f = sequenceA (zipWithTFError "GenericZip.zipWithTFA" g t f)
+ src/Data/Typeable/FSDTypeRepLib.hs view
@@ -0,0 +1,97 @@+module Data.Typeable.FSDTypeRepLib (+ FSDType,+ FSDTypeRep,+ FSDTypeCon,+ fsdTy,+ fsdTyCon,+ fsdTyConApp,+ fsdTupleTyCon,+ fsdTyConName,+ fsdUnArrowT,+ fsdTyRep,+ fsdTyConOf,+ fsdTypeOf,+ fsdSplitTyConApp,+ type2FSDTypeRep+) where++import Data.Typeable+import Data.Typeable.TypeRepLib+import Data.Typeable.Internal+import Language.Haskell.TH (Type)+import Language.Haskell.TH.TypeLib (type2TypeRep)++newtype FSDTypeRep = FSDTypeRep' TypeRep deriving (Eq, Ord, Show)+newtype FSDTypeCon = FSDTypeCon' TyCon deriving (Eq, Ord, Show)++class FSDType a where+ fsdTy :: a -> FSDTypeRep++-- | e.g. use as fsd.typeOf (undefined :: FSVec)+instance FSDType TypeRep where+ fsdTy tr = FSDTypeRep' $ typeRepNormalize tr++--instance FSDType Type where+-- fsdTy ty = FSDTypeRep' typeRepNormalize $ type2TypeRep ty+++fsdTyRep :: FSDTypeRep -> TypeRep+fsdTyRep (FSDTypeRep' tr) = tr++fsdTyCon :: FSDTypeRep -> FSDTypeCon+fsdTyCon (FSDTypeRep' tr) = FSDTypeCon' $ typeRepTyCon tr++fsdUnTyCon :: FSDTypeCon -> TyCon+fsdUnTyCon (FSDTypeCon' tc) = tc++fsdSplitTyConApp :: FSDTypeRep -> (FSDTypeCon, [FSDTypeRep])+fsdSplitTyConApp (FSDTypeRep' fsdtr) = (FSDTypeCon' tc, map FSDTypeRep' tr) where+ (tc, tr) = splitTyConApp fsdtr++fsdTyConOf :: (Typeable a) => a -> FSDTypeCon+fsdTyConOf = fsdTyCon.fsdTy.typeOf++fsdTypeOf :: (Typeable a) => a -> FSDTypeRep+fsdTypeOf = fsdTy.typeOf++fsdTyConApp :: FSDTypeCon -> [FSDTypeRep] -> FSDTypeRep+fsdTyConApp c trs = FSDTypeRep' $ mkTyConApp tc targs where+ tc = fsdUnTyCon c+ targs = map fsdTyRep trs++fsdTupleTyCon :: Int -> FSDTypeCon+fsdTupleTyCon nOuts = FSDTypeCon' $ mkTyCon3 "" "GHC.Tuple" $ '(':replicate (nOuts-1) ','++")"++fsdTyConName (FSDTypeCon' tc) = tyConName tc++fsdUnArrowT :: FSDTypeRep -- ^ TypeRep to observe + -> ([FSDTypeRep], FSDTypeRep) -- ^ (args 'TypeRep', ret 'TypeRep')+fsdUnArrowT rep+ | repCon == arrowTyCon = let (args', ret') = fsdUnArrowT arrowArg2+ in (arrowArg1:args', ret')+ | otherwise = ([], rep)+ where (repCon,~[arrowArg1,arrowArg2]) = fsdSplitTyConApp rep++arrowTyCon :: FSDTypeCon+arrowTyCon = fsdTyConOf (undefined :: () -> ())++type2FSDTypeRep :: Type -> Maybe FSDTypeRep+type2FSDTypeRep ftr = do+ tr <- type2TypeRep ftr+ Just $ fsdTy tr++--+-- Strip the package names out of the TypeRep for correct comparison+--+tyConNormalize :: TyCon -> TyCon+tyConNormalize tc = mkTyCon3 "" (tyConModule tc) (tyConName tc)++typeRepNormalize :: TypeRep -> TypeRep+typeRepNormalize tr = mkPolyTyConApp tcN kindsN argsN+ where+ tc = typeRepTyCon tr+ kinds = typeRepKinds tr+ args = typeRepArgs tr+ tcN = tyConNormalize tc+ kindsN = map typeRepNormalize kinds+ argsN = map typeRepNormalize args
+ src/Data/Typeable/TypeRepLib.hs view
@@ -0,0 +1,31 @@+---------------------------------------------------------------------------+-- |+-- Module : Data.Typeable.TypeRepLib+-- Copyright : (c) SAM Group, KTH/ICT/ECS 2007-2008+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- This module provides basic functions related to Data.Typeables's 'TypeRep'.+-- +-----------------------------------------------------------------------------+module Data.Typeable.TypeRepLib (unArrowT) where++import Data.Typeable+++-- | Obtains the arguments and return type of a given 'TypeRep' +-- (normally a function)+-- together with its 'Context' (non-empty if the type is polymorphic)+unArrowT :: TypeRep -- ^ TypeRep to observe + -> ([TypeRep], TypeRep) -- ^ (args 'TypeRep', ret 'TypeRep')+unArrowT rep+ | repCon == arrowTyCon = let (args', ret') = unArrowT arrowArg2+ in (arrowArg1:args', ret')+ | otherwise = ([], rep)+ where (repCon,~[arrowArg1,arrowArg2]) = splitTyConApp rep++arrowTyCon :: TyCon+arrowTyCon = (typeRepTyCon.typeOf) (undefined :: () -> ())
+ src/ForSyDe/Deep.hs view
@@ -0,0 +1,38 @@+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe+-- Copyright : (c) ES Group (KTH) 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- This module is a wrapper for all the publicly usable types and+-- functions of ForSyDe's deep-embedded Domain Specific Language+-- (DSL). For the shallow-embedded DSL, please see "ForSyDe.Shallow".+--+-- +-----------------------------------------------------------------------------+module ForSyDe.Deep +(module ForSyDe.Deep.Ids,+ module ForSyDe.Deep.Signal,+ module ForSyDe.Deep.System,+ module ForSyDe.Deep.Process,+ module ForSyDe.Deep.Backend,+ module ForSyDe.Deep.Bit,+ module ForSyDe.Deep.AbsentExt,+ module ForSyDe.Deep.DFT,+ module ForSyDe.Deep.FIR,+ forsydeVersion) where++import ForSyDe.Deep.Ids+import ForSyDe.Deep.Signal (Signal)+import ForSyDe.Deep.Bit+import ForSyDe.Deep.Process+import ForSyDe.Deep.System+import ForSyDe.Deep.Backend+import ForSyDe.Deep.AbsentExt+import ForSyDe.Deep.DFT+import ForSyDe.Deep.FIR+import ForSyDe.Deep.Config (forsydeVersion)
+ src/ForSyDe/Deep/AbsentExt.hs view
@@ -0,0 +1,91 @@+{-# LANGUAGE TemplateHaskell, DeriveDataTypeable #-}+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.AbsentExt+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- The 'AbstExt' is used to extend existing data types with the value+-- \'absent\', which models the absence of a value.+--+-----------------------------------------------------------------------------++module ForSyDe.Deep.AbsentExt(+ AbstExt (Abst, Prst), fromAbstExt, unsafeFromAbstExt,+ abstExt, psi,+ isAbsent, isPresent, abstExtFunc)+ where++import Data.Data+import Language.Haskell.TH.Lift+++-- |The data type 'AbstExt' has two constructors. The constructor 'Abst' is used to model the absence of a value, while the constructor 'Prst' is used to model present values.+data AbstExt a = Abst+ | Prst a+ deriving (Eq, Data, Typeable)+$(deriveLift1 ''AbstExt)++++-- |The function 'fromAbstExt' extracts the inner value contained in 'AbstExt'+fromAbstExt :: a -- ^ Default value returned if the input is 'Abst'+ -> AbstExt a -> a+-- |Similar to 'fromAbstExt', but without default value+unsafeFromAbstExt :: AbstExt a -> a+-- |The functions 'isPresent' checks for the presence of a value.+isPresent :: AbstExt a -> Bool+-- |The functions 'isAbsent' checks for the absence of a value.+isAbsent :: AbstExt a -> Bool+-- |The function 'abstExtFunc' extends a function in order to process absent extended values. If the input is (\"bottom\"), the output will also be (\"bottom\").+abstExtFunc :: (a -> b) -> AbstExt a -> AbstExt b+-- | The function 'psi' is identical to 'abstExtFunc' and should be used in future.+psi :: (a -> b) -> AbstExt a -> AbstExt b+-- | The function 'abstExt' converts a usual value to a present value.+abstExt :: a -> AbstExt a+++++++-- Implementation of Library Functions++-- | The data type 'AbstExt' is defined as an instance of 'Show' and 'Read'. \'_\' represents the value 'Abst' while a present value is represented with its value, e.g. 'Prst' 1 is represented as \'1\'.+instance Show a => Show (AbstExt a) where+ showsPrec _ x = showsAbstExt x++showsAbstExt :: Show a => AbstExt a -> [Char] -> [Char]+showsAbstExt Abst = (++) "_"+showsAbstExt (Prst x) = (++) (show x)++instance Read a => Read (AbstExt a) where+ readsPrec _ x = readsAbstExt x++readsAbstExt :: (Read a) => ReadS (AbstExt a)+readsAbstExt s = [(Abst, r1) | ("_", r1) <- lex s]+ ++ [(Prst x, r2) | (x, r2) <- reads s]++abstExt v = Prst v++fromAbstExt x Abst = x+fromAbstExt _ (Prst y) = y++unsafeFromAbstExt (Prst x) = x+unsafeFromAbstExt Abst = error "AbsentExt.unsafeFromAbstExt: Abst"++isPresent Abst = False+isPresent (Prst _) = True++isAbsent = not . isPresent++abstExtFunc f = f'+ where f' Abst = Abst+ f' (Prst x) = Prst (f x)+++psi = abstExtFunc
+ src/ForSyDe/Deep/Backend.hs view
@@ -0,0 +1,24 @@+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.Backend+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- Wrapper module exporting all the backends+-- +-----------------------------------------------------------------------------+module ForSyDe.Deep.Backend + (module ForSyDe.Deep.Backend.Simulate,+ module ForSyDe.Deep.Backend.VHDL,+ module ForSyDe.Deep.Backend.GraphML) where++import ForSyDe.Deep.Backend.Simulate+import ForSyDe.Deep.Backend.VHDL+import ForSyDe.Deep.Backend.GraphML+++
+ src/ForSyDe/Deep/Backend/GraphML.hs view
@@ -0,0 +1,45 @@+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.Backend.GraphML+-- Copyright : (c) ES Group, KTH/ICT/ES 2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- This module provides the GraphML backend of ForSyDe's embedded compiler+--+-----------------------------------------------------------------------------+-- FIXME: factorize code shared between backends, and create a common interface for+-- backends (maybe based on a MPTC)++module ForSyDe.Deep.Backend.GraphML+ (writeGraphML, + writeGraphMLOps, + GraphMLOps(..),+ GraphMLDebugLevel(..),+ GraphMLRecursivity(..),+ defaultGraphMLOps) where++import Control.Monad.State (evalStateT)+import ForSyDe.Deep.ForSyDeErr+import ForSyDe.Deep.OSharing (readURef)+import ForSyDe.Deep.System.SysDef+import ForSyDe.Deep.Backend.GraphML.Traverse++-- | Given a System Definition whose name is A generate @A.graphml@ in current +-- working directory using the default compilation options.+writeGraphML :: SysDef a -> IO ()+writeGraphML = writeGraphMLOps defaultGraphMLOps ++-- | 'writeGraphML'-alternative which allows setting GraphML compilation +-- options.+writeGraphMLOps :: GraphMLOps -> SysDef a -> IO ()+writeGraphMLOps ops sysDef = do+ -- initiate the compilation State+ let s = initGraphMLTravST $ (readURef.unPrimSysDef.unSysDef) sysDef+ -- Translate the code+ res <- runErrorT $ evalStateT (setGraphMLOps ops >> writeGraphMLM) s+ -- Check if the compilation went well and print an error in case it didn't+ either printGraphMLError return res
+ src/ForSyDe/Deep/Backend/GraphML/AST.hs view
@@ -0,0 +1,44 @@+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.Backend.GraphML.AST+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- AST covering the GraphML subset we are interested in for this backend+-----------------------------------------------------------------------------++--FIXME: the design of this module is ugly++module ForSyDe.Deep.Backend.GraphML.AST where++import ForSyDe.Deep.Netlist++type GraphMLGraphId = String+type GraphMLPortId = String+type GraphMLNodeId = String++-- | Main AST type, a graph+data GraphMLGraph = GraphMLGraph + GraphMLGraphId -- Graph id+ [GraphMLNode] -- Nodes + [GraphMLEdge] -- Edges+++-- | Edge+data GraphMLEdge = GraphMLEdge + GraphMLNode -- Origin node+ GraphMLPortId -- Origin port id+ GraphMLNode -- Target node+ GraphMLPortId -- Target port id++-- | Node+data GraphMLNode = + ProcNode (NlNode GraphMLPortId) -- Netlist node indicating the input ports+ [GraphMLPortId] -- Output ports + -- FIXME: This representation is ugly+ |+ OutNode GraphMLNodeId GraphMLPortId -- the special output node
+ src/ForSyDe/Deep/Backend/GraphML/FileIO.hs view
@@ -0,0 +1,27 @@+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.Backend.GraphML.FileIO+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- Functions working with files in the GraphML backend. +--+-----------------------------------------------------------------------------+module ForSyDe.Deep.Backend.GraphML.FileIO where++import ForSyDe.Deep.Backend.GraphML.AST+import ForSyDe.Deep.Backend.GraphML.Ppr(YFilesMarkup, pprGraphWithHeaders)++import System.IO+import Text.PrettyPrint.HughesPJ++-- | Write a design file to a file in disk+writeGraph :: YFilesMarkup -> GraphMLGraph -> FilePath -> IO ()+writeGraph yFiles graph fp = do+ handle <- openFile fp WriteMode+ hPutStr handle $ (render . pprGraphWithHeaders yFiles) graph+ hClose handle
+ src/ForSyDe/Deep/Backend/GraphML/Ppr.hs view
@@ -0,0 +1,325 @@+{-# LANGUAGE TypeSynonymInstances, MultiParamTypeClasses #-}+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.Backend.GraphML.Ppr+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : non-portable (Template Haskell)+--+-- GraphML pretty printing instances.+--+-----------------------------------------------------------------------------+module ForSyDe.Deep.Backend.GraphML.Ppr where++import ForSyDe.Deep.Backend.Ppr+import ForSyDe.Deep.Ids+import ForSyDe.Deep.Process.ProcVal+import ForSyDe.Deep.Process.ProcFun+import ForSyDe.Deep.Backend.GraphML.AST+import ForSyDe.Deep.Netlist+import ForSyDe.Deep.Netlist.Traverse+import ForSyDe.Deep.System.SysDef+import ForSyDe.Deep.OSharing++import Data.Maybe (fromJust)+import Data.List (findIndex)+import qualified Data.Foldable as DF (foldr, toList)+import Language.Haskell.TH (pprint, Dec(FunD), Exp, nameBase)+import Text.PrettyPrint.HughesPJ+++-- | The only accepted pretyprinting option+type YFilesMarkup = Bool++-- | Number of spaces used for indentation+nestVal :: Int+nestVal = 5+++instance PprOps YFilesMarkup GraphMLGraph where+ pprOps yFiles (GraphMLGraph id nodes edges) =+ text "<graph" <+> text ("id=\"" ++ id ++ "\"") <+> + text "edgedefault=\"directed\" >" $+$+ nest nestVal (vSpace $+$ + pprOps_list yFiles (vNSpaces 1) nodes $+$+ vSpace $+$+ pprOps_list yFiles (vNSpaces 1) edges $+$+ vSpace) $+$+ text "</graph>" ++instance PprOps YFilesMarkup GraphMLNode where+ pprOps yFiles node =+ text "<node" <+> text ("id=\"" ++ id ++ "\"") <> text ">" $+$ + nest nestVal (+ (case node of+ ProcNode ins _ ->+ case ins of+ InPort _ -> + process_type "InPort" $+$+ yFilesNodeTags dim "#000000" "rectangle" (Just "w") id + Proc _ (Const pval) -> + let arg = (expVal.valAST) pval+ in process_type "ConstSY" $+$+ value_arg arg $+$+ yFilesNodeTags dim "#FFFFFF" "ellipse" Nothing ("ConstSY\n" ++ show id ++ "\nval=" ++ pprint arg)+ Proc _ (ZipWithNSY tpf i) -> + let nins = length i+ typ = case nins of+ 1 -> "MapSY"+ _ -> "ZipWith" ++ show nins ++ "SY"+ pfAST = (tpast.tast) tpf+ in process_type "ZipWithNSY" $+$+ procfun_arg pfAST $+$+ yFilesNodeTags dim "#6F7DBC" "roundrectangle" Nothing (typ ++ "\n" ++ show id ++ "\nfName=" ++ nameBase (name pfAST))+ Proc _ (ZipWithxSY tpf _) -> + process_type "ZipWithxSY" $+$+ procfun_arg ((tpast.tast) tpf) $+$+ yFilesNodeTags dim "#AFADFC" "rectangle" Nothing ("ZipWithxSY\n" ++ show id)+ Proc _ (UnzipNSY t _ _) -> + let typ = "Unzip" ++ show (length t) ++ "SY"+ in process_type "UnzipNSY" $+$+ yFilesNodeTags dim "#5993A3" "roundrectangle" Nothing (typ ++ "\n" ++ show id)+ Proc _ (UnzipxSY _ _ _ _) -> + process_type "UnzipxSY" $+$+ yFilesNodeTags dim "#99D3E3" "rectangle" Nothing ("UnzipxSY\n" ++ show id )+ Proc _ (DelaySY pval _) -> + let arg = (expVal.valAST) pval+ in process_type "DelaySY" $+$+ value_arg arg $+$+ yFilesNodeTags dim "#FF934C" "diamond" Nothing ("DelaySY\n" ++ show id ++ "\nval=" ++ pprint arg)+ Proc _ (SysIns psd _) -> + let parId = (sid.readURef.unPrimSysDef) psd+ in process_type "SysIns" $+$+ instance_parent parId $+$+ yFilesNodeTags dim "#FF934C" "rectangle" Nothing ("SysIns\n" ++ show id ++ "\nparent=" ++ parId)+ OutNode _ _ -> + process_type "OutPort" $+$+ yFilesNodeTags dim "#000000" "rectangle" (Just "e") id + ) $+$ vcat (map port portIds) + ) $+$+ text "</node>" + where + (id, portIds) = + case node of+ ProcNode ins outs ->+ let pids = arguments ins ++ outs+ in case ins of+ InPort id -> (id, pids)+ Proc id _ -> (id, pids)+ OutNode id portid -> (id,[portid])+ dim = nodeDims node+ -- write the yFiles specific markup for the node+ yFilesNodeTags (xsize, ysize) color shape mSide label =+ let labelLocation = maybe "modelName=\"internal\" modelPosition=\"c\""+ (\s -> "modelName=\"sides\" modelPosition=\""+++ s ++ "\"")+ mSide in + if yFiles + then + text "<data key=\"d0\">" $+$+ nest nestVal + (text "<y:ShapeNode>" $+$+ nest nestVal + (text "<y:Geometry height=\"" <> float ysize <> text "\" width=\"" <> float xsize <> text "\" x=\"0.0\" y=\"0.0\"/>" $+$+ text "<y:Fill color=\"" <> text color <> text "\" transparent=\"false\"/>" $+$+ text "<y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\"" <+> text labelLocation <+> text "textColor=\"#000000\" visible=\"true\">" <> text label <> text "</y:NodeLabel>" $+$+ text "<y:Shape type=\"" <> text shape <> text "\"/>"+ ) $+$+ text "</y:ShapeNode>" + ) $+$+ text "</data>"+ else empty++instance PprOps YFilesMarkup GraphMLEdge where+ pprOps yFiles (GraphMLEdge origN origP targetN targetP) = + text "<edge" <+> text ("source=\"" ++ origId ++ "\"") <+> + text ("sourceport=\"" ++ origP ++ "\"") <+>+ text ("target=\"" ++ targetId ++ "\"") <+> + text ("targetport=\"" ++ targetP ++ "\"") <> + if not yFiles + then text "/>"+ else char '>' $+$+ nest nestVal+ (text "<data key=\"d2\">" $+$+ nest nestVal+ (text "<y:PolyLineEdge>" $+$+ nest nestVal+ (text "<y:Path sx=\"" <> float edgeOrigX <> text "\" sy=\"" <> float edgeOrigY <> text "\" tx=\"" <> float edgeTargetX <> text "\" ty=\""<> float edgeTargetY <> text "\"/>" $+$+ text "<y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>" $+$+ text "<y:Arrows source=\"none\" target=\"standard\"/>" $+$+ text "<y:BendStyle smoothed=\"false\"/>"+ ) $+$+ text "</y:PolyLineEdge>") $+$+ text "</data>") $+$+ text "</edge>" + where -- Origin Node identifier+ origId = getId origN+ -- Target Node Identifier+ targetId = getId targetN+ -- Calculate the edge connection point for yFiles markup+ (edgeOrigX, edgeOrigY) = edgeConnection True + origNodeDims nOPortsOrig+ (findOutOrder origN origP)+ (edgeTargetX, edgeTargetY) = edgeConnection False + targetNodeDims nIPortsTarget+ (findInOrder targetN targetP) + (_, nOPortsOrig) = nIOPorts origN+ origNodeDims = nodeDims origN+ (nIPortsTarget, _) = nIOPorts targetN+ targetNodeDims = nodeDims targetN + -- Function to calculate where to connect an edge to a node+ -- note that in yfiles the coordinates origin of a node+ -- is located in the center, but the Y axis is inverted + -- (negative values are in the upper side)+ edgeConnection isSource (nodeXSize, nodeYSize) totalPorts portOrder = + (x,y)+ where x = if isSource then nodeXSize / 2 else -(nodeXSize/2) + ySep = nodeYSize/(fromIntegral totalPorts)+ -- Absolut value of y measure from the top+ yAbs = ySep/2 + (fromIntegral portOrder) * ySep+ y = yAbs - (nodeYSize / 2)++ -- helper functions+ -------------------+ -- Find the order (starting at 0) of a input Port in a node+ findInOrder node portid = findList list+ where findList = fromJust . findIndex (==portid)+ list = case node of + OutNode _ pid -> [pid]+ ProcNode ins _ -> DF.toList ins+ -- Find the order (starting at 0) of an output Port in a node+ findOutOrder node portid = findList list + where findList = fromJust . findIndex (==portid)+ list = case node of + OutNode _ pid -> [pid]+ ProcNode _ outs -> outs+ -- Get the identifier of a node+ getId node = case node of+ OutNode id _ -> id+ ProcNode n _ -> case n of+ InPort pid -> pid+ Proc pid _ -> pid++-- | pretty print a Graph with XML headers and key definitions+pprGraphWithHeaders :: YFilesMarkup -> GraphMLGraph -> Doc+pprGraphWithHeaders yFiles graph = + text "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" $+$+ text "<!-- Automatically generated by ForSyDe -->" $+$+ text "<graphml" <+> xmlns <+> + text "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" <+>+ xmlns_y <+>++ xsi_schemaLocation <>+ char '>' $+$ + nest nestVal (+ text "<key id=\"process_type\" for=\"node\" attr.name=\"process_type\" attr.type=\"string\"/>" $+$+ text "<key id=\"value_arg\" for=\"node\" attr.name=\"value_arg\" attr.type=\"string\"/>" $+$+ text "<key id=\"procfun_arg\" for=\"node\" attr.name=\"procfun_arg\" attr.type=\"string\"/>" $+$+ text "<key id=\"instance_parent\" for=\"node\" attr.name=\"instance_parent\" attr.type=\"string\"/>" $+$+ yFilesAttribs $+$+ pprOps yFiles graph) $+$+ text "</graphml>"+ where+ -- For some silly reason, yFiles uses a different GraphML target namesapce+ -- different to the one used in grapdrawing.org's GraphML primer+ xmlns = if yFiles + then text "xmlns=\"http://graphml.graphdrawing.org/xmlns/graphml\""+ else text "xmlns=\"http://graphml.graphdrawing.org/xmlns\""+ xmlns_y = if not yFiles then empty else+ text "xmlns:y=\"http://www.yworks.com/xml/graphml\""+ xsi_schemaLocation = if yFiles + then text "xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns/graphml http://www.yworks.com/xml/schema/graphml/1.0/ygraphml.xsd\""+ else text "xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd\"" + yFilesAttribs = if not yFiles then empty else + text "<key for=\"node\" id=\"d0\" yfiles.type=\"nodegraphics\"/>" $+$+ text "<key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d1\"/>" $+$+ text "<key for=\"edge\" id=\"d2\" yfiles.type=\"edgegraphics\"/>" $+$+ text "<key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d3\"/>" ++-------------------------+-- Tag printing functions+-------------------------+ +port :: GraphMLPortId -> Doc+port id = text "<port" <+> text ("name=\"" ++ id ++ "\"") <> text "/>" +++process_type :: String -> Doc+process_type str = + text "<data key=\"process_type\">" <> text str <> text "</data>" +++value_arg :: Exp -> Doc+value_arg exp = + text "<data key=\"value_arg\">" <> text (pprint exp) <> text "</data>" ++procfun_arg :: ProcFunAST -> Doc+-- FIXME: support default parameters+procfun_arg (ProcFunAST n cls _) = + text "<data key=\"procfun_arg\">" $+$+ nest nestVal (text $ pprint (FunD n cls)) $+$+ text "</data>" +++instance_parent :: SysId -> Doc+instance_parent id = + text "<data key=\"instance_parent\">" <> text id <> text "</data>" ++-------------------------+-- Other helper functions+-------------------------++-- Location of Edge connections and node size using yFiles Markup+-- ==============================================================+-- * All Nodes (except ports, of 7x7) have a constant width of 100+-- * The height depends on the node:+-- * ConstSY has a constant height of 100 +-- * DelaySY has a constant height of 100+-- * Nodes with three lines of text (ZipWithNSY, SysIns) have a minimum of 55+-- * Nodes with two lines of text (the rest) have a minimum height of 40+--+-- ** The final height of nodes with minimum height is+-- Max(minheight, MaxS*ps)+-- where MaxS = Max(number of input signals, number of output signals)+-- ps = inter-port separation+-- ** The location where both ends of an edge is trivially calculated+-- using the order of the corresponding port, the final size of the+-- node, "bi" and "ps"++-- | port separation space when connecting to a node which surpasses the +-- minimum height+portSep :: Float +portSep = 15++-- | Calculate the dimensions of a Node+nodeDims :: GraphMLNode -> (Float, Float) -- ^ Node dimensions (x,y)+nodeDims node = case node of+ OutNode _ _ -> (7,7)+ ProcNode n _ ->+ case n of+ InPort _ -> (7,7)+ Proc _ n' ->+ case n' of+ Const _ -> (100,100) + DelaySY _ _ -> (100,100)+ ZipWithNSY _ _ -> (100, height 55 maxio)+ SysIns _ _ -> (100, height 55 maxio)+ _ -> (100, height 40 maxio) + where height :: Float -- ^ Minimum height + -> Int -- ^ Max(input port number, output port number)+ -> Float -- ^ Final height+ height min maxio = max min + (portSep*(fromIntegral maxio))+ maxio :: Int -- ^ Max(input port number, output port number) + maxio = uncurry max $ nIOPorts node+++-- | Calculate the number of input and output ports of a node+nIOPorts :: GraphMLNode -> (Int, Int)+nIOPorts node = + case node of+ ProcNode ins outs -> (DF.foldr (\_ b -> b+1) 0 ins, length outs)+ OutNode _ _ -> (1,0)
+ src/ForSyDe/Deep/Backend/GraphML/Traverse.hs view
@@ -0,0 +1,139 @@+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.Backend.GraphML.Traverse+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- This module provides specialized Netlist traversing functions aimed at+-- GraphML compilation.+-----------------------------------------------------------------------------+module ForSyDe.Deep.Backend.GraphML.Traverse + (writeGraphMLM,+ module ForSyDe.Deep.Backend.GraphML.Traverse.GraphMLM) where++import ForSyDe.Deep.Backend.GraphML.Traverse.GraphMLM+import ForSyDe.Deep.Backend.GraphML.FileIO+import ForSyDe.Deep.Backend.GraphML.AST+++import ForSyDe.Deep.System.SysDef+import ForSyDe.Deep.Netlist.Traverse+import ForSyDe.Deep.Netlist+import ForSyDe.Deep.OSharing++import Data.Traversable.GenericZipWith++import System.Directory+import System.FilePath+import Control.Monad.State+++-- | Internal GraphML-Monad version of 'ForSyDe.Backend.writeGraphML+writeGraphMLM :: GraphMLM ()+writeGraphMLM = do+ -- create and change to systemName/graphml+ rootDir <- gets (sid.globalSysDef.global)+ let graphmlDir = rootDir </> "graphml"+ liftIO $ createDirectoryIfMissing True graphmlDir+ liftIO $ setCurrentDirectory graphmlDir+ -- write the local results for the first-level entity+ writeLocalGraphMLM+ -- if we are in recursive mode, also write the local results+ -- for the rest of the subsystems+ rec <- isRecursiveSet+ when rec $ do subs <- gets (subSys.globalSysDef.global)+ let writeSub s = + withLocalST (initLocalST ((readURef.unPrimSysDef) s))+ writeLocalGraphMLM + mapM_ writeSub subs+ -- go back to the original directory+ liftIO $ setCurrentDirectory (".." </> "..")+++-- | Traverse the netlist and write the local results (i.e. system graphs)+writeLocalGraphMLM :: GraphMLM ()+writeLocalGraphMLM = do+ lSysDefVal <- gets (currSysDef.local)+ let lSysDefId = sid lSysDefVal+ debugMsg $ "Compiling system definition `" ++ lSysDefId ++ "' ...\n"+ -- Obtain the output Nodes of the system+ -- Obtain the netlist of the system definition + let nl = netlist lSysDefVal+ -- Traverse the netlist, and get the traversing results+ intOutsInfo <- traverseGraphML nl + LocalTravResult nodes edges <- gets (localRes.local)+ -- For each output signal, we need a node and an edge between its + -- intermediate signal and the final output signal declared in the system + -- interface.+ let outIds = map fst (oIface lSysDefVal)+ outNodes = map (\id -> OutNode id (id ++ "_in")) outIds+ outEdges = + zipWith (\(IntSignalInfo n pId) id -> + GraphMLEdge n pId (OutNode id (id ++ "_in")) (id ++ "_in")) + intOutsInfo + outIds+ -- Generate the final Graph + finalGraph = GraphMLGraph lSysDefId (nodes ++ outNodes) + (edges ++ outEdges) + -- and write it to disk+ yFiles <- genyFilesMarkup+ return ()+ liftIO $ writeGraph yFiles finalGraph (lSysDefId ++ ".graphml") ++-- | Traverse the netlist of a System Definition, +-- returning the (implicit) final traversing state and a list+-- containing the 'IntSignalInfo' of each output of the system+traverseGraphML :: Netlist [] -> GraphMLM [IntSignalInfo]+traverseGraphML = traverseSEIO newGraphML defineGraphML++-- | \'new\' traversing function for the GraphML backend+newGraphML :: NlNode NlSignal -> GraphMLM [(NlNodeOut, IntSignalInfo)]+newGraphML node = do+ let id = case node of+ InPort id -> id + Proc pid _ -> pid+ -- node inputs+ insNode = zipWithTF (\_ n -> id ++ "_in" ++ show n) node [(1::Int)..]+ -- node outputs tagged with the edge label+ taggedOutsNode = zipWith (\t n -> (t, id ++ "_out" ++ show n)) + (outTags node)+ [(1::Int)..] + -- graphml node+ gMLNode = ProcNode insNode (map snd taggedOutsNode)+ return $ map (\(t,out) -> (t, IntSignalInfo gMLNode out)) taggedOutsNode ++ ++ +-- | \'define\' traversing function for the GraphML backend+defineGraphML :: [(NlNodeOut, IntSignalInfo)] + -> NlNode IntSignalInfo + -> GraphMLM ()+defineGraphML outs ins = do + let id = case ins of+ InPort id -> id + Proc pid _ -> pid+ -- Formal input signals of the proces+ formalInL = [id ++ "_in" ++ show n | n <- [(1::Int)..]]+ -- Generate the graph node+ -- Formal output ports of the process+ outPids = map (\(_, IntSignalInfo _ pid) -> pid) outs+ -- Substitute actual inputs by formal inputs in "ins"+ insFormal = zipWithTF (\_ n -> n) ins formalInL+ node = ProcNode insFormal outPids+ -- Generate the input edges of the node + -- Actual input signals of the process+ actualInL = arguments ins+ inEdges = zipWith (\(IntSignalInfo aN aPid) fPid ->+ GraphMLEdge aN aPid node fPid ) actualInL formalInL++ mapM_ addEdge inEdges+ addNode node++++
+ src/ForSyDe/Deep/Backend/GraphML/Traverse/GraphMLM.hs view
@@ -0,0 +1,247 @@+{-# LANGUAGE FlexibleContexts #-}+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.Backend.GraphML.Traverse.GraphMLM+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- 'GraphMLM' (GraphML Monad), related types and functions+--+-----------------------------------------------------------------------------+module ForSyDe.Deep.Backend.GraphML.Traverse.GraphMLM where++import ForSyDe.Deep.Backend.GraphML.AST++import ForSyDe.Deep.Ids+import ForSyDe.Deep.ForSyDeErr+import ForSyDe.Deep.System.SysDef (SysDefVal(..))+import ForSyDe.Deep.Netlist.Traverse (TravSEIO)++import Control.Monad.State+++--------------+-- GraphMLM --+--------------++-- | GraphMLM backend monad+type GraphMLM a = TravSEIO GraphMLTravST ContextErr a+++-------------------+-- GraphMLTravST --+-------------------++-- | GraphML traversing State. (see 'ForSyDe.Netlist.Traverse.traverseSIO')+data GraphMLTravST = GraphMLTravST+ {local :: LocalGraphMLST, -- Local State (related to the system currently + -- compiled)+ global :: GlobalGraphMLST} -- Global state (related to all systems being + -- recursively compiled) ++data LocalGraphMLST = LocalGraphMLST+ {currSysDef :: SysDefVal, -- System definition which is currently + -- being compiled+ context :: Context, -- Error Context+ localRes :: LocalTravResult} -- Result accumulated during the + -- traversal of current System Definition + -- netlist+++++-- | initialize the local state+initLocalST :: SysDefVal -> LocalGraphMLST+initLocalST sysDefVal = + LocalGraphMLST sysDefVal (SysDefC (sid sysDefVal) (loc sysDefVal)) + emptyLocalTravResult++-- | Execute certain operation with a concrete local state.+-- The initial local state is restored after the operation is executed+withLocalST :: LocalGraphMLST -> GraphMLM a -> GraphMLM a+withLocalST l' action = do+ -- get the initial local state+ st <- get+ let l = local st+ -- set the modified state+ put st{local=l'}+ -- execute the action+ res <- action+ -- restore the initial local state+ st' <- get + put st'{local=l}+ -- return the result+ return res++data GlobalGraphMLST = GlobalGraphMLST+ {globalSysDef :: SysDefVal,+ ops :: GraphMLOps, -- Compilation options+ globalRes :: GlobalTravResult} -- Result accumulated during the + -- whole compilation+ ++++-- | Empty initial traversing state+initGlobalGraphMLST :: SysDefVal -> GlobalGraphMLST+initGlobalGraphMLST sysDefVal = + GlobalGraphMLST sysDefVal defaultGraphMLOps emptyGlobalTravResult++-- | Empty initial traversing state +initGraphMLTravST :: SysDefVal -> GraphMLTravST+initGraphMLTravST sysDefVal = + GraphMLTravST (initLocalST sysDefVal) (initGlobalGraphMLST sysDefVal)++-------------+-- TravResult+-------------++-- | Local result accumulated during the traversal of a netlist+data LocalTravResult = LocalTravResult + {nodes :: [GraphMLNode], -- generated nodes+ edges :: [GraphMLEdge]} -- generated edges++++-- | empty local GraphML compilation result+emptyLocalTravResult :: LocalTravResult+emptyLocalTravResult = LocalTravResult [] []+++-- | Global Results accumulated throughout the whole compilation+-- (empty right now)+type GlobalTravResult = ()+++-- | empty global GraphML compilation result+emptyGlobalTravResult :: GlobalTravResult+emptyGlobalTravResult = ()+++-------------+-- GraphMLOps+-------------++-- | GraphML Compilation options+data GraphMLOps = GraphMLOps + {debugGraphML :: GraphMLDebugLevel, + recursivityGraphML :: GraphMLRecursivity,+ yFilesMarkup :: Bool -- ^ Generate yFiles markup? + }+ deriving (Eq, Show)++-- | Debug level+data GraphMLDebugLevel = GraphMLNormal | GraphMLVerbose+ deriving (Eq, Ord, Show)++-- | Print a message to stdout if in verbose mode+debugMsg :: String -> GraphMLM ()+debugMsg str = do+ debugLevel <- gets (debugGraphML.ops.global)+ when (debugLevel > GraphMLNormal) + (liftIO $ putStr ("DEBUG: " ++ str))++-- | Recursivity, should the parent systems of system instances be compiled as +-- well?+data GraphMLRecursivity = GraphMLRecursive | GraphMLNonRecursive+ deriving (Eq, Show)++-- | Check if we are in recursive mode+isRecursiveSet :: GraphMLM Bool+isRecursiveSet = do + recOp <- gets (recursivityGraphML.ops.global)+ return $ recOp == GraphMLRecursive++-- | Check if we want to generate yFiles markup+genyFilesMarkup :: GraphMLM Bool+genyFilesMarkup = gets (yFilesMarkup.ops.global)+++-- | Default traversing options+defaultGraphMLOps :: GraphMLOps+defaultGraphMLOps = GraphMLOps GraphMLNormal GraphMLRecursive False+++-- | Set GraphML options inside the GraphML monad+setGraphMLOps :: GraphMLOps -> GraphMLM ()+setGraphMLOps options = modify (\st -> st{global=(global st){ops=options}})+++----------------------------------------+-- Useful functions in the GraphML Monad+----------------------------------------++-- | Add a signal declaration to the 'LocalTravResult' in the State+addEdge :: GraphMLEdge -> GraphMLM ()+addEdge e = modify addFun + -- FIXME: use a queue for the declarations+ where addFun st = st{local=l{localRes=r{edges=edg ++ [e]}}}+ where l = local st+ r = localRes l+ edg = edges r +++-- | Add a statement to the 'LocalTravResult' in the State+addNode :: GraphMLNode -> GraphMLM ()+addNode node = modify addFun+ -- FIXME: use a queue for the statements+ where addFun st = st{local=l{localRes=r{nodes= nds ++ [node]}}}+ where l = local st+ r = localRes l+ nds = nodes r ++++-- | Lift an 'EProne' value to the GraphML monad setting current error context+-- for the error+liftEProne :: EProne a -> GraphMLM a+liftEProne ep = do+ cxt <- gets (context.local)+ either (throwError.(ContextErr cxt)) return ep++-- | Throw a ForSyDe error, setting current error context+throwFError :: ForSyDeErr -> GraphMLM a+throwFError = liftEProne.Left++++-- | Execute certain operation with a concrete process context.+-- The initial context is restored after the operation is executed+-- Note: the initial context must be a system context or 'InconsistenContexts'+-- will be raised.+withProcC :: ProcId -> GraphMLM a -> GraphMLM a+withProcC pid action = do+ -- get the initial context+ st <- get+ let l = local st+ c = context l+ -- set the modified name context+ put st{local=l{context=setProcC pid c}}+ -- execute the action+ res <- action+ -- restore the initial name context+ st' <- get+ let l' = local st'+ put st'{local=l'{context=c}}+ -- return the result+ return res+++++----------------+-- IntSignalInfo+----------------++-- | Intermediate edge information. Tag generated for each output of each+-- node found during the traversal. +-- (see ForSyDe.Netlist.Traverse.traverseSIO).+-- It contains the GraphML node identifier +-- and port identifier associated with the process output.+data IntSignalInfo = IntSignalInfo GraphMLNode -- Source Node + GraphMLPortId -- Source Port Identifier+
+ src/ForSyDe/Deep/Backend/Ppr.hs view
@@ -0,0 +1,107 @@+{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.Backend.VHDL.Ppr+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : non-portable (Template Haskell)+--+-- ForSyDe pretty-printing class and auxiliar functions.+--+-----------------------------------------------------------------------------+module ForSyDe.Deep.Backend.Ppr where++import Text.PrettyPrint.HughesPJ++-- | Pretty printing class+class Ppr a where+ ppr :: a -> Doc++-- identity instantiation+instance Ppr Doc where+ ppr = id++-- | Pretty printing class with associated printing options+class PprOps ops toPpr | toPpr -> ops where+ -- NOTE: Would it be better to use a State Monad?+ -- i.e. pprOps :: toPpr -> State ops Doc+ pprOps :: ops -> toPpr -> Doc +++-- dot+dot :: Doc+dot = char '.'++-- One line vertical space+vSpace :: Doc+vSpace = text ""++-- Multi-line vertical space+multiVSpace :: Int -> Doc+multiVSpace n = vcat (replicate n (text "")) ++-- Pretty-print a list supplying the document joining function+ppr_list :: Ppr a => (Doc -> Doc -> Doc) -> [a] -> Doc+ppr_list _ [] = empty+ppr_list join (a1:rest) = go a1 rest + where go a1 [] = ppr a1+ go a1 (a2:rest) = ppr a1 `join` go a2 rest++-- Pretty-print a list supplying the document joining function+-- (PprOps version)+pprOps_list :: PprOps ops toPpr => ops -> (Doc -> Doc -> Doc) -> [toPpr] -> Doc+pprOps_list _ _ [] = empty+pprOps_list ops join (a1:rest) = go a1 rest+ where go a1 [] = pprOps ops a1+ go a1 (a2:rest) = pprOps ops a1 `join` go a2 rest++-- | Join two documents vertically leaving n vertical spaces between them+vNSpaces :: Int -> Doc -> Doc -> Doc+vNSpaces n doc1 doc2 = doc1 $+$ + multiVSpace n $+$+ doc2++-- Join two documents vertically putting a semicolon in the middle+vSemi :: Doc -> Doc -> Doc+vSemi doc1 doc2 = doc1 <> semi $+$ doc2+++-- Join two documents vertically putting a comma in the middle+vComma :: Doc -> Doc -> Doc+vComma doc1 doc2 = doc1 <> comma $+$ doc2++-- Join two documents horizontally putting a comma in the middle+hComma :: Doc -> Doc -> Doc+hComma doc1 doc2 = doc1 <> comma <+> doc2+++-- | apply sep to a list of prettyprintable elements, +-- previously interspersing commas+commaSep :: Ppr a => [a] -> Doc+commaSep = sep.(punctuate comma).(map ppr)+ ++-- | Only append if both of the documents are non-empty+($++$) :: Doc -> Doc -> Doc+d1 $++$ d2 + | isEmpty d1 || isEmpty d2 = empty+ | otherwise = d1 $+$ d2+++-- | Only append if both of the documents are non-empty+(<++>) :: Doc -> Doc -> Doc+d1 <++> d2 + | isEmpty d1 || isEmpty d2 = empty+ | otherwise = d1 <+> d2++-- | Enclose in parenthesis only if the document is non-empty+parensNonEmpty :: Doc -> Doc+parensNonEmpty doc | isEmpty doc = empty+parensNonEmpty doc = parens doc++-- | Enclose in parenthesis only if the predicate is True+parensIf :: Bool -> Doc -> Doc+parensIf p d = if p then parens d else d
+ src/ForSyDe/Deep/Backend/Simulate.hs view
@@ -0,0 +1,248 @@+{-# LANGUAGE ScopedTypeVariables #-}+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.Backend.Simulate+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : non-portable (Template Haskell, LSTV)+--+-- This module provides the simulation backend of ForSyDe's embedded compiler+--+-- /This module is based on Lava2000/: <http://www.cs.chalmers.se/~koen/Lava/>+--+-----------------------------------------------------------------------------+module ForSyDe.Deep.Backend.Simulate (simulate) where++import ForSyDe.Deep.OSharing+import ForSyDe.Deep.Netlist+import ForSyDe.Deep.Netlist.Traverse+import ForSyDe.Deep.System.SysDef+import ForSyDe.Deep.System.SysFun(SysFunToSimFun(..))+import ForSyDe.Deep.ForSyDeErr+import ForSyDe.Deep.Process.ProcVal++import Data.Maybe (fromJust)+import Control.Monad.ST+import qualified Control.Monad.ST.Unsafe as STU+import Data.STRef+import qualified Data.Traversable as DT+import Data.List (transpose)+import Data.Dynamic++-- | 'simulate' takes a system definition and generates a function+-- able simulate a System using a list-based representation+-- of its signals.+simulate :: SysFunToSimFun sysFun simFun => SysDef sysFun -> simFun+simulate sysDef = fromDynSimFun (simulateDyn (unSysDef sysDef)) []++-- FIXME: clean and document the following horrible code!++--------------------------------------+-- The following was adapted from Lava+--------------------------------------++type Var s+ = (STRef s Dynamic, STRef s (Wire s))++data Wire s+ = Wire+ { dependencies :: [Var s]+ , kick :: ST s ()+ }++----------------------------------------------------------------+-- simulateDyn+++simulateDyn :: PrimSysDef -> [[Dynamic]] -> [[Dynamic]]+simulateDyn pSysDef inps | any null inps = replicate outN []+ where outN = (length . oIface . readURef . unPrimSysDef) pSysDef+simulateDyn pSysDef inps = runST (+ do let sysDefVal = (readURef . unPrimSysDef) pSysDef+ sysDefInIface = iIface sysDefVal+ -- List where to store the Vars generated by delay processes+ roots <- newSTRef []+ -- Input port ids paired with a reference to each input value list+ inpPairs <- zipWithM (\(id,_) inputL ->+ do {ref <- newSTRef inputL; return (id,ref)})+ sysDefInIface inps++ let -- Add a Var to the roots list+ root r =+ do rs <- readSTRef roots+ writeSTRef roots (r:rs)++ -- Create an empty var+ empty = do rval <- newSTRef (error "val?")+ rwir <- newSTRef (error "wire?")+ return (rval, rwir)+ new node =+ do mapM (\tag -> do {e <- empty; return (tag, e)}) (outTags node)+ newInstance varPairs node =+ let funName = "ForSyDe.Backend.Simulate.simulateDyn"+ in case node of+ InPort id ->+ case lookup id varPairs of+ -- FIXME: replace the Other error with a custom one+ Nothing -> intError funName (Other "inconsistency")+ Just var -> return [(InPortOut, var)]+ _ -> new node++ -- define for the general traversal+ define nodeVarPairs childVars =+ case (nodeVarPairs,childVars) of+ ([(InPortOut, var)], InPort name) -> do+ let inputRef = fromJust $ lookup name inpPairs+ relate var [] $+ do (curr:rest) <- readSTRef inputRef+ writeSTRef inputRef rest+ return curr++ _ -> defineShared nodeVarPairs childVars++ -- define for instances+ defineInstance nodeVarPairs childVars =+ case (nodeVarPairs,childVars) of+ ([(InPortOut, _)], InPort _) -> return ()+ _ -> defineShared nodeVarPairs childVars++ -- Shared part of define define for instances and the main traversal+ defineShared nodeVarPairs childVars = -- r s =+ case (nodeVarPairs,childVars) of+ ([(InPortOut, _)], InPort _) -> return ()++ (nodeVarPairs,+ Proc _ (SysIns pSysDef ins)) ->+ -- FIXME: ugly ugly ugly+ do let sysDefVal = (readURef . unPrimSysDef) pSysDef+ taggedIns = zipWith (\(id,_) var -> (id,var))+ (iIface sysDefVal) ins+ sr <- traverseST+ (newInstance taggedIns)+ defineInstance+ (netlist sysDefVal)++ let relateIns prevVar@(prevValR,_) (_,nextVar) =+ relate nextVar [prevVar] (readSTRef prevValR)++ zipWithM_ relateIns sr nodeVarPairs++ ([(DelaySYOut, nodeVar)],+ Proc _ (DelaySY (ProcVal init _) sigVar)) ->+ do valVar <- empty+ relate valVar [] (return init)+ delay nodeVar valVar sigVar+ _ ->+ do let evalPairs = eval `fmap` DT.mapM (readSTRef.fst) childVars+ args = arguments childVars+ relEval (tag, var) =+ relate var args $+ -- FIXME: remove fromJust and write a proper error+ liftM (fromJust.(lookup tag)) evalPairs+ mapM_ relEval nodeVarPairs+ where+ delay r ri@(rinit,_) r1@(pre,_) =+ do state <- newSTRef Nothing+ r2 <- empty+ root r2++ relate r [ri] $+ do ms <- readSTRef state+ case ms of+ Just s -> return s+ Nothing ->+ do s <- readSTRef rinit+ writeSTRef state (Just s)+ return s++ relate r2 [r,r1] $+ do s <- readSTRef pre+ writeSTRef state (Just s)+ return s++ sr <- traverseST new define (netlist sysDefVal)+ rs <- readSTRef roots+ -- remove tags of the resulting vars (all the root nodes should only+ -- have one output and thus a must return a unique list)+ step <- drive (sr ++ rs)++ outs <- lazyloop $+ do step+ s <- DT.mapM (readSTRef . fst) sr+ return s+ -- Since the simulation is done in a per-cycle basis+ -- the results (outs) are transposed (not what we want)+ -- e.g.+ -- imagine a system whose outputs are its inputs plus 1+ -- then, for this these two inputs [[1,2,3],[4,5,6]]+ -- outs would be [[2,5],[3,6],[4,7]]+ --+ -- We need as well to check that all inputs are defined in+ -- each simulation cycle e.g. [[1,2,3],[4,5,6,7]] as input+ -- makes imposible to simulate cycle 4+ --+ -- NOTE: having to check this makes simulation really inneficient+ -- a solution would providing a cycle-based simulation input/output+ -- which wouldn't suffer from this problems+ -- Or, even better, a simulation which showed a diffierent type of output+ let inN = length sysDefInIface+ res = if inN == 0 then+ transpose outs+ else transpose (checkIns inN (transpose inps) outs)+ return res+ )+++-- evaluation order++relate :: Var s -> [Var s] -> ST s Dynamic -> ST s ()+relate (rval, rwir) rs f =+ do writeSTRef rwir $+ Wire{ dependencies = rs+ , kick = do b <- f+ writeSTRef rval b+ }++drive :: [Var s] -> ST s (ST s ())+drive [] =+ do return (return ())++drive ((_,rwir):rs) =+ do wire <- readSTRef rwir+ writeSTRef rwir (error "detected combinational loop")+ driv1 <- drive (dependencies wire)+ writeSTRef rwir $+ Wire { dependencies = [], kick = return () }+ driv2 <- drive rs+ return $+ do driv1+ kick wire+ driv2++----------------------------------------------------------------+-- helper functions++lazyloop :: ST s a -> ST s [a]+lazyloop m =+ do a <- m+ as <- STU.unsafeInterleaveST (lazyloop m)+ return (a:as)++-- | check that there will only be output as long as there are inputs+checkIns :: Int -- ^ number of inputs+ -> [[a]] -- ^ transposed inputs+ -> [[b]] -- ^ transposed outputs (infinitie list)+ -> [[b]] -- ^ selected outputs++-- The lazy pattern match is used to avoid evaluating the output list+-- if length i /= nIns. If that happens the input lists of simulate will+-- implicitly be simulated, and due to lack of inputs it will cause an error.+checkIns nIns (i:is) ~(o:os) | length i == nIns = o : checkIns nIns is os+checkIns _ _ _ = []++++
+ src/ForSyDe/Deep/Backend/VHDL.hs view
@@ -0,0 +1,125 @@+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.Backend.VHDL+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- This module provides the VHDL backend of ForSyDe's embedded compiler+--+-----------------------------------------------------------------------------+module ForSyDe.Deep.Backend.VHDL + (writeVHDL, + writeVHDLOps,+ writeAndModelsimVHDL,+ writeAndModelsimVHDLOps, + writeAndGhdlVHDL,+ writeAndGhdlVHDLOps, + VHDLOps(..),+ QuartusOps(..),+ QuartusAction(..),+ checkSynthesisQuartus,+ VHDLDebugLevel(..),+ VHDLRecursivity(..),+ defaultVHDLOps) where++import Control.Monad.State (evalStateT)+import qualified Language.Haskell.TH as TH++import ForSyDe.Deep.System.SysFun+import ForSyDe.Deep.ForSyDeErr+import ForSyDe.Deep.OSharing (readURef)+import ForSyDe.Deep.System.SysDef+import ForSyDe.Deep.Backend.VHDL.Traverse+import ForSyDe.Deep.Backend.VHDL.Modelsim+import ForSyDe.Deep.Backend.VHDL.Ghdl++-- | Given a System Definition whose name is a valid VHDL _basic_ identifier +-- (call it \"A\") generate @A.vhd@ in current working directory using +-- default compilation options.+-- Imp: the input and output signal names of A must be valid VHDL identifiers+-- (basic or extended) and different to @clk@ and @reset@+-- which are reserved for the main clock and reset signals+writeVHDL :: SysDef a -> IO ()+writeVHDL = writeVHDLOps defaultVHDLOps ++-- | 'writeVHDL'-alternative which allows setting VHDL compilation options.+writeVHDLOps :: VHDLOps -> SysDef a -> IO ()+writeVHDLOps ops sysDef = do+ -- initiate the compilation State+ let sinit = initVHDLTravST $ (readURef.unPrimSysDef.unSysDef) sysDef+ -- Compile the code+ res <- runErrorT $ evalStateT (setVHDLOps ops >> writeVHDLM) sinit+ -- Check if the compilation went well and print an error in case it didn't+ either printVHDLError return res++++-- | Generate a function which, given a system definition and some simulation+-- stimuli:+-- +-- (1) Writes a VHDL model of the system +-- +-- (2) Simulates the VHDL model with Modelsim getting the results back to Haskell+writeAndModelsimVHDL :: SysFunToIOSimFun sysF simF => + Maybe Int -- ^ Number of cycles to simulate+ -- if 'Nothing' the number will be determined+ -- by the length of the input stimulti.+ -- Useful when the system to simulate doesn't+ -- have inputs or the inputs provided are + -- infinite+ -> SysDef sysF -- ^ system definition to simulate+ -> simF +writeAndModelsimVHDL = writeAndModelsimVHDLOps defaultVHDLOps+++-- | 'VHDLOps'-alternative of 'writeAndModelsimVHDL', note that+-- compileModelSim will implicitly be set to True+writeAndModelsimVHDLOps :: SysFunToIOSimFun sysF simF => + VHDLOps -> Maybe Int -> SysDef sysF -> simF +writeAndModelsimVHDLOps ops mCycles sysDef = fromTHStrSimFun simIO []+ where sinit = initVHDLTravST $ (readURef.unPrimSysDef.unSysDef) sysDef+ simVHDLM :: [[TH.Exp]] -> VHDLM [[String]] + simVHDLM stimuli = do + setVHDLOps ops{compileModelsim=True} + writeVHDLM+ executeTestBenchModelsim mCycles stimuli + simIO :: [[TH.Exp]] -> IO [[String]] + simIO stimuli = do+ res <- runErrorT $ evalStateT (simVHDLM stimuli) sinit+ either printVHDLError return res++-- | Generate a function which, given a system definition and some simulation+-- stimuli:+-- +-- (1) Writes a VHDL model of the system +-- +-- (2) Simulates the VHDL model with Ghdl getting the results back to Haskell+writeAndGhdlVHDL :: SysFunToIOSimFun sysF simF => + Maybe Int -- ^ Number of cycles to simulate+ -- if 'Nothing' the number will be determined+ -- by the length of the input stimulti.+ -- Useful when the system to simulate doesn't+ -- have inputs or the inputs provided are + -- infinite+ -> SysDef sysF -- ^ system definition to simulate+ -> simF +writeAndGhdlVHDL = writeAndGhdlVHDLOps defaultVHDLOps++-- | 'VHDLOps'-alternative of 'writeAndGhdlVHDL'+writeAndGhdlVHDLOps :: SysFunToIOSimFun sysF simF => + VHDLOps -> Maybe Int -> SysDef sysF -> simF +writeAndGhdlVHDLOps ops mCycles sysDef = fromTHStrSimFun simIO []+ where sinit = initVHDLTravST $ (readURef.unPrimSysDef.unSysDef) sysDef+ simVHDLM :: [[TH.Exp]] -> VHDLM [[String]] + simVHDLM stimuli = do + setVHDLOps ops+ writeVHDLM+ executeTestBenchGhdl mCycles stimuli + simIO :: [[TH.Exp]] -> IO [[String]] + simIO stimuli = do+ res <- runErrorT $ evalStateT (simVHDLM stimuli) sinit+ either printVHDLError return res
+ src/ForSyDe/Deep/Backend/VHDL/AST.hs view
@@ -0,0 +1,720 @@+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.Backend.VHDL.AST+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : non-portable (Template Haskell)+--+-- A VHDL 93 subset AST (Abstract Syntax Tree), coded so that it can be easy +-- to extend, please see doc/VHDL/vhdl93-syntax.html as reference +-- in order to extend it (this AST is based on that grammar)+-----------------------------------------------------------------------------++-- This AST is aimed at code generation not parsing, and thus, +-- it was simplified+-- The incompatibilities or simplifications from the standard+-- are properly commented++-- FIXME: shouldn't be records used instead of bare algebraic types?+-- FIXME: shouldn't the unused type redefinitions be removed?+-- FIXME: It would maybe be a good idea to create a sequence ADT which+-- guaranteed to hold at least one element (i.e. the grammar+-- has some cases such as "choices ::= choice | {choice}"+-- which are not well represented as "[Choice]", +-- "Choices Choice (Maybe [Choice])" is not easy to handle either+-- and thus, it was discarded.++module ForSyDe.Deep.Backend.VHDL.AST where++import Data.Char (toLower)+import Text.Regex.Posix++import ForSyDe.Deep.ForSyDeErr+++--------------------+-- VHDL identifiers+--------------------++-- VHDL identifier, use mkVHDLId to create it from a String, +-- making sure a string is a proper VHDL identifier+data VHDLId = Basic String | Extended String+ deriving Eq++-- | Obtain the String of a VHDL identifier+fromVHDLId :: VHDLId -> String+fromVHDLId (Basic str) = str+fromVHDLId (Extended str) = '\\' : (escapeBackslashes str) ++ "\\"+ where escapeBackslashes [] = []+ escapeBackslashes (x : xs) + | x == '\\' = "\\\\" ++ escapeBackslashes xs+ | otherwise = x : escapeBackslashes xs+instance Show VHDLId where+ show = show.fromVHDLId+++-- | unsafely create a basic VHDLId (without cheking if the string is correct)+unsafeVHDLBasicId :: String -> VHDLId+unsafeVHDLBasicId str = Basic str+++-- | unsafely create an exteded VHDLId (without cheking if the string is +-- correct)+unsafeVHDLExtId :: String -> VHDLId+unsafeVHDLExtId str = Extended str++-- | unsafely create a VHDLId for different containers. They are basic or+-- extended based on the type of values they contain.+unsafeVHDLContainerId :: [VHDLId] -> String -> VHDLId+unsafeVHDLContainerId ids = if any isExt ids then unsafeVHDLExtId+ else unsafeVHDLBasicId+ where+ isExt (Extended _) = True+ isExt (Basic _) = False++-- | Create a VHDL basic identifier from a String, previously checking if the +-- String is correct+mkVHDLBasicId ::String -> EProne VHDLId+-- FIXME: we relax the fact that two consecutive underlines +-- are not allowed+mkVHDLBasicId [] = throwError EmptyVHDLId+mkVHDLBasicId id+ | id =~ basiIdPat && (not $ elem lowId reservedWords) = return $ Basic id+ | otherwise = throwError $ IncVHDLBasId id+ where lowId = map toLower id+ basiIdPat = "^[A-Za-z](_?[A-Za-z0-9])*$"++-- | Create a VHDL extended identifier from a String, previously checking +-- if the String is correct. The input string must not include the initial+-- and ending backslashes nad the intermediate backslashes shouldn't be escaped.+mkVHDLExtId :: String -> EProne VHDLId+mkVHDLExtId [] = throwError EmptyVHDLId+mkVHDLExtId id+ | id =~ extIdPat = return $ Extended id+ | otherwise = throwError $ IncVHDLExtId id+ where -- Regular expression pattern for extended identifiers.+ -- According to the VHDL93 standard, an extended identifier must:+ -- * Start and end with a backslash '\'+ -- * Its middle characters can be+ -- * two contiguous backslashes \\+ -- * an alphanumeric (A-Za-z0-9)+ -- * a Special Character + -- * an Other Special Character + -- (backslashes can only appear in pairs as indicated above)+ -- + -- However, backslashes will be handled when printing the identifier,+ -- (an initial and ending backslash are added and the intermediate backslashes+ -- are escaped)+ --+ -- Note that we cannot use specialChars and otherSpecialChars+ -- to build the pattern because of the double-backslash rule+ -- and also, the right bracket (according to the posix+ -- standard) needs to go in the first place of a bracket+ -- expression to lose its special meaning. Furthermore,+ -- (according to the POSIX standard as well) we also need to put+ -- '-' in the last place of the bracket expression.+ extIdPat = + "^[]A-Za-z0-9 \"#&\\'()*+,./:;<=>_|!$%@?[^`{}~-]+$"+++-- | Unsafely append a string to a VHDL identifier (i.e. without checking if+-- the resulting identifier is valid)+unsafeIdAppend :: VHDLId -> String -> VHDLId+unsafeIdAppend (Basic id) suffix = Basic $ id ++ suffix+unsafeIdAppend (Extended id) suffix = Extended $ id ++ suffix++-- | special characters as defined in the VHDL93 standard+specialChars :: [Char]+specialChars = ['"' , '#' , '&' , '\'' , '(' , ')' , '*' , '+' , ',',+ '-' , '.' , '/' , ':' , ';' , '<' , '=' , '>' , '_' , '|']++-- | other special characters as defined in the VHDL93 standard+otherSpecialChars :: [Char]+otherSpecialChars =['!' , '$' , '%' , '@' , '?' , '[' , '\\' , ']',+ '^' , '`' , '{' , '}' , '~']++-- | Reserved identifiers+reservedWords :: [String]+reservedWords = ["abs", "access", "after", "alias", "all", "and",+ "architecture", "array", "assert", "attribute", "begin", "block",+ "body", "buffer", "bus", "case", "component", "configuration",+ "constant", "disconnect", "downto", "else", "elsif", "end", "entity",+ "exit", "file", "for", "function", "generate", "generic", "group",+ "guarded", "if", "impure", "in", "inertial", "inout", "is", "label",+ "library", "linkage", "literal", "loop", "map", "mod", "nand", "new",+ "next", "nor", "not", "null", "of", "on", "open", "or", "others",+ "out", "package", "port", "postponed", "procedure", "process", "pure",+ "range", "record", "register", "reject", "rem", "report", "return",+ "rol", "ror", "select", "severity", "shared", "signal", "sla", "sll",+ "sra", "srl", "subtype", "then", "to", "transport", "type",+ "unaffected", "units", "until", "use", "variable", "wait", "when",+ "while", "with", "xnor", "xor"]+++++ ---------+ -- AST --+ ---------+++-- { } (0 or more) is expressed as [ ]+-- [ ] (optional) is expressed as Maybe+++-- design_file+-- Having ContextClauses associated to library units is messy+-- instead we only allow ContextClause for the whole design file.+-- Furthermore we incorrectly (and deliberately) accept a file with +-- no library units +data DesignFile = DesignFile [ContextItem] [LibraryUnit] + deriving Show++-- context_item+-- We don't allow the "name1,name2,name3" syntax, only one name is allowed+-- at once+data ContextItem = Library VHDLId | Use SelectedName+ deriving Show++-- library_unit+-- We avoid adding the overhead of a PrimaryUnit and SecondaryUnit types+data LibraryUnit = LUEntity EntityDec | + LUArch ArchBody | + LUPackageDec PackageDec |+ LUPackageBody PackageBody+ deriving Show++-- entity_declaration+-- No declarative nor statemet part is allowed +-- Only interface signal declarations are allowed in the port clause+data EntityDec = EntityDec VHDLId [IfaceSigDec]+ deriving Show++-- | interface_signal_declaration+-- We don't allow the "id1,id2,id3" syntax, only one identifier is allowed+-- at once+-- The Mode is mandatory+-- Bus is not allowed +-- Preasigned values are not allowed+-- Subtype indications are not allowed, just a typemark +-- Constraints are not allowed: just add a new type with the constarint+-- in ForSyDe.vhd if it is required+data IfaceSigDec = IfaceSigDec VHDLId Mode TypeMark+ deriving Show++-- | type_mark+-- We don't distinguish between type names and subtype names+-- We dont' support selected names, only simple names because we won't need+-- name selection (i.e. Use clauses will make name selection unnecesary)+type TypeMark = SimpleName+++-- | mode+-- INOUT | BUFFER | LINKAGE are not allowed+data Mode = In | Out+ deriving (Show, Eq)++-- | architecture_body +-- [ ARCHITECTURE ] and [ architecture_simple_name ] are not allowed+data ArchBody = ArchBody VHDLId VHDLName [BlockDecItem] [ConcSm]+ deriving Show++-- | package_declaration+-- [ PACKAGE ] and [ package_simple_name ] are not allowed+data PackageDec = PackageDec VHDLId [PackageDecItem]+ deriving Show+++-- | package_declarative_item+-- only type declarations, subtype declarations and subprogram specifications +-- (working as subprogram_declaration) allowed+data PackageDecItem = PDITD TypeDec | PDISD SubtypeDec | PDISS SubProgSpec+ deriving Show++-- | package_body+-- [ PACKAGE ] and [ package_simple_name ] are not allowed+data PackageBody = PackageBody VHDLId [PackageBodyDecItem]+ deriving Show++-- | only subprogram_body is allowed+type PackageBodyDecItem = SubProgBody++-- | subtype-declaration+data SubtypeDec = SubtypeDec VHDLId SubtypeIn+ deriving Show++-- | subtype_indication+-- resolution functions are not permitted+data SubtypeIn = SubtypeIn TypeMark (Maybe Constraint)+ deriving Show++-- | constraint+-- Only index constraints are allowed+type Constraint = IndexConstraint++-- | range+-- the direction must always be \"to\"+data Range = AttribRange AttribName | ToRange Expr Expr+ deriving Show++-- | index_constraint+data IndexConstraint = IndexConstraint [DiscreteRange]+ deriving Show++-- | discrete_range+-- only ranges are allowed+type DiscreteRange = Range++-- | type_declaration+-- only full_type_declarations are allowed+data TypeDec = TypeDec VHDLId TypeDef+ deriving Show++-- | type_declaration+-- only composite types and enumeration types (a specific scalar type)+data TypeDef = TDA ArrayTypeDef | TDR RecordTypeDef | TDE EnumTypeDef+ deriving Show++-- | array_type_definition+-- unconstrained_array_definition+-- constrained_array_definition+-- A TypeMark is used instead of a subtype_indication. If subtyping is required,+-- declare a subtype explicitly. +data ArrayTypeDef = UnconsArrayDef [TypeMark] TypeMark |+ ConsArrayDef IndexConstraint TypeMark+ deriving Show++-- | record_type_definition+-- [ record_type_simple_name ] not allowed+data RecordTypeDef = RecordTypeDef [ElementDec]+ deriving Show++-- | element_declaration +-- multi-identifier element declarations not allowed+-- element_subtype_definition is simplified to a type_mark+data ElementDec = ElementDec VHDLId TypeMark+ deriving Show++-- | enumeration_type_definition +-- enumeration literals can only be identifiers+data EnumTypeDef = EnumTypeDef [VHDLId]+ deriving Show++-- | name+-- operator_names are not allowed +data VHDLName = NSimple SimpleName | + NSelected SelectedName | + NIndexed IndexedName |+ NSlice SliceName |+ NAttribute AttribName + deriving Show++-- | simple_name+type SimpleName = VHDLId++-- | selected_name+data SelectedName = Prefix :.: Suffix+ deriving Show++infixl :.:++-- | indexed_name+-- note that according to the VHDL93 grammar the index list cannot be empty +data IndexedName = IndexedName Prefix [Expr]+ deriving Show++-- | prefix+-- only names (no function calls)+type Prefix = VHDLName++-- | suffix+-- no character or operator symbols are accepted+data Suffix = SSimple SimpleName | All+ deriving Show+++-- | slice_name+data SliceName = SliceName Prefix DiscreteRange+ deriving Show++-- | attribute_name+-- signatures are not allowed+data AttribName = AttribName Prefix SimpleName (Maybe Expr)+ deriving Show++-- | block_declarative_item+-- Only subprogram bodies and signal declarations are allowed+data BlockDecItem = BDISPB SubProgBody | BDISD SigDec+ deriving Show+++-- | subprogram_body+-- No subprogram kind nor designator is allowed+data SubProgBody = SubProgBody SubProgSpec [SubProgDecItem] [SeqSm]+ deriving Show++-- | subprogram_declarative_item+-- only varaible declarations are allowed.+data SubProgDecItem = SPVD VarDec | SPSB SubProgBody+ deriving Show++-- | variable_declaration+-- identifier lists are not allowed+data VarDec = VarDec VHDLId SubtypeIn (Maybe Expr)+ deriving Show++-- | subprogram_specification+-- Only Functions are allowed+-- [Pure | Impure] is not allowed+-- Only an identifier is valid as the designator+-- In the formal parameter list only variable declarations are accepted +data SubProgSpec = Function VHDLId [IfaceVarDec] TypeMark + deriving Show++-- | interface_variable_declaration+-- [variable] is not allowed+-- We don't allow the "id1,id2,id3" syntax, only one identifier is allowed+-- Mode is not allowed+-- Resolution functions and constraints are not allowed, thus a TypeMark+-- is used instead of a subtype_indication. If subtyping is required,+-- declare a subtype explicitly.+data IfaceVarDec = IfaceVarDec VHDLId TypeMark+ deriving Show++-- | sequential_statement+-- Only If, case, return, for loops, assignment, @wait for@ procedure calls+-- allowed.+-- Only for loops are allowed (thus loop_statement doesn't exist) and cannot+-- be provided labels.+-- The target cannot be an aggregate.+-- General wait statements are not allowed, only @wait for@+-- It is incorrect to have an empty [CaseSmAlt]+data SeqSm = IfSm Expr [SeqSm] [ElseIf] (Maybe Else) |+ CaseSm Expr [CaseSmAlt] |+ ReturnSm (Maybe Expr) |+ ForSM VHDLId DiscreteRange [SeqSm] |+ VHDLName := Expr |+ WaitFor Expr |+ SigAssign VHDLName Wform |+ ProcCall VHDLName [AssocElem]+ deriving Show++-- | helper type, they doesn't exist in the origianl grammar+data ElseIf = ElseIf Expr [SeqSm]+ deriving Show++-- | helper type, it doesn't exist in the origianl grammar+data Else = Else [SeqSm]+ deriving Show++-- | case_statement_alternative+-- it is incorrect to have an empty [Choice]+data CaseSmAlt = CaseSmAlt [Choice] [SeqSm]+ deriving Show++-- | choice+-- although any expression is allowed the grammar specfically only allows +-- simple_expressions (not covered in this AST) +data Choice = ChoiceE Expr | Others+ deriving Show++-- | signal_declaration+-- We don't allow the "id1,id2,id3" syntax, only one identifier is allowed+-- at once+-- Resolution functions and constraints are not allowed, thus a TypeMark+-- is used instead of a subtype_indication+-- Signal kinds are not allowed+data SigDec = SigDec VHDLId TypeMark (Maybe Expr)+ deriving Show++-- | concurrent_statement+-- only block statements, component instantiations and signal assignments +-- are allowed+data ConcSm = CSBSm BlockSm | + CSSASm ConSigAssignSm | + CSISm CompInsSm |+ CSPSm ProcSm+ deriving Show++-- | block_statement+-- Generics are not supported+-- The port_clause (with only signals) and port_map_aspect are mandatory+-- The ending [ block_label ] is not allowed+-- +data BlockSm = BlockSm Label [IfaceSigDec] PMapAspect [BlockDecItem] [ConcSm]+ deriving Show++-- | port_map_aspect+newtype PMapAspect = PMapAspect [AssocElem]+ deriving Show++-- | label+type Label = VHDLId++-- | association_element+data AssocElem = Maybe (FormalPart) :=>: ActualPart+ deriving Show++-- | formal_part+-- We only accept a formal_designator (which is a name after all),+-- in the forme of simple name (no need for selected names) +-- "function_name ( formal_designator )" and "type_mark ( formal_designator )"+-- are not allowed+type FormalPart = SimpleName++-- | actual_part+-- We only accept an actual_designator,+-- "function_name ( actual_designator )" and "type_mark ( actual_designator )"+-- are not allowed+type ActualPart = ActualDesig++-- | actual_designator+data ActualDesig = ADName VHDLName | ADExpr Expr | Open+ deriving Show++-- | concurrent_signal_assignment_statement+-- Only conditional_signal_assignment is allowed (without options)+-- The LHS (targets) are simply signal names, no aggregates+data ConSigAssignSm = VHDLName :<==: ConWforms+ deriving Show++-- | conditional_waveforms +data ConWforms = ConWforms [WhenElse] Wform (Maybe When) + deriving Show++-- | Helper type, it doesn't exist in the VHDL grammar+data WhenElse = WhenElse Wform Expr+ deriving Show++-- | Helper type, it doesn't exist in the VHDL grammar+newtype When = When Expr+ deriving Show++-- | waveform+-- although it is possible to leave [Expr] empty, that's obviously not+-- valid VHDL waveform+data Wform = Wform [WformElem] | Unaffected+ deriving Show++-- | waveform_element+-- Null is not accepted+data WformElem = WformElem Expr (Maybe Expr)+ deriving Show++ +-- | component_instantiation_statement+-- No generics supported+-- The port map aspect is mandatory+data CompInsSm = CompInsSm Label InsUnit PMapAspect+ deriving Show++-- | process_statement+-- The label is mandatory+-- Only simple names are accepted in the sensitivity list+-- No declarative part is allowed+data ProcSm = ProcSm Label [SimpleName] [SeqSm]+ deriving Show++-- | instantiated_unit+-- Only Entities are allowed and their architecture cannot be specified+data InsUnit = IUEntity VHDLName+ deriving Show++-----------------+-- Expression AST+-----------------++-- | expression, instead of creating an AST like the grammar +-- (see commented section below) we made our own expressions which are +-- easier to handle, but which don't don't show operand precedence+-- (that is a responsibility of the pretty printer)++data Expr = -- Logical operations+ And Expr Expr |+ Or Expr Expr |+ Xor Expr Expr |+ Nand Expr Expr |+ Nor Expr Expr |+ Xnor Expr Expr |+ -- Relational Operators+ Expr :=: Expr |+ Expr :/=: Expr |+ Expr :<: Expr |+ Expr :<=: Expr |+ Expr :>: Expr |+ Expr :>=: Expr |+ -- Shift Operators+ Sll Expr Expr |+ Srl Expr Expr |+ Sla Expr Expr |+ Sra Expr Expr |+ Rol Expr Expr |+ Ror Expr Expr |+ -- Adding Operators+ Expr :+: Expr |+ Expr :-: Expr |+ Expr :&: Expr |+ -- Sign Operators+ Neg Expr |+ Pos Expr |+ -- Multiplying Operators+ Expr :*: Expr |+ Expr :/: Expr |+ Mod Expr Expr |+ Rem Expr Expr |+ -- Miscellaneous Operators+ Expr :**: Expr |+ Abs Expr |+ Not Expr |+ -- Primary expressions+ -- Only literals, names and function calls are allowed+ PrimName VHDLName |+ PrimLit Literal |+ PrimFCall FCall | + -- Composite_types-related operators+ Aggregate [ElemAssoc] -- (exp1,exp2,exp3, ...)+ deriving Show ++-- operand precedences, according to the VHDL LRM: +-- "Where the language+-- allows a sequence of operators, operators with the same+-- precedence level are associated with their operands in textual+-- order, from left to right." In other words, they are all left associative+--+-- For example: a / b / c = (a/b)/c+-- a and b or c is illegal (mixing operators is not allowed+-- by the language and only+-- can avoid parenthesis in some +-- cases)++infixl 2 `And`, `Or`, `Xor`, `Nand`, `Nor`, `Xnor`+infixl 3 :=:, :/=:, :<:, :<=:, :>:, :>=:+infixl 4 `Sll`, `Srl`, `Sla`, `Sra`, `Rol`, `Ror`+infixl 5 :+:, :-:, :&:+infix 6 `Neg`, `Pos` +infixl 7 :*:, :/:, `Mod`, `Rem`+infixl 8 :**:, `Abs`, `Not` ++-- | Logical Operators precedence+logicalPrec :: Int+logicalPrec = 2++-- | Relational Operators Precedence+relationalPrec :: Int+relationalPrec = 3++-- | Shift Operators Precedence+shiftPrec :: Int+shiftPrec = 4++-- | Plus Operators precedence+plusPrec :: Int+plusPrec = 5++-- | Sign Operators Precedence+signPrec :: Int+signPrec = 6++-- | Multplying Operators Precedecne+multPrec :: Int+multPrec = 7++-- | Miscellaneous Operators Precedence+miscPrec :: Int+miscPrec = 8+++++-- | element_association+-- only one choice is allowed+data ElemAssoc = ElemAssoc (Maybe Choice) Expr+ deriving Show++-- | literal+-- Literals are expressed as a string (remember we are generating+-- code, not parsing)+type Literal = String++-- | function_call+data FCall = FCall VHDLName [AssocElem]+ deriving Show+ + +{-++Expression AST following the grammar (discarded)++-- again, even if it possible to leave the [Relation] lists empty+-- that wouldn't be valid VHDL code+-- regading NandExpr and NorExpr, their Relation list should +-- have a maximum size of two (i.e. NandExpr Expr (Maybe Expr))+data Expr = AndExpr [Relation] | + OrExpr [Relation] |+ XorExpr [Relation] |+ NandExpr [Relation] |+ NorExpr [Relation] |+ XnorExpr [Relation]+ deriving Show++-- relation +data Relation = Relation ShiftExpr (Maybe (RelOp,ShiftExpr))+ deriving Show++-- relational_operator+data RelOp = Eq | NEq | Less | LessEq | Gter | GterEq + deriving Show ++-- shift_expression+data ShiftExpr = ShiftExpr SimpleExpr (Maybe(ShiftOp,SimpleExpr)) + deriving Show++-- simple_expression+data SimpleExpr = SimpleExpr (Maybe Sign) Term [(AddOp,Term)]+ deriving Show++-- sign+data Sign = Pos | Neg+ deriving Show++-- shift_operator+data ShiftOp = Sll | Srl | Sla | Sra | Rol | Ror+ deriving Show ++-- adding_operator+data AddOp = Plus | Minus | Concat + deriving Show++-- term+data Term = Term Factor (Maybe (MultOp, Factor))+ deriving Show++-- multiplying_operator+data MultOp = Mult | Div | Mod | Rem+ deriving Show++-- factor+data Factor = Exp Primary (Maybe (Primary)) |+ Abs Primary |+ Not Primary+ deriving Show++-- primary+-- Only literals, names and function calls are allowed+data Primary = PrimName VHDLName |+ PrimLit Literal |+ PrimFCall FCall+ deriving Show++-- literal+-- Literals are expressed as a string (remember we are generating+-- code, not parsing)+type Literal = String++-- function_call+data FCall = FCall VHDLName [AssocElem]+ deriving Show+-}
+ src/ForSyDe/Deep/Backend/VHDL/Constants.hs view
@@ -0,0 +1,342 @@+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.Backend.VHDL.Constants+-- Copyright : (c) ES Group, KTH/ICT/ES+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- Constants used in the VHDL backend+--+-----------------------------------------------------------------------------+module ForSyDe.Deep.Backend.VHDL.Constants where++import ForSyDe.Deep.Backend.VHDL.AST++-- | Standard context clause used in all generated vhdl files. It imports+commonContextClause :: [ContextItem]+commonContextClause = + [Library forsydeId, + Library ieeeId,+ Use $ NSelected (NSimple forsydeId :.: SSimple typesId) :.: All,+ Use $ NSelected (NSimple ieeeId :.: SSimple std_logic_1164Id) :.: All,+ Use $ NSelected (NSimple ieeeId :.: SSimple numeric_stdId) :.: All]+ where forsydeId = unsafeVHDLBasicId "forsyde" + ieeeId = unsafeVHDLBasicId "ieee"+ typesId = unsafeVHDLBasicId "types"+ std_logic_1164Id = unsafeVHDLBasicId "std_logic_1164"+ numeric_stdId = unsafeVHDLBasicId "numeric_std"++--------------+-- Identifiers+--------------++-- | reset and clock signal identifiers in String form+resetStr, clockStr :: String+resetStr = "resetn"+clockStr = "clock"++-- | reset and clock signal identifiers in basic VHDLId form+resetId, clockId :: VHDLId+resetId = unsafeVHDLBasicId resetStr+clockId = unsafeVHDLBasicId clockStr+++-- | \"types\" identifier+typesId :: VHDLId+typesId = unsafeVHDLBasicId "types"++-- | work identifier+workId :: VHDLId+workId = unsafeVHDLBasicId "work"++-- | std identifier+stdId :: VHDLId+stdId = unsafeVHDLBasicId "std"+++-- | textio identifier+textioId :: VHDLId+textioId = unsafeVHDLBasicId "textio"++-- | range attribute identifier+rangeId :: VHDLId+rangeId = unsafeVHDLBasicId "range"+++-- | range attribute identifier+imageId :: VHDLId+imageId = unsafeVHDLBasicId "image"++-- | event attribute identifie+eventId :: VHDLId+eventId = unsafeVHDLBasicId "event"+++-- | default function identifier+defaultId :: VHDLId+defaultId = unsafeVHDLBasicId "default"+++-- AsbtExt function identifiers++-- | absent function identifier+absentId :: VHDLId+absentId = unsafeVHDLBasicId "absent"++-- | present function identifier+presentId :: VHDLId+presentId = unsafeVHDLBasicId "present"++-- | fromAbstExt function identifier+fromAbstExtId :: VHDLId+fromAbstExtId = unsafeVHDLBasicId "fromAbstExt"++-- | unsafeFromAbstExt function identifier+unsafeFromAbstExtId :: VHDLId+unsafeFromAbstExtId = unsafeVHDLBasicId "unsafeFromAbstExt"++-- | value element identifier+valueId :: VHDLId+valueId = unsafeVHDLBasicId "value"++-- | value element suffix+valueSuffix :: Suffix+valueSuffix = SSimple valueId++-- | isPresent function and element identifier+isPresentId :: VHDLId+isPresentId = unsafeVHDLBasicId "isPresent"++-- | isAbsent function identifier+isAbsentId :: VHDLId+isAbsentId = unsafeVHDLBasicId "isAbsent"++-- FSVec function identifiers++-- | ex (operator ! in original Haskell source) function identifier+exId :: VHDLId+exId = unsafeVHDLBasicId "ex"++-- | sel (function select in original Haskell source) function identifier+selId :: VHDLId+selId = unsafeVHDLBasicId "sel"+++-- | ltplus (function (<+) in original Haskell source) function identifier+ltplusId :: VHDLId+ltplusId = unsafeVHDLBasicId "ltplus"+++-- | plusplus (function (++) in original Haskell source) function identifier+plusplusId :: VHDLId+plusplusId = unsafeVHDLBasicId "plusplus"+++-- | empty function identifier+emptyId :: VHDLId+emptyId = unsafeVHDLBasicId "empty"++-- | plusgt (function (+>) in original Haskell source) function identifier+plusgtId :: VHDLId+plusgtId = unsafeVHDLBasicId "plusgt"++-- | singleton function identifier+singletonId :: VHDLId+singletonId = unsafeVHDLBasicId "singleton"++-- | length function identifier+lengthId :: VHDLId+lengthId = unsafeVHDLBasicId "length"+++-- | isnull (function null in original Haskell source) function identifier+isnullId :: VHDLId+isnullId = unsafeVHDLBasicId "isnull"+++-- | replace function identifier+replaceId :: VHDLId+replaceId = unsafeVHDLBasicId "replace"+++-- | head function identifier+headId :: VHDLId+headId = unsafeVHDLBasicId "head"+++-- | last function identifier+lastId :: VHDLId+lastId = unsafeVHDLBasicId "last"+++-- | init function identifier+initId :: VHDLId+initId = unsafeVHDLBasicId "init"+++-- | tail function identifier+tailId :: VHDLId+tailId = unsafeVHDLBasicId "tail"+++-- | take function identifier+takeId :: VHDLId+takeId = unsafeVHDLBasicId "take"+++-- | drop function identifier+dropId :: VHDLId+dropId = unsafeVHDLBasicId "drop"++-- | shiftl function identifier+shiftlId :: VHDLId+shiftlId = unsafeVHDLBasicId "shiftl"++-- | shiftr function identifier+shiftrId :: VHDLId+shiftrId = unsafeVHDLBasicId "shiftr"++-- | rotl function identifier+rotlId :: VHDLId+rotlId = unsafeVHDLBasicId "rotl"++-- | reverse function identifier+rotrId :: VHDLId+rotrId = unsafeVHDLBasicId "rotr"++-- | reverse function identifier+reverseId :: VHDLId+reverseId = unsafeVHDLBasicId "reverse"++-- | toBitVector8 function identifier+toBitVector8Id :: VHDLId+toBitVector8Id = unsafeVHDLBasicId "toBitVector8"++-- | toBitVector16 function identifier+toBitVector16Id :: VHDLId+toBitVector16Id = unsafeVHDLBasicId "toBitVector16"++-- | toBitVector32 function identifier+toBitVector32Id :: VHDLId+toBitVector32Id = unsafeVHDLBasicId "toBitVector32"+++-- | fromBitVector8 function identifier+fromBitVector8Id :: VHDLId+fromBitVector8Id = unsafeVHDLBasicId "fromBitVector8"+++-- | fromBitVector16 function identifier+fromBitVector16Id :: VHDLId+fromBitVector16Id = unsafeVHDLBasicId "fromBitVector16"+++-- | fromBitVector32 function identifier+fromBitVector32Id :: VHDLId+fromBitVector32Id = unsafeVHDLBasicId "fromBitVector32"++-- | fixmul8 function identifier+fixmul8Id :: VHDLId+fixmul8Id = unsafeVHDLBasicId "fixmul8"++++-- | copy function identifier+copyId :: VHDLId+copyId = unsafeVHDLBasicId "copy"+++-- | show function identifier+showId :: VHDLId+showId = unsafeVHDLBasicId "show"+++-- | write function idenfier (from std.textio)+writeId :: VHDLId+writeId = unsafeVHDLBasicId "write"++-- | output file identifier (from std.textio)+outputId :: VHDLId+outputId = unsafeVHDLBasicId "output"++--------+-- Names+--------++defaultSN :: VHDLName+defaultSN = NSimple defaultId++------------------+-- VHDL type marks+------------------++-- | Stardard logic type mark+std_logicTM :: TypeMark+std_logicTM = unsafeVHDLBasicId "std_logic"++-- | boolean type mark+booleanTM :: TypeMark+booleanTM = unsafeVHDLBasicId "boolean"++-- | fsvec_index typemark+fsvec_indexTM :: TypeMark+fsvec_indexTM = unsafeVHDLBasicId "fsvec_index"++-- | natural typemark+naturalTM :: TypeMark+naturalTM = unsafeVHDLBasicId "natural"++-- | int32 typemark (defined in ForSyDe's VHDL library)+int64TM :: TypeMark+int64TM = unsafeVHDLBasicId "int64"++-- | int32 typemark (defined in ForSyDe's VHDL library)+int32TM :: TypeMark+int32TM = unsafeVHDLBasicId "int32"++-- | int32 typemark (defined in ForSyDe's VHDL library)+int16TM :: TypeMark+int16TM = unsafeVHDLBasicId "int16"++-- | int8 typemark (defined in ForSyDe's VHDL library)+int8TM :: TypeMark+int8TM = unsafeVHDLBasicId "int8"++-- | string typemark+stringTM :: TypeMark+stringTM = unsafeVHDLBasicId "string"++--------------+-- Expressions+--------------++-- | true expression+trueExpr :: Expr+trueExpr = PrimName (NSimple $ unsafeVHDLBasicId "true")+ +-- | false expression+falseExpr :: Expr+falseExpr = PrimName (NSimple $ unsafeVHDLBasicId "false")++-- | \'0\' bit expression+lowExpr :: Expr+lowExpr = PrimLit "'0'"++-- | \'1\' bit expression+highExpr :: Expr+highExpr = PrimLit "'1'"++-- | tup string record suffix+tupStrSuffix :: Int -> String+tupStrSuffix n = "tup_" ++ show n++-- | tup VHLID record suffix+tupVHDLIdSuffix :: Int -> VHDLId+tupVHDLIdSuffix = unsafeVHDLBasicId . tupStrSuffix++-- | tup VHDLName suffix+tupVHDLSuffix :: Int -> Suffix+tupVHDLSuffix = SSimple . tupVHDLIdSuffix
+ src/ForSyDe/Deep/Backend/VHDL/FileIO.hs view
@@ -0,0 +1,30 @@+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.Backend.VHDL.FileIO+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- Functions working with files in the VHDL backend. +--+-----------------------------------------------------------------------------+module ForSyDe.Deep.Backend.VHDL.FileIO (writeDesignFile) where++import ForSyDe.Deep.Backend.VHDL.AST+import ForSyDe.Deep.Backend.VHDL.Ppr() -- instances+import qualified ForSyDe.Deep.Backend.Ppr as ForSyDePpr (ppr)++import System.IO+import Text.PrettyPrint.HughesPJ++-- | Write a design file to a file in disk+writeDesignFile :: DesignFile -> FilePath -> IO ()+writeDesignFile df fp = do+ handle <- openFile fp WriteMode+ hPutStrLn handle "-- Automatically generated by ForSyDe"+ hPutStr handle $ (renderStyle mystyle . ForSyDePpr.ppr) df+ hClose handle+ where mystyle = style{lineLength=80, ribbonsPerLine=1}
+ src/ForSyDe/Deep/Backend/VHDL/Generate.hs view
@@ -0,0 +1,721 @@+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.Backend.VHDL.Generate+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- Functions used to generate VHDL AST elements without making any+-- explicit translation.+--+-----------------------------------------------------------------------------+module ForSyDe.Deep.Backend.VHDL.Generate where++import ForSyDe.Deep.Backend.VHDL.Constants+import ForSyDe.Deep.Backend.VHDL.AST+import ForSyDe.Deep.Backend.VHDL.Traverse.VHDLM++-- | Generate a list of asignments (in 'ConcSm' form) of intermediate signals+-- (first argument) to final output signals (second argument)+genOutAssigns :: [VHDLId] -> [VHDLId] -> [ConcSm]+genOutAssigns = zipWith assign+ where assign dest orig = CSSASm $ dest `genSignalAssign` orig++-- | Generate a simple signal assignment, from a VHDL identifier to a+-- VHDL identifier+genSignalAssign :: VHDLId -- ^ destination+ -> VHDLId -- ^ origin+ -> ConSigAssignSm+genSignalAssign dest orig = genExprAssign dest (PrimName $ NSimple orig)++-- | Generate a function call asignment+genFCallAssign :: VHDLId -- ^ destination signal+ -> VHDLId -- ^ Function Name+ -> [VHDLId] -- ^ Function formal parameters+ -> [VHDLId] -- ^ Function actual parameters+ -> ConSigAssignSm+genFCallAssign dest fName formal actual =+ genExprAssign dest (PrimFCall $ genFCall fName formal actual)++-- | Generate a simple assignment from an expression to a name+genExprAssign :: VHDLId -> Expr -> ConSigAssignSm+genExprAssign dest origExpr =+ NSimple dest :<==: (ConWforms [] (Wform [WformElem origExpr Nothing])+ Nothing)++-- | Generate a system design file for a system from the global system+-- identifier,+-- local traversing results and the translated entity declaration+genSysDesignFile :: String -> EntityDec -> LocalTravResult -> DesignFile+genSysDesignFile globalSysId ent@(EntityDec id _) (LocalTravResult decs stms) =+ DesignFile contextClause [LUEntity ent, LUArch archBody]+ where archBody = ArchBody archId (NSimple id) decs stms+ archId = unsafeVHDLBasicId "synthesizable"+ libName = globalSysId ++ "_lib"+ libId = unsafeVHDLBasicId libName+ contextClause = commonContextClause +++ [Library libId,+ Use $ NSelected (NSimple libId :.: SSimple typesId) :.: All]++-- | Generate a library design file from the global results+genLibDesignFile :: GlobalTravResult -> DesignFile+genLibDesignFile (GlobalTravResult typeDecs subtypeDecs subProgBodies) =+ DesignFile commonContextClause [LUPackageDec packageDec,+ LUPackageBody packageBody]+ -- Due to dependency among types and subtypes we first output+ -- unconstrained types (which may have constarined types depending on),+ -- then subtypes (which we may have composite types depending on)+ -- A general solution could be a type dependency resolving algorithm+ where packageDec = PackageDec typesId (packageUnconsTypeDecs +++ packageSubtypeDecs +++ packageTypeDecs +++ subProgSpecs)+ packageUnconsTypeDecs = map PDITD $ filter (\a -> isUnconsType a) typeDecs+ packageTypeDecs = map PDITD $ filter (\a -> (not.isUnconsType) a) typeDecs+ packageSubtypeDecs = map PDISD subtypeDecs+ subProgSpecs = map (\(SubProgBody spec _ _) -> PDISS spec) subProgBodies+ packageBody = PackageBody typesId subProgBodies+ isUnconsType (TypeDec _ (TDA (UnconsArrayDef _ _))) = True+ isUnconsType _ = False+++-- | Generate a list of association from two lists of signal identifiers+-- The first one establishes the formal parameters+genAssocElems :: [VHDLId] -> [VHDLId] -> [AssocElem]+genAssocElems formalNames actualNames = zipWith genAssoc formalNames actualNames++-- | Generate a port map from two lists of signal identifiers+-- The first list establishes the formal parameters+genPMap :: [VHDLId] -> [VHDLId] -> PMapAspect+genPMap formalIds actualIds =+ PMapAspect $ genAssocElems formalIds actualIds+++-- | Generate a function call from two lists of signal identifiers+-- The first list establishes the formal parameters+genFCall :: VHDLId -> [VHDLId] -> [VHDLId] -> FCall+genFCall fName formalIds actualIds =+ FCall (NSimple fName) $ zipWith genAssoc formalIds actualIds+++-- | Generate a function call from the Function Name and a list of expressions+-- (its arguments)+genExprFCall :: VHDLId -> [Expr] -> Expr+genExprFCall fName args =+ PrimFCall $ FCall (NSimple fName) $+ map (\exp -> Nothing :=>: ADExpr exp) args++-- | version of genExprFCall which requires exactly n arguments+genExprFCallN :: VHDLId -> Int -> [Expr] -> Expr+genExprFCallN fName n args = genExprFCall fName (takeExactly n args)+ where takeExactly 0 [] = []+ takeExactly n (x:xs) | n > 0 = x : takeExactly (n-1) xs+ takeExactly _ _ = error "takeExactly: non exact length of input list"++-- | Generate a function call from the Function Name (constant function)+genExprFCall0 :: VHDLId -> Expr+genExprFCall0 fName = genExprFCall fName []++-- | List version of genExprFCall0+genExprFCall0L :: VHDLId -> [Expr] -> Expr+genExprFCall0L fName [] = genExprFCall fName []+genExprFCall0L _ _ = error "ForSyDe.Backend.VHDL.Generate.genExprFCall0L incorrect length"++-- | Generate a function call from the Function Name and an expression argument+genExprFCall1 :: VHDLId -> Expr -> Expr+genExprFCall1 fName arg = genExprFCall fName [arg]++-- | List version of genExprFCall1+genExprFCall1L :: VHDLId -> [Expr] -> Expr+genExprFCall1L fName [arg] = genExprFCall fName [arg]+genExprFCall1L _ _ = error "ForSyDe.Backend.VHDL.Generate.genExprFCall1L incorrect length"++-- | Generate a function call from the Function Name and two expression arguments+genExprFCall2 :: VHDLId -> Expr -> Expr -> Expr+genExprFCall2 fName arg1 arg2 = genExprFCall fName [arg1,arg2]++-- | List version of genExprFCall2+genExprFCall2L :: VHDLId -> [Expr] -> Expr+genExprFCall2L fName [arg1, arg2] = genExprFCall fName [arg1,arg2]+genExprFCall2L _ _ = error "ForSyDe.Backend.VHDL.Generate.genExprFCall2L incorrect length"++-- | Generate a function call from the Function Name and two expression arguments+genExprFCall4 :: VHDLId -> Expr -> Expr -> Expr -> Expr -> Expr+genExprFCall4 fName arg1 arg2 arg3 arg4 =+ genExprFCall fName [arg1,arg2,arg2,arg3,arg4]+++-- | List version of genExprFCall4+genExprFCall4L :: VHDLId -> [Expr] -> Expr+genExprFCall4L fName [arg1, arg2, arg3, arg4] =+ genExprFCall fName [arg1,arg2,arg2,arg3,arg4]+genExprFCall4L _ _ = error "ForSyDe.Backend.VHDL.Generate.genExprFCall4L incorrect length"++-- | Generate a procedure call from the Function Name and a list of expressions+-- (its arguments)+genExprProcCall :: VHDLId -> [Expr] -> SeqSm+genExprProcCall pName args = ProcCall (NSimple pName) $+ map (\exp -> Nothing :=>: ADExpr exp) args+++-- | Generate a procedure call from the Function Name (constant procedure)+genExprProcCall0 :: VHDLId -> SeqSm+genExprProcCall0 fName = genExprProcCall fName []+++-- | Generate a procedure call from the Function Name and an expression argument+genExprProcCall1 :: VHDLId -> Expr -> SeqSm+genExprProcCall1 pName arg = genExprProcCall pName [arg]+++-- | Generate a procedure call from the Function Name and four expression+-- arguments+genExprProcCall2 :: VHDLId -> Expr -> Expr -> SeqSm+genExprProcCall2 pName arg1 arg2 = genExprProcCall pName [arg1,arg2]+++-- | Generate a procedure call from the Function Name and two expression+-- arguments+genExprProcCall4 :: VHDLId -> Expr -> Expr -> Expr -> Expr -> SeqSm+genExprProcCall4 pName arg1 arg2 arg3 arg4 =+ genExprProcCall pName [arg1,arg2,arg2,arg3,arg4]+++-- Generate an association of a formal and actual parameter+genAssoc :: VHDLId -> VHDLId -> AssocElem+genAssoc formal actual = Just formal :=>: ADName (NSimple actual)+++-- | Generate the default functions for an unconstrained custom vector type+genUnconsVectorFuns :: TypeMark -- ^ type of the vector elements+ -> TypeMark -- ^ type of the vector+ -> [SubProgBody]+genUnconsVectorFuns elemTM vectorTM =+ [SubProgBody exSpec [] [exExpr] ,+ SubProgBody selSpec [SPVD selVar] [selFor, selRet] ,+ SubProgBody emptySpec [SPVD emptyVar] [emptyExpr] ,+ SubProgBody lengthSpec [] [lengthExpr] ,+ SubProgBody isnullSpec [] [isnullExpr] ,+ SubProgBody replaceSpec [SPVD replaceVar] [replaceExpr, replaceRet] ,+ SubProgBody headSpec [] [headExpr] ,+ SubProgBody lastSpec [] [lastExpr] ,+ SubProgBody initSpec [SPVD initVar] [initExpr, initRet] ,+ SubProgBody tailSpec [SPVD tailVar] [tailExpr, tailRet] ,+ SubProgBody takeSpec [SPVD takeVar] [takeExpr, takeRet] ,+ SubProgBody dropSpec [SPVD dropVar] [dropExpr, dropRet] ,+ SubProgBody shiftlSpec [SPVD shiftlVar] [shiftlExpr, shiftlRet] ,+ SubProgBody shiftrSpec [SPVD shiftrVar] [shiftrExpr, shiftrRet] ,+ SubProgBody rotlSpec [SPVD rotlVar] [rotlExpr, rotlRet] ,+ SubProgBody rotrSpec [SPVD rotrVar] [rotrExpr, rotrRet] ,+ SubProgBody reverseSpec [SPVD reverseVar] [reverseFor, reverseRet] ,+ SubProgBody copySpec [SPVD copyVar] [copyExpr] ,+ SubProgBody plusgtSpec [SPVD plusgtVar] [plusgtExpr, plusgtRet] ,+ SubProgBody ltplusSpec [SPVD ltplusVar] [ltplusExpr, ltplusRet] ,+ SubProgBody plusplusSpec [SPVD plusplusVar] [plusplusExpr, plusplusRet] ,+ SubProgBody singletonSpec [SPVD singletonVar] [singletonRet] ,+ SubProgBody showSpec [SPSB doShowDef] [showRet] ,+ SubProgBody defaultSpec [] [defaultExpr] ]+ where ixPar = unsafeVHDLBasicId "ix"+ vecPar = unsafeVHDLBasicId "vec"+ vec1Par = unsafeVHDLBasicId "vec1"+ vec2Par = unsafeVHDLBasicId "vec2"+ fPar = unsafeVHDLBasicId "f"+ nPar = unsafeVHDLBasicId "n"+ sPar = unsafeVHDLBasicId "s"+ iId = unsafeVHDLBasicId "i"+ iPar = iId+ aPar = unsafeVHDLBasicId "a"+ resId = unsafeVHDLBasicId "res"+ exSpec = Function exId [IfaceVarDec vecPar vectorTM,+ IfaceVarDec ixPar naturalTM] elemTM+ exExpr =+ ReturnSm (Just $ PrimName $ NIndexed+ (IndexedName (NSimple vecPar) [PrimName $ NSimple ixPar]))+ selSpec = Function selId [IfaceVarDec fPar naturalTM,+ IfaceVarDec nPar naturalTM,+ IfaceVarDec sPar naturalTM,+ IfaceVarDec vecPar vectorTM ] vectorTM+ -- variable res : fsvec_x (0 to n-1);+ selVar =+ VarDec resId+ (SubtypeIn vectorTM+ (Just $ IndexConstraint+ [ToRange (PrimLit "0")+ ((PrimName (NSimple nPar)) :-:+ (PrimLit "1")) ]))+ Nothing+ -- for i res'range loop+ -- res(i) := vec(f+i*s);+ -- end loop;+ selFor = ForSM iId (AttribRange $ applyRangeAttrib resId) [selAssign]+ -- res(i) := vec(f+i*s);+ selAssign = let origExp = PrimName (NSimple fPar) :+:+ (PrimName (NSimple iId) :*:+ PrimName (NSimple sPar)) in+ NIndexed (IndexedName (NSimple resId) [PrimName (NSimple iId)]) :=+ (PrimName $ NIndexed (IndexedName (NSimple vecPar) [origExp]))+ -- return res;+ selRet = ReturnSm (Just $ PrimName (NSimple resId))+ emptySpec = Function emptyId [] vectorTM+ emptyVar =+ VarDec resId+ (SubtypeIn vectorTM+ (Just $ IndexConstraint+ [ToRange (PrimLit "0")+ (PrimLit "-1")]))+ Nothing+ emptyExpr = ReturnSm (Just $ PrimName (NSimple resId))+ lengthSpec = Function lengthId [IfaceVarDec vecPar vectorTM] naturalTM+ lengthExpr = ReturnSm (Just $ PrimName (NAttribute $+ AttribName (NSimple vecPar) lengthId Nothing))+ isnullSpec = Function isnullId [IfaceVarDec vecPar vectorTM] booleanTM+ -- return vec'length = 0+ isnullExpr = ReturnSm (Just $+ PrimName (NAttribute $+ AttribName (NSimple vecPar) lengthId Nothing) :=:+ PrimLit "0")+ replaceSpec = Function replaceId [IfaceVarDec vecPar vectorTM,+ IfaceVarDec iPar naturalTM,+ IfaceVarDec aPar elemTM ] vectorTM+ -- variable res : fsvec_x (0 to vec'length-1);+ replaceVar =+ VarDec resId+ (SubtypeIn vectorTM+ (Just $ IndexConstraint+ [ToRange (PrimLit "0")+ (PrimName (NAttribute $+ AttribName (NSimple vecPar) lengthId Nothing) :-:+ (PrimLit "1")) ]))+ Nothing+ -- res := vec(0 to i-1) & a & vec(i+1 to length'vec-1)+ replaceExpr = NSimple resId :=+ (vecSlice (PrimLit "0") (PrimName (NSimple iPar) :-: PrimLit "1") :&:+ PrimName (NSimple aPar) :&:+ vecSlice (PrimName (NSimple iPar) :+: PrimLit "1")+ ((PrimName (NAttribute $+ AttribName (NSimple vecPar) lengthId Nothing))+ :-: PrimLit "1"))+ replaceRet = ReturnSm (Just $ PrimName $ NSimple resId)+ vecSlice init last = PrimName (NSlice+ (SliceName+ (NSimple vecPar)+ (ToRange init last)))+ headSpec = Function headId [IfaceVarDec vecPar vectorTM] elemTM+ -- return vec(0);+ headExpr = ReturnSm (Just $ (PrimName $ NIndexed (IndexedName+ (NSimple vecPar) [PrimLit "0"])))+ lastSpec = Function lastId [IfaceVarDec vecPar vectorTM] elemTM+ -- return vec(vec'length-1);+ lastExpr = ReturnSm (Just $ (PrimName $ NIndexed (IndexedName+ (NSimple vecPar)+ [PrimName (NAttribute $+ AttribName (NSimple vecPar) lengthId Nothing)+ :-: PrimLit "1"])))+ initSpec = Function initId [IfaceVarDec vecPar vectorTM] vectorTM+ -- variable res : fsvec_x (0 to vec'length-2);+ initVar =+ VarDec resId+ (SubtypeIn vectorTM+ (Just $ IndexConstraint+ [ToRange (PrimLit "0")+ (PrimName (NAttribute $+ AttribName (NSimple vecPar) lengthId Nothing) :-:+ (PrimLit "2")) ]))+ Nothing+ -- res:= vec(0 to vec'length-2)+ initExpr = NSimple resId := (vecSlice+ (PrimLit "0")+ (PrimName (NAttribute $+ AttribName (NSimple vecPar) lengthId Nothing)+ :-: PrimLit "2"))+ initRet = ReturnSm (Just $ PrimName $ NSimple resId)+ tailSpec = Function tailId [IfaceVarDec vecPar vectorTM] vectorTM+ -- variable res : fsvec_x (0 to vec'length-2);+ tailVar =+ VarDec resId+ (SubtypeIn vectorTM+ (Just $ IndexConstraint+ [ToRange (PrimLit "0")+ (PrimName (NAttribute $+ AttribName (NSimple vecPar) lengthId Nothing) :-:+ (PrimLit "2")) ]))+ Nothing+ -- res := vec(1 to vec'length-1)+ tailExpr = NSimple resId := (vecSlice+ (PrimLit "1")+ (PrimName (NAttribute $+ AttribName (NSimple vecPar) lengthId Nothing)+ :-: PrimLit "1"))+ tailRet = ReturnSm (Just $ PrimName $ NSimple resId)+ takeSpec = Function takeId [IfaceVarDec nPar naturalTM,+ IfaceVarDec vecPar vectorTM ] vectorTM+ -- variable res : fsvec_x (0 to n-1);+ takeVar =+ VarDec resId+ (SubtypeIn vectorTM+ (Just $ IndexConstraint+ [ToRange (PrimLit "0")+ ((PrimName (NSimple nPar)) :-:+ (PrimLit "1")) ]))+ Nothing+ -- res := vec(0 to n-1)+ takeExpr = NSimple resId :=+ (vecSlice (PrimLit "1")+ (PrimName (NSimple $ nPar) :-: PrimLit "1"))+ takeRet = ReturnSm (Just $ PrimName $ NSimple resId)+ dropSpec = Function dropId [IfaceVarDec nPar naturalTM,+ IfaceVarDec vecPar vectorTM ] vectorTM+ -- variable res : fsvec_x (0 to vec'length-n-1);+ dropVar =+ VarDec resId+ (SubtypeIn vectorTM+ (Just $ IndexConstraint+ [ToRange (PrimLit "0")+ (PrimName (NAttribute $+ AttribName (NSimple vecPar) lengthId Nothing) :-:+ (PrimName $ NSimple nPar):-: (PrimLit "1")) ]))+ Nothing+ -- res := vec(n to vec'length-1)+ dropExpr = NSimple resId := (vecSlice+ (PrimName $ NSimple nPar)+ (PrimName (NAttribute $+ AttribName (NSimple vecPar) lengthId Nothing)+ :-: PrimLit "1"))+ dropRet = ReturnSm (Just $ PrimName $ NSimple resId)+ shiftlSpec = Function shiftlId [IfaceVarDec vecPar vectorTM,+ IfaceVarDec aPar elemTM ] vectorTM+ -- variable res : fsvec_x (0 to vec'length-1);+ shiftlVar =+ VarDec resId+ (SubtypeIn vectorTM+ (Just $ IndexConstraint+ [ToRange (PrimLit "0")+ (PrimName (NAttribute $+ AttribName (NSimple vecPar) lengthId Nothing) :-:+ (PrimLit "1")) ]))+ Nothing+ -- res := a & init(vec)+ shiftlExpr = NSimple resId :=+ (PrimName (NSimple aPar) :&:+ genExprFCall1 initId (PrimName $ NSimple vecPar))+ shiftlRet = ReturnSm (Just $ PrimName $ NSimple resId)+ shiftrSpec = Function shiftrId [IfaceVarDec vecPar vectorTM,+ IfaceVarDec aPar elemTM ] vectorTM+ -- variable res : fsvec_x (0 to vec'length-1);+ shiftrVar =+ VarDec resId+ (SubtypeIn vectorTM+ (Just $ IndexConstraint+ [ToRange (PrimLit "0")+ (PrimName (NAttribute $+ AttribName (NSimple vecPar) lengthId Nothing) :-:+ (PrimLit "1")) ]))+ Nothing+ -- res := tail(vec) & a+ shiftrExpr = NSimple resId :=+ (genExprFCall1 tailId (PrimName $ NSimple vecPar) :&:+ PrimName (NSimple aPar))+ shiftrRet = ReturnSm (Just $ PrimName $ NSimple resId)+ rotlSpec = Function rotlId [IfaceVarDec vecPar vectorTM] vectorTM+ -- variable res : fsvec_x (0 to vec'length-1);+ rotlVar =+ VarDec resId+ (SubtypeIn vectorTM+ (Just $ IndexConstraint+ [ToRange (PrimLit "0")+ (PrimName (NAttribute $+ AttribName (NSimple vecPar) lengthId Nothing) :-:+ (PrimLit "1")) ]))+ Nothing+ -- if null(vec) then res := vec else res := last(vec) & init(vec)+ rotlExpr = IfSm (genExprFCall1 isnullId (PrimName $ NSimple vecPar))+ [NSimple resId := (PrimName $ NSimple vecPar)]+ []+ (Just $ Else [rotlExprRet])+ where rotlExprRet =+ NSimple resId :=+ (genExprFCall1 lastId (PrimName $ NSimple vecPar) :&:+ genExprFCall1 initId (PrimName $ NSimple vecPar))+ rotlRet = ReturnSm (Just $ PrimName $ NSimple resId)+ rotrSpec = Function rotrId [IfaceVarDec vecPar vectorTM] vectorTM+ -- variable res : fsvec_x (0 to vec'length-1);+ rotrVar =+ VarDec resId+ (SubtypeIn vectorTM+ (Just $ IndexConstraint+ [ToRange (PrimLit "0")+ (PrimName (NAttribute $+ AttribName (NSimple vecPar) lengthId Nothing) :-:+ (PrimLit "1")) ]))+ Nothing+ -- if null(vec) then res := vec else res := tail(vec) & head(vec)+ rotrExpr = IfSm (genExprFCall1 isnullId (PrimName $ NSimple vecPar))+ [NSimple resId := (PrimName $ NSimple vecPar)]+ []+ (Just $ Else [rotrExprRet])+ where rotrExprRet =+ NSimple resId :=+ (genExprFCall1 lastId (PrimName $ NSimple vecPar) :&:+ genExprFCall1 initId (PrimName $ NSimple vecPar))+ rotrRet = ReturnSm (Just $ PrimName $ NSimple resId)+ reverseSpec = Function reverseId [IfaceVarDec vecPar vectorTM] vectorTM+ reverseVar =+ VarDec resId+ (SubtypeIn vectorTM+ (Just $ IndexConstraint+ [ToRange (PrimLit "0")+ (PrimName (NAttribute $+ AttribName (NSimple vecPar) lengthId Nothing) :-:+ (PrimLit "1")) ]))+ Nothing+ -- for i in 0 to res'range loop+ -- res(vec'length-i-1) := vec(i);+ -- end loop;+ reverseFor =+ ForSM iId (AttribRange $ applyRangeAttrib resId) [reverseAssign]+ -- res(vec'length-i-1) := vec(i);+ reverseAssign = NIndexed (IndexedName (NSimple resId) [destExp]) :=+ (PrimName $ NIndexed (IndexedName (NSimple vecPar)+ [PrimName $ NSimple iId]))+ where destExp = PrimName (NAttribute $ AttribName (NSimple vecPar)+ lengthId Nothing) :-:+ PrimName (NSimple iId) :-:+ (PrimLit "1")+ -- return res;+ reverseRet = ReturnSm (Just $ PrimName (NSimple resId))+ copySpec = Function copyId [IfaceVarDec nPar naturalTM,+ IfaceVarDec aPar elemTM ] vectorTM+ -- variable res : fsvec_x (0 to n-1) := (others => a);+ copyVar =+ VarDec resId+ (SubtypeIn vectorTM+ (Just $ IndexConstraint+ [ToRange (PrimLit "0")+ ((PrimName (NSimple nPar)) :-:+ (PrimLit "1")) ]))+ (Just $ Aggregate [ElemAssoc (Just Others)+ (PrimName $ NSimple aPar)])+ -- return res+ copyExpr = ReturnSm (Just $ PrimName $ NSimple resId)+ plusgtSpec = Function plusgtId [IfaceVarDec aPar elemTM,+ IfaceVarDec vecPar vectorTM] vectorTM+ -- variable res : fsvec_x (0 to vec'length);+ plusgtVar =+ VarDec resId+ (SubtypeIn vectorTM+ (Just $ IndexConstraint+ [ToRange (PrimLit "0")+ (PrimName (NAttribute $+ AttribName (NSimple vecPar) lengthId Nothing))]))+ Nothing+ plusgtExpr = NSimple resId :=+ ((PrimName $ NSimple aPar) :&:+ (PrimName $ NSimple vecPar))+ plusgtRet = ReturnSm (Just $ PrimName $ NSimple resId)+ ltplusSpec = Function ltplusId [IfaceVarDec vecPar vectorTM,+ IfaceVarDec aPar elemTM] vectorTM+ -- variable res : fsvec_x (0 to vec'length);+ ltplusVar =+ VarDec resId+ (SubtypeIn vectorTM+ (Just $ IndexConstraint+ [ToRange (PrimLit "0")+ (PrimName (NAttribute $+ AttribName (NSimple vecPar) lengthId Nothing))]))+ Nothing+ ltplusExpr = NSimple resId :=+ ((PrimName $ NSimple vecPar) :&:+ (PrimName $ NSimple aPar))+ ltplusRet = ReturnSm (Just $ PrimName $ NSimple resId)+ plusplusSpec = Function plusplusId [IfaceVarDec vec1Par vectorTM,+ IfaceVarDec vec2Par vectorTM ]+ vectorTM+ -- variable res : fsvec_x (0 to vec1'length + vec2'length -1);+ plusplusVar =+ VarDec resId+ (SubtypeIn vectorTM+ (Just $ IndexConstraint+ [ToRange (PrimLit "0")+ (PrimName (NAttribute $+ AttribName (NSimple vec1Par) lengthId Nothing) :+:+ PrimName (NAttribute $+ AttribName (NSimple vec2Par) lengthId Nothing) :-:+ PrimLit "1")]))+ Nothing+ plusplusExpr = NSimple resId :=+ ((PrimName $ NSimple vec1Par) :&:+ (PrimName $ NSimple vec2Par))+ plusplusRet = ReturnSm (Just $ PrimName $ NSimple resId)+ singletonSpec = Function singletonId [IfaceVarDec aPar elemTM ]+ vectorTM+ -- variable res : fsvec_x (0 to 0) := (others => a);+ singletonVar =+ VarDec resId+ (SubtypeIn vectorTM+ (Just $ IndexConstraint+ [ToRange (PrimLit "0") (PrimLit "0")]))+ (Just $ Aggregate [ElemAssoc (Just Others)+ (PrimName $ NSimple aPar)])+ singletonRet = ReturnSm (Just $ PrimName $ NSimple resId)+ showSpec = Function showId [IfaceVarDec vecPar vectorTM] stringTM+ doShowId = unsafeVHDLBasicId "doshow"+ doShowDef = SubProgBody doShowSpec [] [doShowRet]+ where doShowSpec = Function doShowId [IfaceVarDec vecPar vectorTM]+ stringTM+ -- case vec'len is+ -- when 0 => return "";+ -- when 1 => return head(vec);+ -- when others => return show(head(vec)) & ',' &+ -- doshow (tail(vec));+ -- end case;+ doShowRet =+ CaseSm (PrimName (NAttribute $+ AttribName (NSimple vecPar) lengthId Nothing))+ [CaseSmAlt [ChoiceE $ PrimLit "0"]+ [ReturnSm (Just $ PrimLit "\"\"")],+ CaseSmAlt [ChoiceE $ PrimLit "1"]+ [ReturnSm (Just $+ genExprFCall1 showId+ (genExprFCall1 headId (PrimName $ NSimple vecPar)) )],+ CaseSmAlt [Others]+ [ReturnSm (Just $+ genExprFCall1 showId+ (genExprFCall1 headId (PrimName $ NSimple vecPar)) :&:+ PrimLit "','" :&:+ genExprFCall1 doShowId+ (genExprFCall1 tailId (PrimName $ NSimple vecPar)) ) ]]+ -- return '<' & doshow(vec) & '>';+ showRet = ReturnSm (Just $ PrimLit "'<'" :&:+ genExprFCall1 doShowId (PrimName $ NSimple vecPar) :&:+ PrimLit "'>'" )++ defaultSpec = Function defaultId [] vectorTM+ defaultExpr =+ ReturnSm (Just $ genExprFCall0 emptyId)+++-- | Generate the default functions for a custom tuple type+genTupleFuns :: [TypeMark] -- ^ type of each tuple element+ -> TypeMark -- ^ type of the tuple+ -> [SubProgBody]+genTupleFuns elemTMs tupleTM =+ [SubProgBody defaultSpec [] [defaultExpr],+ SubProgBody showSpec [] [showExpr]]+ where tupPar = unsafeVHDLBasicId "tup"+ defaultSpec = Function defaultId [] tupleTM+ defaultExpr =+ ReturnSm (Just $ Aggregate (replicate tupSize+ (ElemAssoc Nothing $ PrimName defaultSN)))+ showSpec = Function showId [IfaceVarDec tupPar tupleTM ] stringTM+ -- return '(' & show(tup.+ showExpr = ReturnSm (Just $+ PrimLit "'('" :&: showMiddle :&: PrimLit "')'")+ where showMiddle = foldr1 (\e1 e2 -> e1 :&: PrimLit "','" :&: e2) $+ map ((genExprFCall1 showId).+ PrimName .+ NSelected.+ (NSimple tupPar:.:).+ tupVHDLSuffix)+ [1..tupSize]+ tupSize = length elemTMs++-- | Generate the default functions for a custom abst_ext_ type+genAbstExtFuns :: TypeMark -- ^ type of the values nested in AbstExt+ -> TypeMark -- ^ type of the extended values+ -> [SubProgBody]+genAbstExtFuns elemTM absExtTM =+ [SubProgBody defaultSpec [] [defaultExpr],+ SubProgBody absentSpec [] [absentExpr] ,+ SubProgBody presentSpec [] [presentExpr],+ SubProgBody fromAbstExtSpec [] [fromAbstExtExpr],+ SubProgBody unsafeFromAbstExtSpec [] [unsafeFromAbstExtExpr],+ SubProgBody isPresentSpec [] [isPresentExpr],+ SubProgBody isAbsentSpec [] [isAbsentExpr],+ SubProgBody showSpec [] [showExpr] ]+ where defaultPar = unsafeVHDLBasicId "default"+ extPar = unsafeVHDLBasicId "extabst"+ defaultSpec = Function defaultId [] absExtTM+ defaultExpr =+ ReturnSm (Just $ PrimName $ NSimple absentId)+ absentSpec = Function absentId [] absExtTM+ absentExpr =+ ReturnSm (Just $ Aggregate+ [ElemAssoc Nothing falseExpr,+ ElemAssoc Nothing $ PrimName $ defaultSN ])+ presentSpec =+ Function presentId [IfaceVarDec extPar elemTM] absExtTM+ presentExpr =+ ReturnSm (Just $ Aggregate [ElemAssoc Nothing trueExpr,+ ElemAssoc Nothing $ PrimName $ NSimple extPar ])+ fromAbstExtSpec = Function fromAbstExtId [IfaceVarDec defaultPar elemTM,+ IfaceVarDec extPar absExtTM]+ elemTM+ fromAbstExtExpr =+ IfSm (PrimName $ NSelected (NSimple extPar :.: SSimple isPresentId))+ [ReturnSm (Just $ PrimName $+ (NSelected (NSimple extPar :.: SSimple valueId)))]+ []+ (Just $ Else+ [ReturnSm (Just $ PrimName $ NSimple defaultPar)])+ unsafeFromAbstExtSpec =+ Function unsafeFromAbstExtId [IfaceVarDec extPar absExtTM] elemTM+ unsafeFromAbstExtExpr =+ ReturnSm (Just $+ PrimName (NSelected (NSimple extPar :.: SSimple valueId)))+ isPresentSpec =+ Function isPresentId [IfaceVarDec extPar absExtTM] booleanTM+ isPresentExpr =+ ReturnSm (Just $+ PrimName (NSelected (NSimple extPar :.: SSimple isPresentId)))+ isAbsentSpec =+ Function isAbsentId [IfaceVarDec extPar absExtTM] booleanTM+ isAbsentExpr =+ ReturnSm (Just $+ Not $ PrimName (NSelected (NSimple extPar :.: SSimple isPresentId)))+ showSpec = Function showId [IfaceVarDec extPar absExtTM ] stringTM+ -- if extabs.isPresent+ -- return "Prst " & show(extabs.value);+ -- else+ -- return "Abst";+ -- end if;+ showExpr =+ IfSm (PrimName $ NSelected (NSimple extPar :.: SSimple isPresentId))+ [ReturnSm (Just $ PrimLit "\"Prst \"" :&:+ genExprFCall1 showId (PrimName $+ (NSelected (NSimple extPar :.: SSimple valueId))))]+ []+ (Just $ Else+ [ReturnSm (Just $ PrimLit "\"Abst\"")])++++-- | Generate the default functions for a custom enumeration type+genEnumAlgFuns :: TypeMark -- ^ enumeration type+ -> VHDLId -- ^ First enumeration literal of the type+ -> [SubProgBody]+genEnumAlgFuns enumTM firstLit =+ [SubProgBody defaultSpec [] [defaultExpr],+ SubProgBody showSpec [] [showExpr]]+ where enumPar = unsafeVHDLBasicId "enum"+ defaultSpec = Function defaultId [] enumTM+ defaultExpr = ReturnSm (Just $ PrimName (NSimple firstLit))+ showSpec = Function showId [IfaceVarDec enumPar enumTM] stringTM+ -- we slice the resulting image in order to eliminate the+ -- leading and trailing slashes+ --+ -- return enumTM'image(enum)(2 to enumTM'image(enum)'length-1);+ showExpr = ReturnSm (Just $ PrimName $ NSlice $ SliceName image+ (ToRange (PrimLit "2")+ ((PrimName $ NAttribute $+ AttribName image lengthId Nothing) :-:+ PrimLit "1")))+ where image = NAttribute$ AttribName (NSimple enumTM) imageId+ (Just $ PrimName $ NSimple enumPar)++-- | Apply the range attribute out of a simple name+applyRangeAttrib :: SimpleName -> AttribName+applyRangeAttrib sName = AttribName (NSimple sName) rangeId Nothing+
+ src/ForSyDe/Deep/Backend/VHDL/Ghdl.hs view
@@ -0,0 +1,213 @@+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.Backend.VHDL.Ghdl+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- Functions to process the VHDL compilation results with GHDL.+-----------------------------------------------------------------------------+module ForSyDe.Deep.Backend.VHDL.Ghdl (executeTestBenchGhdl) where++import ForSyDe.Deep.Backend.VHDL.Traverse.VHDLM+import ForSyDe.Deep.Backend.VHDL.TestBench++import ForSyDe.Deep.System.SysDef+import ForSyDe.Deep.OSharing+import ForSyDe.Deep.ForSyDeErr+import ForSyDe.Deep.Config (getDataDir)++import Data.Maybe (isJust)+import Control.Monad.State (gets)+import System.Directory (findExecutable,+ setCurrentDirectory,+ getTemporaryDirectory,+ createDirectoryIfMissing)+import System.Process (readProcessWithExitCode, readProcess, runProcess, waitForProcess)+import System.Exit (ExitCode(..))+import System.IO +import System.FilePath ((</>))+import qualified Language.Haskell.TH as TH (Exp)++--+-- This tool driver needs a Ghdl version containing the following commit,+-- otherwise it will fail with an obscure pattern match failure from within TH+-- generated code+--+-- commit f6d8e786a1ca3165b41cea7de05b8f2151ac31ff+-- Author: Tristan Gingold <tgingold@free.fr>+-- Date: Sat May 30 14:05:20 2015 +0200+--+-- write: do not implicitely append LF.+--+-- The oldest release containing this was v0.33 +--+data GhdlCommand = Analyze | Elaborate | Compile | Import | Run deriving Eq++instance Show GhdlCommand where+ show Analyze = "-a"+ show Elaborate = "-e"+ show Compile = "-c"+ show Import = "-i"+ show Run = "-r"++data GhdlEnv = GhdlEnv { sysId :: String+ , sysTb :: String+ , syslib :: String+ , tbFile :: FilePath+ , tbExecutable :: FilePath+ , libFile :: FilePath+ , workFiles :: [FilePath]+ , forsydeLibFile :: FilePath+ , forsydeLibDir :: FilePath+ , systemLibDir :: FilePath+ , workDir :: FilePath+ , paths :: [FilePath]+ }++mkGhdlEnv :: SysDefVal -> FilePath -> GhdlEnv+mkGhdlEnv sys osDataPath = + GhdlEnv+ { sysId = sysId+ , sysTb = sysTb+ , syslib = syslib+ , libFile = syslib </> (syslib ++ ".vhd")+ , tbFile = "test" </> (sysTb ++ ".vhd")+ , forsydeLibFile = osDataPath</>"lib"</>"forsyde.vhd"+ , workFiles = ("work" </> (sysId ++ ".vhd")) :+ map (("work"</>).(++".vhd").sid.readURef.unPrimSysDef)+ (subSys sys)+ , forsydeLibDir = forsydeLibDir+ , systemLibDir = systemLibDir+ , workDir = workDir+ , tbExecutable = workDir </> sysTb+ , paths = [forsydeLibDir, systemLibDir, workDir]+ }+ where+ sysId = sid sys+ syslib = sysId ++ "_lib"+ sysTb = sysId ++ "_tb"+ workDir = "work" </> "ghdl"+ forsydeLibDir = "forsyde" </> "ghdl"+ systemLibDir = syslib </> "ghdl"+++-- | Generate a testbench and execute it with GHDL+-- (Note: the initial and final CWD will be / )+executeTestBenchGhdl :: Maybe Int -- ^ Number of cycles to simulate + -> [[TH.Exp]] -- ^ input stimuli, each signal value+ -- is expressed as a template haskell+ -- expression + -> VHDLM [[String]] -- ^ results, each signal value+ -- is expressed as a string+executeTestBenchGhdl mCycles stimuli = do+ -- Check if GHDL is installed+ installed <- liftIO isGhdlInstalled+ unless installed (throwFError GhdlFailed) + + -- compile testbench+ cycles <- writeVHDLTestBench mCycles stimuli++ -- assemble all the file paths for compilation+ sysid <- gets (sid.globalSysDef.global)+ sys <- gets (globalSysDef.global)+ dataPath <- liftIO getDataDir+ let env = mkGhdlEnv sys dataPath++ -- set up directory structure and tmp files+ file <- liftIO $ do+ setCurrentDirectory (sysid </> "vhdl")+ tmpdir <- getTemporaryDirectory + (file, handle) <- openTempFile tmpdir "tb_out.txt"+ hClose handle -- close handle to avoid opening problems in windows+ mapM_ (createDirectoryIfMissing True) $ paths env+ return file++ -- analyze the installed forsyde library+ runGhdlCommand Analyze "forsyde" -- library (toplevel)+ (forsydeLibDir env) -- workdir+ [] -- include paths+ [forsydeLibFile env] -- files++ -- analyze the generated system library+ runGhdlCommand Analyze (syslib env) -- library (toplevel)+ (systemLibDir env) -- workdir+ [forsydeLibDir env] -- include paths+ [libFile env] -- files++ -- compile test bench hierarchy+ runGhdlCompile (sysTb env) -- toplevel+ (workDir env) -- workdir+ [forsydeLibDir env, systemLibDir env] -- include paths+ (tbFile env:workFiles env) -- files++ -- Run simulation and capture output+ testOutput <- runGhdlSim (sysTb env) cycles++ liftIO $ setCurrentDirectory (".." </> "..")+ --liftIO $ print testOutput -- show what we actually get from Ghdl++ parseTestBenchOut testOutput++runGhdlCompile :: String -> FilePath -> [FilePath] -> [FilePath] -> VHDLM ()+runGhdlCompile toplevel workdir libPaths files = + runGhdlCommandInWorkdir Compile "work" workdir libPaths files extra+ where+ extra = [show Elaborate,+ toplevel]+++runGhdlCommand :: GhdlCommand + -> String -> FilePath -> [FilePath] -> [FilePath] + -> VHDLM ()+runGhdlCommand cmd lib work paths files = + runGhdlCommandInWorkdir cmd lib work paths files []++runGhdlCommandInWorkdir :: GhdlCommand + -> String -> FilePath -> [FilePath] -> [FilePath] + -> [String] + -> VHDLM ()+runGhdlCommandInWorkdir command libName workdir libPaths files extraOpts =+ runCommand "ghdl" $ cmd ++ paths ++ opts ++ files ++ extraOpts+ where+ cmd = [show command]+ paths = map ("-P"++) libPaths+ opts = ["--work="++libName, + "--workdir="++workdir]++runGhdlSim :: String -> Int -> VHDLM String+runGhdlSim toplevel cycles = do+ (output,success) <- liftIO $ do + putStrLn $ "Running: ghdl " ++ unwords args+ (exitcode,stdout,stderr) <- readProcessWithExitCode "ghdl" args stdin+ let success = exitcode == ExitSuccess+ return (stdout,success)+ unless success (throwFError GhdlFailed)+ return output+ where+ stdin = ""+ args = [show Run, toplevel, "--stop-time="++show (cycles*10)++"ns"]+++-- | run a shell command+runCommand :: String -- ^ Command to execute + -> [String] -- ^ Command arguments+ -> VHDLM ()+runCommand command args = do+ success <- liftIO $ do+ putStrLn msg + h <- runProcess command args Nothing Nothing Nothing Nothing Nothing+ code <- waitForProcess h+ return $ code == ExitSuccess + unless success (throwFError GhdlFailed)+ where msg = "Running: " ++ command ++ " " ++ unwords args+++-- Look for GHDL executables+isGhdlInstalled :: IO Bool+isGhdlInstalled = executablePresent "ghdl"+ where executablePresent = liftM isJust .findExecutable+
+ src/ForSyDe/Deep/Backend/VHDL/GlobalNameTable.hs view
@@ -0,0 +1,112 @@+{-# LANGUAGE TemplateHaskell #-}+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.Backend.VHDL.GlobalNameTable+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- Global tranlsation table (symbol table) from Template Haskell Names to VHDL +-- expressions+--+-----------------------------------------------------------------------------+module ForSyDe.Deep.Backend.VHDL.GlobalNameTable (globalNameTable) where++import Data.Bits+import Language.Haskell.TH+++import ForSyDe.Deep.AbsentExt+import qualified ForSyDe.Deep.Bit as B (not)+import ForSyDe.Deep.Bit hiding (not)+import qualified ForSyDe.Deep.Backend.VHDL.AST as VHDL+import ForSyDe.Deep.Backend.VHDL.AST+import ForSyDe.Deep.Backend.VHDL.Constants+import ForSyDe.Deep.Backend.VHDL.Generate+import qualified Data.Param.FSVec as V++-- | Global tranlsation table from Template Haskell function and+-- constructor Names to VHDL expressions. The table works like this+--+-- (function, constant constructor name, (arity, function to obtain a+-- VHDL expression+-- provided its+-- arguments are already+-- translated to VHDL ))+globalNameTable :: [(Name, (Int, [VHDL.Expr] -> VHDL.Expr ) )]+globalNameTable = [+-- Unary constructors+ ('Prst , (1, genExprFCall1L presentId ) ),+-- Constant constructors+ ('True , (0, genZeroConsCall trueExpr ) ),+ ('False , (0, genZeroConsCall falseExpr ) ),+ ('H , (0, genZeroConsCall highExpr ) ),+ ('L , (0, genZeroConsCall lowExpr ) ),+ ('Abst , (0, genZeroConsCall (PrimName $ NSimple absentId) ) ),+-- Quaternary functions+ ('V.select , (4, genExprFCall4L selId ) ),+-- Binary functions+ ('(&&) , (2, genBinOpCall And ) ),+ ('(||) , (2, genBinOpCall Or ) ),+ ('(.&.) , (2, genBinOpCall And ) ),+ ('(.|.) , (2, genBinOpCall Or ) ),+ ('xor , (2, genBinOpCall Xor ) ),+ ('(==) , (2, genBinOpCall (:=:) ) ),+ ('(/=) , (2, genBinOpCall (:/=:) ) ),+ ('(<) , (2, genBinOpCall (:<:) ) ), + ('(<=) , (2, genBinOpCall (:<=:) ) ),+ ('(>) , (2, genBinOpCall (:>:) ) ),+ ('(>=) , (2, genBinOpCall (:>=:) ) ), + ('(+) , (2, genBinOpCall (:+:) ) ),+ ('(-) , (2, genBinOpCall (:-:) ) ),+ ('(*) , (2, genBinOpCall (:*:) ) ),+ ('div , (2, genBinOpCall (:/:) ) ),+ ('mod , (2, genBinOpCall Mod ) ),+ ('rem , (2, genBinOpCall Rem ) ),+ ('(^) , (2, genBinOpCall (:**:) ) ),+ ('(V.+>) , (2, genExprFCall2L plusgtId ) ),+ ('(V.<+) , (2, genExprFCall2L ltplusId ) ),+ ('(V.++) , (2, genExprFCall2L plusplusId ) ),+ ('(V.!) , (2, genExprFCall2L exId ) ),+ ('V.take , (2, genExprFCall2L takeId ) ),+ ('V.drop , (2, genExprFCall2L dropId ) ),+ ('V.shiftl , (2, genExprFCall2L shiftlId ) ),+ ('V.shiftr , (2, genExprFCall2L shiftrId ) ),+ ('V.copy , (2, genExprFCall2L copyId ) ),+ ('fromAbstExt , (2, genExprFCall2L fromAbstExtId ) ),+ ('fixmul8 , (2, genExprFCall2L fixmul8Id ) ),+-- unary functions+ ('B.not , (1, genUnOpCall Not ) ),+ ('not , (1, genUnOpCall Not ) ),+ ('negate , (1, genUnOpCall Neg ) ),+ ('abs , (1, genUnOpCall Abs ) ),+ ('abstExt , (1, genExprFCall1L presentId ) ),+ ('isAbsent , (1, genExprFCall1L isAbsentId ) ),+ ('isPresent , (1, genExprFCall1L isPresentId ) ),+ ('unsafeFromAbstExt, (1, genExprFCall1L unsafeFromAbstExtId ) ),+ ('V.singleton , (1, genExprFCall1L singletonId ) ),+ ('V.length , (1, genExprFCall1L lengthId ) ),+ ('V.lengthT , (1, genExprFCall1L lengthId ) ),+ ('V.genericLength , (1, genExprFCall1L lengthId ) ),+ ('V.null , (1, genExprFCall1L isnullId ) ),+ ('V.head , (1, genExprFCall1L headId ) ),+ ('V.last , (1, genExprFCall1L lastId ) ),+ ('V.init , (1, genExprFCall1L initId ) ),+ ('V.tail , (1, genExprFCall1L tailId ) ),+ ('V.rotl , (1, genExprFCall1L rotlId ) ),+ ('V.rotr , (1, genExprFCall1L rotrId ) ),+ ('V.reverse , (1, genExprFCall1L reverseId ) ),+ ('toBitVector8 , (1, genExprFCall1L toBitVector8Id ) ),+ ('toBitVector16 , (1, genExprFCall1L toBitVector16Id ) ),+ ('toBitVector32 , (1, genExprFCall1L toBitVector32Id ) ),+ ('fromBitVector8 , (1, genExprFCall1L fromBitVector8Id ) ),+ ('fromBitVector16 , (1, genExprFCall1L fromBitVector16Id ) ),+ ('fromBitVector32 , (1, genExprFCall1L fromBitVector32Id ) ),+-- constants+ ('V.empty , (0, genExprFCall0L emptyId ) )]+ where genBinOpCall op = \[e1, e2] -> e1 `op` e2+ genUnOpCall op = \[e] -> op e+ genZeroConsCall cons = \[] -> cons
+ src/ForSyDe/Deep/Backend/VHDL/GlobalNameTable.hs-boot view
@@ -0,0 +1,7 @@+module ForSyDe.Deep.Backend.VHDL.GlobalNameTable (globalNameTable) where+++import Language.Haskell.TH+import qualified ForSyDe.Deep.Backend.VHDL.AST as VHDL++globalNameTable :: [(Name, (Int, [VHDL.Expr] -> VHDL.Expr ) )]
+ src/ForSyDe/Deep/Backend/VHDL/Modelsim.hs view
@@ -0,0 +1,169 @@+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.Backend.VHDL.Modelsim+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- Functions to process the VHDL compilation results with Modelsim.+-----------------------------------------------------------------------------+module ForSyDe.Deep.Backend.VHDL.Modelsim (compileResultsModelsim,+ executeTestBenchModelsim) where++import ForSyDe.Deep.Backend.VHDL.Traverse.VHDLM+import ForSyDe.Deep.Backend.VHDL.TestBench++import ForSyDe.Deep.System.SysDef+import ForSyDe.Deep.OSharing+import ForSyDe.Deep.ForSyDeErr+import ForSyDe.Deep.Config (getDataDir)++import Data.List (intersperse)+import System.Directory (setCurrentDirectory)+import Control.Monad (liftM, when)+import Control.Monad.State (gets)+import System.Directory (findExecutable, getTemporaryDirectory)+import System.Process (runProcess, waitForProcess)+import System.Exit (ExitCode(..))+import System.IO +import System.FilePath ((</>))+import qualified Language.Haskell.TH as TH (Exp)++-- | Generate a testbench and execute it with Modelsim+-- (Note: the initial and final CWD will be / )+executeTestBenchModelsim :: Maybe Int -- ^ Number of cycles to simulate + -> [[TH.Exp]] -- ^ input stimuli, each signal value+ -- is expressed as a template haskell+ -- expression + -> VHDLM [[String]] -- ^ results, each signal value+ -- is expressed as a string+executeTestBenchModelsim mCycles stimuli= do+ cycles <- writeVHDLTestBench mCycles stimuli+ sysid <- gets (sid.globalSysDef.global)+ -- change to sysid/vhdl/+ liftIO $ setCurrentDirectory (sysid </> "vhdl")+ -- compile the testbench with modelsim+ run_vcom ["-93", "-quiet", "-nologo", "-work", "work", + "test" </> (sysid ++ "_tb.vhd")] + -- execute the testbench and capture the results+ tmpdir <- liftIO getTemporaryDirectory + (file, handle) <- liftIO $ openTempFile tmpdir "tb_out.txt"+ -- we close the temporal file to avoid opening problems with vsim on windows+ liftIO $ hClose handle + run_vsim ["-c", "-std_output", file, "-quiet", + "-do", "run " ++ show (cycles*10) ++ " ns; exit",+ "work." ++ sysid ++ "_tb"]+ handle2 <- liftIO $ openFile file ReadMode+ flatOut <- liftIO $ hGetContents handle2+ -- go back to the original directory+ liftIO $ setCurrentDirectory (".." </> "..")+ parseTestBenchOut flatOut++-- | Compile the generated VHDL code with Modelsim+-- (Note: the initial and final CWD will be /systemName/vhdl )+compileResultsModelsim :: VHDLM ()+compileResultsModelsim = do+ -- Check if modelsim is installed+ installed <- liftIO $ isModelsimInstalled+ when (not installed) (throwFError ModelsimFailed) + -- get the name of the vhdl files to compile+ sys <- gets (globalSysDef.global)+ let sysId = sid sys+ syslib = sysId ++ "_lib"+ libFile = syslib </> (syslib ++ ".vhd")+ workFiles = ("work" </> (sysId ++ ".vhd")) :+ map (("work"</>).(++".vhd").sid.readURef.unPrimSysDef)+ (subSys sys)+ -- get the modelsim directory of ForSyDe's vhdl library+ -- (generated during installation)+ dataPath <- liftIO $ getDataDir++ -- map the directory to the logical name forsyde+ let modelsimForSyDe = dataPath </> "lib" </> "modelsim"+ run_vmap ["forsyde", modelsimForSyDe]+ + -- compile the library of current model+ let modelsimLib = syslib </> "modelsim"+ run_vlib [modelsimLib]+ run_vcom ["-93", "-quiet", "-nologo", "-work", modelsimLib, libFile]+ -- map the directory of the library to its logical name+ run_vmap [syslib, modelsimLib]+ + -- compile the work files+ -- NOTE: Since vcom doesn't figure out+ -- the compilation order (according to compoment dependencies),+ -- we first compile the entities and then the architectures.+ -- Another option would be keeping a dependency tree in SysDef+ -- and execute vcom using a depth search (currently we keep+ -- all the subsystems in a flatenned list) + let modelsimWork = "work" </> "modelsim"+ run_vlib [modelsimWork]+ run_vcom ("-93" : "-quiet" : "-nologo" : "-work" : modelsimWork : + "-just" : "e" : workFiles)+ run_vcom ("-93" : "-quiet" : "-nologo" : "-work" : modelsimWork : + "-just" : "a" : workFiles)+ -- map the directory work library to its logical name+ run_vmap ["work", modelsimWork]+ ++++-- | Run vlib+run_vlib :: [String] -- ^ arguments+ -> VHDLM ()+run_vlib = runModelsimCommand "vlib"++-- | Run vmap+run_vmap :: [String] -- ^ arguments+ -> VHDLM ()+run_vmap = runModelsimCommand "vmap"+++-- | Run vmap+run_vcom :: [String] -- ^ arguments+ -> VHDLM ()+run_vcom = runModelsimCommand "vcom"++-- | Run vsim+run_vsim :: [String] -- ^ arguments+ -> VHDLM ()+run_vsim = runModelsimCommand "vsim"++-- | run a ModelSim command+runModelsimCommand :: String -- ^ Command to execute + -> [String] -- ^ Command arguments+ -> VHDLM ()+runModelsimCommand command args = do+ success <- liftIO $ runWait msg command args+ when (not success) (throwFError ModelsimFailed)+ where msg = "Running: " ++ command ++ " " ++ (concat $ intersperse " " args)+++-- | Run a process, previously announcing a message and waiting for it+-- to finnish its execution.+runWait :: String -- ^ message to show+ -> FilePath -- ^ command to execute + -> [String] -- ^ command arguments+ -> IO Bool -- ^ Did the execution end succesfully?+runWait msg proc args = do+ putStrLn msg + h <- runProcess proc args Nothing Nothing Nothing Nothing Nothing+ code <- waitForProcess h+ return $ code == ExitSuccess +++-- Look for modelsim executables+isModelsimInstalled :: IO Bool+isModelsimInstalled = executablePresent "vlib" <&&> + executablePresent "vmap" <&&> + executablePresent "vcom" <&&>+ executablePresent "vsim"+ where executablePresent = (liftM (maybe False (\_-> True))) .findExecutable++-- | short-circuit and for monads+(<&&>) :: Monad m => m Bool -> m Bool -> m Bool+x <&&> y = do p <- x+ if p then y else return False
+ src/ForSyDe/Deep/Backend/VHDL/Ppr.hs view
@@ -0,0 +1,481 @@+{-# LANGUAGE FlexibleInstances #-}+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.Backend.VHDL.Ppr+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : non-portable (Template Haskell)+--+-- VHDL pretty-printing instances.+--+-----------------------------------------------------------------------------+++-- FIXME: we could avoid the LANGUAGE extensions by adding ppr_list to the Ppr +-- class (see Language.Haskell.TH.Ppr)++module ForSyDe.Deep.Backend.VHDL.Ppr () where++import ForSyDe.Deep.Backend.Ppr++import ForSyDe.Deep.Backend.VHDL.AST++import Text.PrettyPrint.HughesPJ hiding (Mode)+++-- | Number of spaces used for indentation+nestVal :: Int+nestVal = 5++instance Ppr a => Ppr (Maybe a) where+ ppr (Just a) = ppr a+ ppr Nothing = empty ++instance (Ppr a, Ppr b) => Ppr (a,b) where+ ppr (a,b) = ppr a <+> ppr b++instance Ppr VHDLId where+ ppr = text.fromVHDLId+ ++instance Ppr DesignFile where+ ppr (DesignFile cx lx) = vNSpaces spaces (ppr_list ($+$) cx) + (ppr_list (vNSpaces spaces) lx) $+$+ vSpace+ where spaces = 2+++ +instance Ppr ContextItem where+ ppr (Library id) = text "library" <+> ppr id <> semi+ ppr (Use name) = text "use" <+> ppr name <> semi+ ++instance Ppr LibraryUnit where+ ppr (LUEntity entityDec) = ppr entityDec+ ppr (LUArch archBody) = ppr archBody+ ppr (LUPackageDec packageDec) = ppr packageDec + ppr (LUPackageBody packageBody) = ppr packageBody++instance Ppr EntityDec where+ ppr (EntityDec id ifaceSigDecs) = + text "entity" <+> idDoc <+> text "is" $+$+ nest nestVal (ppr ifaceSigDecs) $+$+ text "end entity" <+> idDoc <> semi+ where idDoc = ppr id ++instance Ppr [IfaceSigDec] where+-- FIXME: improve pretty printing of the+-- declarations+-- This:+-- S1 : bit+-- Ssdds : bit+-- should really be printed as+-- S1 : bit+-- Ssdds : bit+ ppr [] = empty+ ppr decs = text "port" <+> (parens (ppr_list vSemi decs) <> semi)+++instance Ppr IfaceSigDec where+ ppr (IfaceSigDec id mode typemark) = + ppr id <+> colon <+> ppr mode <+> ppr typemark+++instance Ppr Mode where+ ppr In = text "in"+ ppr Out = text "out"++instance Ppr ArchBody where+ ppr (ArchBody id enName decs conSms) = + text "architecture" <+> idDoc <+> text "of" <+> ppr enName <+> text "is" $+$+ nest nestVal (ppr decs) $+$+ text "begin" $+$+ nest nestVal (ppr_list (vNSpaces 1) conSms) $+$+ text "end architecture" <+> idDoc <> semi+ where idDoc = ppr id++instance Ppr PackageDec where+ ppr (PackageDec id decs) =+ text "package" <+> idDoc <+> text "is" $+$+ vSpace $++$+ nest nestVal (ppr_list (vNSpaces 1) decs) $++$+ vSpace $+$+ text "end package" <+> idDoc <> semi+ where idDoc = ppr id ++instance Ppr PackageDecItem where+ ppr (PDITD typeDec) = ppr typeDec+ ppr (PDISD subtypeDec) = ppr subtypeDec+ ppr (PDISS subProgSpec) = ppr subProgSpec <> semi++instance Ppr PackageBody where+ ppr (PackageBody _ []) = text "" -- skip empty body+ ppr (PackageBody id decs) =+ text "package body" <+> idDoc <+> text "is" $+$+ vSpace $++$+ nest nestVal (ppr_list (vNSpaces 1) decs) $++$+ vSpace $+$+ text "end package body" <+> idDoc <> semi+ where idDoc = ppr id ++instance Ppr SubtypeDec where+ ppr (SubtypeDec id si) = text "subtype" <+> ppr id <+> text "is" <+> ppr si <> semi++instance Ppr SubtypeIn where+ ppr (SubtypeIn tm mCons) = ppr tm <+> (ppr mCons)+++instance Ppr Range where+ ppr (AttribRange an) = ppr an+ ppr (ToRange exp1 exp2) = ppr exp1 <+> text "to" <+> ppr exp2+++instance Ppr IndexConstraint where+ ppr (IndexConstraint dr) = parens (ppr_list hComma dr)+ ++instance Ppr TypeDec where+ ppr (TypeDec id typeDef) = + hang (text "type" <+> ppr id <+> text "is") nestVal (ppr typeDef <> semi)++instance Ppr TypeDef where+ ppr (TDA arrayTD) = ppr arrayTD+ ppr (TDR recordTD) = ppr recordTD+ ppr (TDE enumTD) = ppr enumTD++instance Ppr ArrayTypeDef where+ ppr (UnconsArrayDef unconsIxs elemsTM) = + text "array" <+> parens (ppr_list hComma $ map pprUnconsRange unconsIxs) <+>+ text "of" <+> ppr elemsTM+ where pprUnconsRange tm = ppr tm <+> text "range <>"++ ppr (ConsArrayDef consIxs elemsTM) = + text "array" <+> ppr consIxs <+> text "of" <+> ppr elemsTM ++instance Ppr RecordTypeDef where+ ppr (RecordTypeDef elementDecs) =+ text "record" <+> (ppr_list ($+$) elementDecs) $+$ + text "end record"++instance Ppr ElementDec where+ ppr (ElementDec id tm) = ppr id <+> colon <+> ppr tm <> semi ++instance Ppr EnumTypeDef where+ ppr (EnumTypeDef ids) = lparen <> ppr_list hComma ids <> rparen++instance Ppr VHDLName where+ ppr (NSimple simple) = ppr simple+ ppr (NSelected selected) = ppr selected+ ppr (NIndexed indexed) = ppr indexed+ ppr (NSlice slice) = ppr slice + ppr (NAttribute attrib) = ppr attrib++instance Ppr SelectedName where+ ppr (prefix :.: suffix) = ppr prefix <> dot <> ppr suffix++instance Ppr Suffix where+ ppr (SSimple simpleName) = ppr simpleName+ ppr All = text "all"++instance Ppr IndexedName where+ ppr (IndexedName prefix indexList) = + ppr prefix <> parens ( ppr_list hComma indexList )++instance Ppr SliceName where+ ppr (SliceName prefix discreteRange) = ppr prefix <> parens (ppr discreteRange)++instance Ppr AttribName where+ ppr (AttribName prefix simpleName mExpr) = + ppr prefix <> char '\'' <> ppr simpleName <> parensNonEmpty (ppr mExpr)++instance Ppr [BlockDecItem] where+ ppr = ppr_list ($+$)++instance Ppr BlockDecItem where+ ppr (BDISPB subProgBody) = ppr subProgBody+ ppr (BDISD sigDec) = ppr sigDec+++instance Ppr SubProgBody where+ ppr (SubProgBody subProgSpec decItems statements) = + ppr subProgSpec <+> text "is" $+$+ nest nestVal (ppr_list ($+$) decItems) $+$+ text "begin" $+$+ nest nestVal (ppr_list ($+$) statements) $+$+-- TODO, show the id here+ text "end" <> semi+++instance Ppr SubProgDecItem where+ ppr (SPVD vd) = ppr vd+ ppr (SPSB sb) = ppr sb++instance Ppr VarDec where+ ppr (VarDec id st mExpr) = + text "variable" <+> ppr id <+> colon <+> ppr st <+>+ (text ":=" <++> ppr mExpr) <> semi++instance Ppr SubProgSpec where+-- FIXME: improve pretty printing of the+-- declarations+-- This:+-- (S1 : bit;+-- Ssdds : bit)+-- should really be printed as+-- (S1 : bit;+-- Ssdds : bit)+ ppr (Function name decList returnType) =+ text "function" <+> ppr name <+> (parensNonEmpty (ppr_decs decList) $+$+ text "return" <+> ppr returnType)+ where ppr_decs ds = ppr_list vSemi ds++ ++instance Ppr IfaceVarDec where+ ppr (IfaceVarDec id typemark) = ppr id <+> colon <+> ppr typemark++instance Ppr SeqSm where+ ppr (IfSm cond sms elseIfs maybeElse) = + text "if" <+> ppr cond <+> text "then" $+$+ nest nestVal (ppr sms) $+$+ ppr elseIfs $+$+ ppr maybeElse $+$+ text "end if" <> semi+ ppr (CaseSm patern alts) = text "case" <+> ppr patern <+> text "is" $+$+ nest nestVal (ppr alts) $+$+ text "end case" <> semi+ ppr (ReturnSm maybeExpr) = text "return" <+> ppr maybeExpr <> semi+ ppr (ForSM id dr sms) = + text "for" <+> ppr id <+> text "in" <+> ppr dr <+> text "loop" $+$+ nest nestVal (ppr sms) $+$+ text "end loop" <> semi+ ppr (target := orig) = ppr target <+> text ":=" <+> ppr orig <> semi+ ppr (WaitFor expr) = text "wait for" <+> ppr expr <> semi+ ppr (targ `SigAssign` orig) = ppr targ <+> text "<=" <+> ppr orig <> semi+ ppr (ProcCall name assocs) = + ppr name <> parensNonEmpty (commaSep assocs) <> semi++instance Ppr [SeqSm] where+ ppr = ppr_list ($+$)++instance Ppr [ElseIf] where+ ppr = ppr_list ($+$)++instance Ppr ElseIf where+ ppr (ElseIf cond sms) = text "elseif" <+> ppr cond <+> text "then" $+$+ nest nestVal (ppr sms)++instance Ppr Else where+ ppr (Else sms) = text "else" $+$+ nest nestVal (ppr sms)+++instance Ppr [CaseSmAlt] where+ ppr = ppr_list ($+$)++instance Ppr Choice where+ ppr (ChoiceE expr) = ppr expr+ ppr Others = text "others" ++instance Ppr CaseSmAlt where+ ppr (CaseSmAlt alts sms) = + text "when" <+> ppr_list joinAlts alts <+> text "=>" $+$+ nest nestVal (ppr sms)+ where joinAlts a1 a2 = a1 <+> char '|' <+> a2++instance Ppr SigDec where+ ppr (SigDec id typemark mInit) = + text "signal" <+> ppr id <+> colon <+> ppr typemark <+> + (text ":=" <++> ppr mInit) <> semi++instance Ppr [ConcSm] where+ ppr = ppr_list ($$)+ +instance Ppr ConcSm where+ ppr (CSBSm blockSm) = ppr blockSm+ ppr (CSSASm conSigAssignSm) = ppr conSigAssignSm+ ppr (CSISm compInsSm) = ppr compInsSm+ ppr (CSPSm procSm) = ppr procSm++instance Ppr BlockSm where+ ppr (BlockSm label ifaceSigDecs pMapAspect blockDecItems concSms) =+ labelDoc <+> colon <+> text "block" $+$+ nest nestVal (ppr ifaceSigDecs) $+$+ nest nestVal (ppr pMapAspect) <> semi $+$+ nest nestVal (ppr blockDecItems) $+$+ text "begin" $+$+ nest nestVal (ppr concSms) $+$+ text "end block" <+> labelDoc <> semi+ where labelDoc = ppr label+ ++instance Ppr PMapAspect where+ ppr (PMapAspect assocs) = text "port map" $$ + (nest identL $ parens (ppr_list vComma assocs))+ where identL = length "port map "+ ++instance Ppr AssocElem where+ ppr (formalPart :=>: actualPart) = formalPartDoc <+> ppr actualPart+ where formalPartDoc = maybe empty (\fp -> ppr fp <+> text "=>") formalPart++instance Ppr ActualDesig where+ ppr (ADName name) = ppr name+ ppr (ADExpr expr) = ppr expr+ ppr Open = text "open"++instance Ppr ConSigAssignSm where+ ppr (target :<==: cWforms) = ppr target <+> text "<=" <+> (ppr cWforms <> semi)+++instance Ppr ConWforms where+ ppr (ConWforms whenElses wform lastElse) = ppr_list ($+$) whenElses $+$+ ppr wform <+> ppr lastElse++instance Ppr WhenElse where+ ppr (WhenElse wform expr) = ppr wform <+> text "when" <+> + ppr expr <+> text "else"++instance Ppr When where+ ppr (When cond) = text "when" <+> ppr cond++instance Ppr Wform where+ ppr (Wform elems) = ppr_list vComma elems+ ppr Unaffected = text "unaffected"++instance Ppr WformElem where+ ppr (WformElem exp mAfter) = ppr exp <+> (text "after" <++> ppr mAfter)+++instance Ppr CompInsSm where+ ppr (CompInsSm label insUnit assocElems) =+ ppr label <+> colon <+> (ppr insUnit $+$+ nest nestVal (ppr assocElems) <> semi)++instance Ppr ProcSm where+ ppr (ProcSm label sensl seqSms) =+ ppr label <+> colon <+> text "process" <+> + parensNonEmpty (ppr_list hComma sensl) $+$+ text "begin" $+$+ nest nestVal (ppr seqSms) $+$+ text "end process" <+> labelDoc <> semi+ where labelDoc = ppr label++instance Ppr InsUnit where+ ppr (IUEntity name) = text "entity" <+> ppr name++-- FIXME, remove parenthesis according to precedence+instance Ppr Expr where+ ppr = pprExprPrec 0 ""+++-- | Prettyprint an binary infix operator +pprExprPrecInfix :: Int -- ^ Accumulated precedence value (initialized to 0)+ -> String -- ^ Enclosing operator (initialized to \"\")+ -> Int -- ^ Precedence of current infix operator + -> Expr -- ^ lhs expression+ -> String -- ^ operator name+ -> Expr -- ^ rhs expression+ -> Doc+-- To avoid priting parenthesis based on the left+-- associativity of all operators within the same precedence class, +-- the precedence passed to the left branch is curr instead of+-- curr+1.+--+-- However, the VHDL grammar sometimes doesn't allow it +-- e.g. "and a or b and d" is illegal. In fact,+-- you can only put two operators together without parenthesis+-- in some cases:+-- +-- * logical operators: "and", "or", "xor", "xnor", but cannot be mixed up.+-- * relational operators: none+-- * shift operators: none+-- * adding operators: all, can be mixed up+-- * multiplying operator: all, can be mixed up+-- * misc operator: none+--+-- Thus, we keep track of the enclosing+-- operator, and skip parenthesis according to left parsing associativity+-- in the cases stated above.+--+-- Note that we are only making use of syntactic associativity. e.g.+-- even if "+" is semantially associative: a+(b+c)=(a+b)+c,+-- we will only skip parenthesis in (a+b)+c++pprExprPrecInfix ac encop curr lhs op rhs = + parensIf (ac>curr || (ac == curr && not skipParenSameLevel)) $+ sep [pprExprPrec curr op lhs <+> text op, pprExprPrec (curr+1) op rhs]+ where skipParenSameLevel = curr == plusPrec || curr == multPrec ||+ (encop == op && op `elem` ["and","or","xor","xnor"])++-- | Prettyprint unary prefix operators+pprExprPrecPrefix :: Int -- ^ Accumulated precedence value (initialized to 0)+ -> Int -- ^ Precedence of current infix operator+ -> String -- ^ operator name+ -> Expr -- ^ operator argument+ -> Doc+pprExprPrecPrefix ac curr op arg = parensIf (ac>curr) $+ text op <+> pprExprPrec (curr+1) op arg+++-- | Prints an expression taking precedence and left associativity+-- in account+pprExprPrec :: Int -- ^ Accumulated precedence value (initialized to 0)+ -> String -- ^ Enclosing operator+ -> Expr -- ^ Expression curently prettyprinted + -> Doc+-- Logical operations+pprExprPrec p e (And e1 e2) = pprExprPrecInfix p e logicalPrec e1 "and" e2+pprExprPrec p e (Or e1 e2) = pprExprPrecInfix p e logicalPrec e1 "or" e2+pprExprPrec p e (Xor e1 e2) = pprExprPrecInfix p e logicalPrec e1 "xor" e2+pprExprPrec p e (Nand e1 e2) = pprExprPrecInfix p e logicalPrec e1 "nand" e2+pprExprPrec p e (Nor e1 e2) = pprExprPrecInfix p e logicalPrec e1 "nor" e2+pprExprPrec p e (Xnor e1 e2) = pprExprPrecInfix p e logicalPrec e1 "xnor" e2+-- Relational Operators+pprExprPrec p e (e1 :=: e2) = pprExprPrecInfix p e relationalPrec e1 "=" e2+pprExprPrec p e (e1 :/=: e2) = pprExprPrecInfix p e relationalPrec e1 "/=" e2+pprExprPrec p e (e1 :<: e2) = pprExprPrecInfix p e relationalPrec e1 "<" e2+pprExprPrec p e (e1 :<=: e2) = pprExprPrecInfix p e relationalPrec e1 "<=" e2+pprExprPrec p e (e1 :>: e2) = pprExprPrecInfix p e relationalPrec e1 ">" e2+pprExprPrec p e (e1 :>=: e2) = pprExprPrecInfix p e relationalPrec e1 ">=" e2+-- Shift Operators+pprExprPrec p e (Sll e1 e2) = pprExprPrecInfix p e shiftPrec e1 "sll" e2+pprExprPrec p e (Srl e1 e2) = pprExprPrecInfix p e shiftPrec e1 "srl" e2+pprExprPrec p e (Sla e1 e2) = pprExprPrecInfix p e shiftPrec e1 "sla" e2+pprExprPrec p e (Sra e1 e2) = pprExprPrecInfix p e shiftPrec e1 "sra" e2+pprExprPrec p e (Rol e1 e2) = pprExprPrecInfix p e shiftPrec e1 "rol" e2+pprExprPrec p e (Ror e1 e2) = pprExprPrecInfix p e shiftPrec e1 "ror" e2+-- Adding Operators+pprExprPrec p e (e1 :+: e2) = pprExprPrecInfix p e plusPrec e1 "+" e2+pprExprPrec p e (e1 :-: e2) = pprExprPrecInfix p e plusPrec e1 "-" e2+pprExprPrec p e (e1 :&: e2) = pprExprPrecInfix p e plusPrec e1 "&" e2+-- Sign Operators+pprExprPrec p _ (Neg e) = pprExprPrecPrefix p signPrec "-" e +pprExprPrec p _ (Pos e) = pprExprPrecPrefix p signPrec "+" e +-- Multiplying Operators+pprExprPrec p e (e1 :*: e2) = pprExprPrecInfix p e multPrec e1 "*" e2+pprExprPrec p e (e1 :/: e2) = pprExprPrecInfix p e multPrec e1 "/" e2+pprExprPrec p e (Mod e1 e2) = pprExprPrecInfix p e multPrec e1 "mod" e2+pprExprPrec p e (Rem e1 e2) = pprExprPrecInfix p e multPrec e1 "rem" e2+-- Miscellaneous Operators+pprExprPrec p e (e1 :**: e2) = pprExprPrecInfix p e miscPrec e1 "**" e2+pprExprPrec p _ (Abs e) = pprExprPrecPrefix p signPrec "abs" e +pprExprPrec p _ (Not e) = pprExprPrecPrefix p signPrec "not" e +-- Primary expressions+pprExprPrec _ _ (PrimName name) = ppr name+pprExprPrec _ _ (PrimLit lit) = text lit+pprExprPrec _ _ (PrimFCall fCall) = ppr fCall+-- Composite-type expressions+pprExprPrec _ _ (Aggregate assocs) = parens (ppr_list hComma assocs)++instance Ppr ElemAssoc where+ ppr (ElemAssoc mChoice expr) = (ppr mChoice <++> text "=>") <+> ppr expr++instance Ppr FCall where+ ppr (FCall name assocs) = + ppr name <> parensNonEmpty (commaSep assocs)+
+ src/ForSyDe/Deep/Backend/VHDL/Quartus.hs view
@@ -0,0 +1,115 @@+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.Backend.VHDL.Quartus+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- Functions to process the VHDL compilation results with Altera's Quartus II+-- software.+-----------------------------------------------------------------------------+module ForSyDe.Deep.Backend.VHDL.Quartus + (callQuartus) where++import ForSyDe.Deep.ForSyDeErr+import ForSyDe.Deep.Config (getDataDir)+import ForSyDe.Deep.OSharing (readURef)+import ForSyDe.Deep.System.SysDef (subSys,sid,unPrimSysDef)++import ForSyDe.Deep.Backend.VHDL.Traverse.VHDLM ++import System.IO+import System.Directory+import System.Process+import Control.Monad.State+import System.Exit (ExitCode(..))++++-- | Analyze the results with Quartus+-- (Note: the initial and final CWD will be /systemName/vhdl )+callQuartus :: VHDLM ()+callQuartus = do+ mQuartus <- gets (execQuartus.ops.global)+ -- is it necessary to call quartus?+ case mQuartus of+ Nothing -> return ()+ -- Yes, we create the tcl script and call quartus_sh checking if it+ -- exists in the system+ Just ops -> do+ mPath <- liftIO $ findExecutable "quartus_sh"+ case mPath of+ Nothing -> do liftIO $ hPutStrLn stderr "Error: quartus_sh not found"+ throwFError QuartusFailed+ Just _ -> do contents <- gen_quartus_tcl ops+ liftIO $ writeFile "quartus.tcl" contents+ liftIO $ putStrLn "Running quartus_sh -t quartus.tcl"+ code <- liftIO $ waitForProcess =<< runCommand + "quartus_sh -t quartus.tcl" + case code of+ ExitFailure _ -> throwFError QuartusFailed+ _ -> return ()+ +-- | Generate the content of quartus.tcl+-- Note that, even in windows, the tcl interpreter requires pathnames+-- to use \"/\" instead of \"\\\"+gen_quartus_tcl :: QuartusOps -> VHDLM String+gen_quartus_tcl (QuartusOps act mFMax mFamDev assigs) = do+ sysName <- gets (sid.globalSysDef.global)+ dataPath <- liftIO $ getDataDir+ recursive <- isRecursiveSet+ subs <- gets (subSys.globalSysDef.global)+ let libDir = (changeSlashes dataPath) ++ "/lib"+ sysLib = sysName ++ "_lib"+ return $ unlines (+ packages +++ [projectNew sysName] ++ + mDefault mFMax fmax +++ mDefault mFamDev famDev +++ map mkAssig assigs +++ [topLevelEntity sysName,+ includeVHDLFile ("work/" ++ sysName ++ ".vhd") Nothing,+ includeVHDLFile ('"' : (libDir ++ "/forsyde.vhd") ++ "\"") (Just "forsyde"),+ includeVHDLFile (sysLib ++ "/" ++ sysLib ++ ".vhd") (Just sysLib)] +++ (if recursive then+ map (\s -> includeVHDLFile + ((("work/"++).(++".vhd").sid.readURef.unPrimSysDef) s)+ Nothing) subs+ else []) +++ [actionCmd act]+ )+ where mDefault m f = maybe [] f m+ actionCmd act = case act of+ AnalysisAndElaboration -> + "execute_flow -analysis_and_elaboration"+ AnalysisAndSynthesis ->+ "execute_module -tool map"+ FullCompilation ->+ "execute_flow -compile"+ fmax max = ["set_global_assignment -name FMAX_REQUIREMENT \"" ++ + show max ++ " MHz\""]+ famDev (fam, mDev) = + ["set_global_assignment -name FAMILY " ++ show fam] +++ case mDev of+ Nothing -> []+ Just dev -> ["set_global_assignment -name DEVICE " ++ show dev]+ mkAssig (vhdlPin, fpgaPin) = "set_location_assignment " ++ + fpgaPin ++ " -to " ++ vhdlPin+ packages = ["load_package project", "load_package flow"]+ includeVHDLFile :: FilePath -- ^ system name+ -> Maybe String -- ^ what library to include the + -- file in+ -> String+ includeVHDLFile file mLib = + "set_global_assignment -name VHDL_FILE " ++ file +++ maybe "" (" -library "++) mLib+ topLevelEntity name = + "set_global_assignment -name TOP_LEVEL_ENTITY " ++ name+ projectNew name = "project_new " ++ name ++ " -overwrite" + changeSlashes [] = []+ changeSlashes ('\\':xs) = '/' : changeSlashes xs+ changeSlashes (x:xs) = x : changeSlashes xs +
+ src/ForSyDe/Deep/Backend/VHDL/TestBench.hs view
@@ -0,0 +1,220 @@+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.Backend.VHDL.TestBench+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- Functions used to generate a VHDL test bech.+--+-----------------------------------------------------------------------------+module ForSyDe.Deep.Backend.VHDL.TestBench + (writeVHDLTestBench,+ parseTestBenchOut) where+++import ForSyDe.Deep.Backend.VHDL.Constants+import ForSyDe.Deep.Backend.VHDL.AST+import ForSyDe.Deep.Backend.VHDL.Translate+import ForSyDe.Deep.Backend.VHDL.Traverse.VHDLM+import ForSyDe.Deep.Backend.VHDL.Generate+import ForSyDe.Deep.Backend.VHDL.FileIO++import ForSyDe.Deep.Ids+import ForSyDe.Deep.System.SysDef++import qualified Language.Haskell.TH as TH (Exp)+import Control.Monad.State+import Data.List (transpose)+import System.Directory+import System.FilePath+++-- | Parse the output of the testbench (a tab separated files)+-- into strings denoting the individual output signals+parseTestBenchOut :: String -- ^ String containing the complete output file+ -> VHDLM [[String]] -- ^ output signal values in form + -- of strings+parseTestBenchOut str = do+ outN <- gets (length.oIface.globalSysDef.global)+ case tabSeparatedRows of+ [] -> return (replicate outN [])+ _ -> return $ transpose tabSeparatedRows+ where tabSeparatedRows = ((map (unintersperse '\t')).lines) str + unintersperse _ [] = [] + unintersperse e (c:cs) + -- the null check makes unintersperse imposible to define with foldr+ -- or unfoldr+ | c == e = if null cs then [[],[]] + else [] : unintersperse e cs+ | otherwise = let rest = unintersperse e cs in+ case rest of+ [] -> [[c]]+ (a:as) -> (c:a):as++-- | write a test bench, using a clock cycle of 10 ns+-- (Note: the initial and final CWD will be / )+writeVHDLTestBench :: Maybe Int -- ^ Number of cycles to simulate+ -- if 'Nothing' the number will be determined+ -- by the length of the input stimulti.+ -- Useful when the system to simulate doesn't+ -- have inputs or the inputs provided are + -- infinite+ -> [[TH.Exp]] -- ^ Input stimuli (one list per signal)+ -> VHDLM Int -- ^ Number of cycles simulated+writeVHDLTestBench mCycles stimuli = do+ sys <- gets (globalSysDef.global)+ let sysId = sid sys+ cxt = genVHDLTestBenchContext sysId+ ent = genVHDLTestBenchEntity sysId+ (arch, cycles) <- genVHDLTestBenchArch mCycles stimuli+ let design = DesignFile cxt [LUEntity ent, LUArch arch]+ tbdir = sysId </> "vhdl" </> "test"+ tbpath = tbdir </> (sysId ++ "_tb.vhd")+ liftIO $ createDirectoryIfMissing True tbdir+ liftIO $ writeDesignFile design tbpath+ return cycles++-- | Generate the Context Clause+genVHDLTestBenchContext :: SysId -- ^ Main system Id+ -> [ContextItem]+genVHDLTestBenchContext id = commonContextClause +++ [Library libId,+ Use $ NSelected (NSimple libId :.: SSimple typesId) :.: All,+ Use $ NSelected (NSimple stdId :.: SSimple textioId) :.: All]+ where libId = unsafeVHDLBasicId (id ++ "_lib")+ +-- | Generates an empty entity fot the testbench+genVHDLTestBenchEntity :: SysId -- ^ Main system Id + -> EntityDec+genVHDLTestBenchEntity id = EntityDec (unsafeVHDLBasicId (id ++ "_tb")) []++--------------------------+-- Test Bench Architecture+--------------------------+++-- | generate the architecture+genVHDLTestBenchArch :: Maybe Int -- ^ Maximum number of cycles+ -> [[TH.Exp]] -- ^ Input stimuli + -> VHDLM (ArchBody, Int) -- ^ Number of cycles simulated+genVHDLTestBenchArch mCycles stimuli = do+ sys <- gets (globalSysDef.global)+ let sysId = sid sys+ iface = iIface sys+ oface = oIface sys+ l = logic sys+ iIds = map fst iface+ iVHDLIds = map unsafeVHDLExtId iIds+ oIds = map fst oface+ -- Get the signal declarations for the input signals+ iDecs <- mapM + (\(pId, t) -> transVHDLName2SigDec (unsafeVHDLExtId pId) t Nothing) iface+ let finalIDecs = iDecs ++ + [SigDec clockId std_logicTM (Just $ PrimLit "'0'"),+ SigDec resetId std_logicTM (Just $ PrimLit "'0'")]+ -- Get the component instantiation and the signal declarations for the output+ -- signals+ (mIns, outDecs) <- + transSysIns2CompIns l+ (unsafeVHDLBasicId "totest") + iVHDLIds + (map (\(id, t) -> (unsafeVHDLExtId id,t)) oface)+ sysId+ iIds+ oIds+ -- Generate the signal assignments+ (stimuliAssigns, cycles) <- genStimuliAssigns mCycles stimuli iVHDLIds+ -- Add an assignment to turn off the reset signal after 3 ns+ -- (everything lower than 5 ns should work)+ let finalAssigns = + (NSimple resetId :<==: + ConWforms [] + (Wform [WformElem (PrimLit "'1'") (Just $ PrimLit "3 ns")])+ Nothing) : stimuliAssigns+ -- Get the two processes (clock and output)+ clkProc = genClkProc+ outputProc = genOutputProc (map unsafeVHDLExtId oIds)+ -- return the architecture+ return $ (ArchBody + (unsafeVHDLBasicId "test") + (NSimple $ unsafeVHDLBasicId (sysId ++ "_tb"))+ (map BDISD (finalIDecs ++ outDecs))+ ( maybe [] (\s -> [CSISm s]) mIns ++ + ( (CSPSm clkProc) : (CSPSm outputProc) : (map CSSASm finalAssigns) ) ),+ cycles)++-- | generate the assignments for the input stimuli+genStimuliAssigns :: Maybe Int -- ^ Maximum number of cycles+ -> [[TH.Exp]] -- ^ Input stimuli + -> [VHDLId] -- ^ Input signal ids+ -> VHDLM ([ConSigAssignSm], Int) -- ^ (Assignments,+ -- number of cycles + -- simulated)+-- if the number of input signas is zero+genStimuliAssigns mCycles [] _ = return ([], maybe 0 id mCycles)++-- if the nu,ber of input signals is /= zero+genStimuliAssigns mCycles stimuli signals = do+ let genWformElem time thExp = + do vExp <- transExp2VHDL thExp+ return (WformElem vExp (Just $ PrimLit (show time ++ " ns")))+ wformElems <- mapM (zipWithM genWformElem ([0,10..] :: [Int])) stimuli+ let (normWformElems, cycles) = normalize maxCycles wformElems+ if cycles == 0 + then return ([],0)+ else return + (zipWith + (\s elems -> NSimple s :<==: ConWforms [] (Wform elems) Nothing)+ signals + normWformElems,+ cycles)+ where maxCycles = maybe (-1) id mCycles + -- FIXME: this is not efficient at all+ -- Normalize a matrix. Make sure that all the rows in a matrix have the+ -- same length, setting a maximum row length (0 establishes no limit)+ normalize :: Int -- ^ maximum row-length to process + -> [[a]] -- ^ input matrix+ -> ([[a]], Int) -- ^ (output matrix, maximum row length)+ normalize max xss+ | any null xss || max == 0 = (replicate l [], 0)+ | otherwise = let (transres, acum) = normalize' max (transpose xss)+ in (transpose transres, acum)+ where l = length xss+ normalize' max (xs:xss)+ | length xs == l && max /= 0 =+ let (nextlist, nextacum) = normalize' (max-1) xss+ in (xs : nextlist, nextacum+1)+ normalize' _ _ = ([], 0)++-- | generates a clock process with a period of 10ns+genClkProc :: ProcSm+genClkProc = ProcSm (unsafeVHDLBasicId "clkproc") [] sms+ where sms = -- wait for 5 ns -- (half a cycle)+ [WaitFor $ PrimLit "5 ns",+ -- clk <= not clk;+ NSimple clockId `SigAssign` + Wform [WformElem (Not (PrimName $ NSimple clockId)) Nothing]]++-- | generate the output process+genOutputProc :: [VHDLId] -- ^ output signals+ -> ProcSm +genOutputProc outs = + ProcSm (unsafeVHDLBasicId "writeoutput") + [clockId]+ [IfSm clkPred (writeOuts outs) [] Nothing]+ where clkPred = PrimName (NAttribute $ AttribName (NSimple clockId) + eventId+ Nothing ) `And` + (PrimName (NSimple clockId) :=: PrimLit "'1'")+ writeOuts [] = []+ writeOuts [i] = [writeOut i (PrimLit "LF")]+ writeOuts (i:is) = writeOut i (PrimLit "HT") : writeOuts is+ writeOut outSig suffix = + genExprProcCall2 writeId + (PrimName $ NSimple outputId)+ (genExprFCall1 showId (PrimName $ NSimple outSig) :&: + suffix)
+ src/ForSyDe/Deep/Backend/VHDL/Translate.hs view
@@ -0,0 +1,921 @@+{-# LANGUAGE TemplateHaskell, TypeOperators #-}+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.Backend.VHDL.Traverse+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- Functions to translate elements of the intermediate system+-- representation to elements of the VHDL AST.+-----------------------------------------------------------------------------+module ForSyDe.Deep.Backend.VHDL.Translate where++import ForSyDe.Deep.Backend.VHDL.AST+import qualified ForSyDe.Deep.Backend.VHDL.AST as VHDL+import ForSyDe.Deep.Backend.VHDL.Constants+import ForSyDe.Deep.Backend.VHDL.Generate+import ForSyDe.Deep.Backend.VHDL.Traverse.VHDLM+import ForSyDe.Deep.Backend.VHDL.Translate.HigherOrderFunctions++import ForSyDe.Deep.Ids+import ForSyDe.Deep.AbsentExt+import ForSyDe.Deep.Signal+import ForSyDe.Deep.Bit hiding (not)+import ForSyDe.Deep.ForSyDeErr+import ForSyDe.Deep.System.SysDef+import ForSyDe.Deep.Process.ProcFun+import ForSyDe.Deep.Process.ProcVal+import ForSyDe.Deep.Process.ProcType++import Data.Data (tyconUQname)+import Data.Int+import Data.Char (digitToInt)+import Data.List (intersperse)+import Data.Maybe (isJust, fromJust)+import Control.Monad.State+import qualified Data.Set as S+import qualified Language.Haskell.TH as TH+import Language.Haskell.TH hiding (global,Loc)+import qualified Data.Traversable as DT+import Data.Typeable+import Data.Typeable.Internal+import qualified Data.Param.FSVec as V+import Text.Regex.Posix ((=~))++import Data.TypeLevel.Num.Reps+import Data.Typeable.FSDTypeRepLib++-- enable tracing of type translation++-- import Debug.Trace+debug :: a -> String -> a+-- debug = flip trace+debug a _ = a+++-- | Translate a System Definition to an Entity, explicitly returning+-- the VHDL identifiers of its output signals.+transSysDef2Ent :: SysLogic -- ^ logic of the system+ -> SysDefVal -- ^ system to translate+ -> VHDLM EntityDec+transSysDef2Ent logic sysDefVal = do+ entId <- transSysId2VHDL (sid sysDefVal)+ inDecs <- mapM (uncurry $ transPort2IfaceSigDec In) (iIface sysDefVal)+ outDecs <- mapM (uncurry $ transPort2IfaceSigDec Out) (oIface sysDefVal)+ -- clock and reset implicit declarations+ let implicitDecs = if logic == Sequential then+ [IfaceSigDec resetId In std_logicTM,+ IfaceSigDec clockId In std_logicTM]+ else []+ return $ EntityDec entId (implicitDecs ++ inDecs ++ outDecs)++-- | Translate a 'ZipwithNSY' process to a block returning a declaration of+-- the resulting signal.+transZipWithN2Block :: Label -- ^ process identifier+ -> [VHDLId] -- ^ input signals+ -> Loc -- ^ location of the inner function+ -> TypedProcFunAST -- ^ AST of the inner function+ -> VHDLId -- ^ output signal+ -> VHDLM (BlockSm, SigDec)+transZipWithN2Block vPid ins loc ast out = do+ -- Translate the process function+ (f,fName , inFPars, inFTypes, retFType) <-+ withProcFunC ((name.tpast) ast) loc $ transProcFun2VHDL ast+ -- Generate the formal parameters of the block+ let inPars = map (\n -> unsafeIdAppend vPid ("_in" ++ show n)) [1..length ins]+ outPar = unsafeIdAppend vPid "_out"+ -- Generate the port interface of the block+ inDecs = zipWith (\par typ -> IfaceSigDec par In typ) inPars inFTypes+ outDec = IfaceSigDec outPar Out retFType+ iface = inDecs ++ [outDec]+ -- Generate the port map+ pMap = genPMap (inPars ++ [outPar]) (ins ++ [out])+ -- Generate the function call and signal assignment+ outAssign = genFCallAssign out fName inFPars ins+ return (BlockSm vPid iface pMap [BDISPB f] [CSSASm outAssign],+ SigDec out retFType Nothing)+++-- | Translate a 'ZipwithxSY' process to a block returning a declaration of+-- the resulting signal.+transZipWithx2Block :: Label -- ^ process identifier+ -> [VHDLId] -- ^ input signals+ -> Loc -- ^ location of the inner function+ -> TypedProcFunAST -- ^ AST of the inner function+ -> VHDLId -- ^ output signal+ -> VHDLM (BlockSm, SigDec)+transZipWithx2Block vPid ins loc ast out = do+ -- Translate the process function+ (f, fName, [inFPar], [inFType], retFType) <-+ withProcFunC ((name.tpast) ast) loc $ transProcFun2VHDL ast+ -- Figure out the type of the inputs from the+ -- function's input vector type (horrible hack, but it works)+ let [[_,suffix]] = (fromVHDLId inFType) =~ "^fsvec_[0-9]*_(.*)$"+ inType = unsafeVHDLBasicId $ suffix+ -- Generate the formal parameters of the block+ inPars = map (\n -> unsafeIdAppend vPid ("_in" ++ show n)) [1..length ins]+ outPar = unsafeIdAppend vPid "_out"+ -- Generate the port interface of the block+ inDecs = map (\par -> IfaceSigDec par In inType) inPars+ outDec = IfaceSigDec outPar Out retFType+ iface = inDecs ++ [outDec]+ -- Generate the port map+ pMap = genPMap (inPars ++ [outPar]) (ins ++ [out])+ -- Generate the function call and signal assignment+ aggregate = Aggregate $+ map (\e -> ElemAssoc Nothing (PrimName(NSimple e))) inPars+ fCall = PrimFCall $ FCall (NSimple fName)+ [Just inFPar :=>: ADExpr aggregate]+ outAssign = genExprAssign outPar fCall+ return (BlockSm vPid iface pMap [BDISPB f] [CSSASm outAssign],+ SigDec out retFType Nothing)++-- | Translate a 'UnzipNSY' process to a block returning a declaration of+-- the resulting signal.+transUnzipNSY2Block :: Label -- ^ process identifier+ -> VHDLId -- ^ input signal+ -> [VHDLId] -- ^ output signals+ -> [FSDTypeRep] -- ^ output signal types+ -> VHDLM (BlockSm, [SigDec])+transUnzipNSY2Block vPid inSig outSigs outTRTypes = do+ -- Generate the formal parameters of the block+ let inPar = unsafeIdAppend vPid "_in"+ outPars = map (\n -> unsafeIdAppend vPid ("_out" ++ show n))+ [1..length outSigs]+ -- Generate the port interface of the block+ nOuts = length outSigs+ inTRType = (fsdTupleTyCon nOuts) `fsdTyConApp` outTRTypes+ outTMTypes <- mapM transTR2TM outTRTypes+ inTMType <- transTR2TM inTRType+ let inDec = IfaceSigDec inPar In inTMType+ outDecs = zipWith (\par typ -> IfaceSigDec par Out typ) outPars outTMTypes+ iface = inDec : outDecs+ -- Generate the port map+ pMap = genPMap (inPar : outPars) (inSig : outSigs)+ -- Generate the signal assignments+ genOrigExp n = (PrimName $ NSelected+ (NSimple inPar :.: tupVHDLSuffix n))+ genOutAssign outSig n = CSSASm $ genExprAssign outSig (genOrigExp n)+ outAssigns = zipWith genOutAssign outPars [(1::Int)..]+ return (BlockSm vPid iface pMap [] outAssigns,+ zipWith (\sig typ -> SigDec sig typ Nothing) outSigs outTMTypes)++++-- | Translate a 'UnzipxSY' process to a block returning a declaration of+-- the resulting signal.+transUnzipxSY2Block :: Label -- ^ process identifier+ -> VHDLId -- ^ input signal+ -> [VHDLId] -- ^ output signals+ -> FSDTypeRep -- ^ type of vector elements+ -> Int -- ^ vector Size+ -> VHDLM (BlockSm, [SigDec])+transUnzipxSY2Block vPid inSig outSigs elemTR vSize = do+ -- Generate the formal parameters of the block+ let inPar = unsafeIdAppend vPid "_in"+ outPars = map (\n -> unsafeIdAppend vPid ("_out" ++ show n))+ [1..length outSigs]+ -- Generate the port interface of the block+ inTRType = fSVecTyCon `fsdTyConApp` [transInt2TLNat vSize, elemTR]+ inTMType <- transTR2TM inTRType+ elemTM <- transTR2TM elemTR+ let inDec = IfaceSigDec inPar In inTMType+ outDecs = map (\par -> IfaceSigDec par Out elemTM) outPars+ iface = inDec : outDecs+ -- Generate the port map+ pMap = genPMap (inPar : outPars) (inSig : outSigs)+ -- Generate the signal assignments+ genOrigExp n =+ PrimName $ NIndexed (NSimple inPar `IndexedName` [PrimLit $ show n])+ genOutAssign outSig n = CSSASm $ genExprAssign outSig (genOrigExp n)+ outAssigns = zipWith genOutAssign outPars [(0::Int)..]+ return (BlockSm vPid iface pMap [] outAssigns,+ map (\sig -> SigDec sig elemTM Nothing) outSigs)+++++-- | Translate a 'DelaySY' process to a block returning a declaration of+-- the resulting signal.+transDelay2Block :: Label -- ^ process identifier+ -> VHDLId -- ^ input signal+ -> ProcValAST -- ^ AST of the initial value+ -- of the delay process+ -> VHDLId -- ^ output signal+ -> VHDLM (BlockSm, SigDec)+transDelay2Block vPid inS (ProcValAST exp tr enums) outS = do+ -- Add the enumerated types associated with the value to the global results+ addEnumTypes enums+ -- Get the type of the initial value+ initTR <- transTR2TM tr+ -- Translate the initial value+ initExp <- withProcValC exp $ withInitFunTransST $ (transExp2VHDL exp)+ -- Build the block+ let formalIn = unsafeIdAppend vPid "_in"+ formalOut = unsafeIdAppend vPid "_out"+ iface = [IfaceSigDec resetId In std_logicTM,+ IfaceSigDec clockId In std_logicTM,+ IfaceSigDec formalIn In initTR,+ IfaceSigDec formalOut Out initTR]+ assocs = [Just resetId :=>: ADName (NSimple resetId),+ Just clockId :=>: ADName (NSimple clockId),+ Just formalIn :=>: ADName (NSimple inS),+ Just formalOut :=>: ADName (NSimple outS)]+ sigAssign = CSSASm (NSimple formalOut :<==:+ (ConWforms [whenElseReset] inWform (Just whenRE)))+ whenElseReset = WhenElse (Wform [WformElem initExp Nothing])+ (PrimName (NSimple resetId) :=: PrimLit "'0'")+ inWform = Wform [WformElem (PrimName $ NSimple formalIn) Nothing]+ whenRE = When (PrimFCall $ FCall (NSimple $ unsafeVHDLBasicId "rising_edge")+ [Nothing :=>: ADName (NSimple clockId) ])+ return (BlockSm vPid iface (PMapAspect assocs) [] [sigAssign],+ SigDec outS initTR Nothing)++-- | Translate a System instance into a VHDL component instantion+-- returning the declartion of the output signals+transSysIns2CompIns :: SysLogic -- ^ parent system logic+ -> Label -- ^ instance identifier+ -> [VHDLId] -- ^ input signals+ -> [(VHDLId, FSDTypeRep)] -- ^ output signals+ -> SysId -- ^ parent system identifier+ -> [PortId] -- ^ parent input identifiers+ -> [PortId] -- ^ parent output identifiers+ -> VHDLM (Maybe CompInsSm, [SigDec])+transSysIns2CompIns logic vPid ins typedOuts parentId parentInIds parentOutIds = do+ if length ins == 0 && length typedOuts == 0+ then return (Nothing, [])+ else do+ -- Create the declarations for the signals+ decs <- mapM (\(name,typ) -> transVHDLName2SigDec name typ Nothing) typedOuts+ -- Create the portmap+ vParentId <- transSysId2VHDL parentId+ vParentInIds <- liftEProne $ mapM mkVHDLExtId parentInIds+ vParentOutIds <- liftEProne $ mapM mkVHDLExtId parentOutIds+ let implicitAssocIds = if logic == Sequential then [resetId, clockId] else []+ assocs = genAssocElems+ (implicitAssocIds ++ vParentInIds ++ vParentOutIds)+ (implicitAssocIds ++ ins ++ map fst typedOuts)+ entityName = NSelected (NSimple workId :.: SSimple vParentId)+ instantiation = CompInsSm vPid (IUEntity entityName) (PMapAspect assocs)+ return (Just instantiation, decs)+++-- | Translate a VHDL Signal to a VHDL Signal declaration+transVHDLName2SigDec :: SimpleName -- ^ Signal name+ -> FSDTypeRep -- ^ Type of the intermediate signal+ -> Maybe TH.Exp -- ^ Maybe an initializer expression for the signal+ -> VHDLM SigDec+transVHDLName2SigDec vId tr mExp = do+ tm <- transTR2TM tr+ mVExp <- DT.mapM (\e -> withInitFunTransST (transExp2VHDL e)) mExp+ return $ SigDec vId tm mVExp++++-------------------------+-- Identifier translation+-------------------------+++-- | Translate a VHDL identifier and a type to an interface signal declaration+transVHDLId2IfaceSigDec :: Mode -> VHDLId -> FSDTypeRep -> VHDLM IfaceSigDec+transVHDLId2IfaceSigDec m vid trep = do+ tm <- transTR2TM trep+ return $ IfaceSigDec vid m tm+++-- | Translate a Port to a VHDL Interface signal declaration+transPort2IfaceSigDec :: Mode -> PortId -> FSDTypeRep -> VHDLM IfaceSigDec+transPort2IfaceSigDec m pid trep = do+ sid <- transPortId2VHDL pid+ transVHDLId2IfaceSigDec m sid trep++-- | Translate a local TH name to a VHDL Identifier+transTHName2VHDL :: TH.Name -> VHDLM VHDLId+-- we use pprint becase it shows unique names for local names+-- e.g. let x = 1 in let x =2 in x is printed as+-- let x_0 = 1 in let x_1 = 2 in x_1+-- we want unique names because it saves us from dealing wiht+-- name scopes and having a global name table.+transTHName2VHDL = transPortId2VHDL . tyconUQname . pprint++-- | Translate a system identifier to a VHDL identifier+transSysId2VHDL :: SysId -> VHDLM VHDLId+transSysId2VHDL = transPortId2VHDL++-- | Translate a process identifier to a VHDL identifier+transProcId2VHDL :: ProcId -> VHDLM VHDLId+transProcId2VHDL = transPortId2VHDL++-- | translate a port identifier to a VHDL Identifier+transPortId2VHDL :: PortId -> VHDLM VHDLId+transPortId2VHDL str = liftEProne $ mkVHDLExtId str+++-------------------+-- Type translation+-------------------+++-- | translate a 'TypeRep' to a VHDL 'TypeMark'+-- We don't distinguish between a type and its version nested in 'Signal'+-- since it makes no difference in VHDL+transTR2TM :: FSDTypeRep -> VHDLM TypeMark+transTR2TM rep+ -- Is it a Signal?+ | isSignal = transTR2TM nestedTR `debug` (dbgStr "S")+ -- Is it a primitive type?+ | isJust mPrimitiveTM = return $ fromJust mPrimitiveTM `debug` (dbgStr "P")+ -- Non-Primitive type, try to translate it+ | otherwise = customTR2TM rep `debug` (dbgStr "T")+ where (isSignal, nestedTR) = let (tc,~[tr]) = fsdSplitTyConApp rep+ in (tc == signalTyCon, tr)+ signalTyCon = fsdTyConOf (undefined :: Signal ())+ mPrimitiveTM = lookup rep primTypeTable+ dbgStr k = ">>>>" ++ k ++ " " ++ (typeRepQName rep) ++ "/" ++ (show rep)+++-- | Translate a custom 'TypeRep' to a VHDL 'TypeMark'+customTR2TM :: FSDTypeRep -> VHDLM TypeMark+customTR2TM rep = do+ -- Check if it was previously translated+ mTranslated <- lookupCustomType rep+ case mTranslated of+ -- Not translated previously+ Nothing -> do+ -- translate it+ e <- doCustomTR2TM rep+ -- update the translation table and the accumulated type declarations+ addCustomType rep e+ -- return the translation+ case e of+ Left (TypeDec id _) -> return id+ Right (SubtypeDec id _) -> return id+ -- Translated previously+ Just tm -> return tm `debug` "'--> (cache hit)"++-- | Really do the translation (customTR2TM deals with caching)+doCustomTR2TM :: FSDTypeRep -> VHDLM (Either TypeDec SubtypeDec)++-- | FSVec?+-- FSVecs are translated to subtypes of unconstrained vectors.+-- All FSVec operations are translated as operations for the+-- unconstrained type.+doCustomTR2TM rep | isFSVec = do+ -- Translate the type of the elements contained in the vector+ valTM <- transTR2TM valueType+ -- Build the unconstrained vector identifier+ let vectorId = unsafeVHDLContainerId [valTM] ("fsvec_"++ fromVHDLId valTM)+ -- Obtain the unconstrained vector together with its functions and add them+ -- to the global traversing-results (if this wasn't previously done):+ -- * Check if the unconstrained array was previously translated+ vecs <- gets (transUnconsFSVecs.global)+ -- * if it wasn't ...+ when (not $ elem valueType vecs) `debug` (" vectors: " ++ (show vecs)) $ do+ -- create the unconstrained vector type and add it to the global+ -- results. _Only_ if we are not working with "FSVec _ Bit" becuase+ -- "type fsvec_std_logic" is already included in forsyde.vhd.+ when (valueType /= (fsdTy $ typeOf (undefined :: Bit)))+ ((addTypeDec $ TypeDec vectorId (TDA (UnconsArrayDef [fsvec_indexTM] valTM))) `debug` "allegiedly not FSVec _ Bit")+ -- Add the default functions for the unconstrained+ -- vector type to the global results+ let funs = genUnconsVectorFuns valTM vectorId+ mapM_ addSubProgBody funs+ -- Mark the unconstrained array as translated+ addUnconsFSVec $ valueType++ -- Create the vector subtype identifier+ let subvectorId = unsafeVHDLBasicId ("fsvec_" ++ show size ++ "_" +++ fromVHDLId valTM)+ -- Create the vector subtype declaration+ return $ Right $+ SubtypeDec subvectorId (SubtypeIn vectorId+ (Just $ IndexConstraint [ToRange (PrimLit "0")+ (PrimLit (show $ size-1))]))+ where (cons, ~[sizeType,valueType]) = fsdSplitTyConApp rep+ isFSVec = cons == fSVecTyCon+ size = transTLNat2Int sizeType+++-- | Tuple?+doCustomTR2TM rep | isTuple = do+ -- Create the elements of the record+ fieldTMs <- mapM transTR2TM args+ let elems = zipWith (\fieldId fieldTM -> ElementDec fieldId fieldTM )+ [tupVHDLIdSuffix n | n <- [1..]] fieldTMs+ -- Create the Type Declaration identifier+ recordId = unsafeVHDLContainerId fieldTMs $+ (tupStrSuffix $ length fieldTMs) ++ "_" +++ (concatMap fromVHDLId.intersperse (unsafeVHDLBasicId "_")) fieldTMs+ -- Add the default functions for the tuple type to the global results+ funs = genTupleFuns fieldTMs recordId+ mapM_ addSubProgBody funs+ -- Create the record+ return $ Left $ (TypeDec recordId (TDR $ RecordTypeDef elems))+ where (cons, args) = fsdSplitTyConApp rep+ conStr = fsdTyConName cons+ isTuple = (length conStr > 2) && (all (==',') (reverse.tail.reverse.tail $ conStr))+++-- | AbstExt?+doCustomTR2TM rep | isAbsExt = do+ -- Create the elements of the record+ valueTM <- transTR2TM valueTR+ let elems = [ElementDec isPresentId booleanTM,+ ElementDec valueId valueTM ]++ -- Create the Type Declaration identifier+ recordId = unsafeVHDLContainerId [valueTM] $+ "abs_ext_" ++ fromVHDLId valueTM+ -- Add the default functions for the vector type to the global results+ funs = genAbstExtFuns valueTM recordId+ mapM_ addSubProgBody funs+ -- Return the resulting the record+ return $ Left $ (TypeDec recordId (TDR $ RecordTypeDef elems))+ where (cons, ~[valueTR]) = fsdSplitTyConApp rep+ absExtTyCon = fsdTyConOf (undefined :: AbstExt ())+ isAbsExt = cons == absExtTyCon++-- | Finally, it is an Enumerated algebraic type+-- or an unkown custom type (in that case we throw an error)+--+-- NOTE: It would be cleaner to have a different clauses for each case but+-- since we need to access the state to check if it's an enumerated+-- algebraic type, we cannot do it.+doCustomTR2TM rep = do+ -- Get the accumulated Enumerated Algebraic Types+ eTys <- gets (enumTypes.global)+ -- Check if current Type representation can be found in eTys+ let strRep = typeRepQName rep+ let equalsRep (EnumAlgTy name _) = name == strRep+ case (S.toList.(S.filter equalsRep)) eTys of+ -- Found!+ [enumDef] -> liftM Left $ enumAlg2TypeDec enumDef `debug` (">>>>? "++strRep)+ -- Not found, unkown custom type+ _ -> throwFError $ UnsupportedType rep++-- | Transform an enumerated Algebraic type to a VHDL+-- TypeMark adding its default function to the global results+enumAlg2TypeDec :: EnumAlgTy -- ^ Enumerated type definition+ -> VHDLM TypeDec+enumAlg2TypeDec (EnumAlgTy tn cons) = do+ -- Get the TypeMark+ tMark <- liftEProne $ mkVHDLExtId tn+ -- Get the enumeration literals+ enumLits@(firstLit:_) <- liftEProne $ mapM mkVHDLExtId cons+ -- Add the default functions for the enumeration type+ let funs = genEnumAlgFuns tMark firstLit+ mapM_ addSubProgBody funs+ -- Create the enumeration type+ return (TypeDec tMark (TDE $ EnumTypeDef enumLits))++-- | Translation table for primitive types+primTypeTable :: [(FSDTypeRep, TypeMark)]+primTypeTable = [-- Commented out due to representation overflow+ -- (typeOf (undefined :: Int64), int64TM) ,+ (fsdTypeOf (undefined :: Int32), int32TM) ,+ (fsdTypeOf (undefined :: Int16), int16TM) ,+ (fsdTypeOf (undefined :: Int8) , int8TM) ,+ (fsdTypeOf (undefined :: Bool) , booleanTM) ,+ (fsdTypeOf (undefined :: Bit) , std_logicTM)]++---------------------------------------+-- Translating functions and expresions+---------------------------------------++------------------------+-- Translating functions+------------------------+++-- | Throw a function error+funErr :: VHDLFunErr -> VHDLM a+funErr err = throwFError $ UntranslatableVHDLFun err++-- | Translate a typed function AST to VHDL+transProcFun2VHDL :: TypedProcFunAST -- ^ input ast+ -> VHDLM (SubProgBody, VHDLId, [VHDLId], [TypeMark], TypeMark)+ -- ^ Function, Function name, name of inputs, type of inputs, return type+transProcFun2VHDL (TypedProcFunAST fType fEnums fAST) = do+ -- Add the enumerated types associated with the function to the global results+ addEnumTypes fEnums+ -- Check if the procFunAST fullfils the restrictions of the VHDL Backend+ -- FIXME: translate the default arguments+ (fName, fInputPats, fBodyExp, whereDecs) <- checkProcFunAST fAST+ -- Get the function spec and initialize the translation namespace+ (fSpec, fVHDLName, fVHDLPars, argsTM, retTM) <-+ transProcFunSpec fName fType fInputPats+ -- Translate the where declarations and them to the+ -- auxiliary declarations of the function+ transDecs whereDecs+ -- Translate the function's body+ putCurrentFunctionSpec fSpec+ bodySms <- transFunBodyExp2VHDL fBodyExp+ decs <- gets (auxDecs.funTransST.local)+ let fBody = SubProgBody fSpec decs bodySms+ return (fBody, fVHDLName, fVHDLPars, argsTM, retTM)++-- | Translate a typed function AST to VHDL (only returning the functions body+transProcFun2VHDLBody :: TypedProcFunAST -> VHDLM SubProgBody+transProcFun2VHDLBody tpf = do+ (body, _, _, _, _) <- transProcFun2VHDL tpf+ return body++-- | Translate a list of declarations to a list of process function+-- ASTs+decs2ProcFuns :: [Dec] -> VHDLM [TypedProcFunAST]+decs2ProcFuns [] = return []+decs2ProcFuns decs = do+ (dec, t, name, clauses, restDecs) <- case decs of+ -- A type signature followed by its function declaration+ SigD n1 t : f@(FunD n2 cls) : xs | n1 == n2 ->+ return (f, t, n1, cls, xs)+ -- A type signature followed by its value declaration+ -- which will be translated to a function+ SigD n1 t : v@(ValD (VarP n2) bdy ds) : xs | n1 == n2 -> do+ return (v, t, n1, [Clause [] bdy ds] , xs)+ -- Otherwise the provided declaration block is not supported+ _ -> funErr $ UnsupportedDecBlock decs+ t' <- maybe (funErr $ PolyDec dec) return (type2FSDTypeRep t)+ let tpf = TypedProcFunAST t' S.empty (ProcFunAST name clauses [])+ restTPFs <- decs2ProcFuns restDecs+ return $ tpf:restTPFs++-- | Tranlate a list of declarations and add them to the auxiliary+-- declarations in the function translation state+transDecs :: [Dec] -> VHDLM ()+transDecs decs = do+ -- Before anything, clear the previous declaration blocks+ -- FIXME: this shouldn't be here+ clearAux+ -- first we tranlsate the declarations to process functions+ tpfs <- decs2ProcFuns decs+ -- Before translating the process functions we add their names to the+ -- name translation table. It is important to note that, since+ -- Template Haskell makes local names unique (e.g. [| let x = 1 in+ -- let x = 2 in x |] is tranlsated to let x_0 = 1 in let x_1 = 2 in x_2),+ -- we don't have to take care of name scopes i.e. we can have a global name+ -- scope.+ mapM_ addDecName tpfs+ -- Translate the declarations to VHDL and add them+ -- to the auxiliary declarations of the function translation+ bodyDecs <- mapM (liftM SPSB . transProcFun2VHDLBody) tpfs+ addDecsToFunTransST bodyDecs+ where addDecName :: TypedProcFunAST -> VHDLM ()+ addDecName (TypedProcFunAST t _ (ProcFunAST n _ _)) = do+ let arity = (length.fst.fsdUnArrowT) t+ vhdlId <- transTHName2VHDL n+ addTransNamePair n arity (genExprFCallN vhdlId arity)+ clearAux = do+ lState <- gets local+ let s = funTransST lState+ modify (\st -> st{local=lState{funTransST=s{auxDecs=[]}}})++-- | Check if a process function AST fulfils the VHDL backend restrictions.+-- It returs the function TH-name its input paterns, its body expression,+-- and the list of theclarations in the where construct.+checkProcFunAST :: ProcFunAST+ -> VHDLM (Name, [Pat], Exp, [Dec])+-- FIXME: translate the default arguments!+checkProcFunAST (ProcFunAST thName [Clause pats (NormalB exp) decs] []) =+ return (thName, pats, exp, decs)+checkProcFunAST (ProcFunAST _ _ (_:_)) =+ intError "ForSyDe.Backend.VHDL.Translate.checkProcFunSpec"+ (UntranslatableVHDLFun $ GeneralErr (Other "default parameters are not yet supported"))+checkProcFunAST (ProcFunAST _ [Clause _ bdy@(GuardedB _) _] _) =+ funErr (FunGuardedBody bdy)+checkProcFunAST (ProcFunAST _ clauses@(_:_) _) =+ funErr (MultipleClauses clauses)+-- cannot happen+checkProcFunAST (ProcFunAST _ [] _) =+ -- FIXME, use a custom error+ intError "ForSyDe.Backend.VHDL.Translate.checkProcFunSpec"+ (UntranslatableVHDLFun $ GeneralErr (Other "inconsistentency"))++++-- | Get the spec of a VHDL function from the Haskell function name, its type+-- and its input patterns. This function also takes care of initalizing the+-- translation namespace.+transProcFunSpec :: TH.Name -- ^ Function name+ -> FSDTypeRep -- ^ Function type+ -> [Pat] -- ^ input patterns+ -> VHDLM (SubProgSpec, VHDLId, [VHDLId], [TypeMark], TypeMark)+-- ^ translated function spec, function name, inpt parameters, input types+-- and return types+transProcFunSpec fName fType fPats = do+ -- FIXME: translate the default arguments!+ -- Get the input and output types+ let (argsTR, retTR) = fsdUnArrowT fType+ -- Check that the number of patterns equal the function parameter number+ expectedN = length argsTR `debug` ("expected (args): "++ (show (length argsTR)))+ actualN = length fPats `debug` ("actual (patterns): "++ (show (length fPats)))+ when (expectedN /= actualN) (funErr $ InsParamNum fName actualN)+ -- Get a VHDL identifier for each input pattern and+ -- initialize the translation namespace+ fVHDLParIds <- mapM transInputPat2VHDLId fPats+ -- Translate the function name+ fVHDLName <- transTHName2VHDL fName+ -- Translate the types+ argsTM <- mapM transTR2TM argsTR+ retTM <- transTR2TM retTR+ -- Create the spec+ let iface = zipWith (\name typ -> IfaceVarDec name typ) fVHDLParIds argsTM+ fSpec = Function fVHDLName iface retTM+ -- Finally, return the results+ return (fSpec, fVHDLName, fVHDLParIds, argsTM, retTM)++-- | Translate an input pattern to a VHDLID,+-- making the necessary changes in the translation namespace+transInputPat2VHDLId :: TH.Pat -> VHDLM VHDLId+transInputPat2VHDLId pat = do+ -- Get the parameter identifier+ id <- case pat of+ -- if we get a variable or and @ patterm, we just translate it to VHDL+ VarP name -> transTHName2VHDL name+ AsP name _ -> transTHName2VHDL name+ -- otherwise, generate a fresh identifier+ _ -> genFreshVHDLId++ -- Prepare the namespace for the pattern+ preparePatNameSpace (NSimple id) pat+ -- Finally return the generated id+ return id+++-- | prepare the translation namespace for an input pattern+preparePatNameSpace :: Prefix -- ^ name prefix obtained so far+ -> Pat -- ^ pattern+ -> VHDLM ()+-- NOTE: a good alternative to adding selected names to the+-- translation table would be declaring a variable+-- assignment. It would probably make the generated code more+-- readable but at the same time, it requires knowing the+-- pattern type, and TH's AST is unfortunately not+-- type-annotated which would make things more difficult.++-- variable pattern+preparePatNameSpace prefix (VarP name) =+ addTransNamePair name 0 (\[] -> PrimName prefix)++-- '@' pattern+preparePatNameSpace prefix (AsP name pat) = do+ addTransNamePair name 0 (\[] -> PrimName prefix)+ preparePatNameSpace prefix pat++-- wildcard pattern+preparePatNameSpace _ WildP = return ()++-- tuple pattern+preparePatNameSpace prefix (TupP pats) = do+ let prepTup n pat = preparePatNameSpace+ (NSelected (prefix :.: tupVHDLSuffix n)) pat+ zipWithM_ prepTup [1..] pats++-- AbstExt patterns++-- Since we only support one clause per function+-- they are not really useful, but we accept them anyways+-- FIXME: true, they are not useful, but again, since we only support one+-- clause per function they denote a programming error. Should they+-- really be supported?+preparePatNameSpace prefix (ConP name ~[pat]) | isAbstExt name =+ when isPrst (preparePatNameSpace (NSelected (prefix :.: valueSuffix)) pat)+ where isAbstExt name = isPrst || name == 'Abst+ isPrst = name == 'Prst++-- Unary Constructor patterns+-- We try an enumerated type patterns+-- otherwise we throw an unknown constructor pattern error+preparePatNameSpace _ pat@(ConP name []) = do+ mId <- getEnumConsId name+ case mId of+ -- it is an enumerated data constructor, however, since we only admit+ -- one clause per function there is nothing to do about it+ Just _ -> return ()+ -- it is an unknown data constructor+ Nothing -> funErr $ UnsupportedFunPat pat++-- otherwise the pattern is not supported+preparePatNameSpace _ pat = funErr $ UnsupportedFunPat pat++++--------------------------+-- Translating expressions+--------------------------++-- | Throw an expression error+expErr :: Exp -> VHDLExpErr -> VHDLM a+expErr exp err = throwFError $ UntranslatableVHDLExp exp err+++-- | Create the unique statement of a VHDL from a TH expression.+transFunBodyExp2VHDL :: TH.Exp -> VHDLM [SeqSm]+transFunBodyExp2VHDL (CondE condE thenE elseE) =+ do condVHDLE <- transExp2VHDL condE+ thenVHDLSm <- transFunBodyExp2VHDL thenE+ elseVHDLSm <- transFunBodyExp2VHDL elseE+ return [IfSm condVHDLE thenVHDLSm [] (Just $ Else elseVHDLSm)]+transFunBodyExp2VHDL caseE@(CaseE exp matches) =+ do caseVHDLE <- transExp2VHDL exp+ caseSmAlts <- mapM (transMatch2VHDLCaseSmAlt caseE) matches+ return [CaseSm caseVHDLE caseSmAlts]+-- A higher order function needs to be treated specially+transFunBodyExp2VHDL e@(AppE _ _) + | isHigherOrderFunction e = translateHigherOrderFunctionBody e+-- In other case it is an expression returned directly+transFunBodyExp2VHDL e =+ do vHDLe <- transExp2VHDL e+ return [ReturnSm $ Just vHDLe]++-- | Translate a case alternative from Haskell to VHDL+transMatch2VHDLCaseSmAlt :: TH.Exp -> TH.Match -> VHDLM CaseSmAlt+-- FIXME: the exp passed (which contains the full case expression for+-- error reporting purposes) should be part of the context once VHDLM+-- is reworked+transMatch2VHDLCaseSmAlt contextExp (Match pat (NormalB matchExp) decs) =+ do transDecs decs+ sm <- transFunBodyExp2VHDL matchExp+ case pat of+ -- FIXME: support pattern matching with tuples, AbsExt,+ -- and enumerated types+ WildP -> return $ CaseSmAlt [Others] sm+ LitP lit -> do vHDLExp <- transExp2VHDL (LitE lit)+ return $ CaseSmAlt [ChoiceE vHDLExp] sm+ -- FIXME: check! this case introduces new names into scope+ VarP name -> do vHDLExp <- transExp2VHDL (VarE name)+ return $ CaseSmAlt [ChoiceE vHDLExp] sm+ _ -> expErr contextExp $ UnsupportedCasePat pat+transMatch2VHDLCaseSmAlt contextExp (Match _ bdy@(GuardedB _) _) =+ expErr contextExp $ CaseGuardedBody bdy+++-- | Translate a Haskell expression to a VHDL expression+transExp2VHDL :: TH.Exp -> VHDLM VHDL.Expr+++-- TypeLevel-package numerical constant aliases+transExp2VHDL (VarE name) | isTypeLevelAlias = do+ let constant = nameBase name+ ([baseSym], val) = splitAt 1 constant+ basePrefix = case baseSym of+ 'b' -> "2#"+ 'o' -> "8#"+ 'h' -> "16#"+ 'd' -> ""+ _ -> error "unexpected base symbol"+ return (PrimLit $ basePrefix ++ val)+ where isTypeLevelAlias = (show name =~ aliasPat)+ aliasPat = "^Data\\.TypeLevel\\.Num\\.Aliases\\.(b[0-1]+|o[0-7]+|d[0-9]+|h[0-9A-F]+)$"++++-- A FSVec generated with Template Haskell+transExp2VHDL (VarE unsafeFSVecCoerce `AppE` _ `AppE` (ConE con `AppE` ListE exps))+ | show unsafeFSVecCoerce == "Data.Param.FSVec.unsafeFSVecCoerce" &&+ show con == "Data.Param.FSVec.FSVec" = do+ vhdlExps <- mapM transExp2VHDL exps+ return $ Aggregate (map (\e -> ElemAssoc Nothing e) vhdlExps)+++-- Is it function/constructor application, a constant+-- or an unkown name.+transExp2VHDL e | isConsOrFun =+ do -- get the symbol table (name translation table)+ nameTable <- gets (nameTable.funTransST.local)+ case lookup name nameTable of+ -- found name+ Just (arity, transF) ->+ if arity /= numArgs+ then expErr e $ CurryUnsupported arity numArgs+ else do exps <- mapM transExp2VHDL args+ return $ transF exps+ -- Didn't find the name in the global table+ Nothing -> do+ -- Check if it is a user-defined enumerated data constructor+ mId <- getEnumConsId name+ case mId of+ Just id -> return $ PrimName (NSimple id)+ Nothing -> expErr e $ UnkownIdentifier name+ where (f,args,numArgs) = unApp e+ mName = getName f+ name = fromJust mName+ isConsOrFun = isJust mName+ getName (VarE n) = Just n+ getName (ConE n) = Just n+ getName _ = Nothing++++-- Literals+transExp2VHDL (LitE (IntegerL integer)) = (return.transInteger2VHDL) integer+transExp2VHDL (LitE (IntPrimL integer)) = (return.transInteger2VHDL) integer++-- Unsupported literal+transExp2VHDL lit@(LitE _) = expErr lit $ UnsupportedLiteral++-- Infix expressions+transExp2VHDL (InfixE (Just argl) f@(VarE _) (Just argr)) =+ transExp2VHDL $ f `AppE` argl `AppE` argr++-- Sections (unsupported)+transExp2VHDL infixExp@(InfixE _ (VarE _) _) = expErr infixExp Section++-- Tuples: e.g. (1,2)+transExp2VHDL (TupE exps) = do+ vExps <- mapM transExp2VHDL exps+ return $ Aggregate $ map (\expr -> ElemAssoc Nothing expr) vExps++-- Let expressions+transExp2VHDL (LetE decs e) = do+ transDecs decs+ transExp2VHDL e++-- Unsupported expressions+transExp2VHDL lamE@(LamE _ _) = expErr lamE LambdaAbstraction+transExp2VHDL condE@(CondE _ _ _) = expErr condE Conditional+transExp2VHDL caseE@(CaseE _ _) = expErr caseE Case+transExp2VHDL doE@(DoE _) = expErr doE Do+transExp2VHDL compE@(CompE _) = expErr compE ListComprehension+transExp2VHDL arithSeqE@(ArithSeqE _) = expErr arithSeqE ArithSeq+transExp2VHDL listE@(ListE _) = expErr listE List+transExp2VHDL sigE@(SigE _ _) = expErr sigE Signature+transExp2VHDL reConE@(RecConE _ _) = expErr reConE Record+transExp2VHDL recUpE@(RecUpdE _ _) = expErr recUpE Record++-- The rest of expressions are not valid in practice and thus, not supported+-- (e.g. InfixE Nothing (RecConE _ _) _+transExp2VHDL exp = expErr exp Unsupported+++-- | Translate an integer to VHDL+transInteger2VHDL :: Integer -> Expr+transInteger2VHDL = PrimLit . show+++--------------------+-- Helper Functions+--------------------++-- Translate the TypeRep of a type-level natural (e.g: D1 :* D2) to a number+-- Make sure you don't supply an incorrect TypeRep or the function will break+transTLNat2Int :: FSDTypeRep -> Int+transTLNat2Int tr+ -- Digit+ -- FIXME: Could be made cleaner. It was like this before:+ -- isDigit = (digitToInt.last.tyConName) cons+ -- which was not able to take care of e.g. Data.TypeLevel.Num.Aliases.D10+ | isDigit = (read.reverse.takeWhile (/='D').reverse.fsdTyConName) cons+ -- Connective+ | otherwise = 10 * (transTLNat2Int prefix) + (transTLNat2Int lastDigit)+ where (cons, args@(~[prefix, lastDigit])) = fsdSplitTyConApp tr+ isDigit = null args+++-- Tranlate an Int to the TypeRep of a type-level natural (e.g: D1 :* D2)+transInt2TLNat :: Int -> FSDTypeRep+transInt2TLNat n+ | n < 0 = intError fName (Other "negative index")+ | n < 10 = digit n+ | otherwise = fsdTyConApp conTyCon [transInt2TLNat suffix, digit last]+ where fName = "ForSyDe.Backend.VHDL.Translate.transInt2TLNat"+ (suffix, last) = n `divMod` 10+ digit 0 = fsdTypeOf (undefined :: D0)+ digit 1 = fsdTypeOf (undefined :: D1)+ digit 2 = fsdTypeOf (undefined :: D2)+ digit 3 = fsdTypeOf (undefined :: D3)+ digit 4 = fsdTypeOf (undefined :: D4)+ digit 5 = fsdTypeOf (undefined :: D5)+ digit 6 = fsdTypeOf (undefined :: D6)+ digit 7 = fsdTypeOf (undefined :: D7)+ digit 8 = fsdTypeOf (undefined :: D8)+ digit 9 = fsdTypeOf (undefined :: D9)+ -- Just to hush the compiler warnings+ digit _ = undefined+ conTyCon = fsdTyConOf (undefined :: () :* ())++-- Type constructor of FSVec+fSVecTyCon :: FSDTypeCon+fSVecTyCon = fsdTyConOf (undefined :: V.FSVec () ())++-- unApply an expression and obtain the number of arguments found+unApp :: Exp -> (Exp, [Exp], Int)+unApp e = (first, rest, n)+ where (first:rest, n) = unAppAc ([],0) e+ unAppAc (xs,n) (f `AppE` arg) = unAppAc (arg:xs, n+1) f+ unAppAc (xs,n) f = (f:xs,n)++++typeRepQName :: FSDTypeRep -> String+typeRepQName rep = mod ++ dot ++ name+ where tr = fsdTyRep rep+ tc = typeRepTyCon tr+ mod = tyConModule tc+ name = tyConName tc+ dot = if mod=="" then "" else "."
+ src/ForSyDe/Deep/Backend/VHDL/Translate/HigherOrderFunctions.hs view
@@ -0,0 +1,211 @@+{-# LANGUAGE TemplateHaskell #-}++module ForSyDe.Deep.Backend.VHDL.Translate.HigherOrderFunctions (+ isHigherOrderFunction,+ translateHigherOrderFunctionBody+ ) where++import Language.Haskell.TH+import qualified Language.Haskell.TH as TH+import ForSyDe.Deep.Backend.VHDL.AST+import qualified ForSyDe.Deep.Backend.VHDL.AST as VHDL+import qualified Data.Param.FSVec as V++import ForSyDe.Deep.Backend.VHDL.Traverse.VHDLM+import Data.Maybe (isJust)+import Data.Data (tyconUQname)+import Control.Monad (when)+import Control.Monad.State+++isHigherOrderFunction :: TH.Exp -> Bool+isHigherOrderFunction = isJust.getHoF++translateHigherOrderFunctionBody :: TH.Exp -> VHDLM [SeqSm]+translateHigherOrderFunctionBody exp = case getHoF exp of+ (Just transF) -> transF exp+ -- This should not happen (translation should be guarded by isHigherOrderFunction)+ Nothing -> error $ "This is not a supported higher-order function: "++(show.ppr $ exp)++type TrFun = TH.Exp -> VHDLM [VHDL.SeqSm]+-- -------------------------------------+-- Name Table for higher order functions+-- -------------------------------------++getHoF :: TH.Exp -> Maybe TrFun+getHoF exp@(AppE _ _) = lookup (fname, nargs) hofNameTable+ where+ ((VarE fname), _, nargs) = unApp exp+getHoF _ = Nothing++++hofNameTable :: [((TH.Name, Arity), TrFun)]+hofNameTable = [(('V.foldl, 3), translateFold 'V.foldl "RANGE"),+ (('V.foldr, 3), translateFold 'V.foldr "REVERSE_RANGE"),+ (('V.map, 2), translateZipWith 'V.map 2),+ (('V.zipWith, 3), translateZipWith 'V.zipWith 3),+ (('V.zipWith3, 4), translateZipWith 'V.zipWith3 4)]+++-- ---------------------+-- Translation functions+-- ---------------------++translateFold :: TH.Name -> String -> TH.Exp -> VHDLM [VHDL.SeqSm]+translateFold selfName attributeName exp@((((VarE foldname) `AppE` (VarE fname)) `AppE` (VarE _)) `AppE` (VarE _)) | foldname == selfName = do+ fnameId <- transTHName2VHDL fname+ loopvarId <- genVHDLId "i"+ stateId <- genVHDLId "state"++ --- Nat s => (a -> b -> a) -> a -> FSVec s b -> a + fSpec <- getCurrentFunctionSpec+ let Function _ [IfaceVarDec initId stateType,+ IfaceVarDec argId _] _ = fSpec++ assertNormalForm exp fSpec++ -- check whether this is a known operator+ knownTranslation <- lookupName fname+ let fCall = case knownTranslation of + Just (arity, genFCallExp) -> (\args -> genFCallExp (map PrimName args))+ Nothing -> (\args -> fnameId $: args)++ -- variable state : stateType+ let vardecl = SPVD $ VarDec stateId (SubtypeIn stateType Nothing) Nothing+ addDecsToFunTransST [vardecl]++ -- state := init+ let initStmt = (NSimple stateId) := (PrimName . NSimple $ initId)++ -- for i in arg'range loop+ -- state := f(state, arg(i))+ -- end loop+ let rangeAttrib = unsafeVHDLBasicId attributeName+ range = AttribRange $ AttribName (NSimple argId) rangeAttrib Nothing+ body = [(NSimple stateId) := (fCall [NSimple stateId, argId!:loopvarId])]+ forLoopStmt = ForSM loopvarId range body++ -- return state+ let retStmt = ReturnSm . Just . PrimName . NSimple $ stateId++ return [initStmt, forLoopStmt, retStmt]+translateFold selfName _ e = error $ "Invalid application of "++(show selfName)++": got `"++(pprint e)++"` expected `"++(show selfName)++" f arg`"+++translateZipWith :: TH.Name -> Int -> TH.Exp -> VHDLM [VHDL.SeqSm]+translateZipWith selfName selfArity exp | (hofName == selfName) && (nArgs == selfArity) = do+ fnameV <- transTHName2VHDL fname+ loopvar <- genVHDLId "i"+ retnameV <- genVHDLId "ret"++ fSpec <- getCurrentFunctionSpec+ let Function _ _ rettype = fSpec++ assertNormalForm exp fSpec++ -- check whether this is a known operator+ knownTranslation <- lookupName fname+ let fCall = case knownTranslation of + Just (arity, genFCallExp) -> (\args -> genFCallExp (map PrimName args))+ Nothing -> (\args -> fnameV $: args)++ argNames <- mapM (\(VarE argName) -> transTHName2VHDL argName) thArgs++ -- variable ret : rettype+ let vardecl = SPVD $ VarDec retnameV (SubtypeIn rettype Nothing) Nothing+ addDecsToFunTransST [vardecl]++ -- for i in arg'range loop+ -- ret(i) := f(arg1(i), arg2(i), [...])+ -- end loop+ let rangeAttrib = unsafeVHDLBasicId "RANGE"+ range = AttribRange $ AttribName (NSimple retnameV) rangeAttrib Nothing+ indexedArgLst = map (\name -> name!:loopvar) argNames+ body = [(retnameV!:loopvar) := (fCall indexedArgLst)]+ loopStmt = ForSM loopvar range body+ -- return ret+ let returnStmt = ReturnSm . Just . PrimName . NSimple $ retnameV++ return $ [loopStmt, returnStmt]+ where+ ((VarE hofName), (VarE fname):thArgs, nArgs) = unApp exp+translateZipWith selfName selfArity e = error $ "Invalid application of "++self++": got `"++exp++"` expected `"++self++" f "++args++"`"+ where self = show selfName+ exp = pprint e+ args = unwords $ replicate selfArity "arg"+++-- ---------+-- Utilities+-- ---------++-- AST level indexing operator+(!:) :: VHDLId -> VHDLId -> VHDLName+(!:) name index = NIndexed $ IndexedName (NSimple name) [PrimName $ NSimple index]++-- AST level function call operator+($:) :: VHDLId -> [VHDLName] -> VHDL.Expr+($:) fname args = PrimFCall $ FCall (NSimple fname) (map (\a -> Nothing :=>: (ADName a)) args)++-- AST level sequential assignment operator+-- (:=) :: VHDLName -> VHDL.Expr -> VHDL.SeqSm+-- defined in ForSyDe.Deep.Backend.VHDL.AST++-- AST level map-association operator+-- (:=>: :: Maybe SimpleName -> ActualDesig -> AssocElem+-- defined in ForSyDe.Deep.Backend.VHDL.AST++-- unApply an expression and obtain the number of arguments found+unApp :: Exp -> (Exp, [Exp], Int)+unApp e = (first, rest, n)+ where (first:rest, n) = unAppAc ([],0) e+ unAppAc (xs,n) (f `AppE` arg) = unAppAc (arg:xs, n+1) f+ unAppAc (xs,n) f = (f:xs,n)++transTHName2VHDL :: TH.Name -> VHDLM VHDLId+transTHName2VHDL = liftEProne . mkVHDLExtId . tyconUQname . pprint++lookupName :: Name -> VHDLM (Maybe (Arity, [VHDL.Expr] -> VHDL.Expr))+lookupName name = do+ nameTable <- gets (nameTable.funTransST.local)+ return $ lookup name nameTable+++-- Check whether the given function application and the given function+-- signature are in normal form. Raises an error when the normal form is+-- violated, otherwise simply returns ().+--+-- The signature is derived from the outer definition, while the application+-- expression represents the body of the specialized function +-- Some specializations in normal form:+-- map_f v = map f v+-- foldl_f i v = foldl f i v+-- zipWith_f a b = zipWith f a b+--+-- Normal form means, that apart from the higher order argument, the+-- argument lists agree. This means that all arguments are explicitly written+-- and the names of all arguments are the same. This ensures, that the types+-- within the signature can be relied upon for translation purposes.+-- Additionally, the application of the specialized function needs to be the+-- sole expresssion in the function body, which ensures that the return type of+-- the function is valid as well.+--+-- Some specializations not in normal form:+-- map_f = map f+-- => incomplete argument list+-- foldl_f v i = foldl f i v+-- => order of arguments does not match+-- zipWith_f v = zipWith f v v+-- => duplicated argument+--+assertNormalForm :: TH.Exp -> VHDL.SubProgSpec -> VHDLM ()+assertNormalForm appl sig = do+ let (_, _:args, _) = unApp appl+ (Function _ argDecls _) = sig+ + argNamesBody <- mapM (\(VarE name) -> transTHName2VHDL name) args+ let argNamesSig = map (\(IfaceVarDec name _) -> name) argDecls+ + when (argNamesBody /= argNamesSig) $ error ("Higher order function application is not in normal form: "++(pprint appl)++"\n The argument list must agree")+ return ()
+ src/ForSyDe/Deep/Backend/VHDL/Traverse.hs view
@@ -0,0 +1,234 @@+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.Backend.VHDL.Traverse+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- This module provides specialized Netlist traversing functions aimed at+-- VHDL compilation.+-----------------------------------------------------------------------------+module ForSyDe.Deep.Backend.VHDL.Traverse+ (writeVHDLM,+ module ForSyDe.Deep.Backend.VHDL.Traverse.VHDLM) where++import ForSyDe.Deep.Backend.VHDL.Traverse.VHDLM+import ForSyDe.Deep.Backend.VHDL.Translate+import ForSyDe.Deep.Backend.VHDL.Generate+import ForSyDe.Deep.Backend.VHDL.FileIO+import ForSyDe.Deep.Backend.VHDL.AST+import ForSyDe.Deep.Backend.VHDL.Quartus (callQuartus)+import ForSyDe.Deep.Backend.VHDL.Modelsim++import ForSyDe.Deep.ForSyDeErr+import ForSyDe.Deep.System.SysDef+import ForSyDe.Deep.Process.ProcVal+import ForSyDe.Deep.Process.ProcFun+import ForSyDe.Deep.Netlist.Traverse+import ForSyDe.Deep.Netlist+import ForSyDe.Deep.OSharing++import Control.Monad.State+import System.Directory+import System.FilePath+import Data.Maybe (fromJust, isJust)++-- | Internal VHDL-Monad version of 'ForSyDe.Backend.writeVHDL'+-- (Note: the initial and final CWD will be / )+writeVHDLM :: VHDLM ()+writeVHDLM = do+ -- create and change to systemName/vhdl/work+ rootDir <- gets (sid.globalSysDef.global)+ let workDir = rootDir </> "vhdl" </> "work"+ liftIO $ createDirectoryIfMissing True workDir+ liftIO $ setCurrentDirectory workDir+ -- if we are in recursive mode, also write the local results+ -- for the rest of the subsystems+ rec <- isRecursiveSet+ when rec $ do subs <- gets (subSys.globalSysDef.global)+ let writeSub s =+ withLocalST (initLocalST ((readURef.unPrimSysDef) s))+ writeLocalVHDLM+ mapM_ writeSub subs+ -- write the local results for the first-level entity+ writeLocalVHDLM+ -- create and change to systemName/vhdl/systemName_lib+ -- (remember we are in workDir)+ let libDir = ".." </> rootDir ++ "_lib"+ liftIO $ createDirectoryIfMissing True libDir+ liftIO $ setCurrentDirectory $ libDir+ -- write the global results+ writeGlobalVHDLM+ -- change to systemName/vhdl+ liftIO $ setCurrentDirectory ".."+ -- call quartus if necessary+ callQuartus+ -- analyze with modelsim if necessary+ compile <- isCompileModelsimSet+ when compile compileResultsModelsim+ -- go back to the original directory+ liftIO $ setCurrentDirectory (".." </> "..")++-- | Write the global traversing results (i.e. the library design file)+-- accumulated in the state of the monad+writeGlobalVHDLM :: VHDLM ()+writeGlobalVHDLM = do+ gSysId <- gets (sid.globalSysDef.global)+ debugMsg $ "Generating global system library for `" ++ gSysId ++ "' ...\n"+ globalRes <- gets (globalRes.global)+ -- We can create the id unsafely because sysId was already checked in+ -- transSysDef2Ent+ let libName = gSysId ++ "_lib"+ libDesignFile = genLibDesignFile globalRes+ liftIO $ writeDesignFile libDesignFile (libName ++ ".vhd")+++-- | Traverse the netlist and write the local results (i.e. system design files)+writeLocalVHDLM :: VHDLM ()+writeLocalVHDLM = do+ gSysDefVal <- gets (globalSysDef.global)+ lSysDefVal <- gets (currSysDef.local)+ let lSysDefId = sid lSysDefVal+ debugMsg $ "Compiling system definition `" ++ lSysDefId ++ "' ...\n"+ -- Obtain the netlist of the system definition+ let nl = netlist lSysDefVal+ -- Traverse the netlist, and get the traversing results+ intOutsInfo <- traverseVHDLM nl+ LocalTravResult decs stms <- gets (localRes.local)+ let finalLogic = logic lSysDefVal+ -- Obtain the entity declaration of the system and the VHDL identifiers+ -- of the output signals.+ entity@(EntityDec _ eIface) <- transSysDef2Ent finalLogic lSysDefVal+ -- For each output signal, we need an assigment between its intermediate+ -- signal and the final output signal declared in the entity interface.+ let outIds = mapFilter (\(IfaceSigDec id _ _) -> id)+ (\(IfaceSigDec _ m _) -> m == Out) eIface+ outAssigns = genOutAssigns outIds intOutsInfo+ finalRes = LocalTravResult decs (stms ++ outAssigns)+ -- Finally, generate the design file+ sysDesignFile = genSysDesignFile (sid gSysDefVal) entity finalRes+ -- and write it to disk+ liftIO $ writeDesignFile sysDesignFile (lSysDefId ++ ".vhd")+ where mapFilter f p = foldr (\x ys -> if p x then (f x):ys else ys) []++-- | Traverse the netlist of a System Definition,+-- returning the (implicit) final traversing state and a list+-- containing the 'IntSignalInfo' of each output of the system+traverseVHDLM :: Netlist [] -> VHDLM [IntSignalInfo]+traverseVHDLM = traverseSEIO newVHDL defineVHDL++-- | \'new\' traversing function for the VHDL backend+newVHDL :: NlNode NlSignal -> VHDLM [(NlNodeOut, IntSignalInfo)]+newVHDL node = case node of+ -- FIXME: Skip the case, basing the generation of tags on+ -- outTags+ InPort id -> do vId <- transPortId2VHDL id+ return [(InPortOut, vId)]+ Proc pid proc -> withProcC pid $ do+ -- Obtain the VHDL id of the process+ vpid <- transProcId2VHDL pid+ -- function to create an intermediate signal out of the process+ -- identifier and a string suffix+ let procSuffSignal sigSuffix = unsafeIdAppend vpid sigSuffix+ -- Multiple output tags, add a numeric suffix specifying the output+ multOutTags =+ zipWith (\tag n -> (tag, procSuffSignal $ outSuffix ++ show n))+ (outTags node) [(1::Int)..]+ case proc of+ Const _ -> return [(ConstOut, procSuffSignal outSuffix)]+ ZipWithNSY _ _ -> return [(ZipWithNSYOut, procSuffSignal outSuffix)]+ ZipWithxSY _ _ -> return [(ZipWithxSYOut, procSuffSignal outSuffix)]+ UnzipNSY _ _ _ -> return multOutTags+ UnzipxSY _ _ _ _ -> return multOutTags+ DelaySY _ _ -> return [(DelaySYOut, procSuffSignal outSuffix)]+ SysIns _ _ ->+ -- Note: Here we could use the name of the System outputs instead of+ -- instanceid_out_n but ... that could cause+ -- clashes with the oher signal names (we only check for the+ -- of the uniqueness of all process ids within a system when+ -- creating it). We could check for those clashes but it would be+ -- ineffective and ilogical.+ return multOutTags+ where outSuffix = "_out"++-- | \'define\' traversing function for the VHDL backend+defineVHDL :: [(NlNodeOut, IntSignalInfo)]+ -> NlNode IntSignalInfo+ -> VHDLM ()+defineVHDL outs ins = do+ case (outs,ins) of+ (_, InPort _) -> return ()+ (outs, Proc pid proc) -> withProcC pid $ do+ -- We can unsafely transform the pid to a VHDL identifier because+ -- it was checked in newVHDL+ let vPid = unsafeVHDLExtId pid+ case (outs, proc) of+ ([(ConstOut, intSig)], Const ProcVal{valAST=ast}) -> do+ -- Generate a Signal declaration for the constant+ let cons = expVal ast+ dec <- withProcValC cons $ transVHDLName2SigDec+ intSig (expTyp ast) (Just cons)+ addSigDec dec+ ([(ZipWithNSYOut, intOut)], ZipWithNSY f intIns) -> do+ -- Translate the zipWithN process to a block+ -- and get the declaration of its output signal+ (block, dec) <- transZipWithN2Block vPid intIns (tpfloc f) (tast f) intOut+ addStm $ CSBSm block+ -- Generate a signal declaration for the resulting signal+ addSigDec dec+ ([(ZipWithxSYOut, intOut)], ZipWithxSY f intIns) -> do+ -- Translate the zipWithx process to a block+ -- and get the declaration of its output signal+ (block, dec) <- transZipWithx2Block vPid intIns (tpfloc f) (tast f) intOut+ addStm $ CSBSm block+ -- Generate a signal declaration for the resulting signal+ addSigDec dec+ (intOuts, UnzipNSY outTypes _ intIn) -> do+ -- Translate the zipWithNSY process to a block+ -- and get the declaration of its output signal+ (block, decs) <- transUnzipNSY2Block vPid intIn (map snd intOuts) outTypes+ addStm $ CSBSm block+ -- Generate a signal declaration for the resulting signals+ mapM_ addSigDec decs+ (intOuts, UnzipxSY typ size _ intIn) -> do+ -- Translate the UnzipxSY process to a block+ -- and get the declaration of its output signal+ (block, decs) <- transUnzipxSY2Block vPid intIn (map snd intOuts) typ size+ addStm $ CSBSm block+ -- Generate a signal declaration for the resulting signals+ mapM_ addSigDec decs+ ([(DelaySYOut, intOut)], DelaySY initExp intIn) -> do+ -- Translate the delay process to a block+ -- and get the declaration of its output signal+ (block, dec) <- transDelay2Block vPid intIn (valAST initExp) intOut+ addStm $ CSBSm block+ -- Generate a signal declaration for the resulting delayed signal+ addSigDec dec++ (intOuts, SysIns pSys intIns) -> do+ let parentSysRef = unPrimSysDef pSys+ parentSysVal = readURef parentSysRef+ parentLogic = logic parentSysVal+ parentInIface = iIface parentSysVal+ parentOutIface = oIface parentSysVal+ typedOuts = zipWith (\(_, t) (_, int) -> (int,t)) parentOutIface+ intOuts+ parentId = sid parentSysVal+ -- Translate the instance to a component instantiation+ -- and get the declaration of the output signals+ (mCompIns, decs) <- transSysIns2CompIns parentLogic+ vPid+ intIns+ typedOuts+ parentId+ (map fst parentInIface)+ (map fst parentOutIface)+ when (isJust mCompIns) (addStm $ CSISm $ fromJust mCompIns)+ -- Generate a signal declaration for each of the resulting signals+ mapM_ addSigDec decs++-- Othewise there is a problem of inconsisten tags+ _ -> intError "ForSyDe.Backend.VHDL.Traverse.defineVHDL" InconsOutTag
+ src/ForSyDe/Deep/Backend/VHDL/Traverse/VHDLM.hs view
@@ -0,0 +1,614 @@+{-# LANGUAGE FlexibleContexts #-}+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.Backend.VHDL.Traverse.VHDLM+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- 'VHDM' (VHDL Monad), related types and functions+--+-----------------------------------------------------------------------------+module ForSyDe.Deep.Backend.VHDL.Traverse.VHDLM where++import ForSyDe.Deep.Backend.VHDL.AST+import qualified ForSyDe.Deep.Backend.VHDL.AST as VHDL+import {-# SOURCE #-} ForSyDe.Deep.Backend.VHDL.GlobalNameTable (globalNameTable)++import ForSyDe.Deep.Ids+import ForSyDe.Deep.ForSyDeErr+import ForSyDe.Deep.System.SysDef (SysDefVal(..))+import ForSyDe.Deep.Netlist.Traverse (TravSEIO)+import ForSyDe.Deep.Process.ProcType (EnumAlgTy(..))++import Data.Data (tyconModule)+import Data.Maybe (fromJust)+import qualified Data.Set as S (filter)+import Data.Set (Set, union, empty, toList)+import Control.Monad.State+import Language.Haskell.TH (nameBase, nameModule, Name, Exp, Arity)+import qualified Language.Haskell.TH as TH+import Data.Typeable (TypeRep,typeRepTyCon,tyConName,tyConPackage,tyConModule)+import Data.Typeable.FSDTypeRepLib++-------------------------------------+-- How does the VHDL Backend work? --+-------------------------------------+-- FIXME: This documentation is a bit outated+-- All the types used in the the System Defintion are translated to VHDL+-- put into Package, the into a Design File and written to disk.+--+-- The System Definition itself is translated to another VHDL Design File and+-- written to disk.+--+-- This Design File will contain only two library units;+-- an Entity Declaration and an Architecture.+-- 1) The Entity Declaration can be obtained from the SysDef directly (without+-- traversing the netlist)+-- 2) The Architecture (or more specifically, its declarations and+-- the statements) is obtained from the netlist by traversing it.+--+-- The state of the traversal is composed by+-- * list of type defintions translated during the traversal+-- * table of equivalence between Haskell types and the VHDL identifier+-- of its translated type (used to avoid translating the same type+-- multiple times)+-- * the list of declarations of the architecture+-- * the list of statements of the architecture+-- * a table of System Definition references, used to keep track of the+-- system definitions (corresponding to one or more instances in the+-- netlist) whose code was already generated.+--+-- For each process (netlist node) found during the traversal:+-- * A signal declaration is generated for each output and added to+-- the list of architecture declarations.+-- * A VHDL block including the translation of the process is generated+-- and added to the list of architecture statements.+--+-- In the special case of finding a System Instance+-- 1) a port map statement is generated and added to the list of+-- architecture statements.+-- 2) the System Definition table is used to check if the Design File of+-- the System Definition associated with the instance was written to+-- disk.+-- 3) if the the associated System Definition wasn't in the table+-- 1) generate and write to disk the corresponding Design File+-- 2) add the System Definition to the table++-------------+-- FunTransST+-------------++-- | Function translation state. State used during the translation of+-- ProcFuns to VHDL.+--+-- This type provides the number of fresh names already generated,+-- a translation table from Template Haskell Names to VHDL Expressions+-- (a symbol table) and auxiliary VHDL declarations.+--+-- It only makes sense in a process-function context.+data FunTransST = FunTransST+ {freshNameCount :: Int,+ nameTable :: [(Name, (Arity, [VHDL.Expr] -> VHDL.Expr ) )],+ -- The table entries work as follows:+ -- (Template Haskell Name (table key),+ -- (Arity, function with which to construct the translated VHDL expression+ -- given itsarguments already translated to VHDL+ -- )+ auxDecs :: [SubProgDecItem],+ -- Auxiliary VHDL declarations generated during the translation of+ -- the ProcFun to be put in the declaration block of the translated VHDL+ -- function.+ currentFSpec :: Maybe VHDL.SubProgSpec}+ -- The function signature currently being compiled+++-- | Initial translation state for functions+initFunTransST :: FunTransST+initFunTransST = FunTransST 0 globalNameTable [] Nothing++-----------+-- VHDLM --+-----------++-- | VHDL backend monad+type VHDLM a = TravSEIO VHDLTravST ContextErr a++----------------+-- VHDLTravST --+----------------++-- | VHDL traversing State. (see 'ForSyDe.Netlist.Traverse.traverseSIO')+data VHDLTravST = VHDLTravST+ {local :: LocalVHDLST, -- Local State (related to the system currently+ -- compiled)+ global :: GlobalVHDLST} -- Global state (related to all systems being+ -- recursively compiled)++data LocalVHDLST = LocalVHDLST+ {currSysDef :: SysDefVal, -- System definition which is currently+ -- being compiled+ context :: Context, -- Error Context+ funTransST :: FunTransST, -- Translation state for functions (ProcFuns)+ -- It only makes sense+ -- in a process-function context+ localRes :: LocalTravResult} -- Result accumulated during the+ -- traversal of current System Definition+ -- netlist+++++-- | initialize the local state+initLocalST :: SysDefVal -> LocalVHDLST+initLocalST sysDefVal =+ LocalVHDLST sysDefVal (SysDefC (sid sysDefVal) (loc sysDefVal))+ initFunTransST emptyLocalTravResult++-- | Execute certain operation with a concrete local state.+-- The initial local state is restored after the operation is executed+withLocalST :: LocalVHDLST -> VHDLM a -> VHDLM a+withLocalST l' action = do+ -- get the initial local state+ st <- get+ let l = local st+ -- set the modified state+ put st{local=l'}+ -- execute the action+ res <- action+ -- restore the initial local state+ st' <- get+ put st'{local=l}+ -- return the result+ return res++-- | Execute certain operation with the initial function translation state+-- The initial state is restored after the operation is executed+withInitFunTransST :: VHDLM a -> VHDLM a+withInitFunTransST action = do+ -- get the initial name space+ st <- get+ let l = local st+ ns = funTransST l+ -- set the empty name space+ put st{local=l{funTransST=initFunTransST}}+ -- execute the action+ res <- action+ -- restore the initial name table+ st' <- get+ let l' = local st'+ put st'{local=l'{funTransST=ns}}+ -- return the result+ return res++++data GlobalVHDLST = GlobalVHDLST+ {globalSysDef :: SysDefVal, -- global system definition+ -- (the first-level system being compiled)+ ops :: VHDLOps, -- Compilation options+ globalRes :: GlobalTravResult, -- Result accumulated during the+ -- whole compilation+ enumTypes :: Set EnumAlgTy, -- Set of the enumerated+ -- algebraic types accumulated+ -- by all ProcFuns and ProcVals+ -- in the system+ typeTable :: [(FSDTypeRep, TypeMark)], -- Type translation table+ transUnconsFSVecs :: [FSDTypeRep]} -- Unconstrained FSVecs previously translated.+ -- Each unconstrained FSVec is represented by+ -- the 'TypeRep' of its elements+++-- | Empty initial traversing state+initGlobalVHDLST :: SysDefVal -> GlobalVHDLST+initGlobalVHDLST sysDefVal =+ GlobalVHDLST sysDefVal defaultVHDLOps emptyGlobalTravResult empty [] []++-- | Empty initial traversing state+initVHDLTravST :: SysDefVal -> VHDLTravST+initVHDLTravST sysDefVal =+ VHDLTravST (initLocalST sysDefVal) (initGlobalVHDLST sysDefVal)++-------------+-- TravResult+-------------++-- | Local result accumulated during the traversal of a netlist+data LocalTravResult = LocalTravResult+ {archDecs :: [BlockDecItem], -- generated architecture declarations+ archSms :: [ConcSm] } -- generated architecture statements++++-- | empty local VHDL compilation result+emptyLocalTravResult :: LocalTravResult+emptyLocalTravResult = LocalTravResult [] []+++-- | Global Results accumulated throughout the whole compilation+data GlobalTravResult = GlobalTravResult+ {typeDecs :: [TypeDec], -- Types translated during the traversal+ subtypeDecs :: [SubtypeDec], -- Subtypes translated during the traversal+ subProgBodies :: [SubProgBody] } -- Functions or procedures generated during+ -- the traversal++++-- | empty global VHDL compilation result+emptyGlobalTravResult :: GlobalTravResult+emptyGlobalTravResult = GlobalTravResult [] [] []+++----------+-- VHDLOps+----------++-- | VHDL Compilation options+data VHDLOps = VHDLOps {debugVHDL :: VHDLDebugLevel, -- ^ Debug mode+ recursivityVHDL :: VHDLRecursivity,+ execQuartus :: Maybe QuartusOps, -- ^ Analyze the generated code with Quartus+ compileModelsim :: Bool -- ^ Compile the generated code with Modelsim+ }+ deriving (Eq, Show)++-- | Debug level+data VHDLDebugLevel = VHDLNormal | VHDLVerbose+ deriving (Eq, Ord, Show)++-- | Print a message to stdout if in verbose mode+debugMsg :: String -> VHDLM ()+debugMsg str = do+ debugLevel <- gets (debugVHDL.ops.global)+ when (debugLevel > VHDLNormal)+ (liftIO $ putStr ("DEBUG: " ++ str))++-- | Recursivity, should the parent systems of system instances be compiled as+-- well?+data VHDLRecursivity = VHDLRecursive | VHDLNonRecursive+ deriving (Eq, Show)++-------------+-- QuartusOps+-------------++-- Quartus options++-- | Options passed to Quartus II by the VHDL Backend. Most of them are optional+-- and Quartus will use a default value.+--+-- It contains:+--+-- * What action to perform+--+-- * Optinally, the minimum acceptable clock frequency (fMax) expressed in MHz+--+-- * FPGA family and specific device model (both are independently optional).+--+-- * Pin assignments, in the form (VHDL Pin, FPGA Pin). Note+-- that Quartus will automatically split composite VHDL ports+--- (arrays and records) in various pins with different logical names.+data QuartusOps =+ QuartusOps {action :: QuartusAction,+ fMax :: Maybe Int,+ fpgaFamiliyDevice :: Maybe (String, Maybe String),+ pinAssigs :: [(String,String)] }+ deriving (Eq, Show)++-- | Action to perform by Quartus+data QuartusAction = AnalysisAndElaboration -- ^ Analysis and eleboration flow+ | AnalysisAndSynthesis -- ^ Call map executable+ | FullCompilation -- ^ Compile flow+ deriving (Eq, Show)++-- | Options to check if the model is synthesizable, all options except+-- the action to take are set to default.+checkSynthesisQuartus :: QuartusOps+checkSynthesisQuartus = QuartusOps AnalysisAndSynthesis Nothing Nothing []+++-- | Check if we are in recursive mode+isRecursiveSet :: VHDLM Bool+isRecursiveSet = do+ recOp <- gets (recursivityVHDL.ops.global)+ return $ recOp == VHDLRecursive++-- | Check if we want to compile the results with modelsim+isCompileModelsimSet :: VHDLM Bool+isCompileModelsimSet = gets (compileModelsim.ops.global)++-- | Default traversing options+defaultVHDLOps :: VHDLOps+defaultVHDLOps = VHDLOps VHDLNormal VHDLRecursive Nothing False+++-- | Set VHDL options inside the VHDL monad+setVHDLOps :: VHDLOps -> VHDLM ()+setVHDLOps options = modify (\st -> st{global=(global st){ops=options}})+++-------------------------------------+-- Useful functions in the VHDL Monad+-------------------------------------+++-- | Add a signal declaration to the 'LocalTravResult' in the State+addSigDec :: SigDec -> VHDLM ()+addSigDec dec = modify addFun+ -- FIXME: use a queue for the declarations+ where addFun st = st{local=l{localRes=r{archDecs=ads ++ [BDISD dec]}}}+ where l = local st+ r = localRes l+ ads = archDecs r+++-- | Add a statement to the 'LocalTravResult' in the State+addStm :: ConcSm -> VHDLM ()+addStm sm = modify addFun+ -- FIXME: use a queue for the statements+ where addFun st = st{local=l{localRes=r{archSms=aSms ++ [sm]}}}+ where l = local st+ r = localRes l+ aSms = archSms r+++-- | Find a previously translated custom type+lookupCustomType :: FSDTypeRep -> VHDLM (Maybe SimpleName)+lookupCustomType rep = do+ transTable <- gets (typeTable.global)+ let res = lookup rep transTable+ return res+ --when (res==Nothing) $ mapM_ (\(elem,_) -> liftIO $ putStrLn $ (tyConName.typeRepTyCon) elem ++ "," ++ (tyConModule.typeRepTyCon) elem ++ "," ++(tyConPackage.typeRepTyCon) elem) transTable+ --when (res==Nothing) (liftIO $ putStrLn $ (tyConName.typeRepTyCon) rep ++ "," ++ (tyConModule.typeRepTyCon) rep ++ "," ++(tyConPackage.typeRepTyCon) rep ++ "\n")+ --let gkey = typeRepKey rep+ --mapM_ (\(elem,_) -> let key = typeRepKey elem in liftIO $ putStrLn $ show (gkey==key) ++ ".." ++(tyConPackage.typeRepTyCon) elem ++ ".." ++ (tyConModule.typeRepTyCon) elem ++ ".." ++(tyConName.typeRepTyCon) elem) transTable++ --liftIO$putStrLn$ "\n" ++ (tyConPackage.typeRepTyCon) rep ++ ".." ++ (tyConModule.typeRepTyCon) rep ++ ".." ++(tyConName.typeRepTyCon) rep++ --liftIO$putStrLn$show (lookup rep transTable) ++ "\n\n"++ --return $ lookup rep transTable+++-- | Add enumerated types to the global state+addEnumTypes :: Set EnumAlgTy -> VHDLM ()+addEnumTypes newETs = do+ globalST <- gets global+ let oldETs = enumTypes globalST+ modify (\st -> st{global = globalST {enumTypes = oldETs `union` newETs}})++-- | Check if a Template haskell 'Name' corresponding to+-- a Enumerated-type data constructor is present in the enumerated+-- types accumulated in the global state and return the corresponding+-- VHDL identifier.+getEnumConsId :: Name -> VHDLM (Maybe VHDLId)+getEnumConsId consName = do+ let consModule = (fromJust.nameModule) consName+ consBase = nameBase consName+ enums <- gets (enumTypes.global)+ let matchesName (EnumAlgTy dataName enums) =+ tyconModule dataName == consModule && elem consBase enums+ case (toList.(S.filter matchesName)) enums of+ [] -> return Nothing+ [_] -> liftM Just (liftEProne $ mkVHDLExtId consBase)+ -- _ -> this shouldn't happen since the enumerated types stored are unique+ _ -> intError "ForSyDe.Backend.VHDL.Traverse.VHDLM.getEnum"+ (UntranslatableVHDLFun $ GeneralErr (Other "pattern match inconsistency"))+++-- | Add a cutom type to the global results and type translation table+addCustomType :: FSDTypeRep -> Either TypeDec SubtypeDec -> VHDLM ()+addCustomType rep eTD = do+ globalST <- gets global+ let transTable = typeTable globalST+ gRes = globalRes globalST+ tDecs = typeDecs gRes+ stDecs = subtypeDecs gRes+ -- FIXME: use queues+ modify (\st -> st{global =+ case eTD of+ Left td@(TypeDec id _) ->+ if id `notElem` (map snd transTable) then+ globalST+ {typeTable = transTable ++ [(rep, id)],+ globalRes = gRes{typeDecs = tDecs ++ [td]}}+ else globalST+ Right std@(SubtypeDec id _) ->+ if id `notElem` (map snd transTable) then+ globalST+ {typeTable = transTable ++ [(rep, id)],+ globalRes = gRes{subtypeDecs = stDecs ++ [std]}}+ else globalST+ })++-- | Add type declaration to the global results+addTypeDec :: TypeDec -> VHDLM ()+addTypeDec td = do+ globalST <- gets global+ let gRes = globalRes globalST+ tDecs = typeDecs gRes+ -- FIXME: use queues+ modify (\st -> st{global = globalST{globalRes = gRes{typeDecs = tDecs ++ [td]}}})+++-- | Add subtype declaration to the global results+addSubtypeDec :: SubtypeDec -> VHDLM ()+addSubtypeDec std = do+ globalST <- gets global+ let gRes = globalRes globalST+ stDecs = subtypeDecs gRes+ -- FIXME: use queues+ modify (\st -> st{global = globalST{+ globalRes = gRes{subtypeDecs = stDecs ++ [std]}}})+++-- | Add an unconstrained FSVec to the global results+addUnconsFSVec :: FSDTypeRep -> VHDLM ()+addUnconsFSVec trep = do+ globalST <- gets global+ -- FIXME: use queues+ modify (\st -> st{global =+ globalST{+ transUnconsFSVecs = (transUnconsFSVecs globalST) ++ [trep]}})++-- | Add a subprogram to the global results+addSubProgBody :: SubProgBody -> VHDLM ()+addSubProgBody newBody = do+ globalST <- gets global+ let gRes = globalRes globalST+ bodies = subProgBodies gRes+ -- FIXME: use queues+ modify (\st -> st{global = globalST+ {globalRes = gRes{subProgBodies = bodies ++ [newBody]}}})+++-- | Add a TH-name (arity, VHDL expression construtor function) pair to the translation namespace table+addTransNamePair :: Name -> Arity -> ([Expr] -> Expr) -> VHDLM ()+addTransNamePair thName arity vHDLFun = do+ lState <- gets local+ let s = funTransST lState+ table = nameTable s+ modify (\st -> st{local=lState{funTransST=s{+ nameTable=(thName,(arity,vHDLFun)):table}}})++-- | Add a declarations to Auxiliary VHDL declarations of the Function+-- translation state+addDecsToFunTransST :: [SubProgDecItem] -> VHDLM ()+addDecsToFunTransST decs = do+ lState <- gets local+ let s = funTransST lState+ auxs = auxDecs s+ modify (\st -> st{local=lState{funTransST=s{+ auxDecs=decs++auxs}}})++++getCurrentFunctionSpec :: VHDLM SubProgSpec+getCurrentFunctionSpec = do + fSpecM <- gets (currentFSpec.funTransST.local)+ case fSpecM of+ Just fSpec -> return fSpec+ Nothing -> error "Bug: Incomplete translation context, found `Nothing` expected `Just SubProgSpec`"++putCurrentFunctionSpec :: SubProgSpec -> VHDLM ()+putCurrentFunctionSpec spec = do+ lState <- gets local+ let s = funTransST lState+ modify (\st -> st{local=lState{funTransST=s{+ currentFSpec=Just spec}}})++-- | Get a fresh VHDL Identifier and increment the+-- tranlation-namespace-count of freshly generated identifiers.+--+-- Note that all user-supplied identifiers (process ids, port ids,+-- and function parameters) are translated to extended VHDL+-- identifiers. That, together with the fact that basic and extended+-- VHDL identifers live in different namespaces, guarantees that+-- freshly generated basic VHDL identifiers cannot clash with the+-- ones supplied by the frontend.+genFreshVHDLId :: VHDLM VHDLId+genFreshVHDLId = genVHDLId "fresh_"++genVHDLId :: String -> VHDLM VHDLId+genVHDLId prefix = do+ lState <- gets local+ let ns = funTransST lState+ count = freshNameCount ns+ modify (\st -> st{local=lState{funTransST=ns{freshNameCount=count+1}}})+ liftEProne.mkVHDLBasicId $ (prefix ++ show count)++-- | Lift an 'EProne' value to the VHDL monad setting current error context+-- for the error+-- liftEProne :: EProne a -> VHDLM a+liftEProne :: EProne a -> VHDLM a+liftEProne ep = do+ cxt <- gets (context.local)+ either (throwError.(ContextErr cxt)) return ep++-- | Throw a ForSyDe error, setting current error context+throwFError :: ForSyDeErr -> VHDLM a+throwFError = liftEProne.Left++++-- | Execute certain operation with a concrete process context.+-- The initial context is restored after the operation is executed+-- Note: the initial context must be a system context or 'InconsistenContexts'+-- will be raised.+withProcC :: ProcId -> VHDLM a -> VHDLM a+withProcC pid action = do+ -- get the initial context+ st <- get+ let l = local st+ c = context l+ -- set the modified name context+ put st{local=l{context=setProcC pid c}}+ -- execute the action+ res <- action+ -- restore the initial name context+ st' <- get+ let l' = local st'+ put st'{local=l'{context=c}}+ -- return the result+ return res++++-- | Execute certain operation with a concrete process function context.+-- The initial context is restored after the operation is executed+-- Note: the initial context must be a process context or+-- 'InconsistenContexts' will be raised.+withProcFunC :: Name -> Loc -> VHDLM a -> VHDLM a+withProcFunC name loc action = do+ -- get the initial context+ st <- get+ let l = local st+ c = context l+ -- set the modified context+ put st{local=l{context=setProcFunC name loc c}}+ -- execute the action+ res <- action+ -- restore the initial context+ st' <- get+ let l' = local st'+ put st'{local=l'{context=c}}+ -- return the result+ return res++++-- | Execute certain operation with a concrete process function context.+-- The initial context is restored after the operation is executed+-- Note: the initial context must be a process context or+-- 'InconsistenContexts' will be raised.+withProcValC :: Exp -> VHDLM a -> VHDLM a+withProcValC exp action = do+ -- get the initial context+ st <- get+ let l = local st+ c = context l+ -- set the modified context+ put st{local=l{context=setProcValC exp c}}+ -- execute the action+ res <- action+ -- restore the initial context+ st' <- get+ let l' = local st'+ put st'{local=l'{context=c}}+ -- return the result+ return res+++----------------+-- IntSignalInfo+----------------++-- | Intermediate signal information. Tag generated for each output of each+-- node found during the traversal.+-- (see ForSyDe.Netlist.Traverse.traverseSIO).+-- It contains the VHDL intemediate signal name associated with the process+-- output.+type IntSignalInfo = SimpleName+
+ src/ForSyDe/Deep/Bit.hs view
@@ -0,0 +1,175 @@+{-# LANGUAGE TemplateHaskell, DeriveDataTypeable, MultiParamTypeClasses,+ FunctionalDependencies, TypeSynonymInstances #-} +-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Bit+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- 'Bit' Datatype. Note that the 'Num' instance is phony and shouldn't be used+-- +-----------------------------------------------------------------------------+module ForSyDe.Deep.Bit (Bit(..), + not, + bitToBool, + boolToBit,+ toBitVector8,+ fromBitVector8,+ toBitVector16,+ fromBitVector16,+ toBitVector32,+ fromBitVector32,+ toBitVector64,+ fromBitVector64,+ fixmul8) where++import Language.Haskell.TH.Lift+import Data.Int+import Data.Bits+import Data.Data (Data, Typeable)+import Prelude hiding (not)++import Data.Param.FSVec (FSVec, reallyUnsafeVector)+import qualified Data.Param.FSVec as V+import Data.TypeLevel.Num (D8,D16,D32,D64,Nat)++data Bit = H -- ^ High value + | L -- ^ Low value+ deriving (Eq, Show, Data, Typeable)++$(deriveLift1 ''Bit)++-- | Not operation over bits+not :: Bit -> Bit+not = complement+++instance Bits Bit where+ H .&. x = x+ L .&. _ = L+ H .|. _ = H+ L .|. x = x+ xor H L = H+ xor L H = H+ xor _ _ = L+ complement L = H+ complement H = L+ shift x 0 = x+ shift _ _ = L+ rotate x _ = x+ bitSize _ = 1+ isSigned _ = False+++-- Phony instance of Num+instance Num Bit where+ H + L = H+ H + H = L+ L + x = x+ -- since they are unsigned and there are only two elements, (-) == (+)+ (-) = (+)+ -- multiplication is equivalent to (.&.)+ (*) = (.&.)+ -- since a bit is unsigned, it is equivalent to identity+ abs = id+ signum _ = L+ fromInteger n = if n<=0 then L else H++-- | Convert a bit to a boolean+bitToBool :: Bit -> Bool+bitToBool H = True+bitToBool L = False++-- | Convert a boolean to a bit+boolToBit :: Bool -> Bit+boolToBit True = H+boolToBit False = L++toBitVector8 :: Int8 -> FSVec D8 Bit+toBitVector8 = reallyUnsafeToBitVector+ +fromBitVector8 :: FSVec D8 Bit -> Int8+fromBitVector8 = fromBitVectorDef 0+++toBitVector16 :: Int16 -> FSVec D16 Bit+toBitVector16 = reallyUnsafeToBitVector+ +fromBitVector16 :: FSVec D16 Bit -> Int16+fromBitVector16 = fromBitVectorDef 0+++toBitVector32 :: Int32 -> FSVec D32 Bit+toBitVector32 = reallyUnsafeToBitVector+ +fromBitVector32 :: FSVec D32 Bit -> Int32+fromBitVector32 = fromBitVectorDef 0+++toBitVector64 :: Int64 -> FSVec D64 Bit+toBitVector64 = reallyUnsafeToBitVector+ +fromBitVector64 :: FSVec D64 Bit -> Int64+fromBitVector64 = fromBitVectorDef 0+++fixmul8 :: Int8 -> Int8 -> Int8+fixmul8 a b = fromIntegral $ (fromIntegral a * fromIntegral b::Int16) `div` 128++++{- This would have been much more convenient+ But it makes things really complicated int he VHDL+ backend++-- | Datatypes which can be converted to and from+-- vectors of bits+class BitStream d s | d -> s where+ -- | convert a data type to a bit vector+ toBitVector :: d -> FSVec s Bit+ -- | get a datatype from a bit vector+ fromBitVector :: FSVec s Bit -> d++instance BitStream Int8 D8 where+ toBitVector = reallyUnsafeToBitVector+ fromBitVector = fromBitVectorDef 0+++instance BitStream Int16 D16 where+ toBitVector = reallyUnsafeToBitVector+ fromBitVector = fromBitVectorDef 0+++instance BitStream Int32 D32 where+ toBitVector = reallyUnsafeToBitVector+ fromBitVector = fromBitVectorDef 0+++instance BitStream Int64 D64 where+ toBitVector = reallyUnsafeToBitVector+ fromBitVector = fromBitVectorDef 0+-}++-------------------+-- Helper functions+-------------------+reallyUnsafeToBitVector :: Bits a => a -> FSVec s Bit+reallyUnsafeToBitVector x = + reallyUnsafeVector $ map (boolToBit.(testBit x)) [size-1,size-2..0]+ where size = bitSize x++-- | version of fromBitVector supplying a default initial value from which to +-- start working+fromBitVectorDef :: (Bits a, Nat s) => a -> FSVec s Bit -> a+fromBitVectorDef def vec = fst $ V.foldr f (def, 0) vec+ where f e (ac, pos) = (copyBit e ac pos, pos+1)+ copyBit H = setBit+ copyBit L = clearBit+ ++ +
+ src/ForSyDe/Deep/Config.hs view
@@ -0,0 +1,29 @@+{-# LANGUAGE TemplateHaskell, CPP #-}+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.Config+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- Configuration values of ForSyDe+--+-----------------------------------------------------------------------------+module ForSyDe.Deep.Config (forsydeVersion,+ maxTupleSize,+ module Paths_forsyde_deep) where++import Paths_forsyde_deep+import ForSyDe.Deep.Version (getVersion)++#ifdef DEVELOPER+maxTupleSize :: Int+maxTupleSize = 8+#else+import GHC.Exts (maxTupleSize)+#endif++forsydeVersion = $(getVersion)
+ src/ForSyDe/Deep/DFT.hs view
@@ -0,0 +1,79 @@+{-# LANGUAGE ScopedTypeVariables, FlexibleContexts #-}+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.DFT+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- This module includes the standard Discrete Fourier Transform (DFT)+-- function, and a fast Fourier transform (FFT) algorithm, for+-- computing the DFT, when the input vectors' length is a power of 2.+-----------------------------------------------------------------------------+module ForSyDe.Deep.DFT(dft, fft) where+++import qualified Data.Param.FSVec as V+import Data.Param.FSVec +import Data.TypeLevel (Nat, IsPowOf, D2)+import Data.Complex+++-- | The function 'dft' performs a standard Discrete Fourier Transformation+dft :: forall s . Nat s => FSVec s (Complex Double) -> FSVec s (Complex Double)+dft v = V.map (bigX_k v) nVector+ where+ lT = V.lengthT v+ lV = V.genericLength v+ -- FIXME: dft does not type-check without this type signature:+ -- learn why!+ nVector :: Num a => FSVec s a+ nVector = kVector lT+ fullCircle = V.map (\n -> -2*pi*n/lV) nVector+ bigX_k v k = sumV (V.zipWith (*) v (bigW k))+ bigW k = V.map (** k) (V.map cis fullCircle)+ sumV = V.foldl (+) (0:+ 0)+++-- | The function 'fft' implements a fast Fourier transform (FFT) algorithm, +-- for computing the DFT, when the size N is a power of 2.+fft :: (Nat s, IsPowOf D2 s) => + FSVec s (Complex Double) -> FSVec s (Complex Double)+fft v = V.map (bigX v) (kVector lT)+ where lT = V.lengthT v++kVector :: (Num a, Nat s) => s -> FSVec s a +kVector s = V.iterate s (+1) 0 +++bigX :: (Nat s, IsPowOf D2 s) => + FSVec s (Complex Double) -> Integer -> Complex Double+-- since there are no output length constraints (no vector is being returned)+-- we can simply obtain the list inside the vector and work with it directly+bigX v k = bigX' (V.genericLength v) (V.fromVector v) k+ where bigX' :: Integer -> [Complex Double] -> Integer -> Complex Double+ -- The first argument is the length of the list (bigN)+ bigX' _ (x0:[x1]) k | even k = x0 + x1 * bigW 2 0+ | odd k = x0 - x1 * bigW 2 0+ bigX' l xs k = bigF_even + bigF_odd * bigW l k+ where bigF_even = bigX' halfl (evens xs) k+ bigF_odd = bigX' halfl (odds xs) k+ halfl = l `div` 2++bigW :: Integer -> Integer -> Complex Double+bigW bigN k = cis (-2 * pi * (fromInteger k) / (fromInteger bigN))++evens :: [a] -> [a]+evens [] = []+evens [v1] = [v1] +evens (v1:_:v) = v1 : evens v++odds :: [a] -> [a]+odds [] = []+odds [_] = []+odds (_:v2:v) = v2 : odds v++
+ src/ForSyDe/Deep/FIR.hs view
@@ -0,0 +1,63 @@+{-# LANGUAGE TemplateHaskell, RelaxedPolyRec, PatternGuards #-}+-- The PatternGuards are used to hush innapropiate compiler warnings+-- see http://hackage.haskell.org/trac/ghc/ticket/2017+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.FIR+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- This module implements FIR filters for the synchronous computational model.+-----------------------------------------------------------------------------+module ForSyDe.Deep.FIR (fir) where++import ForSyDe.Deep.Ids+import ForSyDe.Deep.Signal+import ForSyDe.Deep.Process++import Data.TypeLevel.Num (Nat, Pos)+import Data.Param.FSVec hiding ((++))+import qualified Data.Param.FSVec as V+import Data.Typeable++-- | +-- All kinds of FIR-filters can now be modeled by means of 'fir'. The+-- only argument needed is the list of coefficients, which is given as+-- a vector of any size. To illustrate this, an 8-th order band pass+-- filter is modeled as follows.+--+-- > bp = fir "fir Id" $(vectorTH [0.06318761339784, 0.08131651217682, 0.09562326700432, +-- > 0.10478344432968, 0.10793629404886, 0.10478344432968, +-- > 0.09562326700432, 0.08131651217682, 0.06318761339784 ])+-- ++fir :: (Fractional b, ProcType b, Pos s, Typeable s) => + ProcId -> FSVec s b -> Signal b -> Signal b+fir id h = innerProd (id ++ "_innerProd") h . sipo (id ++ "_sipo") k 0.0+ where k = V.lengthT h++sipo :: (Pos s, Typeable s, Fractional a, ProcType a) =>+ ProcId -> s -> a -> Signal a -> FSVec s (Signal a)+sipo id n s0 = unzipxSY (id ++ "_unzipxSY") . scanldSY (id ++ "_scanldSY") srV initState+ where initState = V.copy n s0+ srV = $(newProcFun [d| srV :: Pos s => FSVec s a -> a -> FSVec s a+ srV v a = V.shiftr v a |])++innerProd :: (Fractional a, ProcType a, Nat s, Typeable s) =>+ ProcId -> FSVec s a -> FSVec s (Signal a) -> Signal a+innerProd id h = zipWithxSY id (ipV `defArgVal` h)+ where ipV = $(newProcFun + -- We could make the inner product in one traverse + -- but FSVecs don't allow recursive calls+ -- (they don't allow to check the constraints statically)+ -- Thus, we traverse the vector twice+ [d| ipV :: (Nat s, Num a) => FSVec s a -> FSVec s a -> a+ ipV v1 v2 = + V.foldl (+) 0 $ V.zipWith (*) v1 v2 |])+++
+ src/ForSyDe/Deep/ForSyDeErr.hs view
@@ -0,0 +1,465 @@+{-# LANGUAGE FlexibleContexts #-}+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.ForSyDeErr+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- ForSyDe error-related types and functions.+--+-----------------------------------------------------------------------------+module ForSyDe.Deep.ForSyDeErr + (ForSyDeErr(..),+ ContextErr(..),+ VHDLFunErr(..),+ VHDLExpErr(..),+ Context(..),+ setProcC,+ setProcFunC,+ setProcValC,+ Loc,+ EProne,+ liftEither,+ uError,+ intError,+ qError,+ qGiveUp,+ qPutTraceMsg,+ printError,+ printVHDLError,+ printGraphMLError,+ module Control.Monad.Error,+ module Debug.Trace) where+++import ForSyDe.Deep.Ids++import Data.Maybe (fromMaybe)+import Debug.Trace+import Control.Monad.Error +import Data.Dynamic+import Language.Haskell.TH.Syntax hiding (Loc)+import Language.Haskell.TH.Ppr+import Language.Haskell.TH.PprLib+import Text.PrettyPrint.HughesPJ (render)+import Data.Typeable.FSDTypeRepLib++-------------+-- ForSyDeErr+-------------++-- | All Errors thrown or displayed in ForSyDe+data ForSyDeErr = + -- Used in ForSyDe.ForSyDeErr + InconsistentContexts |+ -- Used in ForSyDe.Netlist+ EvalErr String |++ -- Used in ForSyDe.System.*+ -- | Not a variable name+ NonVarName Name | + -- | Incompatible system function+ IncomSysF Name Type | + -- | Incompatible input interface length + InIfaceLength (SysId,Int) ([String],Int) | + -- | Incompatible output interface length+ OutIfaceLength (SysId,Int) ([String],Int) |+ -- | Multiply defined port identifier + MultPortId PortId |+ -- | Multiply defined process identifier + MultProcId ProcId |+ -- | The system contains components from different subsystems with+ -- the same Identifiers + SubSysIdClash SysId (Maybe Loc) (Maybe Loc) |+ -- | Not a SysDef variable+ NonSysDef Name Type |++ -- Used in ForSyDe.Proc.ProcFun + -- | Incorrect Declarations provided to create a ProcFun+ IncorrProcFunDecs [Dec] |++ -- Used in ForSyDe.Netlist.Traversable + -- | Inconsistent output tag+ InconsOutTag |++ -- Used in ForSyDe.Backend.Simulate+ -- | Inconsistent System Definition Port+ InconsSysDefPort PortId | + -- | Dynamic type mismatch+ DynMisMatch Dynamic TypeRep |+ -- | Signature mismatch+ SigMisMatch Type |+ -- | Simulation input signals length mismatch+ InLengthMisMatch Int Int | ++ -- Used in ForSyDe.Backend.VHDL.*+ -- | Empty VHDL identifier+ EmptyVHDLId |+ -- | Incorrect Basic VHDL Identifier+ IncVHDLBasId String |+ -- | Incorrect Extended VHDL Identifier+ IncVHDLExtId String |+ -- | Function untranslatable to VHDL (function name and error raised)+ UntranslatableVHDLFun VHDLFunErr |+ -- | Expresion untranslatable to VHDL+ UntranslatableVHDLExp Exp VHDLExpErr |+ -- Common Backend errors + -- | UnsUpported type+ UnsupportedType FSDTypeRep |+ -- | Reserved identifier+ ReservedId String |+ -- | Unsupported process+ UnsupportedProc |+ -- | Quartus Failed+ QuartusFailed |+ -- | Ghdl Failed+ GhdlFailed |+ -- | Modelsim Failed+ ModelsimFailed |++ -- | Other Errors+ Other String ++-- | Function translation errors in the VHDL backend+data VHDLFunErr =+ -- | Polymorphic declaration+ PolyDec Dec |+ -- | Unsupported declaration block+ UnsupportedDecBlock [Dec] |+ -- | Insufficient number of parameters + InsParamNum Name Int |+ -- | Unsupported input pattern in function + UnsupportedFunPat Pat |+ -- | Multiple clauses+ MultipleClauses [Clause] |+ -- | Guards in case alternatives are not supported+ FunGuardedBody Body |+ -- | A general Error which applies to a function+ -- (e.g. its name or parameters are not a VHDL identifier,+ -- an error in an inner expression ... )+ GeneralErr ForSyDeErr+++instance Show VHDLFunErr where+ show (PolyDec dec) = "polymorphic daclaration:\n" +++ pprint dec +++ "\nDeclarations within a ProcFun must be monomorphic in order to be " +++ "translatable to VHDL"+ show (UnsupportedDecBlock decs) = "Unsupported declaration block:\n" +++ concatMap pprint decs +++ "All declaration blocks within a process function must follow the following " +++ "pattern:\n" +++ " name1 :: type\n" +++ " name1 arg1 arg2 ... = defintion1\n" +++ " name2 :: type\n" +++ " name2 arg1 arg2 .... = defintion2\n" + show (FunGuardedBody body) = "guards are not supported in functions:\n" +++ (render.to_HPJ_Doc.(pprBody True)) body+ show (InsParamNum name n) = + "insufficient number of parameters (" ++ show n ++ ") in the defintion of `" +++ pprint name +++ "'\n point free definitons are not suported by the VHDL backend"+ show (UnsupportedFunPat pat) = + "input pattern `" ++ pprint pat ++ "' is not supported"+ show (MultipleClauses cs) = "multiple clauses (" ++ + (show.length) cs ++ "):\n" ++ pprint cs+ show (GeneralErr err) = show err++-- | Expression translation errors in the VHDL backend+data VHDLExpErr =+ -- | Guards in case alternatives are not supported+ CaseGuardedBody Body |+ -- Unsupported case pattern + UnsupportedCasePat Pat |+ -- | Where constructs in case alternatives are not supported+ CurryUnsupported Int Int |+ -- | Unkown identifier+ UnkownIdentifier Name |+ -- Unsupported literal+ UnsupportedLiteral |+ -- Sections are not supported+ Section |+ -- Lambda Abstractions are not supported+ LambdaAbstraction |+ -- Conditional expressions are only supported in a function body+ Conditional |+ -- Case expressions are only supported in a function body+ Case |+ -- Do expressions are not supported+ Do |+ -- List comprehensions are not supported+ ListComprehension |+ -- Arithmetic sequences are not supported+ ArithSeq |+ -- Lists are not supported+ List |+ -- Signature expressiosn are not supported+ Signature |+ -- Record expressions are not supported+ Record |+ -- Unsupported expression generic error+ -- it shouldn't be raised, just for pattern completeness+ Unsupported++instance Show VHDLExpErr where+ show (CaseGuardedBody body) = + "guards are not supported in case alternatives:\n" +++ (render.to_HPJ_Doc.(pprBody True)) body+ show (UnsupportedCasePat pat) = "unsupported case pattern: `" ++ + pprint pat ++ "'"+ show (CurryUnsupported expected real) =+ "Currification is not supported, all arguments must be fully supplied\n"+++ " Expected arguments: " ++ show expected ++ " Provided arguments: " ++ + show real + show (UnkownIdentifier name) = "unkown identifier `" ++ pprint name ++ "'"+ show UnsupportedLiteral = "unsupported literal" + show Section = "sections are not supported"+ show LambdaAbstraction = "lambda abstractions are not supported"+ show Conditional = "conditional expressions are only supported within" + ++ " a function body"+ show Case = "case expressions are only supported within" + ++ " a function body"+ show Do = "do expressions are not suupported"+ show ListComprehension = "list comprehensions are not supported"+ show ArithSeq = "arithmetic sequences are not supported"+ show List = "lists are not supported" + show Signature = "signature expressions are not supported"+ show Record = "record expressions are not supported"+ show Unsupported = "unsupported expression"++-- | Show errors+instance Show ForSyDeErr where+ show InconsistentContexts = "Inconsistent contexts"+ show (EvalErr str) = "Non evaluable node (" ++ show str ++ ")"+ show (NonVarName name) = show name ++ " is not a variable name."+ show (IncomSysF fName inctype) = + "Incompatible system function type\n"+++ show strFName ++ " was expected to have type:\n" +++ " Signal i1 -> Signal i2 -> ..... -> Signal in ->\n" +++ " (Signal o1, Signal o2, ... , Signal om)\n" +++ " with n <- |N U {0} and m <- |N U {0}\n" ++ + " i1 .. in, o1 .. im monomorphic types\n" +++ "However " ++ strFName ++ " has type\n " +++ " " ++ pprint inctype+ where strFName = show fName+ show (InIfaceLength sysInInfo portIdsInInfo) =+ showIfaceLength "input interface" sysInInfo portIdsInInfo+ show (OutIfaceLength sysOutInfo portIdsOutInfo) =+ showIfaceLength "output interface" sysOutInfo portIdsOutInfo+ show (MultPortId portId) = + "Multiply defined port identifier " ++ show portId+ show (MultProcId procId) = + "Multiply defined process identifier " ++ show procId+ show (SubSysIdClash subSysId mLoc1 mLoc2) =+ "System contains components of different subsystems " ++ + "(defined at locations " ++ finalLoc1 ++ " and " ++ finalLoc2 ++ ") " +++ "which share the same system identifier (`"++subSysId++")"+ where finalLoc1 = fromMaybe "<unkown>" mLoc1+ finalLoc2 = fromMaybe "<unkown>" mLoc2 + show (NonSysDef name t) = + "A variable with SysDef type was expected\n" +++ "However " ++ show name ++ " has type " ++ pprint t+ show (IncorrProcFunDecs decs) =+ "Only a function declaration (possibly precedeeded by a signature)" ++ + "is accepted\n"++ + "The specific, incorrect declarations follow:\n" +++ pprint decs+ show InconsOutTag = "Inconsistent output tag"+ show (InconsSysDefPort id) = "Inconsistent port in SysDef: " ++ show id+ show (DynMisMatch dyn rep) = + "Type matching error in dynamic value with typerep " ++ + show (dynTypeRep dyn) +++ "\n(Expected type: " ++ show rep ++ " )."+ show (SigMisMatch t) = + "Signal mismatch: expected a Signal type but got " ++ pprint t + show (InLengthMisMatch l1 l2) = + "Cannot simulate: simulation arguments length-mismatch: " ++ + show l1 ++ " /= " ++ show l2+ show EmptyVHDLId = "Empty VHDL identifier"+ show (IncVHDLBasId id) = "Incorrect VHDL basic identifier " ++ + "`" ++ id ++ "'"+ show (IncVHDLExtId id) = "Incorrect VHDL extended identifier " ++ + "`" ++ id ++ "'"+ show (UnsupportedType tr) = "Unsupported type " ++ show tr+ show (ReservedId str) = "Identifier `" ++ str ++ "' is reserved"+ show UnsupportedProc = "Unsupported process"+ show (UntranslatableVHDLFun err) = + "Untranslatable function: " ++ show err + show (UntranslatableVHDLExp exp err) = + "Untranslatable expression `" ++ pprint exp ++ "': " ++ show err + show QuartusFailed = "Quartus failed"+ show GhdlFailed = "Ghdl failed"+ show ModelsimFailed = "Modelsim failed"+ show (Other str) = str ++-- | help function for the show instance+showIfaceLength :: String -> (SysId, Int) -> ([String],Int) -> String+showIfaceLength ifaceMsg (sysName, sysIfaceL) (ifaceIds, ifaceL) = + "Incorrect length of " ++ ifaceMsg ++ " (" ++ show ifaceL ++ ")\n" +++ " " ++ show ifaceIds ++ "\n" ++ + sysName ++ " expects an " ++ show ifaceMsg ++ " length of " +++ show sysIfaceL ++-----------------+-- Context Error+-----------------++-- | A context error: a 'ForSyDeErr' with context information (indicating where+-- the error ocurred)+data ContextErr = ContextErr Context ForSyDeErr++-- | A context: it indicates where an error ocurred.+data Context =+ -- | Empty context + EmptyC | + -- | In a System definition + SysDefC SysId (Maybe Loc) | + -- | In a Process+ ProcC SysId (Maybe Loc) ProcId | + -- | In a Proces Function+ ProcFunC SysId (Maybe Loc) ProcId Name Loc | + -- | In a Process value+ ProcValC SysId (Maybe Loc) ProcId Exp++-- | type indicating a location in the user's source+-- code+type Loc = String++-- | Set a process context from a system context+setProcC :: ProcId -- ^ Identifier of the process+ -> Context -- ^ system context+ -> Context+setProcC pid (SysDefC sysid mSysloc) = ProcC sysid mSysloc pid +setProcC _ _ = intError funName InconsistentContexts+ where funName = "ForSyDe.ForSyDeErr.setProcC" ++-- | Set a process function context from a process context +setProcFunC :: Name -- ^ Function name+ -> Loc -- ^ Function location+ -> Context -- ^ system context+ -> Context+setProcFunC name loc (ProcC sysid sysloc pid) = + ProcFunC sysid sysloc pid name loc+setProcFunC _ _ _ = intError funName InconsistentContexts+ where funName = "ForSyDe.ForSyDeErr.setProcFunC" ++-- | Set a process value context from a process context +setProcValC :: Exp -- ^ Expression value+ -> Context -- ^ system context+ -> Context+setProcValC exp (ProcC sysid sysloc pid) = ProcValC sysid sysloc pid exp+setProcValC _ _ = intError funName InconsistentContexts+ where funName = "ForSyDe.ForSyDeErr.setProcValC" ++instance Show Context where+ show EmptyC = ""+ show (SysDefC id mLoc) = "system definition `" ++ id ++ + "' (created in " ++ finalLoc ++ ")"+ where finalLoc = fromMaybe "<unkown>" mLoc+ show (ProcC sysid sysloc pid) = "process `" ++ pid ++ "' belonging to " ++ + show (SysDefC sysid sysloc)+ show (ProcFunC sysid sysloc pid fName fLoc) = + "process function `" ++ pprint fName ++ "' (created in " ++ fLoc ++ ") " +++ " used by " ++ show (ProcC sysid sysloc pid)+ show (ProcValC sysid sysloc pid valExp) =+ "process argument `" ++ pprint valExp ++ "' used by "+ ++ show (ProcC sysid sysloc pid)+ ++instance Show ContextErr where+ show (ContextErr cxt err) = case cxt of+ EmptyC -> show err+ _ -> show err ++ "\nin " ++ show cxt++--------------+-- Error Monad+--------------++-- | We make ForSyDeErr an instance of the Error class to be able to throw it+-- as an exception.+instance Error ForSyDeErr where+ noMsg = Other "An Error has ocurred"+ strMsg = Other+++instance Error ContextErr where+ noMsg = ContextErr EmptyC noMsg+ strMsg = \str -> ContextErr EmptyC (strMsg str)++-- | 'EProne' represents failure using Left ForSyDeErr or a successful +-- result of type a using Right a+-- +-- 'EProne' is implicitly an instance of +-- ['MonadError'] (@Error e => MonadError e (Either e)@)+-- ['Monad'] (@Error e => Monad (Either e)@)+type EProne = Either ForSyDeErr+-- FIXME: Rethink Eprone so that it takes contexts in account++-------------------+-- Helper functions+-------------------++-- | Throws a an error caused by improper use of a user-exported function+uError :: String -- ^ User-exported function which cuased the error + -> ForSyDeErr -- ^ Error to show+ -> a+uError funName err = error $ "User error in " ++ funName ++ ": " ++ + show err ++ "\n"+++-- | Throws an internal error+intError :: String -- ^ Function which caused the internal error + -> ForSyDeErr -- ^ Error to show+ -> a+intError funName err = error $ "Internal error in " ++ funName ++ ": " ++ + show err ++ "\n" +++ "Please report!"++-- | lift an Either expression to an Error Monad+liftEither :: MonadError e m => Either e a -> m a+liftEither = either throwError return++++-- | An error reporting function for Quasi monads+-- Executing in the monad will stop inmideately after calling qError+-- Note, it does not work for GHC<6.8+-- see <http://hackage.haskell.org/trac/ghc/ticket/1265>+qError :: Quasi m => String -- ^ The name of the function + -- called in the splice + -> ForSyDeErr -- ^ Error to show+ -> m a+qError fname err = fail $ "Error when calling " ++ fname ++ ":\n" ++ + show err+++-- | Stop execution, find the enclosing qRecover+-- if a recover is not found, it is considered as an internal error+-- and the string provided will used as a reference to +-- the origin of the error.+-- Note, it does not work for GHC<6.8+-- see <http://hackage.haskell.org/trac/ghc/ticket/1265>+qGiveUp :: Quasi m => String -> m a+qGiveUp name = fail $ "qGiveUp: Internal error in " ++ name ++ + ", please report."++-- | Output a trace message in a quasi monad (similar to 'putTraceMsg')+qPutTraceMsg :: Quasi m => String -> m ()+qPutTraceMsg msg = qRunIO (putTraceMsg msg)+++-- | Print an Error+printError :: Show a => a -> IO ()+printError = putStrLn.("Error: "++).show++-- | Print a VHDL compilation error+printVHDLError :: Show a => a -> IO b+printVHDLError = error.("VHDL Compilation Error: "++).show++-- | Print a GraphML compilation error+printGraphMLError :: Show a => a -> IO ()+printGraphMLError = putStrLn.("VHDL Compilation Error: "++).show
+ src/ForSyDe/Deep/Ids.hs view
@@ -0,0 +1,23 @@+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.Ids+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- ForSyDe identifier types+--+-----------------------------------------------------------------------------+module ForSyDe.Deep.Ids where++-- | A process identifier+type ProcId = String++-- | A Port identifier+type PortId = String++-- | A System identifier+type SysId = String
+ src/ForSyDe/Deep/Netlist.hs view
@@ -0,0 +1,252 @@+{-# LANGUAGE DeriveDataTypeable, GeneralizedNewtypeDeriving #-}+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.Netlist+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- This module provides a definition for the netlist of a system description, +-- which is used as the intermediate representation of the embedded compiler.+-- +-- The netlist is modelled as a directed cyclic graph whose nodes +-- are shared using Observable Sharing ("ForSyDe.OSharing").+--+-- /This module is based on Lava2000/: <http://www.cs.chalmers.se/~koen/Lava/>+-- +-----------------------------------------------------------------------------++module ForSyDe.Deep.Netlist where+++import ForSyDe.Deep.Ids+import ForSyDe.Deep.OSharing (URef, newURef, readURef)+import {-# SOURCE #-} ForSyDe.Deep.System.SysDef + (SysDef(..), PrimSysDef(..), oIface)+import ForSyDe.Deep.Process.ProcFun (TypedProcFun(..))+import ForSyDe.Deep.ForSyDeErr+import ForSyDe.Deep.Process.ProcVal (ProcVal(..))++import Data.Dynamic+import Data.Typeable.FSDTypeRepLib+++-----------+-- Netlist+-----------+++-- | The netlist of a system is modelled as a directed cyclic graph but is+-- really equivalently implemented as a set of (possibly overlapping) trees+-- capable of sharing nodes between them.+--+-- There is no specific data structure for the netlist itself. Instead, a+-- netlist is simply represented by its outputs. Each output is the root node+-- of a tree ('NlTree'). The NlTrees can (and probably will) have common nodes.+--+-- It is important to note that, due to the use of trees, the only way to+-- traverse a 'Netlist' is from its outputs to its inputs.++newtype Netlist container = Netlist {netlistOuts :: (container NlTree)}++++----------+-- NlNode+----------++-- | A node of the netlist can be either a process, component instances or+-- special nodes to help traversing the graph. +--+-- Since the netlist graph is implemented with trees, they only store+-- information about their inputs (which are their child nodes in the tree).++data NlNode inputi =+ -- Ports+ InPort PortId | -- ^ Input Ports of the system + -- Processes+ Proc ProcId (NlProc inputi)+++-- | A process node+-- Note that vectors and transformed to lists puls an Int parameter +-- indicating its size+data NlProc inputi =+ -- Constant signal + Const ProcVal | -- ^ A signal with constant value+ -- During all its periods + + -- | mapSY and zipWithSY processes+ ZipWithNSY (TypedProcFun Dynamic) -- Process function in dynamic form+ [inputi] | + + -- | Vector version of zipWithNSY+ ZipWithxSY (TypedProcFun ([Dynamic] -> Dynamic)) -- Process function + -- with dynamic arguments + [inputi] | + + -- ^ Inverse of ZipWithNSY+ UnzipNSY [FSDTypeRep] -- Type of the elements in the input tuple+ -- (type of outputs)+ (Dynamic -> [Dynamic]) -- Dynamic version of the unzipping function+ -- for the concrete, monomorphic types+ -- of the process+ inputi | + + -- | Vector version of UnzipSY+ UnzipxSY FSDTypeRep -- Type of elements in the input vector+ -- (and type of the outputs)+ Int -- Size of output vector (Number of output signals)+ (Dynamic -> [Dynamic]) + inputi | ++ -- | delaySY process+ DelaySY ProcVal inputi | + + -- A System Instance is considered a special process+ + -- | System Instance+ SysIns PrimSysDef [inputi] + + ++---------+-- NlEdge+---------++-- FIXME: The NLNodeOut should merely be an integer++-- | The node connection is carried out by directed edges modelled as +-- Unsafe Unmutable References (allowing to share nodes) connected +-- in the output->input direction (remember we are using trees). +-- Since the node to which the edge is directed can have various outputs +-- (e.g a system instance) the edge is tagged to indicate to what +-- output it refers to.+data NlEdge node = NlEdge (ForSyDe.Deep.OSharing.URef node) NlNodeOut+ ++-- FIXME: output tags are ugly, create a variant of NlNode which takes outputs in account++-- | The different outputs which the different nodes can have+data NlNodeOut = InPortOut |+ ConstOut |+ ZipWithNSYOut |+ ZipWithxSYOut |+ UnzipNSYOut Int |+ UnzipxSYOut Int |+ DelaySYOut |+ SysInsOut PortId + deriving (Show, Eq) ++-- | Get the output tags of a node+outTags :: NlNode a -> [NlNodeOut]+outTags (InPort _) = [InPortOut]+outTags (Proc _ proc) =+ case proc of+ Const _ -> [ConstOut]+ ZipWithNSY _ _ -> [ZipWithNSYOut]+ ZipWithxSY _ _ -> [ZipWithxSYOut] + UnzipNSY types _ _ -> map UnzipNSYOut [1..length types]+ UnzipxSY _ nout _ _ -> map UnzipxSYOut [1..nout]+ DelaySY _ _ -> [DelaySYOut]+ SysIns primSysDefRef _ -> + map (SysInsOut . fst) ((oIface . readURef . unPrimSysDef) primSysDefRef)++ ++------------+-- NlTree+------------++-- | We tie the knot to connect nodes through 'NlEdge's, building a Tree which+-- can have shared nodes. +--+-- That is done by storing 'NlTree's as the the input information of each node+-- of the tree. Child nodes of the tree are closer to the inputs of the system+-- and parents are closer to the outputs.++newtype NlTree = NlTree {rootEdge :: (NlEdge (NlNode NlTree))}++-----------+-- NlSignal+-----------++-- | A 'NlTree', or more preciselly its root 'NlEdge', is after all, how+-- signals are implemented in the netlist.+type NlSignal = NlTree++-- | Get the tag of a signal+nlSignalNlOut :: NlSignal -> NlNodeOut+nlSignalNlOut (NlTree (NlEdge _ tag)) = tag++---------+-- Signal+---------+++-- | A signal can be seen as wire which carries values of certain type +-- and which can be connected and processed by the two computational +-- entities of a ForSyDe system: processes and block instances.+--++-- A Signal is internally represented as an edge of the graph representing+-- the system netlist. +-- The phantom type parameter ensures type-consistency for the signal+-- processing functions. ++newtype Signal a = Signal {unSignal :: NlSignal}+ deriving Typeable+++-------------------+-- Helper functions+-------------------++-- FIXME: All these newWhatever functions probably shouldn't be here++-- | Generate a signal pointing to an 'InPort' node+newInPort :: PortId -> NlSignal+newInPort id = NlTree (NlEdge (newURef (InPort id)) InPortOut)+++-- | Generate a reference to a new 'SysIns' node+newSysIns :: ProcId -> SysDef a -> [NlSignal] + -> URef (NlNode NlSignal)+newSysIns id (SysDef primSysDef) inputInfo = + newURef (Proc id (SysIns primSysDef inputInfo)) ++-- | Generate the output signal of a node+newNodeOutSig :: URef (NlNode NlSignal) -- ^ Reference to the node + -> NlNodeOut -- ^ Output tag+ -> NlSignal+newNodeOutSig ref mTag = NlTree (NlEdge ref mTag)+++-- | Evaluate the output of a process within a synchronous period+-- The inputs and outputs are given in dynamic form+eval :: NlNode Dynamic -> [(NlNodeOut, Dynamic)]+eval node = case node of+ InPort _ -> intError evalStr (EvalErr "InPort")+ Proc _ proc -> case proc of+ Const pv -> [(ConstOut, dyn pv)]+ ZipWithNSY fun dynArgs+ -> [(ZipWithNSYOut, foldl1 dynApp ((tval fun) : dynArgs))] + ZipWithxSY fun dynListArg+ -> [(ZipWithxSYOut, (tval fun) dynListArg)]+ UnzipNSY _ fun dynArg+ -> zipWith (\n dyn -> (UnzipNSYOut n, dyn))+ [1..] + (fun dynArg) + UnzipxSY _ _ fun dynArg+ -> zipWith (\n dyn -> (UnzipxSYOut n, dyn))+ [1..] + (fun dynArg) + DelaySY _ _ + -> intError evalStr (EvalErr "DelaySY")+ SysIns _ _ + -> intError evalStr (EvalErr "SysIns")+ + where evalStr = "ForSyDe.Netlist.eval"
+ src/ForSyDe/Deep/Netlist/Traverse.hs view
@@ -0,0 +1,193 @@+{-# LANGUAGE ScopedTypeVariables #-} +-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Netlist.Traverse+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : non-portable (LSTV)+--+-- This module provides traversing operations for 'ForSyDe.Deep.Netlist'+--+--+-- /This module is based on Lava2000/: <http://www.cs.chalmers.se/~koen/Lava/>+-- +-----------------------------------------------------------------------------+module ForSyDe.Deep.Netlist.Traverse where+++import ForSyDe.Deep.Netlist+import ForSyDe.Deep.OSharing+import ForSyDe.Deep.ForSyDeErr+++import Data.Maybe (fromMaybe)+-- qualified to avoid nameclash+import qualified Data.Foldable as DF (Foldable(foldMap), toList)+import Data.Monoid (mempty)+-- qualified to avoid nameclash+import qualified Data.Traversable as DT (Traversable(traverse,mapM)) +import Control.Applicative (pure, (<$>))+import Control.Monad.State+import Control.Monad.ST (ST)++-- Instances to traverse a netlist Node (and implicitly the whole netlist)++instance DF.Foldable NlProc where+ foldMap _ (Const _) = mempty + foldMap f (ZipWithNSY _ is) = DF.foldMap f is+ foldMap f (ZipWithxSY _ is) = DF.foldMap f is+ foldMap f (UnzipNSY _ _ i) = f i+ foldMap f (UnzipxSY _ _ _ i) = f i + foldMap f (DelaySY _ i) = f i+ foldMap f (SysIns _ i) = DF.foldMap f i++++instance DF.Foldable NlNode where+ foldMap _ (InPort _) = mempty+ foldMap f (Proc _ proc) = DF.foldMap f proc++instance Functor NlProc where+ fmap _ (Const val) = Const val+ fmap f (ZipWithNSY pf is) = ZipWithNSY pf (fmap f is)+ fmap f (ZipWithxSY pf is) = ZipWithxSY pf (fmap f is)+ fmap f (UnzipNSY ts pf i) = UnzipNSY ts pf (f i)+ fmap f (UnzipxSY t n pf i) = UnzipxSY t n pf (f i) + fmap f (DelaySY c i) = DelaySY c (f i)+ fmap f (SysIns def is) = SysIns def (fmap f is)++++instance Functor NlNode where+ fmap _ (InPort id) = InPort id+ fmap f (Proc id proc) = Proc id (fmap f proc) ++instance DT.Traversable NlProc where+ traverse _ (Const val) = pure (Const val) + traverse f (ZipWithNSY pf is) = ZipWithNSY pf <$> DT.traverse f is+ traverse f (ZipWithxSY pf is) = ZipWithxSY pf <$> DT.traverse f is+ traverse f (UnzipNSY ts pf i) = UnzipNSY ts pf <$> f i+ traverse f (UnzipxSY t n pf i) = UnzipxSY t n pf <$> f i+ traverse f (DelaySY c i) = DelaySY c <$> f i+ traverse f (SysIns def is) = SysIns def <$> DT.traverse f is+++++instance DT.Traversable NlNode where+ traverse _ (InPort id) = pure (InPort id)+ traverse f (Proc id proc) = Proc id <$> DT.traverse f proc +++-- | Traversing monad, stacking state and error transformers over IO+type TravSEIO s e a = (StateT s (ErrorT e IO)) a++++-- | traverseSIO traverses a netlist and returns a final user-defined +-- traversing state (@s@) given:+-- new: generates a new (and normally unique) tag for the outputs of each +-- netlist node given the traversing state (which is possibly updated +-- as well).+-- define: given the output tags of a node, current iteration state, +-- and the output tags of its children, @define@+-- generates the netlist of that node, possibly updating +-- the traversing state+-- FIXME: shoudn't the arguments of define go the other way around?+-- first inputs then outputs.+-- FIXME: why are tags needed in define? [oinfo] should be enough.+-- tags are ugly in general (see the pattern matches) fix this problem.+traverseSEIO :: (DT.Traversable container, Error e) => + (NlNode NlSignal -> TravSEIO s e [(NlNodeOut, oinfo)]) -- ^ new+ -> ([(NlNodeOut, oinfo)] -> NlNode oinfo -> TravSEIO s e ()) -- ^ define+ -> Netlist container + -> TravSEIO s e (container oinfo)+traverseSEIO new define (Netlist rootSignals) =+ do uRefTable <- liftIO $ newURefTableIO++ let gather (NlTree (NlEdge nodeRef tag)) =+ do visited <- liftIO $ queryIO uRefTable nodeRef+ case visited of+ Just infoPairs -> return (specifyOut tag infoPairs)+ Nothing -> do + let node = readURef nodeRef+ infoPairs <- new node+ liftIO $ addEntryIO uRefTable nodeRef infoPairs+ childInfo <- DT.mapM gather node+ define infoPairs childInfo+ return (specifyOut tag infoPairs)++ specifyOut :: NlNodeOut -> [(NlNodeOut, a)] -> a+ specifyOut tag pairs = fromMaybe err maybeOut+ where funName = "ForSyDe.NetList.Traverse.traverseIO"+ err = intError funName InconsOutTag+ maybeOut = lookup tag pairs++ DT.mapM gather rootSignals++-- | Traversing monad, stacking state and error transformers over ST+type TravSEST s e st a = (StateT s (ErrorT e (ST st))) a++-- | 'ST'-monad version of 'traverseSEIO'+traverseSEST :: (DT.Traversable container, Error e) => + (NlNode NlSignal -> TravSEST s e st [(NlNodeOut, oinfo)]) -- ^ new+ -> ([(NlNodeOut, oinfo)] -> NlNode oinfo -> TravSEST s e st ()) -- ^ define+ -> Netlist container + -> TravSEST s e st (container oinfo)+traverseSEST new define (Netlist rootSignals) =+ do let lift2 = lift.lift+ uRefTable <- lift2 $ newURefTableST+ let gather (NlTree (NlEdge nodeRef tag)) =+ do visited <- lift2 $ queryST uRefTable nodeRef+ case visited of+ Just infoPairs -> return (specifyOut tag infoPairs)+ Nothing -> do + let node = readURef nodeRef+ infoPairs <- new node+ lift2 $ addEntryST uRefTable nodeRef infoPairs+ childInfo <- DT.mapM gather node+ define infoPairs childInfo+ return (specifyOut tag infoPairs)++ specifyOut :: NlNodeOut -> [(NlNodeOut, a)] -> a+ specifyOut tag pairs = fromMaybe err maybeOut+ where funName = "ForSyDe.NetList.Traverse.traverseIO"+ err = intError funName InconsOutTag+ maybeOut = lookup tag pairs++ DT.mapM gather rootSignals++-- deprecated, do not use+traverseST :: DT.Traversable container => + (NlNode NlSignal -> ST s [(NlNodeOut, oinfo)]) + -> ([(NlNodeOut, oinfo)] -> NlNode oinfo -> ST s ()) + -> Netlist container + -> ST s (container oinfo)+traverseST new define (Netlist rootSignals) =+ do uRefTable <- newURefTableST++ let gather (NlTree (NlEdge nodeRef tag)) =+ do visited <- queryST uRefTable nodeRef+ case visited of+ Just infoPairs -> return (specifyOut tag infoPairs)+ Nothing -> do let node = readURef nodeRef+ infoPairs <- new node+ addEntryST uRefTable nodeRef infoPairs+ childInfo <- DT.mapM gather node+ define infoPairs childInfo+ return (specifyOut tag infoPairs)++ specifyOut :: NlNodeOut -> [(NlNodeOut, a)] -> a+ specifyOut tag pairs = fromMaybe err maybeOut+ where funName = "ForSyDe.NetList.Traverse.traverseST"+ err = intError funName InconsOutTag+ maybeOut = lookup tag pairs++ DT.mapM gather rootSignals++-- | Obtain the arguments of a node+arguments :: NlNode a -> [a]+arguments = DF.toList
+ src/ForSyDe/Deep/OSharing.hs view
@@ -0,0 +1,208 @@+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.OSharing+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- This module provides support for Observable Sharing: <http://www.cs.chalmers.se/~koen/pubs/entry-asian99-lava.html>+--+-- It provides:+--+-- * 'URef': Unsafe Unmutable references, using them causes side effects.+--+-- * 'URefTable': a table were 'URef's are used as key and can store+-- any value.+--+-- /This Module was taken from Lava2000's/ @Ref.hs@ /module/:+-- <http://www.cs.chalmers.se/~koen/Lava/>+--+-- IMPORTANT WARNING: Even if all the functions causing side effects+-- are set as NOINLINE and that all currently known+-- Haskell compilers are based in graph reduction+-- (i.e. referential transparency will be preserved if+-- sharing is), there are other optimisations than inlining+-- that can break Observable Sharing, e.g. Common+-- Subexpression Elimination (CSE).+-----------------------------------------------------------------------------+module ForSyDe.Deep.OSharing+ ( -- Unsafe references+ URef,+ newURef,+ readURef,+ -- Tables of Unsafe References (IO version)+ URefTableIO,+ newURefTableIO,+ addEntryIO,+ queryIO,+ -- Tables of Unsafe References (IO version)+ URefTableST,+ newURefTableST,+ addEntryST,+ queryST,+ -- Memoizating functions+ memoURef,+ memoURefIO,+ memoURefST)+ where++import ForSyDe.Deep.OSharing.UDynamic++import System.IO (fixIO)+import System.IO.Unsafe (unsafePerformIO)+import Data.IORef (IORef, newIORef, readIORef, writeIORef)+import Control.Monad.ST+import qualified Control.Monad.ST.Unsafe as STU++++-- | An Unsafe Unmutable Reference to a value of type a+--+-- An 'URef' is implemented as follows: it has two pieces of information. The+-- first one is an updatable list of entries for each table it is a member in.+-- Since the types of the Tables vary, the URef has no idea what type of+-- values it is supposed to store. So we use dynamic types.+--+-- Since it is an updatable list, it is an IORef, which we also use to compare+-- two 'URef's. The second part is just the value the URef is pointing at (this+-- can never change anyway since references are unmutable).+--+-- FIXME: why IORef? it should maybe be STRef?+-- Besides: Is it guaranteed that the values of an IORef+-- are not changed by the garbage collector?+data URef a+ = URef (IORef [(URefTableTag, UDynamic)]) a++instance Eq (URef a) where+ URef r1 _ == URef r2 _ = r1 == r2++instance Show a => Show (URef a) where+ showsPrec _ (URef _ a) = showChar '{' . shows a . showChar '}'+++-- | Create a new Unsafe Unmutable Reference to a Haskell object+--+-- This operation, as explained in the Observable sharing paper, can cuase+-- side-effects, since the value returned (the 'URef') is not determined by+-- the arguments of the function (i.e. different calls to 'newURef' with+-- the same argument return different 'URef's).+newURef :: a -> URef a+newURef a = unsafePerformIO $+ do r <- newIORef []+ return (URef r a)+{-# NOINLINE newURef #-}++-- | Read the value pointed by the 'URef'.+readURef :: URef a -> a+readURef (URef _ a) = a++--------------+-- URefTableIO+--------------++-- | A unique identifier which univocally designates a table+type URefTableTag+ = IORef ()+++-- | A 'URefTable' a b+--+-- * Unsafe References to a value of type "a" (key of the table)+-- * Values of type "b" associated to each key+--+-- Here is how we implement Tables of URefs:+--+-- A Table is nothing but a unique tag, of type TableTag. TableTag can be+-- anything, as long as it is easy to create new ones, and we can compare them+-- for equality. (I chose IORef ()).+--+-- So how do we store URefs in a Table? We do not want the Tables keeping+-- track of their URefs (which would be disastrous when the table becomes big,+-- and we would not have any garbage collection).+--+-- Instead, every URef keeps track of the value it has in each table it is+-- in. This has the advantage that we have a constant lookup time (if the+-- number of Tables we are using is small), and we get garbage collection of+-- table entries for free.+newtype URefTableIO a b+ = URefTableIO URefTableTag+ deriving Eq++-- | Create a new table+newURefTableIO :: IO (URefTableIO a b)+newURefTableIO = URefTableIO `fmap` newIORef ()++-- | Query the value corresponding to an 'URef'+queryIO :: URefTableIO a b -> URef a -> IO (Maybe b)+queryIO (URefTableIO t) (URef r _) =+ do list <- readIORef r+ return (unsafeFromUDyn `fmap` lookup t list)++-- | Add an ('URef' a, b) pair entry to the table+addEntryIO :: URefTableIO a b -- ^ key of the entry+ -> URef a+ -> b -- ^ value of the entry+ -> IO ()+addEntryIO (URefTableIO t) (URef r _) b =+ do list <- readIORef r+ writeIORef r ((t, unsafeToUDyn b) : filter ((/= t) . fst) list)++--------------+-- URefTableST+--------------++-- | 'ST' version of 'URefTableIO'+newtype URefTableST s a b+ = URefTableST (URefTableIO a b)+ deriving Eq++-- | 'ST' version of 'newURefTableIO'+newURefTableST :: ST s (URefTableST s a b)+newURefTableST = STU.unsafeIOToST (URefTableST `fmap` newURefTableIO)++-- | 'ST' version of 'queryIO'+queryST :: URefTableST s a b -> URef a -> ST s (Maybe b)+queryST (URefTableST tab) r = STU.unsafeIOToST (queryIO tab r)++-- | 'ST' version of 'addEntryIO'+addEntryST :: URefTableST s a b -> URef a -> b -> ST s ()+addEntryST (URefTableST tab) r b = STU.unsafeIOToST (addEntryIO tab r b)++--------------------------------+-- Memoization of URef functions+--------------------------------++-- | Generates a memoizated version of a function taking 'URef' values+memoURef :: (URef a -> b) -> (URef a -> b)+memoURef f = unsafePerformIO . memoURefIO (return . f)+{-# NOINLINE memoURef #-}++-- | 'IO' version of 'memoURef'+memoURefIO :: (URef a -> IO b) -> (URef a -> IO b)+memoURefIO f = unsafePerformIO $+ do tab <- newURefTableIO+ let f' r = do mb <- queryIO tab r+ case mb of+ Just b -> do return b+ Nothing -> fixIO $ \b ->+ do addEntryIO tab r b+ f r+ return f'+{-# NOINLINE memoURefIO #-}++-- | 'ST' version of 'memoURef'+memoURefST :: (URef a -> ST s b) -> (URef a -> ST s b)+memoURefST f = unsafePerformST $+ do tab <- newURefTableST+ let f' r = do mb <- queryST tab r+ case mb of+ Just b -> do return b+ Nothing -> fixST $ \b ->+ do addEntryST tab r b+ f r+ return f'+ where unsafePerformST = unsafePerformIO . STU.unsafeSTToIO+{-# NOINLINE memoURefST #-}
+ src/ForSyDe/Deep/OSharing/UDynamic.hs view
@@ -0,0 +1,54 @@+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.OSharing+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : non-portable (GHC libraries)+--+-- This module provides support for unsafe dynamic typing.+--+-- Use with care. Normally, "Data.Dynamic" is preferred since it provides a+-- safe casting operation thanks to the use of "Data.Typeable". +--+-- /This module is based on Lava2000/: <http://www.cs.chalmers.se/~koen/Lava/>+-- +-----------------------------------------------------------------------------+module ForSyDe.Deep.OSharing.UDynamic + (UDynamic, + unsafeToUDyn,+ unsafeFromUDyn)+ + where++import Unsafe.Coerce (unsafeCoerce)+import GHC.Base (Any)++-- | A value of type 'UDynamic' (from Unsafe Dynamic) can+-- implicitly any Haskell value. +--+-- It is unsafe because, as opposed to 'Data.Dynamic', +--- it does not encapsule type information and does+-- it makes unsafe to cast a 'UDynamic' back to its original Haskell type.+data UDynamic = UDynamic Any++-- | Converts an arbitrary value into an object of type 'UDynamic'.+-- It is unsafe because it does not provide type information during the +-- transformation+unsafeToUDyn :: a -> UDynamic+unsafeToUDyn a = UDynamic (unsafeCoerce a)++-- | Converts a 'UDynamic' object back into an ordinary Haskell value.+-- It is unsafe because there is no way to unsure converting+-- back to the correct original Haskell type. Thus, this+-- function should be used with care since it can cuase disastrous errors.+-- +-- e.g. disastrousCast :: a -> b+-- disastrousCast = unsafeToDyn . unsafeFromDyn+--+-- The following program would coredump+-- main = putStr (disastrousCast 1 :: String) +unsafeFromUDyn :: UDynamic -> a+unsafeFromUDyn (UDynamic obj) = unsafeCoerce obj
+ src/ForSyDe/Deep/Process.hs view
@@ -0,0 +1,24 @@+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.Process+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- Publicly usable functions to create primitive processes. (Reexports +-- "ForSyDe.Process.SynchProc")+-- +-----------------------------------------------------------------------------+module ForSyDe.Deep.Process + (ProcFun, newProcFun, defArgVal, defArgPF,+ ProcType,+ module ForSyDe.Deep.Process.SynchProc) where++import ForSyDe.Deep.Process.ProcFun (ProcFun, newProcFun, defArgVal, defArgPF)++import ForSyDe.Deep.Process.SynchProc +import ForSyDe.Deep.Process.ProcType (ProcType)+import ForSyDe.Deep.Process.ProcType.Instances ()
+ src/ForSyDe/Deep/Process/Desugar.hs view
@@ -0,0 +1,131 @@+{-# LANGUAGE CPP, TemplateHaskell, Rank2Types #-}+module ForSyDe.Deep.Process.Desugar (desugarTransform) where++import qualified Language.Haskell.TH as TH+import Language.Haskell.TH+import Control.Monad.State.Lazy++import Data.Generics+import qualified Data.Param.FSVec as V++{-+desugarTransform :: [Dec] -> Q [Dec]+desugarTransform = return+-}++desugarTransform :: [Dec] -> Q [Dec]+--desugarTransform decs = foldAccDecs (mkM extractTypedLambda) decs+desugarTransform decs = (return decs) + =+=> extractTypedLambda+ =+=> extractInfixOpSection+ =+=> specializeHof+ =+=> deleteSigE+#ifdef DEVELOPER+ >>= dumpTree+ where+ dumpTree :: [Dec] -> Q [Dec]+ dumpTree ts = do _ <- runIO $ mapM (print.ppr) ts+ return ts+#endif++-- Apply declaration-accumulating transformation+(=+=>) :: Typeable a => Q [Dec] -> (a -> DecAccM a) -> Q [Dec]+decs =+=> f = decs >>= (foldAccDecs $ mkM f)++-- Extract all occurences of lambda expressions within type signatures +-- ... ((\... -> ...) :: ...) ...+-- Create functions declarations for them and replace the lambda expression+-- with a reference to the declaration:+-- ... lambda0 ...+-- where+-- lambda0 :: ...+-- lambda0 ... = ...+extractTypedLambda :: Exp -> DecAccM Exp+extractTypedLambda (SigE (LamE patterns lamBody) ty) = do+ name <- lift $ newName "lambda"+ let body = NormalB lamBody+ signature = (SigD name ty)+ function = (FunD name [(Clause patterns body [])])+ modify $ \declState -> signature:function:declState+ return (VarE name)+extractTypedLambda e = return e++-- Make a function out of a suitably annotated operator section+-- ... (+3)::Int32->Int32 ...+-- is transformed to+-- ... infix_section_0 ...+-- where infix_section_0 a = a+3+extractInfixOpSection :: Exp -> DecAccM Exp+extractInfixOpSection (SigE exp@(InfixE _ _ _) ty) = do+ name <- lift.newName $ "infix_section"+ argname <- lift.newName $ "a"+ let body = NormalB $ everywhere (mkT $ insertArgument argname) exp+ patterns = [VarP argname]+ signature = SigD name ty+ function = FunD name [(Clause patterns body [])]+ modify $ \declState -> signature:function:declState+ return (VarE name)+ where+ insertArgument :: Name -> Exp -> Exp+ insertArgument argn (InfixE Nothing op r) = InfixE (Just $ VarE argn) op r+ insertArgument argn (InfixE l op Nothing) = InfixE l op (Just $ VarE argn)+ insertArgument _ e = e+extractInfixOpSection e = return e+++hofNames :: [TH.Name]+hofNames = [+ 'V.foldr,+ 'V.foldl,+ 'V.map,+ 'V.zipWith,+ 'V.zipWith3+ ]++isHigherOrderFunctionName :: TH.Name -> Bool+isHigherOrderFunctionName n = elem n hofNames++-- specializing extraction:+-- find all hof applications: (AppE (VarE hofName) (VarE fname))+-- build specialized function definition in normal form+-- collect specialized declarations in state of DecAccM+-- replace application with call to specialized function+specializeHof :: Exp -> DecAccM Exp+-- arity=2+specializeHof (SigE (AppE hofapp@(AppE (VarE hofName) (VarE argFunName)) arg1@(SigE _ argtype)) rettype)+ | isHigherOrderFunctionName hofName = do + name <- lift.newName $ (nameBase hofName)++"_"++(nameBase argFunName)+ argname <- lift.newName $ "v"++ let body = NormalB $ AppE hofapp (VarE argname)+ patterns = [VarP argname]+ signature = (SigD name (AppT (AppT ArrowT argtype) rettype))+ function = (FunD name [(Clause patterns body [])])+ modify $ \declState -> declState++[signature,function]+ return (SigE (AppE (VarE name) arg1) rettype)+specializeHof e = return e++-- Delete all the type signature expressions as they are not recognized during+-- translation+deleteSigE :: Exp -> DecAccM Exp+deleteSigE (SigE e _) = return e+deleteSigE e = return e++type DecAccM t = StateT [Dec] Q t++-- stateful fold over the tree. Applies f everywhere within the tree,+-- collecting additional declarations during traversal, and adding them to the+-- corresponding scope+foldAccDecs :: GenericM DecAccM -> [Dec] -> Q [Dec]+foldAccDecs transform decs = mapM apply decs+ where+ apply :: Dec -> Q Dec+ apply (FunD name [Clause pat body decls]) = do+ (newBody,newDecls) <- runStateT (everywhereM transform body) []+ transfDecls <- foldAccDecs transform decls+ return $ FunD name [Clause pat newBody (transfDecls++newDecls)]+ apply (ValD pat body decls) = do+ (newBody,newDecls) <- runStateT (everywhereM transform body) []+ transfDecls <- foldAccDecs transform decls+ return $ ValD pat newBody (transfDecls++newDecls)+ apply d = return d
+ src/ForSyDe/Deep/Process/Desugar.hs-boot view
@@ -0,0 +1,7 @@+{-# LANGUAGE Rank2Types #-}+module ForSyDe.Deep.Process.Desugar (desugarTransform) where++import Language.Haskell.TH+import Data.Generics++desugarTransform :: [Dec] -> Q [Dec]
+ src/ForSyDe/Deep/Process/ProcFun.hs view
@@ -0,0 +1,168 @@+{-# LANGUAGE TemplateHaskell #-} +-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.Process.ProcFun+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : non-portable (Template Haskell)+--+-- This module provides a type ('ProcFun') to store the functions passed+-- to process constructors.+--+----------------------------------------------------------------------------- +module ForSyDe.Deep.Process.ProcFun + (ProcFun(..),+ ProcFunAST(..),+ newProcFun, + defArgVal,+ defArgPF,+ TypedProcFun(..),+ TypedProcFunAST(..),+ procFun2Dyn,+ contProcFun2Dyn,+) where++import ForSyDe.Deep.Process.ProcType+import ForSyDe.Deep.Process.Desugar (desugarTransform)+import ForSyDe.Deep.Process.ProcVal (ProcValAST, mkProcValAST)+import ForSyDe.Deep.ForSyDeErr++import Language.Haskell.TH hiding (Loc)+import Language.Haskell.TH.Syntax hiding (Loc)+import Language.Haskell.TH.LiftInstances ()+import Data.Dynamic+import Data.Maybe (fromJust)+import Data.Set (Set)+import Data.Typeable.FSDTypeRepLib (FSDTypeRep, fsdTy)++-----------+-- ProcFun+-----------++-- | A Process Function +data ProcFun a = + ProcFun {val :: a, -- ^ Value of the function+ pfloc :: Loc, -- ^ where was it created+ ast :: ProcFunAST} -- ^ AST of the function++++-- | A process Function AST+data ProcFunAST = + ProcFunAST {name :: Name, -- ^ Function Name + -- (FIXME: maybe just a String?) + cls :: [Clause], -- ^ Function clauses + pars :: [DefArg]} -- ^ Default parameters+++-- | A process Function default argument+-- Either a process function AST or a value AST+data DefArg = FunAST ProcFunAST | ValAST ProcValAST++++-- | Sets a default value for an argument of the process function+defArgVal :: (Lift a, ProcType a) => ProcFun (a -> b) -> a -> ProcFun b+-- FIXME: inneficient, use a queue data structure+defArgVal pf v = + pf{ ast = astPF {pars = ((pars astPF) ++ [ValAST (mkProcValAST v)])}, + val = (val pf) v} + where astPF = ast pf++-- | Sets a default value for an argument of the process function +-- when the argument is a process function itself+defArgPF :: ProcFun (a -> b) -> ProcFun a -> ProcFun b+defArgPF pf v = pf{ ast = astPF {pars = ((pars astPF) ++ [FunAST (ast v)])}, + val = (val pf) (val v)} + where astPF = ast pf + +-- | Template Haskell constructor for 'ProcFun', here is an example on how to use it+--+-- @+-- plus1Fun :: ProcFun (Int -> Int)+-- plus1Fun = $(newProcFun [d| plus1 :: Int -> Int+-- plus1 n = n + 1 |])+-- @+newProcFun :: Q [Dec] -> ExpQ+newProcFun fDecQs = do + fDecsRaw <- fDecQs + fDecs <- desugarTransform fDecsRaw+ -- Check for the declarations to be correct+ (name, cls) <- recover (currErr $ IncorrProcFunDecs fDecs) + (checkDecs fDecs)+ -- Generate the main expression+ loc <- qLocation+ let errInfo = loc_module loc in do+ exp <- [| let fName = name+ fClauses = cls + in ProcFun $(varE name)+ errInfo + (ProcFunAST fName fClauses []) |]+ -- Add the function declarations to the expression+ return $ LetE fDecs exp + where currErr = qError "newProcFun"++----------------+-- TypedProcFun+----------------+++-- | A ProcFun bundled with its type representation. This type is not+-- exported to the end user. Only used internally.+data TypedProcFun a = + TypedProcFun {tval :: a, -- ^ Value of the function+ tpfloc :: Loc,+ tast :: TypedProcFunAST} -- ^ AST of the function+++-- | A ProcFunAST bundled with its type representation:+-- Why a TypeRep and not the Type provided by Template Haskell?+-- We could use the type signature provided by TH but ...+-- 1) We don't want to force the user to provide a signature+-- 2) We don't want to handle polymorphic types. We just want the+-- monomorphic type used by the process using the procfun.+-- Why not just including the 'TypeRep' in ProcFunAST?+-- We need the context of the process to know what monomorphic types are +-- going to be used. Thus it is imposible to guess the TypeRep within+-- the code of newProcFun.+data TypedProcFunAST = + TypedProcFunAST {tptyp :: FSDTypeRep, -- function type+ tpEnums :: Set EnumAlgTy, -- enumerated types associated + -- with the function+ tpast :: ProcFunAST}++-- | transform a ProcFun into a Dynamic TypedProcFun+procFun2Dyn :: Typeable a => Set EnumAlgTy -> ProcFun a -> TypedProcFun Dynamic+procFun2Dyn s (ProcFun v l a) = + TypedProcFun (toDyn v) l (TypedProcFunAST (fsdTy (typeOf v)) s a)++-- FIXME: probably not needed+-- | tranform the arguments and return value of+-- a ProcFun to dynamic+contProcFun2Dyn :: (Typeable1 container,+ Typeable b,+ Functor container, + Typeable a) =>+ Set EnumAlgTy ->+ ProcFun (container a -> b) -> + TypedProcFun (container Dynamic -> Dynamic)+contProcFun2Dyn s (ProcFun v l a) = + TypedProcFun (fmapDyn v) l (TypedProcFunAST (fsdTy (typeOf v)) s a)+ where fmapDyn f cont = toDyn (f (fmap (fromJust.fromDynamic) cont)) +++----------------------------+-- Internal Helper Functions+----------------------------++-- | Check the decarations passed to newProcFun to be correct+checkDecs :: [Dec] -> Q (Name, [Clause])+checkDecs [FunD name2 cls] = return (name2, cls) +-- in case a signature is provided+checkDecs [SigD name1 _, FunD name2 cls] | name1 == name2 = + return (name1, cls) +checkDecs _ = qGiveUp name+ where name = "ForSyDe.Process.ProcFun.checkDecs"
+ src/ForSyDe/Deep/Process/ProcType.hs view
@@ -0,0 +1,178 @@+{-# LANGUAGE TemplateHaskell, PolyKinds, CPP#-}+#if __GLASGOW_HASKELL__ >= 800+{-# OPTIONS_GHC -freduction-depth=64 #-}+#else+{-# OPTIONS_GHC -fcontext-stack=64 #-}+#endif+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.Process.ProcType+-- Copyright : (c) ES Group, KTH/ICT/ES 2008-2013+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : non-portable (non-standard instances)+--+-- This module includes and exports, the internal definition, instantiations+-- and related types of 'ProcType', a class used to constrain the arguments+-- taken by process constructors.+-----------------------------------------------------------------------------+module ForSyDe.Deep.Process.ProcType (+ EnumAlgTy(..),+ ProcType(..),+ genTupInstances) where++import Control.Monad (replicateM)+import Data.List (intersperse)+import Data.Data+import Data.Set (Set, union)+import Language.Haskell.TH+import Language.Haskell.TH.Syntax (Lift(..))+import Text.ParserCombinators.ReadP++-- | Data type describing an algebraic enumerated type (i.e. an algrebraic+-- type whose data constructors have arity zero)+data EnumAlgTy = EnumAlgTy String [String]+ deriving Show++instance Eq EnumAlgTy where+ (EnumAlgTy d1 _) == (EnumAlgTy d2 _) = d1 == d2++instance Ord EnumAlgTy where+ (EnumAlgTy d1 _) `compare` (EnumAlgTy d2 _) = d1 `compare` d2++-- | Class used to constrain the arguments (values and 'ProcFun's) taken by+-- process constructors+class (Data a, Lift a) => ProcType a where+ -- | Get the associated enumerated type-definitions of certain value,+ -- taking nesting in account.+ --+ -- For example:+ --+ -- > module MyMod where+ -- >+ -- > data Colour = Blue | Red+ -- > deriving (Data, Typeable)+ -- > data Shapes = Circle | Square+ -- > deriving (Data, Typeable)+ -- >+ -- > getEnums (Prst Blue, Circle) =+ -- > fromList [EnumAlgTy "MyMod.Colour" ["Blue", "Red"],+ -- > EnumAlgTy "MyMod.Shapes" ["Circle", "Square"]]+ getEnums :: a -> Set EnumAlgTy+ -- | Read a process type+ readProcType :: ReadP a++-- Function to automatically generate ProcType, Data, and Lift+-- instances for tuples (with 2 or more elements) with Template Haskell. For+-- example, in the case of 2 elements, the code generated would be:+--+-- @+-- instance (ProcType o1, ProcType o2) => ProcType (o1, o2) where+-- getEnums _ = getEnums (undefined :: a) `union` getEnums (undefined :: b)+-- readProcType = do+-- skipSpaces >> char '('+-- o1 <- readProcType+-- skipSpaces >> char ','+-- o2 <- readProcType+-- skipSpaces >> char ')'+-- return (o1,o2)+--+-- deriving Data and Lift is only neccessary for tuples with more than 7+-- elements:+--+-- instance (Data o1, Data o2) => Data (o1, o2) where+-- gfoldl k z (o1, o2) = z (,) `k` o1 `k` o2+-- gunfold k z _ = k (k (z (,) ))+-- toConstr a = mkConstr (dataTypeOf a) "(,)" [] Prefix+-- dataTypeOf a = mkDataType "Data.Tuple.(,)" [toConstr a]+--+-- FIXME: This won't be necessary once the Data a => Lift a instance is created+--+-- instance (Lift o1, Lift o2) => Lift (o1, o2) where+-- lift (o1, o2) = tupE [lift o1, lift o2]+-- @+genTupInstances :: Int -- ^ number of outputs to generate+ -> Q [Dec]+genTupInstances n = do+ -- Generate N o names+ outNames <- replicateM n (newName "o")+ let tupType = foldl accumApp (tupleT n) outNames+ accumApp accumT vName = accumT `appT` varT vName+ if n <= 7+ then sequence [genProcTypeIns outNames tupType]+ else sequence [genDataIns outNames tupType,+ genLiftIns outNames tupType,+ genProcTypeIns outNames tupType]++ where+ undef t = sigE [| undefined |] (varT t)+ genProcTypeIns :: [Name] -> Q Type -> Q Dec+ genProcTypeIns names tupType = do+ let getEnumsExpr =+ foldr1 (\e1 e2 -> infixE (Just e1)+ (varE 'union)+ (Just e2) )+ (map (\n -> varE 'getEnums `appE` undef n) names)+ getEnumsD = funD 'getEnums [clause [wildP] (normalB getEnumsExpr) []]+ readProcTypeExpr = doE $+ bindS wildP [| skipSpaces >> char '(' |] :+ (intersperse (bindS wildP [| skipSpaces >> char ',' |])+ (map (\n -> bindS (varP n) [| readProcType |]) names) +++ [bindS wildP [| skipSpaces >> char ')' |],+ noBindS [| return $(tupE $ map varE names) |] ] )+ readProcTypeD = funD 'readProcType+ [clause [] (normalB readProcTypeExpr) []]+ procTypeCxt = map (\vName -> appT (conT ''ProcType) (varT vName)) names +++ map (\vName -> appT (conT ''Data) (varT vName)) names +++ map (\vName -> appT (conT ''Lift) (varT vName)) names+ instanceD (cxt procTypeCxt)+ (conT ''ProcType `appT` tupType)+ [getEnumsD, readProcTypeD]+ genDataIns :: [Name] -> Q Type -> Q Dec+ genDataIns names tupType = do+ k <- newName "k"+ z <- newName "z"+ a <- newName "a"+ let tupCons = conE tupName+ tupName = tupleDataName n+ gfoldlExpr = foldl (\acum n -> infixE (Just acum)+ (varE k)+ (Just $ varE n))+ (varE z`appE` tupCons)+ names+ gfoldlD = funD 'gfoldl+ [clause [varP k, varP z, tupP (map varP names)]+ (normalB gfoldlExpr) []]+ gunfoldExpr = let nKs 0 = (varE z `appE` tupCons)+ nKs n = varE k `appE` (nKs (n-1))+ in nKs n+ gunfoldD = funD 'gunfold+ [clause [varP k, varP z, wildP] (normalB gunfoldExpr) []]+ toConstrExpr = [| mkConstr (dataTypeOf $(varE a))+ $(litE $ stringL (nameBase tupName))+ []+ Prefix |]+ toConstrD = funD 'toConstr+ [clause [varP a] (normalB toConstrExpr) []]+ dataTypeOfExpr = [| mkDataType $(litE $ stringL (show tupName))+ [toConstr $(varE a)] |]+ dataTypeOfD = funD 'dataTypeOf+ [clause [varP a] (normalB dataTypeOfExpr) []]+ dataCxt = map (\vName -> appT (conT ''Data) (varT vName)) names+ instanceD (cxt dataCxt)+ (conT ''Data `appT` tupType)+ [gfoldlD, gunfoldD, toConstrD, dataTypeOfD]+ genLiftIns :: [Name] -> Q Type -> Q Dec+ genLiftIns names tupType = do+ let liftExpr =+ varE 'tupE `appE` listE (map (\n -> varE 'lift `appE` varE n) names)+ liftD = funD 'lift+ [clause [tupP (map varP names)] (normalB liftExpr) []]+ liftCxt = map (\vName -> appT (conT ''Lift) (varT vName)) names+ instanceD (cxt liftCxt)+ (conT ''Lift `appT` tupType)+ [liftD]++
+ src/ForSyDe/Deep/Process/ProcType/Instances.hs view
@@ -0,0 +1,119 @@+{-# LANGUAGE ScopedTypeVariables, FlexibleInstances,+ UndecidableInstances, TemplateHaskell #-}+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.System.SysFun.Instances+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : non-portable (Template Haskell)+--+-- This module provides the 'ProcType' instances (and 'Data',+-- 'Typeable' and 'Lift' when necessary) . It cannot be included in+-- "ForSyDe.Process.ProcType" directly due to a Template Haskell bug+-- which prevents Template Haskell from executing functions defined in+-- the same module: <http://hackage.haskell.org/trac/ghc/ticket/1800>+--+----------------------------------------------------------------------------- +module ForSyDe.Deep.Process.ProcType.Instances where++import ForSyDe.Deep.Config (maxTupleSize)+import ForSyDe.Deep.Process.ProcType+import ForSyDe.Deep.AbsentExt++import Data.TypeLevel.Num.Sets (Nat, toInt)+import Data.Param.FSVec (FSVec, reallyUnsafeVector)+++import Data.Data+import Control.Monad (liftM, liftM2, mzero)+import Text.ParserCombinators.ReadP+import Data.Set (empty, singleton)+import Language.Haskell.TH.Syntax (Lift(..), runIO)++-------------------------------------------------------------+-- Checking if a member of Data belongs to an enumerated type+-------------------------------------------------------------++-- | Phony type used to check if a data constructor is enumerate (has )+newtype IsConsEnum a = IsConsEnum {unIsConsEnum :: Bool}++-- | Tell if a member of "Data" belongs to an enumerated type+isConsEnum :: Data a => Constr -> IsConsEnum a+isConsEnum = gunfold (\_ -> IsConsEnum False) (\_ -> IsConsEnum True) ++-- | Tell if a member of "Data" belongs to an enumerated type+-- and return its description.+getEnumAlgTy :: forall a . (Typeable a, Data a) => a -> Maybe EnumAlgTy+getEnumAlgTy a = case dataTypeRep dt of+ AlgRep cons -> do + strs <- mapM (\c -> toMaybe (unIsConsEnum (isConsEnum c :: IsConsEnum a)) + (showConstr c)) cons + return (EnumAlgTy dn strs)+ _ -> Nothing+ where dt = dataTypeOf a+ tycon = typeRepTyCon.typeOf $ a+ modName = tyConModule tycon+ baseName = tyConName tycon+ dn = modName ++ "." ++ baseName+ toMaybe bool c = if bool then Just c else Nothing++-------------+-- Instances+-------------++instance {-# OVERLAPPABLE #-} (Lift a, Data a) => ProcType a where+ getEnums _ = maybe empty singleton (getEnumAlgTy (undefined :: a))+ -- We add parenthesis and try to use gread. + -- In addition, since gread is broken for unit (), we create or own parser+ readProcType = do skipSpaces+ -- get all the input while no separator is found+ str <- munch1 (\c -> not (elem c " ,()<>" )) + gReadUnaryCons str+ where -- Generically read a unary constructor (possibly an integer, float etc .. )+ gReadUnaryCons :: forall a . Data a => String -> ReadP a+ gReadUnaryCons str = do + cons <- maybe mzero return $ readConstr (dataTypeOf (undefined :: a)) str+ fromConstrM (fail "readProcType: non-unary constructor found") cons++++instance (Typeable s, Nat s, ProcType a) => ProcType (FSVec s a) where+ getEnums _ = getEnums (undefined :: a)+ readProcType = do+ skipSpaces + _ <- char '<'+ elems <- countSepBy (toInt (undefined :: s))+ readProcType + (skipSpaces >> char ',' >> skipSpaces)+ _ <- char '>'+ return (reallyUnsafeVector elems)+ where countSepBy n p sep = + if n == 0 + then return []+ else liftM2 (:) p (sequence (replicate (n-1) (sep >> p))) + ++instance ProcType a => ProcType (AbstExt a) where+ getEnums _ = getEnums (undefined :: a)+ readProcType = skipSpaces >> (absP <++ prstP)+ where absP = do _ <- string "Abst" + return Abst+ prstP = do _ <- string "Prst"+ skipSpaces+ v <- readProcType+ return $ Prst v++-- Tuple instances+$(let concatMapM f xs = liftM concat (mapM f xs) + msg = "Generating and compiling " ++ show (maxTupleSize -2) ++ + " tuple instances of " +++ show ''ProcType ++ + ", this might take some time ... \n"+ in runIO (putStrLn $ msg) >>+ concatMapM genTupInstances [2..maxTupleSize])+++
+ src/ForSyDe/Deep/Process/ProcVal.hs view
@@ -0,0 +1,52 @@+{-# LANGUAGE ScopedTypeVariables #-}+----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.Process.ProcVal+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : non-portable (Template Haskell)+--+-- This module provides a type ('ProcVal') to store the value arguments passed+-- to process constructors.+--+----------------------------------------------------------------------------- +module ForSyDe.Deep.Process.ProcVal where+++import ForSyDe.Deep.Process.ProcType++import Data.Typeable (typeRep)+import Data.Dynamic (toDyn, Dynamic)+import Data.Set+import Data.Proxy+import Language.Haskell.TH (Exp, runQ)+import Language.Haskell.TH.Syntax (Lift(..))+import System.IO.Unsafe (unsafePerformIO)+import Data.Typeable.FSDTypeRepLib (fsdTy, FSDTypeRep)+++data ProcVal = ProcVal + {dyn :: Dynamic, -- Dynamic value + valAST :: ProcValAST} -- its AST++data ProcValAST = ProcValAST+ {expVal :: Exp, -- Its AST representation+ expTyp :: FSDTypeRep, -- Type of the expression + expEnums :: Set EnumAlgTy} -- Enumerated types associated+ -- with the expression++-- | 'ProcVal' constructor+mkProcVal :: (Lift a, ProcType a) => a -> ProcVal+-- FIXME: would unsafePerformIO cause any harm to get the exp out of the+-- Q monad in this context?+mkProcVal val = ProcVal (toDyn val) (mkProcValAST val) ++mkProcValAST :: (Lift a, ProcType a) => a -> ProcValAST +-- FIMXE: the unsafePerformIO won't be needed once the Data a => Lift a+-- instance is created+mkProcValAST (val :: x) = ProcValAST (unsafePerformIO.runQ.lift $ val) + (fsdTy.typeRep $ (Proxy :: Proxy x))+ (getEnums val)
+ src/ForSyDe/Deep/Process/SynchProc.hs view
@@ -0,0 +1,899 @@+{-# LANGUAGE TemplateHaskell, ScopedTypeVariables, RelaxedPolyRec,+ PatternGuards #-}+-- The PatternGuards are used to hush innapropiate compiler warnings+-- see http://hackage.haskell.org/trac/ghc/ticket/2017+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.Process.SynchProc+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : non-portable (Template Haskell)+--+-- This module provides the synchronous process constructors of+-- ForSyDe and some useful synchronous processes.+--+-----------------------------------------------------------------------------+module ForSyDe.Deep.Process.SynchProc (+ -- * Combinational process constructors+ -- | Combinational process constructors are used for processes that do not+ -- have a state.+ constSY, mapSY, zipWithSY, zipWith3SY,+ zipWith4SY, zipWith5SY, zipWith6SY, zipWithxSY,+ -- * Sequential process constructors+ -- | Sequential process constructors are used for processes that have a state.+ -- One of the input parameters is the initial state.+ delaySY, delaynSY,+ scanlSY, scanl2SY, scanl3SY, scanl4SY,+ scanldSY, scanld2SY, scanld3SY, scanld4SY,+ mooreSY, moore2SY, moore3SY, moore4SY,+ mealySY, mealy2SY, mealy3SY, mealy4SY,+ sourceSY, filterSY, fillSY, holdSY,+ -- * Synchronous Processes+ -- | The library contains a few simple processes that are applicable to many+ -- cases.+ whenSY, zipSY, zip3SY, zip4SY, zip5SY, zip6SY,+ unzipSY, unzip3SY, unzip4SY, unzip5SY, unzip6SY,+ zipxSY, unzipxSY, mapxSY,+ fstSY, sndSY, groupSY) where+++import ForSyDe.Deep.Ids+import ForSyDe.Deep.Process.ProcType+import ForSyDe.Deep.Process.ProcType.Instances()+import ForSyDe.Deep.Process.ProcFun+import ForSyDe.Deep.Process.ProcVal+import ForSyDe.Deep.OSharing+import ForSyDe.Deep.Netlist+import ForSyDe.Deep.AbsentExt++import qualified Data.Param.FSVec as V+import Data.Param.FSVec hiding ((++), map)+import Data.TypeLevel (Nat, toInt)+import Data.Typeable.FSDTypeRepLib++import Data.Set (union)+import Data.Maybe+import Data.Dynamic++-----------------------------------+-- Synchronous Process Constructors+-----------------------------------++----------------------------------------+-- --+-- COMBINATIONAL PROCESS CONSTRUCTORS --+-- --+----------------------------------------+++-- | Creates a constant process. A process which outputs the+-- same signal value in every clock cycle.+constSY :: ProcType a =>+ ProcId -- ^ Identifier of the process+ -> a -- ^ Value to output+ -> Signal a -- ^ Resulting output signal+constSY id v = Signal (newNodeOutSig nodeRef ConstOut)+ where nodeRef = newURef $ Proc id $ Const (mkProcVal v)+++++-- | The process constructor 'mapSY' takes an identifier and a+-- combinational function as arguments and returns a process with one+-- input signal and one output signal.+mapSY :: forall a b . (ProcType a, ProcType b) =>+ ProcId -- ^ Identifier of the process+ -> ProcFun (a -> b) -- ^ Function applied to the input signal+ -- in every cycle+ -> Signal a -- ^ Input 'Signal'+ -> Signal b -- ^ Output 'Signal'+mapSY id f s = Signal (newNodeOutSig nodeRef ZipWithNSYOut)+ where nodeRef = newURef $ Proc id $ ZipWithNSY dynPF [unSignal s]+ dynPF = procFun2Dyn (getEnums (undefined ::a) `union`+ getEnums (undefined ::b))+ f++-- | The process constructor 'zipWithSY' takes an identifier and a+-- combinational function as arguments and returns a process with+-- two input signals and one output signal.+zipWithSY :: forall a b c . (ProcType a, ProcType b, ProcType c) =>+ ProcId -- ^ Identifier of the process+ -> ProcFun (a -> b -> c) -- ^ Function applied to the input signals+ -- in every cycle+ -> Signal a -- ^ First input 'Signal'+ -> Signal b -- ^ Second input 'Signal'+ -> Signal c -- ^ Output Signal+zipWithSY id f s1 s2 = Signal (newNodeOutSig nodeRef ZipWithNSYOut)+ where nodeRef = newURef $ Proc id $+ ZipWithNSY dynPF [unSignal s1,unSignal s2]+ dynPF = procFun2Dyn (getEnums (undefined ::a) `union`+ getEnums (undefined ::b) `union`+ getEnums (undefined ::c) )+ f++-- | The process constructor 'zipWith3SY' takes an identifier and a+-- combinational function as arguments and returns a process with+-- three input signals and one output signal.+zipWith3SY :: forall a b c d.+ (ProcType a, ProcType b, ProcType c, ProcType d) =>+ ProcId -- ^ Identifier of the process+ -> ProcFun (a -> b -> c -> d) -- ^ Function applied to the input+ -- signals in every cycle+ -> Signal a -- ^ First input 'Signal'+ -> Signal b -- ^ Second input 'Signal'+ -> Signal c -- ^ Third input 'Signal'+ -> Signal d -- ^ Output Signal+zipWith3SY id f s1 s2 s3 = Signal (newNodeOutSig nodeRef ZipWithNSYOut)+ where nodeRef = newURef $ Proc id $+ ZipWithNSY dynPF+ [unSignal s1,+ unSignal s2,+ unSignal s3]+ dynPF = procFun2Dyn (getEnums (undefined ::a) `union`+ getEnums (undefined ::b) `union`+ getEnums (undefined ::c) `union`+ getEnums (undefined ::d))+ f+++-- | The process constructor 'zipWith4SY' takes an identifier and a+-- combinational function as arguments and returns a process with+-- four input signals and one output signal.+zipWith4SY ::forall a b c d e.+ (ProcType a, ProcType b, ProcType c, ProcType d, ProcType e) =>+ ProcId -- ^ Identifier of the process+ -> ProcFun (a -> b -> c -> d -> e) -- ^ Function applied to the+ -- input signals in every cycle+ -> Signal a -- ^ First input 'Signal'+ -> Signal b -- ^ Second input 'Signal'+ -> Signal c -- ^ Third input 'Signal'+ -> Signal d -- ^ Fourth input 'Signal'+ -> Signal e -- ^ Output Signal+zipWith4SY id f s1 s2 s3 s4 = Signal (newNodeOutSig nodeRef ZipWithNSYOut)+ where nodeRef = newURef $ Proc id $+ ZipWithNSY dynPF+ [unSignal s1,+ unSignal s2,+ unSignal s3,+ unSignal s4]+ dynPF = procFun2Dyn (getEnums (undefined ::a) `union`+ getEnums (undefined ::b) `union`+ getEnums (undefined ::c) `union`+ getEnums (undefined ::d) `union`+ getEnums (undefined ::e) )+ f+-- | The process constructor 'zipWith5SY' takes an identifier and a+-- combinational function as arguments and returns a process with+-- five input signals and one output signal.+zipWith5SY :: forall a b c d e f.+ (ProcType a, ProcType b, ProcType c, ProcType d, ProcType e,+ ProcType f) =>+ ProcId -- ^ Identifier of the process+ -> ProcFun (a -> b -> c -> d -> e -> f)+ -- ^ Function applied to the+ -- input signals in every cycle+ -> Signal a -- ^ First input 'Signal'+ -> Signal b -- ^ Second input 'Signal'+ -> Signal c -- ^ Third input 'Signal'+ -> Signal d -- ^ Fourth input 'Signal'+ -> Signal e -- ^ Fifth input 'Signal'+ -> Signal f -- ^ Output Signal+zipWith5SY id f s1 s2 s3 s4 s5 = Signal (newNodeOutSig nodeRef ZipWithNSYOut)+ where nodeRef = newURef $ Proc id $+ ZipWithNSY dynPF+ [unSignal s1,+ unSignal s2,+ unSignal s3,+ unSignal s4,+ unSignal s5]+ dynPF = procFun2Dyn (getEnums (undefined ::a) `union`+ getEnums (undefined ::b) `union`+ getEnums (undefined ::c) `union`+ getEnums (undefined ::d) `union`+ getEnums (undefined ::e) `union`+ getEnums (undefined ::f) )+ f+++-- | The process constructor 'zipWith6SY' takes an identifier and a+-- combinational function as arguments and returns a process with+-- five input signals and one output signal.+zipWith6SY :: forall a b c d e f g.+ (ProcType a, ProcType b, ProcType c, ProcType d, ProcType e,+ ProcType f, ProcType g) =>+ ProcId -- ^ Identifier of the process+ -> ProcFun (a -> b -> c -> d -> e -> f -> g)+ -- ^ Function applied to the+ -- input signals in every cycle+ -> Signal a -- ^ First input 'Signal'+ -> Signal b -- ^ Second input 'Signal'+ -> Signal c -- ^ Third input 'Signal'+ -> Signal d -- ^ Fourth input 'Signal'+ -> Signal e -- ^ Fifth input 'Signal'+ -> Signal f -- ^ Sixth input 'Signal'+ -> Signal g -- ^ Output Signal+zipWith6SY id f s1 s2 s3 s4 s5 s6 = Signal (newNodeOutSig nodeRef ZipWithNSYOut)+ where nodeRef = newURef $ Proc id $+ ZipWithNSY dynPF+ [unSignal s1,+ unSignal s2,+ unSignal s3,+ unSignal s4,+ unSignal s5,+ unSignal s6]+ dynPF = procFun2Dyn (getEnums (undefined ::a) `union`+ getEnums (undefined ::b) `union`+ getEnums (undefined ::c) `union`+ getEnums (undefined ::d) `union`+ getEnums (undefined ::e) `union`+ getEnums (undefined ::f) `union`+ getEnums (undefined ::g) )+ f++-- | The process constructor 'mapxSY' creates a process network that maps a+-- function onto all signals in a vector of signals. The identifier is used+-- as the identifier prefix of the processes created (a number starting with 1+-- will be appended to each identifier)+mapxSY :: (Nat s, ProcType a, ProcType b) =>+ ProcId+ -> ProcFun (a -> b)+ -> FSVec s (Signal a)+ -> FSVec s (Signal b)+mapxSY id f = V.zipWith (\n s -> mapSY (id ++ show n) f s)+ (V.reallyUnsafeVector [(1::Int)..])++-- | The process constructor 'zipWithxSY' works as 'zipWithSY', but takes a+-- vector of signals as input.+zipWithxSY :: forall s a b .+ (Nat s, Typeable s, ProcType a, ProcType b) =>+ ProcId+ -> ProcFun (FSVec s a -> b)+ -> FSVec s (Signal a)+ -> Signal b+zipWithxSY id f sv = Signal (newNodeOutSig nodeRef ZipWithxSYOut)+ where nodeRef = newURef $ Proc id $+ ZipWithxSY (vecProcFun2List dynPF)+ (map unSignal (V.fromVector sv))+ -- Transform the vector argument of a procfun into a list+ vecProcFun2List :: TypedProcFun (FSVec s' a' -> b') ->+ TypedProcFun ([a'] -> b')+ vecProcFun2List f = f{tval = \x -> (tval f) (reallyUnsafeVector x)}+ dynPF = contProcFun2Dyn (getEnums (undefined ::a) `union`+ getEnums (undefined ::b) )+ f+++-------------------------------------+-- --+-- SEQUENTIAL PROCESS CONSTRUCTORS --+-- --+-------------------------------------++-- | The process constructor 'delaySY' delays the signal one event cycle+-- by introducing an initial value at the beginning of the output signal.+-- Note, that this implies that there is one event (the first) at the+-- output signal that has no corresponding event at the input signal.+-- One could argue that input and output signals are not fully synchronized,+-- even though all input events are synchronous with a corresponding output+-- event. However, this is necessary to initialize feed-back loops.+delaySY :: ProcType a =>+ ProcId -- ^ Identifier of the process+ -> a -- ^ Initial value+ -> Signal a -- ^ 'Signal' to be delayed+ -> Signal a -- ^ Resulting delayed 'Signal'+delaySY id v s = Signal (newNodeOutSig nodeRef DelaySYOut)+ where procVal = mkProcVal v+ nodeRef = newURef $ Proc id $ DelaySY procVal (unSignal s)+++-- | The process constructor 'delaynSY' delays the signal n events by+-- introducing n identical default values. It creates a chain of 'delaySY'+-- processes.+delaynSY :: ProcType a =>+ ProcId -- ^ Identifier+ -> a -- ^Initial state+ -> Int -- ^ Number of Delay cycles+ -> Signal a -- ^Input signal+ -> Signal a -- ^Output signal+delaynSY id e n s = (\(a,_,_) -> a) $ delaynSYacum (s, 1, n)+ where+ delaynSYacum acum@(lastSig, curr, max)+ | curr > max = acum+ | otherwise =+ delaynSYacum (delaySY (id ++ "_" ++ show curr) e lastSig, curr+1, max)++++-- | The process constructor 'scanlSY' is used to construct a finite state+-- machine process without output decoder. It takes an initial value and+-- a function for the next state decoder. The process constructor behaves+-- similar to the Haskell prelude function 'scanlSY' and has the value of+-- the new state as its output value as illustrated by the+-- following example.+--+-- This is in contrast to the function 'scanldSY', which has its current+-- state as its output value.+scanlSY :: (ProcType a, ProcType b) =>+ ProcId -- ^Process Identifier+ -> ProcFun (a -> b -> a) -- ^Combinational function for next+ -- state decoder+ -> a -- ^Initial state+ -> Signal b -- ^ Input signal+ -> Signal a -- ^ Output signal+scanlSY id f mem s = s'+ where s' = zipWithSY (id ++ "_NxtSt") f+ (delaySY (id ++ "_Delay") mem s') s+++-- | The process constructor 'scanl2SY' behaves like 'scanlSY', but has two+-- input signals.+scanl2SY :: (ProcType a, ProcType b, ProcType c) =>+ ProcId -- ^Process Identifier+ -> ProcFun (a -> b -> c -> a) -- ^Combinational function for next+ -- state decoder+ -> a -- ^Initial state+ -> Signal b -- ^ First Input signal+ -> Signal c -- ^ Second Input signal+ -> Signal a -- ^ Output signal+scanl2SY id f mem s1 s2 = s'+ where s' = zipWith3SY (id ++ "_NxtSt") f+ (delaySY (id ++ "_Delay") mem s') s1 s2+++-- | The process constructor 'scanl3SY' behaves like 'scanlSY', but has+-- three input signals.+scanl3SY :: (ProcType a, ProcType b, ProcType c, ProcType d) =>+ ProcId -- ^Process Identifier+ -> ProcFun (a -> b -> c -> d -> a) -- ^Combinational function for next+ -- state decoder+ -> a -- ^Initial state+ -> Signal b -- ^ First Input signal+ -> Signal c -- ^ Second Input signal+ -> Signal d -- ^ Third Input signal+ -> Signal a -- ^ Output signal+scanl3SY id f mem s1 s2 s3 = s'+ where s' = zipWith4SY (id ++ "_NxtSt") f+ (delaySY (id ++ "_Delay") mem s') s1 s2 s3++-- | The process constructor 'scanl4SY' behaves like 'scanlSY', but has+-- four input signals.+scanl4SY :: (ProcType a, ProcType b, ProcType c, ProcType d, ProcType e) =>+ ProcId -- ^Process Identifier+ -> ProcFun (a -> b -> c -> d -> e -> a) -- ^Combinational function+ -- for next state decoder+ -> a -- ^Initial state+ -> Signal b -- ^ First Input signal+ -> Signal c -- ^ Second Input signal+ -> Signal d -- ^ Third Input signal+ -> Signal e -- ^ Fourth Input signal+ -> Signal a -- ^ Output signal+scanl4SY id f mem s1 s2 s3 s4 = s'+ where s' = zipWith5SY (id ++ "_NxtSt") f+ (delaySY (id ++ "_Delay") mem s') s1 s2 s3 s4++-- | The process constructor 'scanldSY' is used to construct a finite state+-- machine process without output decoder. It takes an initial value and a+-- function for the next state decoder. The process constructor behaves+-- similarly to the Haskell prelude function 'scanlSY'. In contrast to the+-- process constructor 'scanlSY' here the output value is the current state+-- and not the one of the next state.+scanldSY :: (ProcType a, ProcType b) =>+ ProcId+ -> ProcFun (a -> b -> a) -- ^Combinational function+ -- for next state decoder+ -> a -- ^Initial state+ -> Signal b -- ^ Input signal+ -> Signal a -- ^ Output signal+scanldSY id f mem s = s'+ where s' = delaySY (id ++ "_Delay") mem $+ zipWithSY (id ++ "_NxtSt") f s' s++-- | The process constructor 'scanld2SY' behaves like 'scanldSY', but has+-- two input signals.+scanld2SY :: (ProcType a, ProcType b, ProcType c) =>+ ProcId+ -> ProcFun (a -> b -> c -> a) -- ^Combinational function+ -- for next state decoder+ -> a -- ^Initial state+ -> Signal b -- ^ First Input signal+ -> Signal c -- ^ Second Input signal+ -> Signal a -- ^ Output signal+scanld2SY id f mem s1 s2 = s'+ where s' = delaySY (id ++ "_Delay") mem $+ zipWith3SY (id ++ "_NxtSt") f s' s1 s2++-- | The process constructor 'scanld3SY' behaves like 'scanldSY', but has+-- three input signals.+scanld3SY :: (ProcType a, ProcType b, ProcType c, ProcType d) =>+ ProcId+ -> ProcFun (a -> b -> c -> d -> a) -- ^Combinational function+ -- for next state decoder+ -> a -- ^Initial state+ -> Signal b -- ^ First Input signal+ -> Signal c -- ^ Second Input signal+ -> Signal d -- ^ Second Input signal+ -> Signal a -- ^ Output signal+scanld3SY id f mem s1 s2 s3 = s'+ where s' = delaySY (id ++ "_Delay") mem $+ zipWith4SY (id ++ "_NxtSt") f s' s1 s2 s3++-- | The process constructor 'scanld4SY' behaves like 'scanldSY', but has+-- four input signals.+scanld4SY :: (ProcType a, ProcType b, ProcType c, ProcType d, ProcType e) =>+ ProcId+ -> ProcFun (a -> b -> c -> d -> e -> a) -- ^Combinational function+ -- for next state decoder+ -> a -- ^Initial state+ -> Signal b -- ^ First Input signal+ -> Signal c -- ^ Second Input signal+ -> Signal d -- ^ Third Input signal+ -> Signal e -- ^ Fourth Input signal+ -> Signal a -- ^ Output signal+scanld4SY id f mem s1 s2 s3 s4 = s'+ where s' = delaySY (id ++ "_Delay") mem $+ zipWith5SY (id ++ "_NxtSt") f s' s1 s2 s3 s4++-- | The process constructor 'mooreSY' is used to model state machines+-- of \"Moore\" type, where the output only depends on the current+-- state. The process constructor is based on the process constructor+-- 'scanldSY', since it is natural for state machines in hardware, that+-- the output operates on the current state and not on the next+-- state. The process constructors takes a function to calculate the+-- next state, another function to calculate the output and a value for+-- the initial state.+--+-- In contrast the output of a process created by the process constructor+-- 'mealySY' depends not only on the state, but also on the input values.+mooreSY :: (ProcType a, ProcType b, ProcType c) =>+ ProcId+ -> ProcFun (a -> b -> a) -- ^ Combinational function for+ -- next state decoder+ -> ProcFun (a -> c) -- ^Combinational function for output decoder+ -> a -- ^Initial state+ -> Signal b -- ^Input signal+ -> Signal c -- ^Output signal+mooreSY id nextState output initial =+ mapSY (id ++ "_OutDec") output .scanldSY id nextState initial+++-- | The process constructor 'moore2SY' behaves like 'mooreSY', but has two+-- input signals.+moore2SY :: (ProcType a, ProcType b, ProcType c, ProcType d) =>+ ProcId+ -> ProcFun (a -> b -> c -> a) -- ^ Combinational function for+ -- next state decoder+ -> ProcFun (a -> d) -- ^Combinational function for output decoder+ -> a -- ^Initial state+ -> Signal b -- ^First Input signal+ -> Signal c -- ^Second Input signal+ -> Signal d -- ^Output signal+moore2SY id nextState output initial i1 i2 =+ mapSY (id ++ "_OutDec") output $ scanld2SY id nextState initial i1 i2+++-- | The process constructor 'moore3SY' behaves like 'mooreSY', but has+-- three input signals.+moore3SY :: (ProcType a, ProcType b, ProcType c, ProcType d, ProcType e) =>+ ProcId+ -> ProcFun (a -> b -> c -> d -> a) -- ^ Combinational function for+ -- next state decoder+ -> ProcFun (a -> e) -- ^Combinational function for output decoder+ -> a -- ^Initial state+ -> Signal b -- ^First Input signal+ -> Signal c -- ^Second Input signal+ -> Signal d -- ^Third Input signal+ -> Signal e -- ^Output signal+moore3SY id nextState output initial i1 i2 i3 =+ mapSY (id ++ "_OutDec") output $ scanld3SY id nextState initial i1 i2 i3++-- | The process constructor 'moore3SY' behaves like 'mooreSY', but has+-- four input signals.+moore4SY :: (ProcType a, ProcType b, ProcType c, ProcType d, ProcType e, ProcType f) =>+ ProcId+ -> ProcFun (a -> b -> c -> d -> e -> a) -- ^ Combinational function for+ -- next state decoder+ -> ProcFun (a -> f) -- ^Combinational function for output decoder+ -> a -- ^Initial state+ -> Signal b -- ^First Input signal+ -> Signal c -- ^Second Input signal+ -> Signal d -- ^Third Input signal+ -> Signal e -- ^Fourth Input signal+ -> Signal f -- ^Output signal+moore4SY id nextState output initial i1 i2 i3 i4 =+ mapSY (id ++ "_OutDec") output $ scanld4SY id nextState initial i1 i2 i3 i4++-- | The process constructor 'melaySY' is used to model state machines of+-- \"Mealy\" type, where the output only depends on the current state and+-- the input values. The process constructor is based on the process+-- constructor 'scanldSY', since it is natural for state machines in+-- hardware, that the output operates on the current state and not on the+-- next state. The process constructors takes a function to calculate the+-- next state, another function to calculate the output and a value for the+-- initial state.+--+-- In contrast the output of a process created by the process constructor+-- 'mooreSY' depends only on the state, but not on the input values.+mealySY :: (ProcType a, ProcType b, ProcType c) =>+ ProcId+ -> ProcFun (a -> b -> a) -- ^Combinational function for next+ -- state decoder+ -> ProcFun (a -> b -> c) -- ^Combinational function for output decoder+ -> a -- ^Initial state+ -> Signal b -- ^Input signal+ -> Signal c -- ^Output signal+mealySY id nextState output initial i =+ zipWithSY (id ++ "_OutDec") output state i+ where state = scanldSY id nextState initial i++-- | The process constructor 'mealy2SY' behaves like 'mealySY', but has+-- two input signals.+mealy2SY :: (ProcType a, ProcType b, ProcType c, ProcType d) =>+ ProcId+ -> ProcFun (a -> b -> c -> a) -- ^Combinational function for next+ -- state decoder+ -> ProcFun (a -> b -> c -> d) -- ^Combinational function for output+ -- decoder+ -> a -- ^Initial state+ -> Signal b -- ^First Input signal+ -> Signal c -- ^Second Input signal+ -> Signal d -- ^Output signal+mealy2SY id nextState output initial i1 i2 =+ zipWith3SY (id ++ "_OutDec") output state i1 i2+ where state = scanld2SY id nextState initial i1 i2+++-- | The process constructor 'mealy3SY' behaves like 'mealySY', but has+-- three input signals.+mealy3SY :: (ProcType a, ProcType b, ProcType c, ProcType d,+ ProcType e) =>+ ProcId+ -> ProcFun (a -> b -> c -> d -> a) -- ^Combinational function for next+ -- state decoder+ -> ProcFun (a -> b -> c -> d -> e) -- ^Combinational function for+ -- output decoder+ -> a -- ^Initial state+ -> Signal b -- ^First Input signal+ -> Signal c -- ^Second Input signal+ -> Signal d -- ^Third Input signal+ -> Signal e -- ^Output signal+mealy3SY id nextState output initial i1 i2 i3 =+ zipWith4SY (id ++ "_OutDec") output state i1 i2 i3+ where state = scanld3SY id nextState initial i1 i2 i3++-- | The process constructor 'mealy4SY' behaves like 'mealySY', but has+-- four input signals.+mealy4SY :: (ProcType a, ProcType b, ProcType c, ProcType d,+ ProcType e, ProcType f) =>+ ProcId+ -> ProcFun (a -> b -> c -> d -> e -> a) -- ^Combinational function for next+ -- state decoder+ -> ProcFun (a -> b -> c -> d -> e -> f) -- ^Combinational function for+ -- output decoder+ -> a -- ^Initial state+ -> Signal b -- ^First Input signal+ -> Signal c -- ^Second Input signal+ -> Signal d -- ^Third Input signal+ -> Signal e -- ^Fourth Input signal+ -> Signal f -- ^Output signal+mealy4SY id nextState output initial i1 i2 i3 i4 =+ zipWith5SY (id ++ "_OutDec") output state i1 i2 i3 i4+ where state = scanld4SY id nextState initial i1 i2 i3 i4+++-- | The process constructor 'filterSY' discards the values who do not fulfill a predicate given by a predicate function and replaces them with absent events.+filterSY :: ProcType a =>+ ProcId+ -> ProcFun (a -> Bool) -- ^ Predicate function+ -> Signal a -- ^ Input signal+ -> Signal (AbstExt a) -- ^ Output signal+filterSY id pred = mapSY id (filterer `defArgPF` pred)+ where filterer =+ $(newProcFun [d| filterer :: (a -> Bool) -> a -> AbstExt a+ filterer pred val =+ if pred val then Prst val+ else Abst |])++-- | The process 'sourceSY' takes a function and an initial state and generates+-- an infinite signal starting with the initial state as first output+-- followed by the recursive application of the function on the current+-- state. The state also serves as output value.+--+-- The process that has the infinite signal of natural numbers as output is+-- con structed by+--+-- sourceSY \"naturals\" (+1) 0+sourceSY :: ProcType a =>+ ProcId+ -> ProcFun (a -> a)+ -> a+ -> Signal a+sourceSY id f s0 = o+ where o = delaySY (id ++ "_Delay") s0 s+ s = mapSY (id ++ "_Nxt") f o+++-- | The process constructor 'fillSY' creates a process that 'fills' a signal+-- with present values by replacing absent values with a given value. The+-- output signal is not any more of the type 'AbstExt'.+fillSY :: ProcType a =>+ ProcId+ -> a -- ^Default value+ -> Signal (AbstExt a) -- ^Absent extended input signal+ -> Signal a -- ^Output signal+fillSY id v s = mapSY id (replaceAbst `defArgVal` v) s+ where replaceAbst :: ProcFun (a -> AbstExt a -> a)+ replaceAbst = $(newProcFun+ [d| replaceAbst :: a -> AbstExt a -> a+ replaceAbst x y = fromAbstExt x y |])++-- | The process constructor 'holdSY' creates a process that 'fills' a signal+-- with values by replacing absent values by the preceding present value.+-- Only in cases, where no preceding value exists, the absent value is+-- replaced by a default value. The output signal is not any more of the+-- type 'AbstExt'.+holdSY :: ProcType a =>+ ProcId -- ^Default value+ -> a+ -> Signal (AbstExt a) -- ^Absent extended input signal+ -> Signal a -- ^Output signal+holdSY id a s = scanlSY id hold a s+ where hold = $(newProcFun [d| hold :: a -> AbstExt a -> a+ hold a abs = fromAbstExt a abs |])++---------------------------+-- --+-- SYNCHRONOUS PROCESSES --+-- --+---------------------------+++-- | The process constructor 'whenSY' creates a process that synchronizes a+-- signal of absent extended values with another signal of absent extended+-- values. The output signal has the value of the first signal whenever an+-- event has a present value and 'Abst' when the event has an absent value.+whenSY :: (ProcType a, ProcType b) =>+ ProcId+ -> Signal (AbstExt a) -> Signal (AbstExt b) -> Signal (AbstExt a)+whenSY id = zipWithSY id whenF+ where whenF = $(newProcFun [d| whenF :: AbstExt a -> AbstExt b -> AbstExt a+ whenF v1 v2 = if isAbsent v2+ then Abst+ else v1 |])+++-- | The process 'zipSY' \"zips\" two incoming signals into one signal of+-- tuples.+zipSY :: (ProcType a, ProcType b) =>+ ProcId+ -> Signal a+ -> Signal b+ -> Signal (a,b)+zipSY id = zipWithSY id tup2+ where tup2 :: ProcFun (a -> b -> (a,b))+ tup2 = $(newProcFun [d| tup2 :: a -> b -> (a,b)+ tup2 a b = (a,b) |])++-- | The process 'zip3SY' works as 'zipSY', but takes three input signals.+zip3SY :: (ProcType a, ProcType b, ProcType c) =>+ ProcId ->+ Signal a ->+ Signal b ->+ Signal c ->+ Signal (a,b,c)+zip3SY id = zipWith3SY id tup3+ where tup3 :: ProcFun (a -> b -> c -> (a,b,c))+ tup3 = $(newProcFun [d| tup3 :: a -> b -> c -> (a,b,c)+ tup3 a b c = (a,b,c) |])+++-- | The process 'zip4SY' works as 'zipSY', but takes four input signals.+zip4SY :: (ProcType a, ProcType b, ProcType c, ProcType d) =>+ ProcId ->+ Signal a ->+ Signal b ->+ Signal c ->+ Signal d ->+ Signal (a,b,c,d)+zip4SY id = zipWith4SY id tup4+ where tup4 :: ProcFun (a -> b -> c -> d -> (a,b,c,d))+ tup4 = $(newProcFun [d| tup4 :: a -> b -> c -> d -> (a,b,c,d)+ tup4 a b c d = (a,b,c,d) |])+++-- | The process 'zip5SY' works as 'zipSY', but takes five input signals.+zip5SY :: (ProcType a, ProcType b, ProcType c, ProcType d, ProcType e) =>+ ProcId ->+ Signal a ->+ Signal b ->+ Signal c ->+ Signal d ->+ Signal e ->+ Signal (a,b,c,d,e)+zip5SY id = zipWith5SY id tup5+ where tup5 :: ProcFun (a -> b -> c -> d -> e -> (a,b,c,d,e))+ tup5 = $(newProcFun [d| tup5 :: a -> b -> c -> d -> e -> (a,b,c,d,e)+ tup5 a b c d e = (a,b,c,d,e) |])+++-- | The process 'zip6SY' works as 'zipSY', but takes six input signals.+zip6SY :: (ProcType a, ProcType b, ProcType c, ProcType d, ProcType e,+ ProcType f) =>+ ProcId ->+ Signal a ->+ Signal b ->+ Signal c ->+ Signal d ->+ Signal e ->+ Signal f ->+ Signal (a,b,c,d,e,f)+zip6SY id = zipWith6SY id tup6+ where tup6 :: ProcFun (a -> b -> c -> d -> e -> f -> (a,b,c,d,e,f))+ tup6 = $(newProcFun [d| tup6 :: a -> b -> c -> d -> e -> f ->+ (a,b,c,d,e,f)+ tup6 a b c d e f = (a,b,c,d,e,f) |])+++-- | The process 'unzipSY' \"unzips\" a signal of tuples into two signals.+unzipSY :: forall a b . (ProcType a, ProcType b) =>+ ProcId+ -> Signal (a,b)+ -> (Signal a,Signal b)+unzipSY id s = (Signal (newNodeOutSig nodeRef (UnzipNSYOut 1)),+ Signal (newNodeOutSig nodeRef (UnzipNSYOut 2)))+ where ts = [fsdTypeOf (undefined :: a), fsdTypeOf (undefined :: b)]+ nodeRef = newURef $ Proc id $+ UnzipNSY ts untup (unSignal s)+ untup :: Dynamic -> [Dynamic]+ untup i = let (t1,t2) = ((fromJust.fromDynamic) i) :: (a,b)+ in [toDyn t1, toDyn t2]++++-- | The process 'unzip3SY' \"unzips\" a signal of tuples into three signals.+unzip3SY :: forall a b c . (ProcType a, ProcType b, ProcType c) =>+ ProcId+ -> Signal (a,b,c)+ -> (Signal a, Signal b, Signal c)+unzip3SY id s = (Signal (newNodeOutSig nodeRef (UnzipNSYOut 1)),+ Signal (newNodeOutSig nodeRef (UnzipNSYOut 2)),+ Signal (newNodeOutSig nodeRef (UnzipNSYOut 3)))+ where ts = [fsdTypeOf (undefined :: a), fsdTypeOf (undefined :: b),+ fsdTypeOf (undefined :: c)]+ nodeRef = newURef $ Proc id $+ UnzipNSY ts untup3 (unSignal s)+ untup3 :: Dynamic -> [Dynamic]+ untup3 i = let (t1,t2,t3) = ((fromJust.fromDynamic) i) :: (a,b,c)+ in [toDyn t1, toDyn t2, toDyn t3]+++-- | The process 'unzip4SY' \"unzips\" a signal of tuples into four signals.+unzip4SY :: forall a b c d . (ProcType a, ProcType b, ProcType c,+ ProcType d) =>+ ProcId+ -> Signal (a,b,c,d)+ -> (Signal a, Signal b, Signal c, Signal d)+unzip4SY id s = (Signal (newNodeOutSig nodeRef (UnzipNSYOut 1)),+ Signal (newNodeOutSig nodeRef (UnzipNSYOut 2)),+ Signal (newNodeOutSig nodeRef (UnzipNSYOut 3)),+ Signal (newNodeOutSig nodeRef (UnzipNSYOut 4)))+ where ts = [fsdTypeOf (undefined :: a), fsdTypeOf (undefined :: b),+ fsdTypeOf (undefined :: c), fsdTypeOf (undefined :: d)]+ nodeRef = newURef $ Proc id $+ UnzipNSY ts untup4 (unSignal s)+ untup4 :: Dynamic -> [Dynamic]+ untup4 i = let (t1,t2,t3,t4) = ((fromJust.fromDynamic) i) :: (a,b,c,d)+ in [toDyn t1, toDyn t2, toDyn t3, toDyn t4]+++-- | The process 'unzip5SY' \"unzips\" a signal of tuples into five signals.+unzip5SY :: forall a b c d e . (ProcType a, ProcType b, ProcType c,+ ProcType d, ProcType e) =>+ ProcId+ -> Signal (a,b,c,d,e)+ -> (Signal a, Signal b, Signal c, Signal d, Signal e)+unzip5SY id s = (Signal (newNodeOutSig nodeRef (UnzipNSYOut 1)),+ Signal (newNodeOutSig nodeRef (UnzipNSYOut 2)),+ Signal (newNodeOutSig nodeRef (UnzipNSYOut 3)),+ Signal (newNodeOutSig nodeRef (UnzipNSYOut 4)),+ Signal (newNodeOutSig nodeRef (UnzipNSYOut 5)))+ where ts = [fsdTypeOf (undefined :: a), fsdTypeOf (undefined :: b),+ fsdTypeOf (undefined :: c), fsdTypeOf (undefined :: d),+ fsdTypeOf (undefined :: e)]+ nodeRef = newURef $ Proc id $+ UnzipNSY ts untup5 (unSignal s)+ untup5 :: Dynamic -> [Dynamic]+ untup5 i = let (t1,t2,t3,t4,t5)+ = ((fromJust.fromDynamic) i) :: (a,b,c,d,e)+ in [toDyn t1, toDyn t2, toDyn t3, toDyn t4, toDyn t5]+++-- | The process 'unzip6SY' \"unzips\" a signal of tuples into six signals.+unzip6SY :: forall a b c d e f . (ProcType a, ProcType b, ProcType c,+ ProcType d, ProcType e, ProcType f) =>+ ProcId+ -> Signal (a,b,c,d,e,f)+ -> (Signal a, Signal b, Signal c, Signal d, Signal e, Signal f)+unzip6SY id s = (Signal (newNodeOutSig nodeRef (UnzipNSYOut 1)),+ Signal (newNodeOutSig nodeRef (UnzipNSYOut 2)),+ Signal (newNodeOutSig nodeRef (UnzipNSYOut 3)),+ Signal (newNodeOutSig nodeRef (UnzipNSYOut 4)),+ Signal (newNodeOutSig nodeRef (UnzipNSYOut 5)),+ Signal (newNodeOutSig nodeRef (UnzipNSYOut 6)))+ where ts = [fsdTypeOf (undefined :: a), fsdTypeOf (undefined :: b),+ fsdTypeOf (undefined :: c), fsdTypeOf (undefined :: d),+ fsdTypeOf (undefined :: e), fsdTypeOf (undefined :: f)]+ nodeRef = newURef $ Proc id $+ UnzipNSY ts untup6 (unSignal s)+ untup6 :: Dynamic -> [Dynamic]+ untup6 i = let (t1,t2,t3,t4,t5,t6)+ = ((fromJust.fromDynamic) i) :: (a,b,c,d,e,f)+ in [toDyn t1, toDyn t2, toDyn t3, toDyn t4, toDyn t5,+ toDyn t6]++-- | The process 'zipxSY' \"zips\" a signal of vectors into a vector of signals.+zipxSY :: (Nat s, Typeable s, ProcType a) =>+ ProcId+ -> FSVec s (Signal a)+ -> Signal (FSVec s a)+zipxSY id = zipWithxSY id vectId+ where vectId = $(newProcFun [d| vectId :: FSVec s a -> FSVec s a+ vectId v = v |])++-- | The process 'unzipxSY' \"unzips\" a vector of n signals into a signal of+-- vectors.+unzipxSY :: forall s a . (Typeable s, Nat s, ProcType a) =>+ ProcId+ -> Signal (FSVec s a)+ -> FSVec s (Signal a)+unzipxSY id vs = V.map (\tag -> Signal (newNodeOutSig nodeRef tag) )+ (reallyUnsafeVector [UnzipxSYOut i | i <- [1..n]])+ where n = toInt (undefined :: s)+ t = fsdTypeOf (undefined :: a)+ nodeRef = newURef $ Proc id $+ UnzipxSY t n unvector (unSignal vs)+ unvector :: Dynamic -> [Dynamic]+ unvector i = let v = ((fromJust.fromDynamic) i) :: FSVec s a+ in map toDyn (V.fromVector v)++-- | The process 'fstSY' selects always the first value from a signal of pairs+fstSY :: (ProcType a, ProcType b) => ProcId -> Signal (a,b) -> Signal a+fstSY id = mapSY id first+ where first = $(newProcFun [d| first :: (a,b) -> a+ first (a,_) = a |])+++-- | The process 'sndSY' selects always the second value from a signal of pairs+sndSY :: (ProcType a, ProcType b) => ProcId -> Signal (a,b) -> Signal b+sndSY id = mapSY id second+ where second = $(newProcFun [d| second :: (a,b) -> b+ second (_,b) = b |])++++-- | The function 'groupSY' groups values into a vector of size n, which takes+-- n cycles. While the grouping takes place the output from this process+-- consists of absent values.+groupSY :: forall k a . (Nat k, Typeable k, ProcType a) =>+ ProcId -> k -> Signal a -> Signal (AbstExt (FSVec k a))+groupSY id k = mooreSY id (f `defArgVal` kV) (g `defArgVal` kV) s0+ where+ kV = toInt k+ -- FIXME, FIXME, this won't work in th VHDL backend+ -- due to the undefined and probably unsafeReplace+ s0 = (0, V.copy k (undefined :: a))+ f = $(newProcFun [d| f :: Nat k' => Int -> (Int, FSVec k' a') -> a' ->+ (Int, FSVec k' a')+ f k (count,v) a =+ (count+1 `mod` k, unsafeReplace v count a) |])+ g = $(newProcFun [d| g :: Nat k' => Int -> (Int, FSVec k' a') -> AbstExt (FSVec k' a')+ g k (count,v) = if k-1 == count then Prst v else Abst |])+ unsafeReplace :: Nat s => FSVec s a' -> Int -> a' ->FSVec s a'+ unsafeReplace v i a =+ reallyUnsafeVector $ unsafeReplace' (fromVector v) i a+ where unsafeReplace' [] _ _ = []+ unsafeReplace' (_:xs) 0 y = (y:xs)+ unsafeReplace' (x:xs) n y = x : (unsafeReplace' xs (n - 1) y)+
+ src/ForSyDe/Deep/Signal.hs view
@@ -0,0 +1,30 @@+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.Signal+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- +-- This module provides the fundamental data structure for deep-embedded +-- ForSyDe models: 'Signal'.+--+-----------------------------------------------------------------------------+++-- This module is simply used to export Signal to the end user hiding its internal +-- representation+module ForSyDe.Deep.Signal (Signal) where+++import ForSyDe.Deep.Netlist (Signal)++++ +++
+ src/ForSyDe/Deep/System.hs view
@@ -0,0 +1,24 @@+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.System+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : non-portable (Template Haskell)+--+-- This module provides publicly usable functions to build a system definition+-- and instantiate it.+-- +-----------------------------------------------------------------------------+module ForSyDe.Deep.System +(SysDef, newSysDef, newSysDefTH, newSysDefTHName,+ SysFun, SysFunToSimFun, SysFunToIOSimFun,+ instantiate)+where++import ForSyDe.Deep.System.SysDef (SysDef, newSysDef, newSysDefTH, newSysDefTHName)+import ForSyDe.Deep.System.SysFun (SysFun, SysFunToSimFun, SysFunToIOSimFun)+import ForSyDe.Deep.System.SysFun.Instances ()+import ForSyDe.Deep.System.Instantiate (instantiate)
+ src/ForSyDe/Deep/System/Instantiate.hs view
@@ -0,0 +1,44 @@+{-# LANGUAGE TemplateHaskell #-} +-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.System.Instantiate+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- This module provides instantiation capabilities to System Definitions+-- ('SysDef's).through a Template-Haskell-based function: 'instantiate'.+--+-- Thanks to 'instantiate' ForSyDe models have hierarchy capabilities.+--+-----------------------------------------------------------------------------+module ForSyDe.Deep.System.Instantiate (instantiate) where++import ForSyDe.Deep.Ids (ProcId)+import ForSyDe.Deep.OSharing (readURef)+import ForSyDe.Deep.Netlist+import ForSyDe.Deep.System.SysDef (SysDef)+import ForSyDe.Deep.System.SysFun (SysFun(fromListSysFun))+++++-- | Generates an instance of a 'SysDef' in the form of +-- function out of the name of a 'SysDef' with the same type as its +-- system function. The resulting function can then be used to plug the +-- instance to the rest of the system.+instantiate :: SysFun f => ProcId -> SysDef f -> f+instantiate id sysDef = fromListSysFun (instantiateList id sysDef) []+++-------------------+-- Helper Functions+-------------------++instantiateList :: ProcId -> SysDef a -> [NlSignal] -> [NlSignal]+instantiateList id sysDef inSigs = map (\t -> newNodeOutSig instanceSig t) tags + where instanceSig = newSysIns id sysDef inSigs + tags = outTags $ readURef instanceSig
+ src/ForSyDe/Deep/System/SysDef.hs view
@@ -0,0 +1,410 @@+{-# LANGUAGE CPP,TemplateHaskell #-} +-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.System.SysDef+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- This module provides the System Definition type ('SysDef') together with+-- a Template Haskell constructor to build it. +--+-----------------------------------------------------------------------------+module ForSyDe.Deep.System.SysDef + (SysDef(..),+ PrimSysDef(..),+ SysDefVal(..), + SysLogic(..),+ newSysDef,+ newSysDefTH,+ newSysDefTHName,+ Iface) where++import ForSyDe.Deep.Ids+import ForSyDe.Deep.Netlist +import ForSyDe.Deep.Netlist.Traverse+import ForSyDe.Deep.OSharing+import ForSyDe.Deep.ForSyDeErr+import ForSyDe.Deep.System.SysFun (checkSysFType, SysFun(..))++import Data.Maybe (isJust, fromJust)+import Control.Monad.ST+import Control.Monad.State+import Data.Typeable+import Language.Haskell.TH hiding (Loc)+import Language.Haskell.TH.LiftInstances ()+import Data.Typeable.FSDTypeRepLib+++++-- | Interface, describes the input or output ports of the system.+-- Each entry contains the name of the port and its type.+type Iface = [(PortId, FSDTypeRep)]+++-- | We add a phantom parameter to indicate the type of the system +newtype SysDef a = SysDef {unSysDef :: PrimSysDef}++-- | The Primitive System Definition.+-- Instead of just providing the value, a reference is provided+-- to allow sharing between instances.+newtype PrimSysDef = PrimSysDef {unPrimSysDef :: URef SysDefVal}++++-- | Indicates wether a system is combinational or sequential+-- In practice, a system is sequential if if contains a delay process or+-- a sequential system instance, otherwise its combinational.+data SysLogic = Combinational | Sequential+ deriving (Eq, Show)+++-- | The System Definition value+data SysDefVal = SysDefVal + {sid :: SysId, -- ^ Identifier of the System + netlist :: Netlist [], -- ^ System netlist+ subSys :: [PrimSysDef], -- ^ List of all unique nested subsystems+ -- (i.e. flattened tree, without duplicates,+ -- of all the systems in lower levels of the + -- hierarchy)+ logic :: SysLogic, -- ^ 'SysLogic' of the system + + iIface :: Iface, -- ^ Input interface+ oIface :: Iface, -- ^ Output interface + loc :: Maybe Loc} -- ^ Location of the call to newSysDef+ -- which created this System definition+ -- (used for later error reporting)+ -- It will initialized or not depending+ -- on the newSysDef* function used++++-- | 'SysDef' constructor+--+-- Builds a system definition out of a system function describing the system +-- and its port identifers. +newSysDef :: SysFun f => f -- ^ system function + -> SysId -- ^ System identifier + -> [PortId] -- ^ Input interface port identifiers + -> [PortId] -- ^ Output interface port identifiers + -> SysDef f+newSysDef f sysId inIds outIds = either currError id eProneResult+ where currError = uError "newSysDef"+ eProneResult = newSysDefEProne f Nothing sysId inIds outIds++-- | CURRENTLY BROKEN, do not use!+--+-- 'SysDef' constructor, Template Haskell version+--+-- Builds a system definition out of a system function, a system identifiers +-- and its port identifers.+--+-- For example @$(newSysDefTH mySysFun \"mysys\" [\"in1\"] [\"out1\"])@ creates a+-- system definition from system funcion @mySysFun@ which should have +-- one input and output signals.+--+-- The advantage of 'newSysDefTH' over 'newSysDef' is that it +-- reports errors (e.g duplicated port and process identifiers) earlier, +-- at host-language (Haskell) compile-time. +--+-- In addition, due to the use of Template Haskell, 'newSysDefTH' is+-- aware of the source location at which it was called, making+-- further error reports friendlier to the user.+newSysDefTH :: SysFun f => f -- ^ system function + -> SysId -- ^ System identifier + -> [PortId] -- ^ Input interface port identifiers + -> [PortId] -- ^ Output interface port identifiers + -> ExpQ+newSysDefTH f sysId inIds outIds = + case eProneResult of+ Left err -> currError err+ -- unfortunately SysDef can't easily be an instance of Lift + -- due to the unsafe, unmutable references used in observable sharing + -- Right sysDef -> [| sysDef |]+ Right _ -> intError "newSysDefTH" (Other "Unimplemented")+-- FIXME: Fix this function or remove (updating the documentation of+-- newSysDef* in the latter case).+{-+ Right _ -> do+ loc <- currentModule+ [| let iIds = inIds+ oIds = outIds+ (nlist, inTypes, outTypes) = applySysFun f iIds+ in SysDef $ PrimSysDef $ newURef $ SysDefVal sysId+ (Netlist nlist)+ (zip iIds inTypes)+ (zip oIds outTypes)+ (Just loc) |]+-}+ where currError = qError "newSysDefTH"+ eProneResult = newSysDefEProne f Nothing sysId inIds outIds+++-- | 'SysDef' constructor, Template Haskell 'Name' version+--+-- Builds a 'SysDef' out of the name of a system function+-- and its port identifers.+--+-- The system will later be identified by the basename +-- (i.e. unqualified name) of the function.+--+-- For example @$(newSysDefTHName \'mySysFun [\"in1\"] [\"out1\"])@ creates a+-- system definition from system funcion @mySysFun@ which has one input and+-- output signals.+-- +-- The advantage of 'newSysDefTHName' over 'newSysDefTH' is that it +-- doesn't suffer from the Template Haskell bug <http://hackage.haskell.org/trac/ghc/ticket/1800>, or in other words, it allows to declare the system +-- defintion and system function in the same module.+--+-- However, since it doesn't have acces to the system function itself,+-- it can only give early error reports related to incorrect port identifiers+-- (process identifier duplicate errors will be reported at runtime).+newSysDefTHName :: Name -- ^ Name of the system function + -> [PortId] -- ^ Input interface port identifiers + -> [PortId] -- ^ Output interface port identifiers+ -> ExpQ +newSysDefTHName sysFName inIds outIds = do+ sysFInfo <- reify sysFName+ -- Check that a function name was provided+ sysFType <- case sysFInfo of+#if __GLASGOW_HASKELL__ >= 800+ -- Last parameter Fixity was removed in GHC8+ VarI _ t _ -> return t+#else+ VarI _ t _ _ -> return t+#endif+ _ -> currError (NonVarName sysFName)+ -- Check that the function complies with the expected type+ -- and extract the port types+ ((inTypes,inN),(outTypes, outN)) <- recover+ (currError $ IncomSysF sysFName sysFType)+ (checkSysFType sysFType)+ -- Check the ports+ let portCheck = checkSysDefPorts (show sysFName)+ (inIds, inN) + (outIds, outN)+ when (isJust portCheck) (currError (fromJust portCheck))+ -- Build the system definition+ loc <- location+ let+ errInfo = loc_module loc+ -- Input arguments passed to the system function+ -- in order to get the netlist+ inArgs = [ [| Signal $ newInPort $(litE $ stringL id) |] + | id <- inIds ]+ -- The system definition without type signature for the+ -- phantom parameter + untypedSysDef =+ -- The huge let part of this quasiquote is not+ -- really necesary but generates clearer code+ [|let + -- Generate the system netlist+ toList = $(signalTup2List outN)+ outNlSignals = toList $ $(appsE $ varE sysFName : inArgs)+ -- Rest of the system defintion+ inIface = $(genIface inIds inTypes)+ outIface = $(genIface outIds outTypes)+ errorInfo = errInfo+ nlist = Netlist outNlSignals+ (subSys,logic) = either (intError currFun) id+ (checkSysDef nlist) + in SysDef $ PrimSysDef $ newURef $ + SysDefVal (nameBase sysFName)+ nlist+ subSys+ logic+ inIface + outIface + (Just errorInfo) |] + -- We are done, we simply specify the concrete type of the SysDef+ sigE untypedSysDef (return $ ConT ''SysDef `AppT` sysFType)+ where currError = qError currFun+ currFun = "newSysDef"+++ ++----------------------------+-- Internal Helper Functions+----------------------------++-- | Error prone version of 'newSysDef'+newSysDefEProne :: SysFun f => f -- ^ system function + -> Maybe Loc -- ^ Location where the originating + -- call took place (if available)+ -> SysId -- ^ System function + -> [PortId] -- ^ Input interface port identifiers + -> [PortId] -- ^ Output interface port identifiers + -> EProne (SysDef f)+newSysDefEProne f mLoc sysId inIds outIds + -- check the ports for problems+ | isJust portCheck = throwError (fromJust portCheck)+ | otherwise = do+ let nl = Netlist nlist+ (subSys, logic) <- checkSysDef nl+ return (SysDef $ PrimSysDef $ newURef $ SysDefVal sysId+ nl+ subSys+ logic+ (zip inIds inTypes)+ (zip outIds outTypes)+ mLoc)+ where (nlist, inTypes, outTypes) = applySysFun f inIds+ inN = length inIds+ outN = length outIds+ portCheck = checkSysDefPorts sysId (inIds, inN) (outIds, outN) ++-- | Check that the system definition ports match certain lengths and+-- don't containt duplicates+checkSysDefPorts :: SysId -- ^ System currently being checked+ -> ([PortId], Int) -- ^ input ports and expected length+ -> ([PortId], Int) -- ^ output ports and expected length+ -> Maybe ForSyDeErr+checkSysDefPorts sysId (inIds, inN) (outIds, outN) + | inN /= inIdsL = Just $ InIfaceLength (sysId, inN) (inIds, inIdsL)+ | outN /= outIdsL = Just $ OutIfaceLength (sysId, outN) (outIds, outIdsL)+ | isJust (maybeDup) = Just $ MultPortId (fromJust maybeDup)+ | otherwise = Nothing+ where inIdsL = length inIds+ outIdsL = length outIds+ maybeDup = findDup (inIds ++ outIds)+++-- | In order to check the system for identifier duplicates we keep track+-- of the process identifiers and of the accumulated subsytem definitions+data CheckState = CheckState {accumSubSys :: [PrimSysDef],+ accumProcIds :: [ProcId] ,+ accumLogic :: SysLogic }++-- Monad used to traverse the system in order to check that there are no +-- duplicates+type CheckSysM st a = TravSEST CheckState ForSyDeErr st a++-- | Check that the system netlist does not contain process identifier+-- duplicates (i.e. different processes with the same process+-- identifier) or instances of different systems with the same identifier.+-- In case there are no duplicates, the list of nested subsystems together+-- with the the logic is returned.+checkSysDef :: Netlist [] -> EProne ([PrimSysDef], SysLogic)+checkSysDef nl = do+ endSt <- runST (runErrorT + (execStateT (traverseSEST newCheckSys defineCheckSys nl) initState))+ let finalSubSys = accumSubSys endSt+ -- we already checked all the delay processes of the system+ -- but the system can still be sequential if any of the subsystems is + -- sequential+ finalLogic = + if (accumLogic endSt == Sequential) ||+ (any (\s -> (logic.readURef.unPrimSysDef) s == Sequential) finalSubSys)+ then Sequential+ else Combinational+ return (finalSubSys, finalLogic)+ where initState = CheckState [] [] Combinational+ ++defineCheckSys :: [(NlNodeOut, ())] -> NlNode () -> CheckSysM st ()+defineCheckSys _ _= return ()+ +newCheckSys :: NlNode NlSignal -> CheckSysM st [(NlNodeOut, ())]+newCheckSys node = do+ st <- get+ let acIds = accumProcIds st+ acSys = accumSubSys st+ acLog = accumLogic st+ -- check the process Id of current node for duplicates+ acIds' <- case node of + -- input ports don't count as process identifiers+ InPort _ -> return acIds + Proc pid _ -> if pid `elem` acIds + then throwError $ MultProcId pid+ else return (pid:acIds)+ -- If the node is a system instance, check that+ -- the system and all its subsytems are either:+ -- * already in the accumulated systems+ -- * not in the accumulated systems, but have a different system+ -- identifiers+ -- FIXME: in order to avoid making so many comparisons, it+ -- would probably be more efficient to also mark which+ -- subsystems belong to the first hierarchy level in+ -- SysVal (i.e. creating a tree-structure as a reult).+ -- Then, if the system to compare (psys) matches a+ -- root in the accumulated subsystems there would be+ -- no need to continue comparing the childs of psys.+ acSys' <- case node of + Proc _ (SysIns pSys _) -> + liftEither $ + mergeSysIds (pSys:(subSys.readURef.unPrimSysDef) pSys) acSys+ _ -> return acSys+ let acLog' = case node of+ Proc _ (DelaySY _ _) -> Sequential + _ -> acLog+ put $ CheckState acSys' acIds' acLog'+ -- return a phony value for each output of the node+ return $ map (\tag -> (tag,())) (outTags node) ++ where mergeSysIds :: [PrimSysDef] -> [PrimSysDef] -> EProne [PrimSysDef]+ mergeSysIds xs [] = return xs+ mergeSysIds [] xs = return xs+ mergeSysIds (x:xs) ys = do + shouldAdd <- addSysId x ys + if shouldAdd then do rest <- mergeSysIds xs ys+ return (x:rest)+ else mergeSysIds xs ys+ -- should we add the Id to the accumulated ones?+ addSysId :: PrimSysDef -> [PrimSysDef] -> EProne Bool+ addSysId _ [] = return True+ addSysId psdef (x:xs) + -- Both systems are equal+ | unx == unpsdef = return False+ -- Both systems are different, but their ids+ -- are equal+ | sdefid == sid xval = + throwError (SubSysIdClash sdefid (loc sdefval) (loc xval))+ | otherwise = addSysId psdef xs+ where unpsdef = unPrimSysDef psdef+ unx = unPrimSysDef x+ xval = readURef unx+ sdefval = readURef unpsdef+ sdefid = sid sdefval+++-- | Generate a lambda expression to transform a tuple of N 'Signal's into a +-- a list of 'NlSignal's+signalTup2List :: Int -- ^ size of the tuple+ -> ExpQ+signalTup2List n = do -- Generate N signal variable paterns and+ -- variable expressions refering to the same names+ names <- replicateM n (newName "i")+ let tupPat = tupP [conP 'Signal [varP n] | n <- names]+ listExp = listE [varE n | n <- names]+ lamE [tupPat] listExp+++-- | Find a duplicate in a list+findDup :: Eq a => [a] -> Maybe a+findDup [] = Nothing +findDup [_] = Nothing+findDup (x:xs)+ | elem x xs = Just x+ | otherwise = findDup xs+++-- | Generate a TypeRep expression given a Template Haskell Type+-- note that the use of typeOf cannot lead to errors since all the signal+-- types in a system function are guaranteed to be Typeable by construction+type2TypeRep :: Type -> ExpQ+type2TypeRep t = [| typeOf $(sigE [| undefined |] (return t) ) |]++-- | Generate an interface given its identifiers and Template Haskell Types+genIface :: [PortId] -> [Type] -> ExpQ+genIface [] _ = listE []+genIface _ [] = listE []+genIface (i:ix) (t:tx) = do+ ListE rest <- genIface ix tx+ tupExp <- tupE [[| i |], type2TypeRep t]+ return (ListE (tupExp:rest)) +
+ src/ForSyDe/Deep/System/SysDef.hs-boot view
@@ -0,0 +1,21 @@+{-# LANGUAGE RoleAnnotations #-}+-- SysDef.hs-boot: GHC bootstrapping module for Netlist.hs+-- (it breaks the recursive import loop with Netlist.hs)+-- See "How to compile mutually recursive modules" in GHC's manual for details++module ForSyDe.Deep.System.SysDef where++import ForSyDe.Deep.OSharing+import ForSyDe.Deep.Ids+import Data.Typeable.FSDTypeRepLib++type Iface = [(PortId, FSDTypeRep)]++type role SysDef phantom+newtype SysDef a = SysDef {unSysDef :: PrimSysDef}++newtype PrimSysDef = PrimSysDef {unPrimSysDef :: URef SysDefVal}++data SysDefVal ++oIface :: SysDefVal -> Iface
+ src/ForSyDe/Deep/System/SysFun.hs view
@@ -0,0 +1,311 @@+{-# LANGUAGE TemplateHaskell, ScopedTypeVariables, MultiParamTypeClasses,+ FunctionalDependencies #-}+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.System.SysFun+-- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- This module provides the 'SysFun' class and a Template Haskell +-- function to check whether a 'Type' complies with the expected +-- type of a system function not.+--+----------------------------------------------------------------------------- +module ForSyDe.Deep.System.SysFun + (SysFun(..),+ SysFunToSimFun(..), + SysFunToIOSimFun(..),+ funOutInstances, + checkSysFType) where++import ForSyDe.Deep.Ids(PortId)+import ForSyDe.Deep.Netlist (NlSignal, Signal(..))+import ForSyDe.Deep.ForSyDeErr+import ForSyDe.Deep.Process.ProcType (ProcType(..))++import Language.Haskell.TH.TypeLib++import Data.Dynamic+import Text.Regex.Posix ((=~))+import qualified Language.Haskell.TH as TH (Exp)+import Language.Haskell.TH+import Text.ParserCombinators.ReadP (readP_to_S)+import Data.Typeable.FSDTypeRepLib++---------------+-- SysFun class+---------------++-- | Class used to describe a System function. It uses the same trick+-- as 'Text.Printf' to implement the variable number of arguments.+class SysFun f where+ -- | Gets the result of applying a system function to input port signals+ -- whose name is given in first argument.+ -- In case the input id list is not long enough, \"default\" will be used+ -- as the id of the remaining ports.+ applySysFun :: f + -> [PortId] -- ^ ids used to build the input ports+ -> ([NlSignal], [FSDTypeRep], [FSDTypeRep])+ -- ^ (output signals returned by the system function,+ -- types of input ports, + -- types of output ports)++ -- | Transforms a primitive-signal-list version of a system function+ -- into a standard system function.+ -- Note the length of input/output lists of the list version must match with + -- the argument number and return tuple size of the system function.+ fromListSysFun :: ([NlSignal] -> [NlSignal]) -- ^ primitive-signal-list sysfun+ -> [NlSignal] -- ^ accumulated primitive signals+ -- (must be initialized to [])+ -> f+ +++-----------------------+-- SysFunToSimFun class+-----------------------++-- | Multiparameter class to transform a System Function into a Simulation +-- Function, able to simulate a System using a list-based representation +-- of its signals.+class SysFun sysFun => + SysFunToSimFun sysFun simFun | sysFun -> simFun, simFun -> sysFun where+ -- | Transforms a dynamic-list version of a simulation function+ -- into a standard simulation function.+ -- Note the length of input/output lists of the list version must match with + -- the argument number and return tuple size of the simulation function.+ fromDynSimFun :: ([[Dynamic]] -> [[Dynamic]]) -- ^ dynamic-list simfun+ -> [[Dynamic]] -- ^ accumulated dynamic values+ -- (must be initialized to [])+ -> simFun++-------------------------+-- SysFunToIOSimFun class+-------------------------++-- | Multiparameter class to transform a System Function into an IO +-- Simulation Function, able to externally simulate a System using a +-- list-based representation of its signals.+class SysFun sysFun => + SysFunToIOSimFun sysFun simFun | + sysFun -> simFun, simFun -> sysFun where+ -- | Transforms a TH/String version of a simulation function into a+ -- standard simulation function. + -- Again, the length of input/output lists of the list version must+ -- match with the argument number and return tuple size of the+ -- simulation function.+ fromTHStrSimFun :: ([[TH.Exp]] -> IO [[String]]) -- ^ TH/String-list simfun+ -> [[TH.Exp]] -- ^ accumulated dynamic values+ -- (must be initialized to [])+ -> simFun++-- Function to automatically generate instances for the system and+-- simulate function outputs with Template Haskell. For example, in+-- the case of 2 outputs, the code generated would be:+--+-- @+-- NOTE: even if all ProcType constraints could be less restrictive (Typeable+-- would do), it makes more sense, and who nows, maybe we end up requiring+-- full ProcType functionality at some point. +--+--+-- instance (ProcType o1, ProcType o2) => SysFun (Signal o1, Signal o2) where+-- applyFun (o1, o2) _ = +-- ([unSignal o1, unSignal o2], [], [typeOf o1, typeOf o2]) +-- fromListSysFun f accum = (Signal o1, Signal o2)+-- where [o1, o2] = f (reverse accum) +--+-- instance (ProcType o1, ProcType o2) => +-- SysFun2SimFun (Signal o1, Signal o2) ([o1], [o2]) where+-- fromDynSimFun f accum = (map unsafeFromDyn o1, map unsafeFromDyn o2)+-- -- use pattern (o1:o2:_) and not not [o1,o2]+-- -- because the second one doesn't when o1 and o2 are infinite lists+-- where (o1:o2:_) = f (reverse accum) +--+-- instance (ProcType o1, ProcType o2) => +-- SysFun2SimFun (Signal o1, Signal o2) (IO ([o1], [o2])) where+-- fromTHStrSimFun f accum = do+-- [o1, o2] <- f (reverse accum)+-- return (map parseProcType o1, map parseProcType o2) +--+--+-- @+funOutInstances :: Int -- ^ number of outputs to generate+ -> Q (Dec, Dec, Dec)+funOutInstances n = do+ -- Generate N output names+ outNames <- replicateM n (newName "o")+ + -- 1) Generate applyFun+ -- Generate an input tuple pattern for applyFun+ -- (o1, o2, ..., on)+ let tupPatApply = tupP (map varP outNames)+ -- Generate the output primitive signal list expression for applyFun+ -- [unsignal o1, unsignal o2, ...., unsignal on]+ outPrimSignalsApply = + listE $ map (\oName -> varE 'unSignal `appE` varE oName) outNames+ -- Generate the output signal types for ApplyFun+ -- [typeOf o1, typeOf o2, ...., typeOf on]+ outTypeRepsApply =+ listE $ map (\oName -> varE 'fsdTypeOf `appE` varE oName) outNames+ -- Generate the full output expression+ outEApply = [| ($outPrimSignalsApply, [], $outTypeRepsApply) |]+ -- Finally, the full declaration of applyFun + applySysFunDec = + funD 'applySysFun [clause [tupPatApply, wildP] (normalB outEApply) []]+ -- 2) Generate fromListSysFun+ -- Generate the parameter names+ fParFromSys <- newName "f"+ accumParFromSys <- newName "accum"+ -- Generate the parameter patterns+ let fPatFromSys = varP fParFromSys+ accumPatFromSys = varP accumParFromSys + -- Generate the list pattern: [o1, o2, .., on]+ listPatFromSys = listP $ map varP outNames+ -- Generate the rhs of the where declaration+ whereRHSFromSys = + [| $(varE fParFromSys) (reverse $(varE accumParFromSys)) |]+ -- Generate the where clause declaration+ whereDecFromSys = valD listPatFromSys (normalB whereRHSFromSys) []+ -- Generate output expression: (Signal o1, Signal o2, ..., Signal on)+ outEFromSys = + tupE $ map (\oName -> conE 'Signal `appE` varE oName) outNames+ -- Finally, the full declaration of fromListSysFun+ fromListSysFunDec = + funD 'fromListSysFun [clause [fPatFromSys, accumPatFromSys] + (normalB outEFromSys) + [whereDecFromSys] ] + -- 3) Generate fromDynSimFun reusing parts of fromListSysFun+ -- Generate the list pattern: o1:o2: .. on:_+ listPatFromDynSim = foldr (\oName pat -> infixP (varP oName) '(:) pat) + wildP outNames+ -- Generate the rhs of the where declaration+ whereRHSFromDynSim = + [| $(varE fParFromSys) (reverse $(varE accumParFromSys)) |]+ -- Generate the where clause declaration+ whereDecFromDynSim = valD listPatFromDynSim (normalB whereRHSFromSys) []+ -- Generate output expression: (Signal o1, Signal o2, ..., Signal on)+ outEFromDynSim = tupE $ map + (\oName -> varE 'map `appE` varE 'unsafeFromDyn `appE` varE oName)+ outNames+ -- Finally, the full declaration of fromDynSimFun+ fromDynSimFunDec = + funD 'fromDynSimFun [clause [fPatFromSys, accumPatFromSys] + (normalB outEFromDynSim) + [whereDecFromDynSim] ] + -- 4) Generate fromTHStrSimFun reusing parts of fromListSysFun+ listPatFromTHStrSim = listP $ map varP outNames+ -- Generate the rhs of the where declaration+ doFromTHStrSim = doE $+ [bindS listPatFromDynSim [| $(varE fParFromSys) + (reverse $(varE accumParFromSys)) |],+ noBindS $ + (varE 'return) `appE`+ (tupE $ map + (\oName -> varE 'map `appE` varE 'parseProcType `appE` varE oName)+ outNames) ]+ -- Finally, the full declaration of fromThStrSimFun+ fromTHStrSimFunDec = + funD 'fromTHStrSimFun [clause [fPatFromSys, accumPatFromSys] + (normalB doFromTHStrSim) + [] ] + -- 5) Generate the SysFun instance+ -- We reuse the output signal names for the head type variables+ -- (Signal o1, Signal o2, ..., Signal on)+ signalTupT = if n == 1 then conT ''Signal `appT` varT (head outNames)+ else foldl accumApp (tupleT n) outNames + where accumApp accumT vName = + accumT `appT` (conT ''Signal `appT` varT vName)+ -- Create the ProcType context+ procTypeCxt = map (\vName -> appT (conT ''ProcType) (varT vName)) outNames++ -- Finally return the instance declaration+ sysFunIns = instanceD (cxt procTypeCxt) + (conT ''SysFun `appT` signalTupT) + [applySysFunDec, fromListSysFunDec]+ -- 6) Generate the SysFun2SimFun instance + listTupT = if n == 1 then listT `appT` varT (head outNames)+ else foldl accumApp (tupleT n) outNames + where accumApp accumT vName = + accumT `appT` (listT `appT` varT vName)+ simFunIns = instanceD (cxt procTypeCxt) + (conT ''SysFunToSimFun `appT` signalTupT `appT` listTupT) + [fromDynSimFunDec]+ -- 7) Generate the SysFun2IOSimFun instance+ ioSimFunIns = instanceD (cxt procTypeCxt)+ (conT ''SysFunToIOSimFun `appT` + signalTupT `appT` + (conT ''IO `appT` listTupT))+ [fromTHStrSimFunDec]+ -- Finally, return the declarations+ liftM3 (,,) sysFunIns simFunIns ioSimFunIns++---------------------------------------------------------------+-- Checking the type of a System Function with Template Haskell+---------------------------------------------------------------++-- | Given a function type expressed with Template Haskell, check+-- whether it complies with the expected type of a system function+-- and, in that case, provide the type and number of the system+-- inputs and outputs.+checkSysFType :: Type -> Q (([Type],Int),([Type],Int))+checkSysFType t = do let (inputTypes, retType, context) = unArrowT t+ (outCons, outTypes, _) = unAppT retType + -- Discard polymorphic system functions+ -- FIXME: We could support polymorphic signals, but it+ -- requires more work and we don't still know if it + -- will be necessary anyway.+ when (isPoly context) (qGiveUp name)+ -- Check that all inputs are signals+ when (not $ all isSignalT inputTypes) (qGiveUp name)+ -- Check the outputs+ outInfo <- checkSysFOutputs (outCons, outTypes)+ return ((inputTypes, length inputTypes), outInfo)+ where name = "ForSyDe.System.checkSysFType"+++-- | Check the output types of the system function given its+-- constructor and its type arguments+checkSysFOutputs :: (Type, [Type]) -> Q ([Type], Int)+-- The system function doesn't output any signals at all+checkSysFOutputs (ConT n, []) | n == ''() = return ([],0)+-- The system function just returns a signal+checkSysFOutputs (ConT s, [arg]) | s == ''Signal = + return ([ConT ''Signal `AppT` arg ], 1)+-- The system function returns various signals in a tuple+-- NOTE: Unfortunatelly due to ghc's bug 1849 TupleT is never returned by reify+-- so we have to match the constructor with a regex+-- FIXME: Update this function whenever the bug is fixed +-- http://hackage.haskell.org/trac/ghc/ticket/1849+checkSysFOutputs (ConT name, outTypes) + | show name =~ "^Data\\.Tuple\\.\\(,+\\)$" = do+ when (not $ all isSignalT outTypes) (qGiveUp checkSysFOutputsNm)+ return (outTypes, length outTypes)+-- Otherwise the function output type is incorrect+checkSysFOutputs _ = qGiveUp checkSysFOutputsNm++checkSysFOutputsNm :: String+checkSysFOutputsNm = "ForSyDe.System.SysFun.checkSysFOutputs"++-- | Check if a type corresponds to a signal+isSignalT :: Type -> Bool+isSignalT ((ConT name) `AppT` _ ) | name == ''Signal = True+isSignalT _ = False++-- | Unsafely transform from a dynamic value+unsafeFromDyn :: forall a . Typeable a => Dynamic -> a+unsafeFromDyn dyn = fromDyn dyn err + where err = intError "unsafeFromDyn" (DynMisMatch dyn targetType) + targetType = typeOf (undefined :: a)++-- | Parse a proctype value+parseProcType :: forall a .ProcType a => String -> a+parseProcType s = + case (readP_to_S readProcType) s of + [(a,_)] -> a+ _ -> error ("parseProcType: error parsing element of type " +++ show (typeOf (undefined :: a)) )
+ src/ForSyDe/Deep/System/SysFun/Instances.hs view
@@ -0,0 +1,82 @@+{-# LANGUAGE TemplateHaskell, FlexibleInstances, ScopedTypeVariables,+ MultiParamTypeClasses, UndecidableInstances #-}+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.System.SysFun.Instances+-- Copyright : (c) The ForSyDe Team 2008-2013+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : non-portable (Template Haskell)+--+-- This module provides the instances for 'SysFun', it cannot+-- be included in "ForSyDe.System.SysFun" directly due to a Template Haskell+-- bug which prevents Template Haskell from executing functions defined in the+-- same module: <http://hackage.haskell.org/trac/ghc/ticket/1800>+--+----------------------------------------------------------------------------- +module ForSyDe.Deep.System.SysFun.Instances () where++import Data.Dynamic+import Control.Monad (liftM)+import Language.Haskell.TH.Syntax (runIO, runQ, lift)+import System.IO.Unsafe (unsafePerformIO)++import ForSyDe.Deep.Config (maxTupleSize)+import ForSyDe.Deep.System.SysFun (+ SysFun(..), + SysFunToSimFun(..), + SysFunToIOSimFun(..), + funOutInstances)+import ForSyDe.Deep.Netlist+-- This aparently unnecesary SysDef import is needed as a workaround for +-- http://hackage.haskell.org/trac/ghc/ticket/1012+import ForSyDe.Deep.System.SysDef()+import ForSyDe.Deep.Process.ProcType (ProcType(..))+import Data.Typeable.FSDTypeRepLib+++-- IMPORTANT NOTE: even if all ProcType constraints in SysFun and+-- SysFunToSimFun instances could be less restrictive+-- (Typeable would do), it makes more sense, and who nows,+-- maybe we end up requiring full ProcType functionality at+-- some point.+++-- This three instances are the ones in charge of providing the necessary +-- recursion step needed to support the variable number of arguments.+-- In each step, the system function is provided with a new input signal port+-- until the output signals are obtained.+instance (ProcType a, SysFun f) => SysFun (Signal a -> f) where+ applySysFun f ids = (outSignals, currInType : nextInTypeReps, outTypeReps)+ where (outSignals, nextInTypeReps, outTypeReps) = + case ids of+ [] -> applySysFun (f (Signal (newInPort "default"))) []+ (i:is) -> applySysFun (f (Signal (newInPort i))) is + currInType = fsdTypeOf (undefined :: Signal a)+ fromListSysFun f accum s = fromListSysFun f ((unSignal s):accum)++instance (ProcType a, SysFunToSimFun sysFun simFun) => + SysFunToSimFun (Signal a -> sysFun) ([a] -> simFun) where+ fromDynSimFun f accum s = fromDynSimFun f ((map toDyn s):accum)++instance (ProcType a, SysFunToIOSimFun sysFun simFun) => + SysFunToIOSimFun (Signal a -> sysFun) ([a] -> simFun) where+ fromTHStrSimFun f accum s = fromTHStrSimFun f ((map unsafeLift s):accum)+ -- FIXME: This won't be needed once the Data a => Lift a instance+ -- is created+ where unsafeLift = unsafePerformIO.runQ.lift++-- Generate instances for the system function outputs up to the maximum+-- tuple size+$(let concatMapM f xs = liftM concat (mapM f xs) + listFunOutInstances = liftM (\(a,b,c) -> [a,b,c]) . funOutInstances+ msg = "Generating and compiling " ++ show maxTupleSize ++ + " output instances of " +++ show ''SysFun ++ ", " ++ show ''SysFunToSimFun ++ + " and " ++ show ''SysFunToIOSimFun ++ + ", this might take some time ... \n"+ in runIO (putStrLn $ msg) >>+ concatMapM listFunOutInstances [0..maxTupleSize]+ )
+ src/ForSyDe/Deep/Version.hs view
@@ -0,0 +1,35 @@+-----------------------------------------------------------------------------+-- |+-- Module : ForSyDe.Deep.Version+-- Copyright : (c) ES Group, KTH/ICT/ES 2016+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- Version information for the current build+--+-----------------------------------------------------------------------------++module ForSyDe.Deep.Version (getVersion) where++import Paths_forsyde_deep (version)+import Data.Version (showVersion)++import Language.Haskell.TH (runIO)+import Language.Haskell.TH.Syntax+import System.Process (readProcess)+import Control.Exception (catch)+import System.IO.Error (IOError)++getVersion :: Q Exp+getVersion = do+ vstr <- runIO $ catch version_git version_cabal+ return (LitE $ StringL $ init vstr)++ where+ version_git :: IO String+ version_git = readProcess "git" ["describe", "--tags", "--dirty"] ""+ version_cabal :: IOError -> IO String+ version_cabal _ = return $ showVersion version
+ src/Language/Haskell/TH/Lift.hs view
@@ -0,0 +1,116 @@+{-# LANGUAGE MagicHash, TemplateHaskell, CPP #-} +{-# OPTIONS_GHC -fno-warn-deprecations #-}+-- Due to the use of unboxed types, TH, and deprecated Packed Strings+-----------------------------------------------------------------------------+-- |+-- Module : Language.Haskell.TH.Lift+-- Copyright : (c) Ian Lynagh, 2006, (c) SAM Group, KTH/ICT/ECS 2007-2008+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- Automatically derive Template Haskell's 'Lift' class for datatypes+-- using Template Haskell splices.+-- +-- Based on Lynagh's th-lift package: <http://hackage.haskell.org/cgi-bin/hackage-scripts/package/th-lift>+-----------------------------------------------------------------------------+module Language.Haskell.TH.Lift (deriveLift1, deriveLift) where++import GHC.Exts+import Language.Haskell.TH+import Language.Haskell.TH.Syntax+import Control.Monad (liftM)++modName :: String+modName = "Language.Haskell.TH.Lift"++-- | Automatically generate an instance of 'Lift' for one data type. For example:+-- +-- >{-# LANGUAGE TemplateHaskell #-}+-- >module Colour where+-- >import Language.Haskell.TH.Lift+-- >+-- >data RGB = Red | Green | Blue+-- >+-- >-- Generate the Lift instance of RGB+-- >$(deriveLift1 ''RGB)+deriveLift1 :: Name -> Q [Dec]+deriveLift1 = (liftM (\a -> [a])) . deriveLift ++-- | Version of 'deriveLif1' used to automatically generate instances+-- of Lift for multiple data types. For instance:+--+-- >{-# LANGUAGE TemplateHaskell #-}+-- >module Colour where+-- >import Language.Haskell.TH.Lift+-- >+-- >data RGB = Red | Green | Blue+-- >+-- >data Num a => Pixel a = Pixel a a a+-- >+-- >-- Generate Lift instances for RGB and Pixel+-- >$(mapM deriveLift [''RGB, ''Pixel])+deriveLift :: Name -> Q Dec+deriveLift n+ = do i <- reify n+ case i of+#if __GLASGOW_HASKELL__ >= 800+ TyConI (DataD dcxt _ vs _ cons _) ->+#else+ TyConI (DataD dcxt _ vs cons _) ->+#endif+ let ctxt = liftM (++ dcxt) $ + cxt [appT (conT ''Lift) (varT v') | v' <- vs']+ typ = foldl appT (conT n) $ map varT vs'+ fun = funD 'lift (map doCons cons)+ vs' = map (\v -> case v of+ PlainTV name -> name+ KindedTV name _ -> name) vs+ in instanceD ctxt (conT ''Lift `appT` typ) [fun]+ --do sh<-instanceD ctxt (conT ''Lift `appT` typ) [fun]+ -- error (pprint sh)+ _ -> error (modName ++ ".deriveLift: unhandled: " ++ pprint i)++doCons :: Con -> Q Clause+doCons (NormalC c sts)+ = do let ns = zipWith (\_ i -> "x" ++ show i) sts [(0::Integer)..]+ con = [| conE c |]+ args = [ [| lift $(varE (mkName n)) |] | n <- ns ]+ e = foldl (\e1 e2 -> [| appE $e1 $e2 |]) con args+ clause [conP c (map (varP . mkName) ns)] (normalB e) []+doCons (InfixC st1 n st2) = doCons (NormalC n [st1,st2])+doCons (RecC n vsts) + = let st (_, s, t) = (s, t)+ in doCons (NormalC n (map st vsts))+doCons c = error (modName ++ ".doCons: Unhandled constructor: " ++ pprint c)++instance Lift Name where+ lift (Name occName nameFlavour) = [| Name occName nameFlavour |]++instance Lift OccName where+ lift (OccName str) = [| OccName str |]+ +instance Lift ModName where+ lift (ModName str) = [| ModName str |]+ +instance Lift PkgName where+ lift (PkgName str) = [| PkgName str |]++--instance Lift PackedString where+-- lift ps = [| packString $(lift $ unpackPS ps) |]++instance Lift NameFlavour where+ lift NameS = [| NameS |]+ lift (NameQ modName) = [| NameQ modName |]+ lift (NameU i) = [| NameU i |]+ lift (NameL i) = [| NameL i |]+ lift (NameG nameSpace pkgName modName)+ = [| NameG nameSpace pkgName modName |]++instance Lift NameSpace where+ lift VarName = [| VarName |]+ lift DataName = [| DataName |]+ lift TcClsName = [| TcClsName |]+
+ src/Language/Haskell/TH/LiftInstances.hs view
@@ -0,0 +1,129 @@+{-# LANGUAGE TemplateHaskell,CPP #-} +-----------------------------------------------------------------------------+-- |+-- Module : Language.Haskell.TH.LiftInstances+-- Copyright : (c) SAM Group, KTH/ICT/ECS 2007-2008+-- License : BSD-style (see the file LICENSE)+-- +-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- This module provides 'Lift' instances for all the AST-types defined+-- in "Language.Haskell.Syntax"+--+-- Furthermore it provides a function (metaLift) which lifts an expression+-- twice, obtaing its meta AST (the AST of the AST)+-- +-----------------------------------------------------------------------------+module Language.Haskell.TH.LiftInstances (metaLift) where++import Language.Haskell.TH.Lift (deriveLift)++import Language.Haskell.TH.Syntax+ (Guard,+#if __GLASGOW_HASKELL__ >= 800+ -- Strict was replaced and became a synonym for Bang in GHC 8.0.1+ Bang,+ Overlap,+ SourceUnpackedness,+ SourceStrictness,+ DecidedStrictness,+ TypeFamilyHead,+ InjectivityAnn,+ FamilyResultSig,+#else+ -- This is only for backwards compatibility with ghc 7.10.+ Strict,+#endif+ Callconv,+ Safety,+ Body, + Con, + FunDep, + Foreign, + Lit, + Pat, + Match, + Stmt, + Range, + Clause, + Type, + Dec, + Exp,+ Q,+ Lift(..),+ TyVarBndr,+ FamFlavour,+ Pragma,+ AnnLookup,+ AnnTarget,+ Fixity,+ FixityDirection,+ Inline,+ Module,+ ModuleInfo,+ Phases,+ Role,+ RuleBndr,+ RuleMatch,+ TyLit,+ TySynEqn+ )++$(mapM deriveLift + [''Guard,+#if __GLASGOW_HASKELL__ >= 800+ -- Strict was replaced and became a synonym (for which we don't need any+ -- lift instances) for Bang in GHC 8.0.1+ ''Bang,+ ''Overlap,+ ''SourceUnpackedness,+ ''SourceStrictness,+ ''DecidedStrictness,+ ''TypeFamilyHead,+ ''InjectivityAnn,+ ''FamilyResultSig,+#else+ -- This is only for backwards compatibility with ghc 7.10.+ ''Strict,+#endif+ ''Callconv,+ ''Safety,+ ''Body, + ''Con, + ''FunDep, + ''Foreign, + ''Lit, + ''Pat, + ''Match, + ''Stmt, + ''Range, + ''Clause, + ''Type, + ''Dec, + ''Exp,+ ''TyVarBndr,+ ''FamFlavour,+ ''Pragma,++ ''AnnLookup,+ ''AnnTarget,+ ''Fixity,+ ''FixityDirection,+ ''Inline,+ ''Module,+ ''ModuleInfo,+ ''Phases,+ ''Role,+ ''RuleBndr,+ ''RuleMatch,+ ''TyLit,+ ''TySynEqn+ ])+ +-- | lift twice, getting the meta AST (the AST of the AST)+metaLift :: Lift a => a -> Q Exp+metaLift exp = do expAST <- lift exp+ lift expAST+
+ src/Language/Haskell/TH/TypeLib.hs view
@@ -0,0 +1,320 @@+{-# LANGUAGE TemplateHaskell #-}+-----------------------------------------------------------------------------+-- |+-- Module : Language.Haskell.TH.TypeLib+-- Copyright : (c) SAM Group, KTH/ICT/ECS 2007-2008+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : forsyde-dev@ict.kth.se+-- Stability : experimental+-- Portability : portable+--+-- This module provides basic functions related to Template-Haskell's 'Type'.+--+-----------------------------------------------------------------------------+module Language.Haskell.TH.TypeLib+ (Context,+ mkContext,+ monoContext,+ isPoly,+ contextVarNames,+ contextConstraints,+ mkForallT,+ unArrowT,+ unAppT,+ (-->),+ reAppT,+ reArrowT,+ dynTHType,+ thTypeOf,+ typeRep2Type,+ tyCon2Type,+ type2TypeRep)+ where++import Data.Typeable.Internal+import Data.Dynamic+import Language.Haskell.TH (Type(..), Cxt, TyVarBndr(..), pprint, mkName, nameModule, nameBase)+import Text.Regex.Posix ((=~))+import Data.Maybe(isJust, fromMaybe)++-- Due to type translations+import Data.Word (Word, Word8, Word16, Word32, Word64)+import Data.Int (Int8, Int16, Int32, Int64)+import System.IO (Handle)+import Data.IORef (IORef)+import Foreign (Ptr, FunPtr, StablePtr, ForeignPtr)+--import Control.OldException (Exception,+-- AsyncException,+-- ArrayException,+-- ArithException,+-- IOException)+import Data.Ratio (Ratio)+import Control.Concurrent.MVar (MVar)++-----------+-- Context+-----------++-- | A 'Context' represents the forall part and constraints of a type+-- (see 'ForallT')+-- For instance, the context of the type+-- @+-- forall a b. (Show a, Show b) => (a,b)+-- @+-- is @forall a b. (Show a, Show b) =>@+-- where @a@ and @b@ are the the context variables and+-- @(Show a, Show b)@ are the context constraints+data Context = Context+ [TyVarBndr] -- Variable names+ Cxt -- Constraints (the context itself)++instance Show Context where+-- FIXME: this is really ugly, refactor and improve its look+ showsPrec _ (Context tvb cxt) =+ showVars tvb . showConstraints cxt+ where showVars tvb = showForall (not (null tvb)) (showVars' tvb)+ showVars' ((PlainTV n):tvbs) = shows n . showChar ' ' . showVars' tvbs+ showVars' [] = id+ showConstraints c = (\s -> if not (null c) then ' ':s else s).+ showParen (length c > 1) (showConstraints' c) .+ (\s -> if not (null c) then s ++ " =>" else s)+ showConstraints' [c] = shows c+ showConstraints' (c:cx) = showString (pprint c) . showString ", " .+ showConstraints' cx+ showConstraints' [] = id+ showForall b s = if b then showString "forall " . s . showChar '.'+ else s++-- | 'Context' constructor+mkContext :: [TyVarBndr] -> Cxt -> Context+mkContext tvb c = Context tvb c++-- | Empty context for monomorphic types+monoContext :: Context+monoContext = Context [] []++-- | Tells if the context corresponds to a polymorphic type+isPoly :: Context -> Bool+isPoly (Context [] _) = False+isPoly _ = True++-- | Returns the variable names related to a context+contextVarNames :: Context -> [TyVarBndr]+contextVarNames (Context tvb _) = tvb++-- | Returns the context constraints+contextConstraints :: Context -> Cxt+contextConstraints (Context _ cxt) = cxt++-- | Builds a 'ForallT' type out of a context and a type+mkForallT :: Context -> Type -> Type+mkForallT (Context tvb cxt) t = ForallT tvb cxt t++--------------------------------+-- Functions to observe a 'Type'+--------------------------------++-- | Obtains the arguments and return type of a given 'Type'+-- (normally a function)+-- together with its 'Context' (non-empty if the type is polymorphic)+unArrowT :: Type -- ^ Type to observe+ -> ([Type], Type, Context) -- ^ (args 'Type', ret 'Type', 'Context')+unArrowT (ForallT names cxt t) = let (args,ret) = unArrowT' t+ in (args, ret, Context names cxt)+unArrowT t = let (args,ret) = unArrowT' t+ in (args, ret, Context [] [])++-- unArrowT for non-Forall Types+unArrowT' :: Type -> ([Type], Type)+unArrowT' ((ArrowT `AppT` first) `AppT` rest) = let (args, ret) = unArrowT' rest+ in (first:args, ret)+unArrowT' t = ([],t)++-- | Obtains the type constructor of a 'Type' together with its+-- arguments and its 'Context' (non-empty if the type is polymorphic)+unAppT :: Type -- ^ Type to observe+ -> (Type, [Type], Context) -- ^ (Constructor, args 'Type', Context)+unAppT (ForallT names cxt t) = let (cons, args) = unAppT' t+ in (cons, args, Context names cxt)+unAppT t = let (cons, args) = unAppT' t+ in (cons, args, Context [] [])++-- unAppT for non-Forall Types+unAppT' :: Type -> (Type, [Type])+unAppT' t = (first,rest)+ where first:rest = unAppT'ac [] t+ -- Since the constructor is a leaf of the Type tree representation,+ -- it is the last element to be gathered and thus, an accumulator+ -- is used to reverse the list to be returned+ unAppT'ac :: [Type] -> Type -> [Type]+ unAppT'ac acum (prefix `AppT` lastarg) = unAppT'ac (lastarg:acum) prefix+ unAppT'ac acum cons = cons:acum+------------------------------+-- Functions to build a 'Type'+------------------------------++-- | Form a function type out of two types+(-->) :: Type -- ^ Argument type+ -> Type -- ^ Return type+ -> Type -- ^ Resulting function+arg --> ret = (ArrowT `AppT` arg) `AppT` ret++-- | Rebuild a type out of a constructor, its argument types and its context+-- (inverse of 'unAppT')+reAppT :: (Type, [Type], Context) -- ^ (Constructor, type arguments, context)+ -> Type -- ^ resulting 'Type'+-- Polymorphic types+reAppT (cons, args, cxt) | isPoly cxt =+ mkForallT cxt (reAppT (cons, args, monoContext))+-- Monomorphic types+reAppT (cons, args, _) = foldl1 AppT (cons:args)++-- | Rebuild a function type out of its argument types, return type+-- and context (inverse of 'ArrowT')+reArrowT :: ([Type], Type, Context) -- ^ (Constructor, type arguments, context)+ -> Type -- ^ resulting 'Type'+-- Polymorphic types+reArrowT (args, ret, cxt) | isPoly cxt =+ mkForallT cxt (reArrowT (args, ret, monoContext))+-- Monomorphic types+reArrowT (args, ret, _) = foldr1 (-->) (args ++ [ret])++-------------------------------------------------------------------+-- Transforming Language.Haskell.TH.Type into Data.Typeable.TypeRep+-------------------------------------------------------------------++-- | Translate monomorphic Template Haskell Types to TypeReps+-- If the type os polymorhpic 'Nothing' will be returned+type2TypeRep :: Type -> Maybe TypeRep+-- Note: In the case of constructors, we don't need to translate to a TyCon first+-- because:+--+-- mkTyConApp tCon [t1 .. tn] = mkTyConApp tCon [] `mkAppTy` t1 ... `mkAppTy` tn+type2TypeRep (ForallT (_:_) _ _) = Nothing+type2TypeRep (ForallT _ (_:_) _) = Nothing+type2TypeRep (ForallT _ _ t) = type2TypeRep t+type2TypeRep (VarT _) = Nothing+-- Tuple tyCon strings don't correspond to hierarchical names. They are+-- simply sequences of commas plus paranthesis: e.g. 2-tuple "(,)" 3-tuple "(,,)" ....+type2TypeRep (TupleT n) = Just $ strCon ('(':replicate (n-1) ','++")")+type2TypeRep ArrowT = Just $ typeableCon (undefined :: () -> ())+type2TypeRep ListT = Just $ typeableCon (undefined :: [()])+type2TypeRep (t1 `AppT` t2) = do+ tRep1 <- type2TypeRep t1+ tRep2 <- type2TypeRep t2+ return $ tRep1 `mkAppTy` tRep2+-- Constructors+type2TypeRep (ConT name)+ -- FIXME: This should not be needed in the newer versions of ghc:+ -- There are certain TyCons whose string does not correspond+ -- to the hierarchical name of the constructor (the instances generated+ -- in Data.Typeable), we have to cover all those cases by hand+ -- See http://hackage.haskell.org/trac/ghc/ticket/1841 for details+ | isJust mSpecialTypeRep = mSpecialTypeRep+ -- Tuples (they cannot be put in the table)+ | isTup = Just $ strCon tupCons+ -- General case+ | otherwise = Just $ strCon (show name)+ where (isTup, tupCons) =+ case (show name =~ "^Data\\.Tuple\\.\\((,+)\\)$")+ :: (String, String, String, [String]) of+ -- it's a tuple, we get the commas subpart (,+)+ (_, _, _, [commas]) -> (True, commas)+ _ -> (False, "")+ mSpecialTypeRep = lookup name specialConTable+ specialConTable =+ [(''() , typeableCon (undefined :: ()) ),+ (''[] , typeableCon (undefined :: [()]) ),+ (''Maybe , typeableCon (undefined :: Maybe ()) ),+ (''Ratio , typeableCon (undefined :: Ratio ()) ),+ (''Either , typeableCon (undefined :: Either () ()) ),+ (''(->) , typeableCon (undefined :: () -> ()) ),+ (''MVar , typeableCon (undefined :: MVar ()) ),+ --(''Exception , typeableCon (undefined :: Exception) ),+ --(''IOException , typeableCon (undefined :: IOException) ),+ --(''ArithException , typeableCon (undefined :: ArithException) ),+ --(''ArrayException , typeableCon (undefined :: ArrayException) ),+ --(''AsyncException , typeableCon (undefined :: AsyncException) ),+ (''Ptr , typeableCon (undefined :: Ptr ()) ),+ (''FunPtr , typeableCon (undefined :: FunPtr ()) ),+ (''ForeignPtr , typeableCon (undefined :: ForeignPtr ()) ),+ (''StablePtr , typeableCon (undefined :: StablePtr ()) ),+ (''IORef , typeableCon (undefined :: IORef ()) ),+ (''Bool , typeableCon (undefined :: Bool) ),+ (''Char , typeableCon (undefined :: Char) ),+ (''Float , typeableCon (undefined :: Float) ),+ (''Double , typeableCon (undefined :: Double) ),+ (''Int , typeableCon (undefined :: Int) ),+ (''Word , typeableCon (undefined :: Word) ),+ (''Integer , typeableCon (undefined :: Integer) ),+ (''Ordering , typeableCon (undefined :: Ordering) ),+ (''Handle , typeableCon (undefined :: Handle) ),+ (''Int8 , typeableCon (undefined :: Int8) ),+ (''Int16 , typeableCon (undefined :: Int16) ),+ (''Int32 , typeableCon (undefined :: Int32) ),+ (''Int64 , typeableCon (undefined :: Int64) ),+ (''Word8 , typeableCon (undefined :: Word8) ),+ (''Word16 , typeableCon (undefined :: Word16) ),+ (''Word32 , typeableCon (undefined :: Word32) ),+ (''Word64 , typeableCon (undefined :: Word64) ),+ (''TyCon , typeableCon (undefined :: TyCon) ),+ (''TypeRep , typeableCon (undefined :: TypeRep) )]+++-------------------------------------------------------------------+-- Transforming Data.Typeable.TypeRep into Language.Haskell.TH.Type+-------------------------------------------------------------------++-- | Obtain the Template Haskel type of a dynamic object+dynTHType :: Dynamic -> Type+dynTHType = typeRep2Type . dynTypeRep++-- | Give the template haskell 'Type' of a Typeable object+thTypeOf :: Typeable a => a -> Type+thTypeOf = typeRep2Type . typeOf++-- | Translate a 'TypeRep' to a Template Haskell 'Type'+typeRep2Type :: TypeRep -> Type+typeRep2Type rep = let (con, reps) = splitTyConApp rep+ in reAppT (tyCon2Type con, map typeRep2Type reps, monoContext)++-- | Gives the corresponding Template Haskell 'Type' of a 'TyCon'+tyCon2Type :: TyCon -> Type+tyCon2Type = tyConStr2Type . tyConName+++----------------------------+-- Internal Helper Functions+----------------------------++-- | transfrom a Typeable type constructor to a Template Haskell Type+tyConStr2Type :: String -> Type+-- NOTE: The tyCon strings of basic types are not qualified and buggy in+-- some cases.+-- See http://hackage.haskell.org/trac/ghc/ticket/1841+-- FIXME: update this function whenever the bug is fixed+-- FIXME FIXME: This code is incorrect:+-- mkName doesn't generate global names! ''Maybe /= mkName "Data.Maybe.Maybe"+-- in addition global names contain a packagename which cannot be guessed from+-- the type representation.+tyConStr2Type "->" = ArrowT+tyConStr2Type tupStr | tupStr =~ "^,+$" =+ ConT (mkName $ "Data.Tuple.(" ++ tupStr ++ ")")+tyConStr2Type str = ConT $ mkName str++-- Get the type constructor corresponding to a String+-- in form of a type representation+strCon :: String -> TypeRep+strCon str = mkTyCon3 pkg mod base `mkTyConApp` []+ where+ name = mkName str+ pkg = ""+ mod = fromMaybe "" (nameModule name)+ base = nameBase name+++-- Get the type constructor corresponding to a typeable value+-- in form of a type representation+typeableCon :: Typeable a => a -> TypeRep+typeableCon t = (typeRepTyCon . typeOf) t `mkTyConApp` []
+ tests/Install.hs view
@@ -0,0 +1,47 @@+module Install (testInstall) where++import Distribution.Simple.SetupWrapper+import System.Process+import System.Directory+import System.FilePath+import System.Exit+import System.IO++-- Test if the ForSyDe project configures, builds, generates+-- the haddock documentation and installs without problems.+-- In order to avoid poisoning the haskell-package databases+-- and the file system, the package is configured with +-- the local prefix "testInstallation/" and registered+-- in a fresh local package database called "testInstallation.conf"+-- Note that this test will only work with ghc because the creation+-- of a new package database relies on ghc-pkg.+-- Also note that the current working directory must be the root of the project.+testInstall :: IO ()+testInstall = do+ cwd <- getCurrentDirectory+ putStrLn "Configuring ForSyDe ..." + setupCWD ["configure","--user","--prefix=" ++ cwd </> "testInstallation",+ "-v0"]+ putStrLn " done.\n" + putStrLn "Building ForSyDe ..." + setupCWD ["build","-v0"]+ putStrLn " done.\n"+ putStrLn "Testing the haddock markup of ForSyDe ... " + setupCWD ["haddock", "-v0"]+ putStrLn " done.\n"+ putStrLn "Copying ForSyDe under testInstallation/ ... "+ setupCWD ["copy", "-v0"]+ putStrLn " done.\n"+ putStrLn ("Registering ForSyDe in fresh package database " +++ "(testInstallation.conf) ...")+ -- Cabal doesn't allow specifying an alternative package database:+ -- http://hackage.haskell.org/trac/hackage/ticket/307+ -- Thus, we register the package by hand using ghc-pkg+ setupCWD ["register", "--gen-pkg-config=ForSyDeconfig","-v0"]+ code <- waitForProcess =<< + runCommand "ghc-pkg -f testInstallation.conf register ForSyDeconfig"+ case code of+ ExitSuccess -> return ()+ e@(ExitFailure _) -> exitWith e+ putStrLn "done."+ where setupCWD command = setupWrapper command Nothing
+ tests/examples/Main.hs view
@@ -0,0 +1,22 @@+-- Property-testing wrapper module+module Main (main) where+import Test.HUnit+import System.Exit+import System.IO+import System.Directory (createDirectoryIfMissing, setCurrentDirectory)+import VHDLBackend (vhdlBackendTest)+++main :: IO ()+main = do+ hSetBuffering stdout LineBuffering+ putStrLn "Running ForSyDe's unit test suite"+ createDirectoryIfMissing True workdir+ setCurrentDirectory workdir+ runTestCount $ test ["VHDL Backend Test" ~: vhdlBackendTest ]+ where runTestCount t = do (c, _) <- myRunTestText t + if errors c /= 0 || failures c /= 0 + then exitFailure + else exitWith ExitSuccess+ myRunTestText = runTestText (PutText (\str _ _ -> putStrLn str) ())+ workdir = "dist/test/temp"
+ tests/examples/VHDLBackend.hs view
@@ -0,0 +1,289 @@+{-# LANGUAGE TemplateHaskell #-}+-- VHDL-Backend property testing module+-- ALl the tests are based on asserting that the internal simulation of the system+-- equals the simulation of the generated vhdl code with modelsim+module VHDLBackend (vhdlBackendTest) where+-- import the example-modules+import ALU+--import Counter+import ParAddFour+import Multiplexer+--import Multiplexer_FSVector+import SeqAddFour+import ButtonEncoder +import ZipTwist+import CarrySelectAdder +import Null+import LFSR+import MapVector+import MapVectorOperatorSection+import MapVectorTransformation+import MapLambdaVector+import FoldlVector+import ZipWithVector+import FoldlVectorOperator+import ZipWithVectorOperator++import Control.Monad (liftM, replicateM)+import Data.List (transpose)+import Data.Param.FSVec (vectorTH, reallyUnsafeVector)+import System.Random+import Test.HUnit+import ForSyDe.Deep+import Data.Int+import Test.QuickCheck (generate, vectorOf, choose)++vhdlBackendTest :: Test+vhdlBackendTest = test [aluTest, + --counterTest,+ parAddFourTest,+ muxTest,+ --muxFSVecTest,+ seqAddFourTest,+ buttonEncoderTest,+ zipTwistTest,+ nullTest,+ lfsrTest,+ mapVTest,+ mapVectorTransformationTest,+ mapLambdaVectorTest,+ mapVectorOperatorSectionTest,+ foldlVOpTest,+ zipWithVOpTest,+ foldlVTest,+ zipWithVTest+ ]++-- systematic test for the ALU+aluTest :: Test+aluTest = "aluTest" ~: outSim <~=?> outVHDL+ where and = $(vectorTH [H,H])+ or = $(vectorTH [H,L])+ add = $(vectorTH [L,H])+ shiftl = $(vectorTH [L,L])+ fourBitCombs = map reallyUnsafeVector (combN 4 [L,H])+ [arg1,arg2] = parCombN 2 fourBitCombs+ l = length arg1+ select = replicate l and +++ replicate l or +++ replicate l add +++ replicate l shiftl+ op1 = arg1 ++ arg1 ++ arg1 ++ arg1+ op2 = arg2 ++ arg2 ++ arg2 ++ arg2+ outSim = simALU select op1 op2+ outVHDL = vhdlTest Nothing aluSys select op1 op2++counterTest :: Test+counterTest = TestCase $ assertFailure "Not running counterTest due to broken newSysFunTHName"+--counterTest = "counterTest" ~: "first 400 outputs" ~: +-- out400Sim <~=?> out400VHDLSim+-- where out400Sim = take 400 simCounter+-- out400VHDLSim = vhdlTest (Just 400) counterSys++parAddFourTest :: Test+parAddFourTest = "parAddFourTest" ~: test doTest+ where -- testt the overflows, and 400 different pseudorandom numbers+ lastInt32 :: Num a => a+ lastInt32 = 2147483647+ firstInt32 :: Num a => a+ -- FIXME: this should be -2147483648, but due to bug http://code.google.com/p/forsyde-comp/issues/detail?id=22 it was set to -2147483647+ firstInt32 = -2147483647 + gen400Int32 :: IO [Int32]+ gen400Int32 = do+ let randomInt32 :: IO Int32+ randomInt32 = do+ let getInt32Range :: IO Integer+ getInt32Range = getStdRandom (randomR (firstInt32, lastInt32))+ liftM fromIntegral getInt32Range+ replicateM 400 randomInt32+ doTest :: IO ()+ doTest = do arg1 <- gen400Int32 + arg2 <- gen400Int32+ arg3 <- gen400Int32+ arg4 <- gen400Int32 + arg5 <- gen400Int32 + let sim = simParAddFour (1:(-1):arg1) + -- (firstInt32:lastInt32:arg2)+ -- (lastInt32:firstInt32:arg3)+ arg2+ arg3+ (-1:1:arg4)+ (0:1:arg5)+ simVHDL = vhdlTest Nothing+ parAddFourSys+ (1:(-1):arg1) + -- (firstInt32:lastInt32:arg2)+ -- (lastInt32:firstInt32:arg3)+ arg2+ arg3+ (-1:1:arg4)+ (0:1:arg5)+ resVHDL <- simVHDL+ sim @=? resVHDL +++muxTest :: Test+muxTest = "muxTest" ~: Multiplexer.simMux sel input <~=?> + vhdlTest Nothing Multiplexer.muxSysDef sel input+ where sel = [(L,L),(L,H),(H,L),(H,H)]+ input = [(L,L,L,H),(L,L,H,L),(L,H,L,L),(H,L,L,L)]+ +++muxFSVecTest :: Test+muxFSVecTest = TestCase $ assertFailure "Not running muxFSVecTest due to broken newSysFunTHName"+--muxFSVecTest = "muxTest" ~: Multiplexer_FSVector.simMux sel input <~=?> +-- vhdlTest Nothing Multiplexer_FSVector.muxSysDef sel input+-- where sel = [$(vectorTH [L,L]),$(vectorTH [L,H]),$(vectorTH [H,L]),$(vectorTH [H,H])]+-- input = [$(vectorTH [L,L,L,H]), $(vectorTH [L,L,H,L]), $(vectorTH [L,H,L,L]),+-- $(vectorTH [H,L,L,L])]+--+seqAddFourTest :: Test+seqAddFourTest = "seqAddFourTest" ~: simAddFour input <~=?>+ vhdlTest Nothing addFourSys input+ where input = [-100,-99..100] ++buttonEncoderTest :: Test+buttonEncoderTest = "buttonEncoderTest" ~: simButtonEncoder input <~=?>+ vhdlTest Nothing buttonEncoderSys input+ where input = [(H,L,L,L), (L,H,L,L), (L,L,H,L), (L,L,L,H), (L,L,L,L)]++zipTwistTest :: Test+zipTwistTest = "zipTwistTest" ~: + simZipTwist input1 input2 input3 input4 input5 input6 <~=?> + vhdlTest Nothing zipTwistSys input1 input2 input3 input4 input5 input6+ where input1 = [-100..0]+ input2 = [-10..90]+ input3 = [100..200]+ input4 = [350..450]+ input5 = [-400..(-300)]+ input6 = [-50..50]++nullTest :: Test+-- we don't test quartus here, because it complains about no logic+--nullTest = "nullTest" ~: simNull <~=?> writeAndModelsimVHDL Nothing nullSysDef +nullTest = "nullTest" ~: simNull <~=?> vhdlTest Nothing nullSysDef+ +lfsrTest :: Test+lfsrTest = "lsfrTest" ~: outSim <~=?> outVHDL+ where take400Tup4 (i1,i2,i3,i4) = (take 400 i1, take 400 i2, take 400 i3,+ take 400 i4)+ outSim = take400Tup4 simlfsr+ outVHDL = vhdlTest (Just 400) lfsrSys++mapVTest :: Test+mapVTest = "mapVTest" ~: outSim <~=?> outVHDL+ where + outSim = take 100 simVecCounter+ outVHDL = vhdlTest (Just 100) vecCounterSys++foldlVTest :: Test+foldlVTest = "foldlVTest" ~: TestCase ioTest+ where + int32Range = (-2147483647,2147483647):: (Int32, Int32)+ ioTest = do+ testData <- generate.(vectorOf 100).(vectorOf 4).choose $ int32Range+ let input = map reallyUnsafeVector testData+ outSim = simFoldingAdder input+ outVHDL <- vhdlTest (Just 100) foldingAdderSys input+ outSim @=? outVHDL ++zipWithVTest :: Test+zipWithVTest = "zipWithVTest" ~: TestCase ioTest+ where+ int32Range = (-2147483647,2147483647):: (Int32, Int32)+ ioTest = do+ testDataA <- generate.(vectorOf 100).(vectorOf 4).choose $ int32Range+ testDataB <- generate.(vectorOf 100).(vectorOf 4).choose $ int32Range+ let inputA = map reallyUnsafeVector testDataA+ inputB = map reallyUnsafeVector testDataB+ outSim = simZipWithVSys inputA inputB+ outVHDL <- vhdlTest (Just 100) zipWithVSys inputA inputB+ outSim @=? outVHDL ++foldlVOpTest :: Test+foldlVOpTest = "foldlVOpTest" ~: TestCase ioTest+ where + int32Range = (-2147483647,2147483647):: (Int32, Int32)+ ioTest = do+ testData <- generate.(vectorOf 100).(vectorOf 4).choose $ int32Range+ let input = map reallyUnsafeVector testData+ outSim = simFoldingAdderOp input+ outVHDL <- vhdlTest (Just 100) foldingAdderOpSys input+ outSim @=? outVHDL ++zipWithVOpTest :: Test+zipWithVOpTest = "zipWithVOpTest" ~: TestCase ioTest+ where+ int32Range = (-2147483647,2147483647):: (Int32, Int32)+ ioTest = do+ testDataA <- generate.(vectorOf 100).(vectorOf 4).choose $ int32Range+ testDataB <- generate.(vectorOf 100).(vectorOf 4).choose $ int32Range+ let inputA = map reallyUnsafeVector testDataA+ inputB = map reallyUnsafeVector testDataB+ outSim = simZipWithVOpSys inputA inputB+ outVHDL <- vhdlTest (Just 100) zipWithVOpSys inputA inputB+ outSim @=? outVHDL + ++mapVectorOperatorSectionTest :: Test+mapVectorOperatorSectionTest = "mapVectorOperatorSectionTest" ~: outSim <~=?> outVHDL+ where + outSim = take 100 simVecOpSecCounter+ outVHDL = vhdlTest (Just 100) vecOpSecCounterSys++mapLambdaVectorTest :: Test+mapLambdaVectorTest = "mapLambdaVectorTest" ~: outSim <~=?> outVHDL+ where + outSim = take 100 simLamVecCounter+ outVHDL = vhdlTest (Just 100) vecLamCounterSys++mapVectorTransformationTest :: Test+mapVectorTransformationTest = "mapVectorTransformationTest" ~: outSim <~=?> outVHDL+ where + outSim = take 100 simTransfVecCounter+ outVHDL = vhdlTest (Just 100) transfVecCounterSys++-------------------+-- Helper functions+-------------------+++-- run a vhdl testbench with custom backend options+vhdlTest :: SysFunToIOSimFun sysF simF => Maybe Int -> SysDef sysF -> simF+-- vhdlTest = writeAndModelsimVHDLOps testVHDLOps+vhdlTest = writeAndGhdlVHDL++-- testing VHDL options+testVHDLOps :: VHDLOps+testVHDLOps = defaultVHDLOps{execQuartus=Just checkSynthesisQuartus}++-- Compare an IO test with a pure expected result+(<~=?>) :: (Eq a, Show a) => a -> IO a -> Test +expected <~=?> actualM = TestCase $ do a <- actualM+ expected @=? a+++-- | permutation a list+-- Each element of the input list indicates the posibilities of each+-- positional elements in the resulting lists+permut :: [[a]] -> [[a]]+permut [] = []+permut [xs] = map (\x -> [x]) xs+permut (xs:xss) = let res = permut xss+ in concatMap (\e -> map (:e) xs) res++-- | Combinations of 2 elements from a set of values +comb2 :: [a] -> [(a,a)]+comb2 xs = concatMap (\i -> map (\j -> (i,j) ) xs) xs++-- | Parallel combination of N elements+parCombN :: Int -> [a] -> [[a]]+parCombN n xs = transpose $ combN n xs++-- | Combinations os N elements from a set of values+combN :: Int -> [a] -> [[a]]+combN n _ | n <= 0 = []+combN 1 xs = map (\x -> [x]) xs+combN n xs = let res = combN (n-1) xs+ in concatMap (\i -> map (\j -> j:i) xs) res