Lookup input port names for instantiations in the session.
[matthijs/master-project/cλash.git] / Translator.hs
1 module Main(main) where
2 import GHC
3 import CoreSyn
4 import qualified CoreUtils
5 import qualified Var
6 import qualified Type
7 import qualified TyCon
8 import qualified DataCon
9 import qualified Maybe
10 import qualified Module
11 import Name
12 import Data.Generics
13 import NameEnv ( lookupNameEnv )
14 import HscTypes ( cm_binds, cm_types )
15 import MonadUtils ( liftIO )
16 import Outputable ( showSDoc, ppr )
17 import GHC.Paths ( libdir )
18 import DynFlags ( defaultDynFlags )
19 import List ( find )
20 -- The following modules come from the ForSyDe project. They are really
21 -- internal modules, so ForSyDe.cabal has to be modified prior to installing
22 -- ForSyDe to get access to these modules.
23 import qualified ForSyDe.Backend.VHDL.AST as AST
24 import qualified ForSyDe.Backend.VHDL.Ppr
25 import qualified ForSyDe.Backend.Ppr
26 -- This is needed for rendering the pretty printed VHDL
27 import Text.PrettyPrint.HughesPJ (render)
28
29 main = 
30                 do
31                         defaultErrorHandler defaultDynFlags $ do
32                                 runGhc (Just libdir) $ do
33                                         dflags <- getSessionDynFlags
34                                         setSessionDynFlags dflags
35                                         --target <- guessTarget "adder.hs" Nothing
36                                         --liftIO (print (showSDoc (ppr (target))))
37                                         --liftIO $ printTarget target
38                                         --setTargets [target]
39                                         --load LoadAllTargets
40                                         --core <- GHC.compileToCoreSimplified "Adders.hs"
41                                         core <- GHC.compileToCoreSimplified "Adders.hs"
42                                         liftIO $ printBinds (cm_binds core)
43                                         let bind = findBind "half_adder" (cm_binds core)
44                                         let NonRec var expr = bind
45                                         let sess = VHDLSession 0 builtin_funcs
46                                         liftIO $ putStr $ showSDoc $ ppr expr
47                                         liftIO $ putStr "\n\n"
48                                         liftIO $ putStr $ render $ ForSyDe.Backend.Ppr.ppr $ getArchitecture sess bind
49                                         return expr
50
51 printTarget (Target (TargetFile file (Just x)) obj Nothing) =
52         print $ show file
53
54 printBinds [] = putStr "done\n\n"
55 printBinds (b:bs) = do
56         printBind b
57         putStr "\n"
58         printBinds bs
59
60 printBind (NonRec b expr) = do
61         putStr "NonRec: "
62         printBind' (b, expr)
63
64 printBind (Rec binds) = do
65         putStr "Rec: \n"        
66         foldl1 (>>) (map printBind' binds)
67
68 printBind' (b, expr) = do
69         putStr $ getOccString b
70         --putStr $ showSDoc $ ppr expr
71         putStr "\n"
72
73 findBind :: String -> [CoreBind] -> CoreBind
74 findBind lookfor =
75         -- This ignores Recs and compares the name of the bind with lookfor,
76         -- disregarding any namespaces in OccName and extra attributes in Name and
77         -- Var.
78         Maybe.fromJust . find (\b -> case b of 
79                 Rec l -> False
80                 NonRec var _ -> lookfor == (occNameString $ nameOccName $ getName var)
81         )
82
83 -- Accepts a port name and an argument to map to it.
84 -- Returns the appropriate line for in the port map
85 getPortMapEntry binds (Port portname) (Var id) = 
86         (Just (AST.unsafeVHDLBasicId portname)) AST.:=>: (AST.ADName (AST.NSimple (AST.unsafeVHDLBasicId signalname)))
87         where
88                 Port signalname = Maybe.fromMaybe
89                         (error $ "Argument " ++ getOccString id ++ "is unknown")
90                         (lookup id binds)
91
92 getPortMapEntry binds _ a = error $ "Unsupported argument: " ++ (showSDoc $ ppr a)
93
94 getInstantiations ::
95         VHDLSession
96         -> PortNameMap                  -- The arguments that need to be applied to the
97                                                                                                                          -- expression. Should always be the Args
98                                                                                                                          -- constructor.
99         -> PortNameMap               -- The output ports that the expression should generate.
100         -> [(CoreBndr, PortNameMap)] -- A list of bindings in effect
101         -> CoreSyn.CoreExpr          -- The expression to generate an architecture for
102         -> [AST.ConcSm]              -- The resulting VHDL code
103
104 -- A lambda expression binds the first argument (a) to the binder b.
105 getInstantiations sess (Args (a:as)) outs binds (Lam b expr) =
106         getInstantiations sess (Args as) outs ((b, a):binds) expr
107
108 -- A case expression that checks a single variable and has a single
109 -- alternative, can be used to take tuples apart
110 getInstantiations sess args outs binds (Case (Var v) b _ [res]) =
111         case altcon of
112                 DataAlt datacon ->
113                         if (DataCon.isTupleCon datacon) then
114                                 getInstantiations sess args outs binds' expr
115                         else
116                                 error "Data constructors other than tuples not supported"
117                 otherwise ->
118                         error "Case binders other than tuples not supported"
119         where
120                 binds' = (zip bind_vars tuple_ports) ++ binds
121                 (altcon, bind_vars, expr) = res
122                 -- Find the portnamemaps for each of the tuple's elements
123                 Tuple tuple_ports = Maybe.fromMaybe 
124                         (error $ "Case expression uses unknown scrutinee " ++ getOccString v)
125                         (lookup v binds)
126
127 -- An application is an instantiation of a component
128 getInstantiations sess args outs binds app@(App expr arg) =
129         if isTupleConstructor f then
130                 let
131                         Tuple outports = outs
132                         (tys, vals) = splitTupleConstructorArgs fargs
133                 in
134                         concat $ zipWith 
135                                 (\outs' expr' -> getInstantiations sess args outs' binds expr')
136                                 outports vals
137         else
138                 [AST.CSISm comp]
139         where
140                 ((Var f), fargs) = collectArgs app
141                 comp = AST.CompInsSm
142                         (AST.unsafeVHDLBasicId "app")
143                         (AST.IUEntity (AST.NSimple (AST.unsafeVHDLBasicId compname)))
144                         (AST.PMapAspect ports)
145                 compname = getOccString f
146                 hwfunc = Maybe.fromMaybe
147                         (error $ "Function " ++ compname ++ "is unknown")
148                         (lookup compname (funcs sess))
149                 HWFunction (Args inports) outport = hwfunc
150                 ports = 
151                         zipWith (getPortMapEntry binds) inports fargs
152                   ++ mapOutputPorts outport outs
153
154 getInstantiations sess args outs binds expr = 
155         error $ "Unsupported expression" ++ (showSDoc $ ppr $ expr)
156
157 -- Is the given name a (binary) tuple constructor
158 isTupleConstructor :: Var.Var -> Bool
159 isTupleConstructor var =
160         Name.isWiredInName name
161         && Name.nameModule name == tuple_mod
162         && (Name.occNameString $ Name.nameOccName name) == "(,)"
163         where
164                 name = Var.varName var
165                 mod = nameModule name
166                 tuple_mod = Module.mkModule (Module.stringToPackageId "ghc-prim") (Module.mkModuleName "GHC.Tuple")
167
168 -- Split arguments into type arguments and value arguments This is probably
169 -- not really sufficient (not sure if Types can actually occur as value
170 -- arguments...)
171 splitTupleConstructorArgs :: [CoreExpr] -> ([CoreExpr], [CoreExpr])
172 splitTupleConstructorArgs (e:es) =
173         case e of
174                 Type t     -> (e:tys, vals)
175                 otherwise  -> (tys, e:vals)
176         where
177                 (tys, vals) = splitTupleConstructorArgs es
178
179 mapOutputPorts ::
180         PortNameMap         -- The output portnames of the component
181         -> PortNameMap      -- The output portnames and/or signals to map these to
182         -> [AST.AssocElem]  -- The resulting output ports
183
184 -- Map the output port of a component to the output port of the containing
185 -- entity.
186 mapOutputPorts (Port portname) (Port signalname) =
187         [(Just (AST.unsafeVHDLBasicId portname)) AST.:=>: (AST.ADName (AST.NSimple (AST.unsafeVHDLBasicId signalname)))]
188
189 -- Map matching output ports in the tuple
190 mapOutputPorts (Tuple ports) (Tuple signals) =
191         concat (zipWith mapOutputPorts ports signals)
192
193 getArchitecture ::
194         VHDLSession
195         -> CoreBind               -- The binder to expand into an architecture
196         -> AST.ArchBody           -- The resulting architecture
197          
198 getArchitecture sess (Rec _) = error "Recursive binders not supported"
199
200 getArchitecture sess (NonRec var expr) =
201         AST.ArchBody
202                 (AST.unsafeVHDLBasicId "structural")
203                 -- Use unsafe for now, to prevent pulling in ForSyDe error handling
204                 (AST.NSimple (AST.unsafeVHDLBasicId name))
205                 []
206                 (getInstantiations sess (Args inportnames) outport [] expr)
207         where
208                 name = (getOccString var)
209                 ty = CoreUtils.exprType expr
210                 (fargs, res) = Type.splitFunTys ty
211                 --state = if length fargs == 1 then () else (last fargs)
212                 ports = if length fargs == 1 then fargs else (init fargs)
213                 inportnames = case ports of
214                         [port] -> [getPortNameMapForTy "portin" port]
215                         ps     -> getPortNameMapForTys "portin" 0 ps
216                 outport = getPortNameMapForTy "portout" res
217
218 data PortNameMap =
219         Args [PortNameMap] -- Each of the submaps represent an argument to the
220                            -- function. Should only occur at top level.
221         | Tuple [PortNameMap]
222         | Port  String
223
224 -- Generate a port name map (or multiple for tuple types) in the given direction for
225 -- each type given.
226 getPortNameMapForTys :: String -> Int -> [Type] -> [PortNameMap]
227 getPortNameMapForTys prefix num [] = [] 
228 getPortNameMapForTys prefix num (t:ts) =
229         (getPortNameMapForTy (prefix ++ show num) t) : getPortNameMapForTys prefix (num + 1) ts
230
231 getPortNameMapForTy     :: String -> Type -> PortNameMap
232 getPortNameMapForTy name ty =
233         if (TyCon.isTupleTyCon tycon) then
234                 -- Expand tuples we find
235                 Tuple (getPortNameMapForTys name 0 args)
236         else -- Assume it's a type constructor application, ie simple data type
237                 -- TODO: Add type?
238                 Port name
239         where
240                 (tycon, args) = Type.splitTyConApp ty 
241
242 data HWFunction = HWFunction { -- A function that is available in hardware
243         inPorts   :: PortNameMap,
244         outPorts  :: PortNameMap
245         --entity    :: AST.EntityDec
246 }
247
248 data VHDLSession = VHDLSession {
249         nameCount :: Int,                      -- A counter that can be used to generate unique names
250         funcs     :: [(String, HWFunction)]    -- All functions available, indexed by name
251 }
252
253 builtin_funcs = 
254         [ 
255                 ("hwxor", HWFunction (Args [Port "a", Port "b"]) (Port "o")),
256                 ("hwand", HWFunction (Args [Port "a", Port "b"]) (Port "o"))
257         ]