{-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleInstances, TypeSynonymInstances, PatternGuards #-} {-# LANGUAGE FlexibleInstances #-} module AVerilog (aVerilog) where import Data.List(nub, partition, intercalate, sort, sortBy, group, union, groupBy) import Data.Char import Data.Maybe import System.IO.Unsafe import qualified Data.Set as S import qualified Data.Map as M import ListUtil(mapSnd) import ListMap(lookupWithDefault) import Util import FileNameUtil(hasSuf) import PFPrint import Error(internalError, ErrorHandle) import Flags(Flags, removeReg, removeCross, removeInoutConnect, removeUnusedMods, useDPI, verilogDeclareAllFirst) import Id import Pragma(PProp(..)) import ASyntax import ASyntaxUtil import Verilog import VPrims(vPriEnc,vMux,vPriMux,verilogInstancePrefix) import AVerilogUtil import InlineReg import BackendNamingConventions(isRegInst, isClockCrossingRegInst, isInoutConnect) import ForeignFunctions(ForeignFuncMap, mkDPIDeclarations, getForeignFunctions) import qualified GraphWrapper as G --import Debug.Trace --import Util(traces) -- ============================== -- aVerilog -- Converts the ASPackage into a VProgram, which is the raw Verilog -- output structure. -- A VProgram is a list of modules (VModule). If primitives (such as -- muxes) are not inlined, then they will appear in the list. If the -- main module is bit blasted, both the blasted and unblasted modules -- will appear in the list. -- Flags are the compiler flags -- [PProp] are the Pragmas -- XXX this function is too big aVerilog :: ErrorHandle -> Flags -> [PProp] -> ASPackage -> ForeignFuncMap -> IO VProgram aVerilog errh flags pps aspack ffmap = return (VProgram mods dpi_decls comments) where vco = flagsToVco flags -- look for pass-through comments, taking care of \n -- XXX should these attach to the main module instead of the -- XXX entire file (attached to file in case of multiple modules) topmod_comments = [line | PPdoc comment <- pps, line <- lines comment] comments_list = filter (not . null) [topmod_comments, inlined_submod_comments, inlined_rule_comments] trailer = ["",""] comments = if (null comments_list) then [] else (intercalate [""] comments_list) ++ trailer -- The modules are: -- (1) The main module -- (if bit-blasting, then this is the bit-blasted module) -- (2) Primitives, if not inlined (Mux, PriMux, etc) -- (3) The unblasted module (if bit-blasting), -- which is just a wrapper for the bit-blasted module -- to give it un-blasted ports mods = [mainMod] ++ prims ++ (if doBitBlast then [ubMod] else []) -- ---------- -- package elements mi = aspkg_name aspack ps = aspkg_parameters aspack os = filter (\(_,t) -> isNotZeroSized t) (aspkg_outputs aspack) is = filter (\(_,t) -> isNotZeroSized t) (aspkg_inputs aspack) fs = aspkg_foreign_calls aspack signal_info = aspkg_signal_info aspack comment_info = aspkg_comment_info aspack -- ---------- -- create scan ports and scan args scanDefs :: [VMItem] scanArgs :: [VArg] (scanDefs, scanArgs) = createScanStructures pps -- separate the signal declarations (scan_decls, scan_others) = expVVDWire scanDefs -- create groups for the output Verilog scan_decl_group = vGroupWithComment False (mergeCommonDecl scan_decls) ["test scan declaration"] scan_def_group = vGroupWithComment False (sort scan_others) ["test scan definitions"] -- ---------- -- bit blast, if requested by the user doBitBlast = PPbitBlast `elem` pps -- the bit-blasted ports ("args") and their definitions (bargs, bitBlastDefs) = createBitBlast args -- separate the signal declarations (blast_decls, blast_others) = expVVDWire bitBlastDefs -- create groups for the output Verilog blast_decl_group = vGroupWithComment False (mergeCommonDecl blast_decls) ["bit blast declarations"] blast_def_group = vGroupWithComment False (sort blast_others) ["bit blast definitions"] -- ---------- -- Create the main module (and un-blasted wrapper, if bit-blasting) -- Module name UB un-blasted, BB Bit blasted... modnameUB = vId mi modnameBB = suff modnameUB "BB" -- The module contents are defined below to be: -- instantiations and declarations for their ports -- definitions for submodule inputs -- definitions for all other signals (which will need declaring) -- other blocks etc -- The order of items in the module is: -- scanDef decls, bitBlastDef decls, output signal decls, -- insts, rule scheduling decls, remaining decls, -- scanDefs, bitBlastDefs, all local defs, -- inlined submodule blocks, foreign function blocks -- defs for the bit-blasted module bbItems = mkModItems True -- defs for the un-blasted module ubItems = mkModItems False mkModItems isBB = -- declarations -- feature decls scan_decl_group ++ if (isBB) then blast_decl_group else [] ++ -- module decls module_decls ++ -- definitions -- feature defs scan_def_group ++ if (isBB) then blast_def_group else [] ++ -- module defs module_defs ++ -- blocks inst_block_group ++ foreignfunc_block_group module_decls = -- output port re-declares output_decls_group ++ -- signals for submodule ports -- (depending on flag, this can also include the instantiations) inst_decl_groups ++ -- remaining declarations rule_decls_group ++ -- includes method_rule_decls mux_decls_group ++ foreign_decls_group ++ other_decls_group module_defs = special_def_groups ++ method_def_groups ++ -- depending on flag, either empty or the submod instantiations inst_def_groups ++ rule_def_groups ++ mux_defs_group ++ submod_def_groups ++ other_defs_group -- The main module -- XXX note, no special port grouping/commenting for bit blasted mod mainMod = if doBitBlast then VModule { vm_name = modnameBB, vm_comments = [], vm_ports = [(bargs,[])], vm_body = bbItems } else VModule { vm_name = modnameUB, vm_comments = [], vm_ports = (groupPorts signal_info args), vm_body = ubItems } -- The un-blasted wrapper, when bit-blasting -- It has the unblasted name, the unblasted ports, -- and it contains an instantiation of the bit-blasted module, -- wires to connect to its ports, and definitions for the -- un-blasted ports based on those wires. ubMod = VModule { vm_name = modnameUB, vm_comments = [], vm_ports = (groupPorts signal_info args), vm_body = wrItems } where wrItems = [wires bbids, ubInst bbids modnameBB] ++ unBlast args bbids = getArgIds bargs wires is = VMDecl (VVDecl VDWire Nothing (map VVar is)) -- ---------- -- convert foreign function calls to always blocks -- defs_in_foreign_blocks = ids of defs which are assigned in -- the foreign blocks and therefore need to be declared "reg" (foreignfunc_blocks, defs_in_foreign_blocks) = apFst concat $ apSnd concat $ unzip $ mapMaybe (vForeignBlock vco ffmap (aspkg_values aspack)) fs -- bool is True b/c foreign function blocks should be separated foreignfunc_block_group = vGroupWithComment True foreignfunc_blocks ["handling of system tasks"] -- ---------- -- create import-DPI statements, if using DPI dpi_decls = if (useDPI flags) then mkDPIDeclarations $ getForeignFunctions ffmap aspack else [] -- ---------- -- define a function (vDef) for mapping an ADef to a Verilog item -- defs which must be declared as reg, and don't need assigns -- because they are assigned in foreign blocks defs_as_reg = defs_in_foreign_blocks vDef :: ADef -> [VMItem] vDef adef@(ADef i t _ _) = let vdef = vDefMpd vco adef ffmap reg_def = [VMDecl $ VVDecl VDReg (vSize t) [VVar (vId i)]] in -- if it's a reg, just declare the reg if (i `elem` defs_as_reg) then reg_def else -- return the vdef, but -- remove definitions which have 0 sized results case (aType adef) of (ATString Nothing) -> vdef otherwise -> if ((aSize adef) == 0) then [] else vdef -- ---------- -- handle the submodule instances -- -- an instance consists of the module instantiation, -- and the declarations for the ports which connect to it. -- the output wires need to be created as they do not exist -- in the aspack defs. the input wires exist in the defs -- because they have been defined to some value; so we need -- to separate the decl from the def. -- the function genInstances will create the submodule instance -- and declarations for submodule inputs and outputs; -- it will handle the inputs which must be high (remove them -- and error/warn if they are not high); -- it will determine which ports of instantiations are unused, -- and remove those port connections; -- it will return: -- * the package defs (still unconverted from A to V) -- without the always_enabled and unused defs -- * the Verilog instantiation groups (submod instantiations -- and declaration of signals for connecting to the ports): -- this is broken into a decl group and a def group; -- depending on flags, either everything is in the decl group -- and the def group is empty or the instantiations are moved -- to the def group (to allow all decls to be first in the module) -- * lists of the defs which have already been declared -- (separated into those which are inputs to submodules -- and others which had to be declared because they were used) -- * any Verilog blocks for submodules (such as for inlined Reg) -- * any not-always-enabled errors to report -- provide foreign blocks, so that it knows which ports are used there (ds, inst_decl_groups, inst_def_groups, inst_inputs, inst_declared_signals, inst_blocks, inout_rewire_map, inlined_submod_comments) = genInstances errh flags foreignfunc_blocks vDef aspack -- currently, inst_blocks are only from inlined registers -- (the always blocks and initial blocks) -- bool is True b/c register always/initial blocks should be separated inst_block_group = vGroupWithComment True inst_blocks ["handling of inlined registers"] -- ---------- -- Compute inout information all_inouts = computeInouts inout_rewire_map aspack -- ---------- -- Compute the port list of the module -- XXX parameters are always given the default value of 0. -- XXX might be good to allow the user to specify a default args :: [VArg] args = [ VAParameter (vId i) r v | (i, t) <- ps, let (r, v) = case t of ATBit sz -> (Just (VEConst (sz-1), VEConst 0), VEWConst (mkVId "0") sz 2 0) ATString _ -> (Nothing, VEString "") ATReal -> (Nothing, VEReal 0.0) _ -> (Nothing, VEConst 0) ] ++ [ VAInput (vId i) (vSize t) | (i, t) <- is ] ++ filterSharedInout ([ VAInout (vId i) (Just (vId i')) (Just (vSize t)) | (i, t, i') <- all_inouts ]) ++ [ VAOutput (vId o) (vSize t) | (o, t) <- os ] ++ scanArgs -- ---------- -- Grouping of definitions -- -- separate the defs into groups: -- * special outputs (clocks, resets) -- * methods -- * rules -- * inputs to submodules -- * used by foreign blocks -- * other -- remember to not redeclare any signals that needed to be -- declared for submodule instantiations isPreDeclared = isDeclFromList inst_declared_signals -- make a map to hold the defs, for easy access defmap = M.fromList [ (i,d) | d@(ADef i _ _ _) <- ds ] -- ----- -- special outputs (special_decls, special_def_groups, defmap2) = groupSpecialOutputDefs vDef signal_info defmap -- ----- -- methods (method_output_decls, method_rule_decls, method_def_groups, defmap3) = groupMethodDefs vDef signal_info defmap2 -- ----- -- redeclare module outputs -- (module output ports need to be declared as wire/reg for -- internal assignment) -- (these will come before instances, so no need to filter) -- note: output_decls should be the same as if we had -- filtered the defs using "os" from the aspack -- XXX check for this with an internal error? output_decls = special_decls ++ method_output_decls output_decls_comment = ["signals for module outputs"] output_decls_group = vGroupWithComment False (mergeCommonDecl output_decls) output_decls_comment -- ----- -- rules (rule_decls, rule_def_groups, defmap4, inlined_rule_comments) = groupRuleDefs vDef signal_info comment_info defmap3 rule_decls_filtered = filter (not . isPreDeclared) (method_rule_decls ++ rule_decls) rule_decls_comment = ["rule scheduling signals"] rule_decls_group = vGroupWithComment False (mergeCommonDecl rule_decls_filtered) rule_decls_comment -- ----- -- inputs to submodules (submod_def_groups, defmap5) = groupSubmoduleDefs vDef inst_inputs defmap4 -- ----- -- inputs to muxes (selectors and values) -- (if they survive optimization) (mux_decls_group, mux_defs_group, defmap6) = groupMuxDefs vDef signal_info defmap5 -- Foreign block decls (should be no corresponding defs) -- Foreign block decls include directly written by foreign calls -- as well as defs inlinded into foreign blocks. foreign_writes = concatMap afc_writes $ concatMap snd fs (foreign_decls_group, defmap7) = groupForeignBlockDefs vDef (foreign_writes ++ defs_in_foreign_blocks) defmap6 -- ----- -- other -- (keep in the package order, for lack of a better order) ds7 = filter (isADefFromList (M.keys defmap7)) ds (other_decls, other_defs) = mkVDeclsAndDefs vDef ds7 other_decls_filtered = filter (not . isPreDeclared) other_decls other_decls_group = vGroupWithComment False (mergeCommonDecl other_decls_filtered) ["remaining internal signals"] other_defs_group = vGroupWithComment False (sort other_defs) ["remaining internal signals"] -- ---------- -- Convert primitives into modules -- this looks for all the prim mux instances in the -- module and constructs the module definitions for them. -- to save work, just hand it the module_defs prims = buildPrimModules flags module_defs -- ============================== -- Grouping and commenting ports -- Using the signal info, convert the portlist into a grouped and -- commented port list, for more readable RTL output groupPorts :: ASPSignalInfo -> [VArg] -> [([VArg], VComment)] groupPorts si as = let -- create pairs of the ports with their name named_as = map (\a -> (vidToId (vargName a), a)) as -- a function to filter out ports whose name appears in a list findIds :: [Id] -> [(Id,VArg)] -> ([VArg],[(Id,VArg)]) findIds is ports = let (found, rest) = partition (\ (i,a) -> i `elem` is) ports in (map snd found, rest) -- find the module input args (inputs, remaining1) = findIds (aspsi_inputs si) named_as -- find the output clocks -- (function to be folded over the clock/gate info) findClock (clk, gates) (cs, ports) = let (ps, remaining) = findIds (clk:gates) ports in ((clk,ps):cs, remaining) (output_clks, remaining2) = foldr findClock ([], remaining1) (aspsi_output_clks si) -- find the output resets (output_rsts, remaining3) = findIds (aspsi_output_rsts si) remaining2 -- find the ports for each method -- (function to be folded over the method port info) findMethod :: ASPMethodInfo -> ([(AId,String,[VArg])],[(Id,VArg)]) -> ([(AId,String,[VArg])],[(Id,VArg)]) findMethod (ASPMethodInfo i ty mr me mv args _) (ms, ports) = let is = (catMaybes [mr, me, mv]) ++ args (ps, remaining) = findIds is ports in ((i,ty,ps):ms, remaining) (method_ports, remaining4) = foldr findMethod ([], remaining3) (aspsi_methods si) -- XXX interface Inouts have not been pulled out -- XXX (so they will end up in the unknown_ports list) -- this should be empty -- (but maybe misc things, like scan ports, end up in the list?) unknown_ports = map snd remaining4 -- ty is the type of the method (action, value, actionvalue) -- XXX could sort the ports? (RDY, args, return val, EN) mkMethodGroup (i,ty,ps) = (ps, [ty ++ " method " ++ getIdString i]) mkClockGroup (clk,ps) = (ps, ["oscillator and gates for output clock " ++ getIdString clk]) in -- no comment on the inputs (if (not (null inputs)) then [(inputs, [])] else []) ++ -- unidentified ports (if (not (null unknown_ports)) then [(unknown_ports, [])] else [] ) ++ -- methods map mkMethodGroup method_ports ++ -- clocks map mkClockGroup output_clks ++ -- resets (if (null output_rsts) then [] else [(output_rsts, ["output resets"])]) -- ============================== -- produce the decls and assignments for special outputs (clocks and resets) -- and return the remaining defs groupSpecialOutputDefs :: (ADef -> [VMItem]) -> ASPSignalInfo -> M.Map AId ADef -> ([VMItem], [VMItem], M.Map AId ADef) groupSpecialOutputDefs vDef si ds = let mkForClock (clk, gates) (decls, gs, defs) = let (clk_defs, other_defs) = findADefs (clk:gates) defs (clk_vdecls, clk_vdefs) = mkVDeclsAndDefs vDef clk_defs comment = ["oscillator and gates for output clock " ++ getIdString clk] -- if (null clk_defs) this returns [] g = vGroupWithComment False clk_vdefs comment in (clk_vdecls ++ decls, g ++ gs, other_defs) mkResetDefGroup rsts defs = let (rst_defs, other_defs) = findADefs rsts defs (rst_vdecls, rst_vdefs) = mkVDeclsAndDefs vDef rst_defs comment = ["output resets"] -- if (null rst_vdefs) this returns [] g = vGroupWithComment False rst_vdefs comment in (rst_vdecls, g, other_defs) clk_infos = aspsi_output_clks si rst_infos = aspsi_output_rsts si (rst_decls, rst_group, ds1) = mkResetDefGroup rst_infos ds (vdecls, groups, remaining_defs) = foldr mkForClock (rst_decls, rst_group, ds1) clk_infos in (vdecls, groups, remaining_defs) -- returns -- * decls for outputs (RDY and output value) -- * decls for internal signals (CAN_FIRE_/WILL_FIRE_) -- * group of defs for both outputs and internal signals -- * remaining ADefs groupMethodDefs :: (ADef -> [VMItem]) -> ASPSignalInfo -> M.Map AId ADef -> ([VMItem], [VMItem], [VMItem], M.Map AId ADef) groupMethodDefs vDef si ds = let getRuleSignals r = case (lookup r (aspsi_rule_sched si)) of Nothing -> internalError ("getRuleSignals: " ++ ppString r) Just ss -> ss -- a function to fold over the method infos -- this function identifies: -- * method outputs (RDY and/or return val) -- which have already been declared, but have defs -- * related internal signals -- (so far just the sched signals for associated rules) mkForMethod :: ASPMethodInfo -> ([VMItem], [VMItem], [VMItem], M.Map AId ADef) -> ([VMItem], [VMItem], [VMItem], M.Map AId ADef) mkForMethod (ASPMethodInfo i ty mr _ mv _ rs) (odecls, idecls, gs, defs) = let -- get the output defs output_ids = catMaybes [mv, mr] (output_defs, other_defs) = findADefs output_ids defs -- get the rule defs rule_sched_ids = concatMap getRuleSignals rs (rule_defs, other_defs2) = findADefs rule_sched_ids other_defs -- convert to Verilog (out_vdecls, out_vdefs) = mkVDeclsAndDefs vDef output_defs (rule_vdecls, rule_vdefs) = mkVDeclsAndDefs vDef rule_defs -- make the method group comment = [ty ++ " method " ++ getIdString i] -- if (null defs) this returns [] g = vGroupWithComment False (out_vdefs ++ rule_vdefs) comment in (out_vdecls ++ odecls, rule_vdecls ++ idecls, g ++ gs, other_defs2) in -- fold over the method infos foldr mkForMethod ([], [], [], ds) (aspsi_methods si) -- returns -- * decls for scheduling signals (CAN_FIRE_/WILL_FIRE_) -- * defs for scheduling signals -- * remaining ADefs -- * top-level comments, from any rules whose signals were inlined -- (leaving no place to attach comments) groupRuleDefs :: (ADef -> [VMItem]) -> ASPSignalInfo -> ASPCommentInfo -> M.Map AId ADef -> ([VMItem], [VMItem], M.Map AId ADef, [String]) groupRuleDefs vDef si ci ds = let sched_info = aspsi_rule_sched si comment_map = aspci_rules ci method_rule_ids = concatMap aspm_assocrules (aspsi_methods si) -- the scheduling info for real rules -- (scheduling info for methods will be grouped with methods) nonmethod_sched_info :: [(AId,[AId])] nonmethod_sched_info = filter (\(i,v) -> i `notElem` method_rule_ids) sched_info mkForRule (rule_id, signal_ids) (decls, gs, defs, inlined_rs) = let (rule_defs, other_defs) = findADefs signal_ids defs (rule_vdecls, rule_vdefs) = mkVDeclsAndDefs vDef rule_defs -- make the rule group user_comment = fromMaybe [] (lookup rule_id comment_map) comment = ("rule " ++ getIdString rule_id) : indentLines 2 (concatMap lines user_comment) -- if (null rule_vdefs) this returns [] g = vGroupWithComment False rule_vdefs comment inlined_rs' = if (null rule_vdefs) then ((rule_id, user_comment):inlined_rs) else inlined_rs in (rule_vdecls ++ decls, g ++ gs, other_defs, inlined_rs') (decls, gs, defs, inlined_rule_info) = foldr mkForRule ([], [], ds, []) nonmethod_sched_info -- compute whether any rule comments need to bubble to the top remaining_rule_comments = filter (not . null . snd) inlined_rule_info top_comments = makeTopComments "rule" remaining_rule_comments in (decls, gs, defs, top_comments) -- returns a list of definition groups -- (as these signals have already been declared) -- and the remaining defs groupSubmoduleDefs :: (ADef -> [VMItem]) -> ([(AId,[VId])], [(AId,[VId])], [VId], [VId]) -> M.Map AId ADef -> ([VMItem], M.Map AId ADef) groupSubmoduleDefs vDef inst_inputs ds = let (submod_inputs, reg_inputs, rwire_inputs, probe_inputs) = inst_inputs -- for grouping inputs to each instance in their own groups mkModDefGroup ty (i, ss) (gs, defs) = let ss' = map vidToId ss (mod_defs, other_defs) = findADefs ss' defs (_{-mod_vdecls-}, mod_vdefs) = mkVDeclsAndDefs vDef mod_defs comment = [ty ++ " " ++ getIdString i] -- if (null mod_vdefs) this returns [] g = vGroupWithComment False mod_vdefs comment in (g ++ gs, other_defs) mkSubmodDefGroup = mkModDefGroup "submodule" mkRegDefGroup = mkModDefGroup "register" -- for grouping all wires into one commented group -- rather than a comment per wire mkWireDefGroup :: VComment -> ([VMItem], M.Map AId ADef) -> [VId] -> ([VMItem], M.Map AId ADef) mkWireDefGroup comment (gs, defs) ss = let ss' = map vidToId ss (wire_defs, other_defs) = findADefs ss' defs (_{-wire_vdecls-}, wire_vdefs) = mkVDeclsAndDefs vDef wire_defs -- if (null wire_vdefs) this returns [] g = vGroupWithComment False wire_vdefs comment in (g ++ gs, other_defs) mkRWireDefGroup = mkWireDefGroup ["inlined wires"] mkProbeDefGroup = mkWireDefGroup ["probes"] -- fold over the mods (gs1, ds1) = foldr mkSubmodDefGroup ([],ds) submod_inputs (gs2, ds2) = foldr mkRegDefGroup (gs1,ds1) reg_inputs (gs3, ds3) = mkRWireDefGroup (gs2,ds2) rwire_inputs (gs4, ds4) = mkProbeDefGroup (gs3,ds3) probe_inputs in (gs4, ds4) -- returns -- * group for decls (of mux inputs) -- * group for defs (of mux inputs) -- * remaining ADefs -- XXX This currently makes one group for all, -- XXX but with more info, it could group them according to mux. -- XXX Also, it could group selectors and values separately. groupMuxDefs :: (ADef -> [VMItem]) -> ASPSignalInfo -> M.Map AId ADef -> ([VMItem], [VMItem], M.Map AId ADef) groupMuxDefs vDef si ds = let mux_input_ids = aspsi_mux_selectors si ++ aspsi_mux_values si (mux_defs, other_defs) = findADefs mux_input_ids ds (mux_vdecls, mux_vdefs) = mkVDeclsAndDefs vDef mux_defs comment = ["inputs to muxes for submodule ports"] mux_decl_group = vGroupWithComment False (mergeCommonDecl mux_vdecls) comment mux_def_group = vGroupWithComment False mux_vdefs comment in (mux_decl_group, mux_def_group, other_defs) -- returns -- * group for decls (of regs for foreign function assignments) -- * remaining ADefs -- Does not return a group of defs because these decls are assigned -- in the foreign function block. groupForeignBlockDefs :: (ADef -> [VMItem]) -> [AId] -> M.Map AId ADef -> ([VMItem], M.Map AId ADef) groupForeignBlockDefs _ [] ds = ([], ds) groupForeignBlockDefs vDef foreign_block_ids ds = let (foreign_defs, other_defs) = findADefs foreign_block_ids ds (foreign_vdecls, foreign_vdefs) = mkVDeclsAndDefs vDef foreign_defs comment = ["declarations used by system tasks"] foreign_vgroup = VMGroup { vg_translate_off = True , vg_body = [foreign_vdecls] } in case foreign_vdefs of [] -> ([VMComment comment foreign_vgroup], other_defs) _ -> internalError ("groupForeignBlockDefs: " ++ ppReadable (foreign_block_ids, ds)) -- ---------- -- from a list of ADef, make a list of the Verilog decls and a list of -- the Verilog assignments (whether by assign statement or always block) mkVDeclsAndDefs :: (ADef -> [VMItem]) -> [ADef] -> ([VMItem], [VMItem]) mkVDeclsAndDefs vDef ds = let converted_ds = concatMap vDef ds in expVVDWire converted_ds -- ============================== -- Top-level inout handing computeInouts :: M.Map AId AId -> ASPackage -> [(Id, AType, Id)] computeInouts inout_rewire_map aspack = let -- ifc inouts are connected to their expressions ifc_inouts = [ (i,t,e) | (ADef i t e _) <- aspkg_inout_values aspack, isNotZeroSized t ] ifc_inout_ids = map fst3 ifc_inouts -- arg_inouts are connected to themselves arg_inouts = [ (i,t, ASPort t i) | (i,t) <- aspkg_inouts aspack, isNotZeroSized t, i `notElem` ifc_inout_ids ] all_inouts = ifc_inouts ++ arg_inouts rewire_inout (ASPort t i) = fromMaybe i (M.lookup i inout_rewire_map) rewire_inout e = internalError ("computeInouts - not port: " ++ ppReadable e) in mapThd rewire_inout all_inouts -- Make sure that a shared inout signal is only declared once filterSharedInout :: [VArg] -> [VArg] filterSharedInout args = let foldFn :: VArg -> (S.Set VId, [VArg]) -> (S.Set VId, [VArg]) foldFn a@(VAInout v mv (Just r)) (s,as) = let i = fromMaybe v mv new_a = VAInout v mv Nothing in if (S.member i s) then (s, new_a:as) else (S.insert i s, a:as) foldFn a@(VAInout _ _ Nothing) (s,as) = (s, a:as) foldFn a _ = internalError ("filterSharedInout: unexpected varg: " ++ ppReadable a) in snd $ foldr foldFn (S.empty,[]) args -- ============================== -- genInstances -- Generates the Verilog for submodule instantiations, grouping the -- module and the port wires for each submodule, and returns the -- items in order (grouped by type and alphabetical in each group) -- and returns the remaining module defs. -- There are several types of instances: -- * instantiations of Probe modules, where the Verilog module is to be -- removed, but the write signal remains (for viewing the probe value) -- * inlined registers, which have no verilog module, but do generate -- additional always blocks, and have an output which needs to be -- declared "reg" instead of "wire" -- * the inputs to inlined rwires are grouped, if the signal is still around -- XXX we could handle comments on rwire insts, attaching them to the signals -- * normal instantiations -- * "ifc args", which are handled like instantiations -- XXX should make the return type a structure. -- XXX this function is too big genInstances :: ErrorHandle -> Flags -> [VMItem] -> (ADef -> [VMItem]) -> ASPackage -> ([ADef], -- filtered (not yet converted) package defs [VMItem], -- decl groups [VMItem], -- def groups ([(AId, [VId])], -- submod inputs which don't need declaring [(AId, [VId])], -- (instances, regs, rwires, probes) [VId], [VId]), [VId], -- other defs that needed to be declared, -- so don't re-declare these either [VMItem], -- always and initial blocks M.Map AId AId, -- inout rewiring map [String]) -- toplevel comments for inlined submodules genInstances errh flags ff_blocks vDef aspack = let -- ---------- -- the package definitions -- converts the adefs to vitems defs = concatMap vDef (aspkg_values aspack) -- we'll need the inout defs, too, to know which inout ports -- of submodule are used (and which should be left unconnected) iodefs = concatMap vDef (aspkg_inout_values aspack) sos = aspkg_state_outputs aspack top_mod_inputs = aspkg_inputs aspack rws = aspkg_inlined_ports aspack inst_comments = aspci_submod_insts (aspkg_comment_info aspack) parent_ps = map fst (aspkg_parameters aspack) -- ---------- -- Sort the instances by name so they are easier to find, -- in the generated Verilog sortAVInst :: AVInst -> AVInst -> Ordering sortAVInst a b = cmpIdByName (avi_vname a) (avi_vname b) aspkg_instances :: [AVInst] aspkg_instances = sortBy sortAVInst (aspkg_state_instances aspack) -- ----------- -- Inline inout connections -- to pacify some picky tools (e.g. Xilinx) inout_connect_instances :: [AVInst] not_inout_connect_instances :: [AVInst] (inout_connect_instances, not_inout_connect_instances) = if (removeInoutConnect flags) then partition isInoutConnect aspkg_instances else ([], aspkg_instances) mkInoutPair [ASPort t1 i1, ASPort t2 i2] | t1 == t2 = (i1, i2) mkInoutPair l = internalError ("bad inout connect: " ++ ppReadable (l, inout_connect_instances)) inout_pairs = map (mkInoutPair . (drop 1) . avi_iargs) inout_connect_instances (inout_rewire_map, inout_inst_wires) = buildInoutConnectInfo inout_pairs buildInoutConnectInfo pairs = apSnd M.keys $ foldr addInoutPair (M.empty, M.empty) inout_pairs addInoutPair :: (AId, AId) -> (M.Map AId AId, M.Map AId [AId]) -> (M.Map AId AId, M.Map AId [AId]) addInoutPair (i1, i2) (cm, rcm) | Just ic1 <- M.lookup i1 cm, Just ic2 <- M.lookup i2 cm = if ic1 == ic2 then (cm, rcm) -- redundant connection -- we need to connect everything ic2 is connected to to ic1 else let ic2_cxns = fromJustOrErr ("ic2 not found: " ++ ppReadable (ic2, rcm)) (M.lookup ic2 rcm) update_cm = M.fromList $ map (\cxn -> (cxn, ic1)) ic2_cxns in (M.unionWith (flip const) cm update_cm, M.delete ic2 $ M.adjust ((++) ic2_cxns) ic1 rcm) addInoutPair (i1, i2) (cm, rcm) | Just i_shared <- M.lookup i1 cm, not (M.member i2 cm) = (M.insert i2 i_shared cm, M.adjust ((:) i2) i_shared rcm) addInoutPair (i1, i2) (cm, rcm) | Just i_shared <- M.lookup i2 cm, not (M.member i1 cm) = (M.insert i1 i_shared cm, M.adjust ((:) i1) i_shared rcm) addInoutPair (i1, i2) (cm, rcm) = if (not (M.member i1 cm) && not (M.member i2 cm)) then (M.insert i1 i1 $ M.insert i2 i1 cm, M.insert i1 [i1,i2] rcm) else internalError ("addInoutPair: " ++ ppReadable ((i1,i2),cm,rcm)) -- ---------- -- separate the Reg* instances, when inlining registers inlined_reg_instances :: [AVInst] noninlined_aspkg_instances :: [AVInst] should_inline x = isRegInst x && ((not (isClockCrossingRegInst x)) || (removeCross flags)) (inlined_reg_instances, noninlined_aspkg_instances) = if (removeReg flags) then partition should_inline not_inout_connect_instances else ([], not_inout_connect_instances) (inlined_reg_comments, noninlinedreg_comments) = let reg_inst_ids = map avi_vname inlined_reg_instances in partition ((`elem` reg_inst_ids) . fst) inst_comments -- ---------- -- translate the inlined registers into Verilog items -- vInlineReg returns the always blocks for implementing the regs -- and port info used to declare the Q_OUT (which needs to be declared -- as "reg" instead of "wire"), to declare the inputs (separate from -- their assignments), and to keep track of any CLK/RSTN uses for -- removed unused ports on other modules (inlined_reg_blocks, reg_infos) = vInlineReg errh flags inlined_reg_instances -- generate the reg/wire declarations reg_decl_groups = map (mkRegGroup sos defs inlined_reg_comments) reg_infos -- the input wire decls (to be later assigned to, but not re-declared) reg_inputs :: [(AId, [VId])] reg_inputs = map (\((VId _ inst_id _),def_name,c,d,e) -> (inst_id, map fst d)) reg_infos -- used by dropUnusedPortWires to know which submod outputs are -- used as non-method inputs (clk and rstn) to registers reg_uses = concatMap (\((VId _ inst_id _),def_name,c,d,e) -> c) reg_infos -- ---------- -- translate the non-inlined submodule instances -- and handle always_enabled attributes -- sorted list of the instances: -- * inst name -- * Verilog instance (VMInst) -- * an info tuple of lists of the ports and params of the inst inst_infos0 :: [ (AId, VMItem, InstInfo) ] inst_infos0 = map (vState flags inout_rewire_map) noninlined_aspkg_instances -- if we're removing unused modules, -- filter out instances that don't connect any wires inst_infos = if (removeUnusedMods flags) then filter (wiredInstance . snd3) inst_infos0 else inst_infos0 -- ---------- -- Compute which always-high signals aren't set to constant 1 -- This is now computed before the backend split, in AAddScheduleDefs -- ---------- -- remove input port connection wires which are always high -- -- ports which are always high shouldn't exist in the Verilog module -- (because they've been removed by always_enabled) -- XXX they have been created with the string "_AlwaysEnabled" -- XXX appended to them, so look for that string alwaysEnabledString string = (hasSuf "_AlwaysEnabled" string) notAlwaysEnabledADef (ADef aid _ _ _) = (not (alwaysEnabledString (getIdString aid))) -- definitions which are not always_enabled filtered_ds = filter notAlwaysEnabledADef (aspkg_values aspack) -- note that we start again from the ASP ds, because vDef might expand -- them into several items, so easier to filter the one item in ds filtered_defs = concatMap vDef filtered_ds -- note that we don't remove the always_ready ports from the insts, -- because dropUnusedPortWires (below) will remove them -- ---------- -- XXX separate out the ifc_args here? {- -- See the commented-out code in IExpand.iExpandModuleLam -- which creates an instance as the way to make the ifc available. -- Since ifc args are not supported, the first list will be empty. -- XXX The method for finding "interface arguments" is to look -- XXX for the string which CType.ifcArgName creates. (ifc_arg_info, real_inst_info) = partition isARG inst_info where isARG (VMAssign (VLId (VId s i _)) _) | take 4 s == "ARG_" = True isARG _ = False -} -- ---------- -- drop unused port wires -- XXX we make fake VMItems for the sole purpose of faking out -- dropUnusedPortWires for bypassed inouts to_inout_decl i = VMDecl $ vVDecl VDInout Nothing (VVar (vId i)) inout_inst_defs = map to_inout_decl inout_inst_wires all_io_wires = iodefs ++ inout_inst_defs (unused_defs, inst_info2) = dropUnusedPortWires ff_blocks (filtered_defs ++ all_io_wires) reg_uses inst_infos filtered_ds2 = let unused_ids = map vidToId unused_defs isUsedDef (ADef { adef_objid=i }) = i `notElem` unused_ids in filter isUsedDef filtered_ds -- ---------- -- separate out probes and generate their items (submod_infos, probe_infos) = partition notProbe inst_info2 (probe_comments, all_submod_comments) = let probe_ids = map fst3 probe_infos in partition ((`elem` probe_ids) . fst) noninlinedreg_comments (probe_inputs, probe_decls_group) = mkProbeGroup sos defs probe_comments probe_infos -- ---------- -- generate a group for inlined rwires whose i/o are still around -- user comments on rwires are handled with "inlined_submod_comments" -- (note that InlineRWire could convert them to PPdoc on the topmod) (rwire_inputs, rwire_decls_group) = mkRWireGroup filtered_defs rws -- ---------- -- signals declared so far -- if flags indicate to intermingle submod instantiations -- with declarations of their ports, then we need a list of -- signals declared so far (so we know which not to re-declare) -- this list doesn't contain submodule inputs because we're only -- concerned with submod outputs and local defs and topmod inputs -- (those are the only things that can appear in the ports exprs) decls_so_far = rwire_inputs ++ map (vId . fst) top_mod_inputs ++ map (vId . fst) sos -- ---------- -- generate items for remaining submodules -- (including declarations of inputs and outputs) (submod_comments, inlined_submod_comments) = let submod_ids = map fst3 submod_infos in partition ((`elem` submod_ids) . fst) all_submod_comments (submod_inputs, submod_decl_groups, submod_def_groups, extra_submod_decls) = mkSubmodGroups flags sos defs submod_comments decls_so_far submod_infos -- ---------- -- convert the comments on inlined submodules to toplevel comments top_level_comments = makeTopComments "module" inlined_submod_comments -- ---------- -- sanity check instantiation parameters and port arguments -- params must be constant bad_submod_ps = [ (p, e) | (_, _, (ps, _, _, _, _)) <- submod_infos, (p, e) <- ps, not (isConstVE parent_ps e) ] -- ports must be top module input, submodule output, or constant -- XXX is this checking for anything? -- XXX perhaps it is checking for a Verilog requirement on the -- XXX legal expressions for a port? otherwise, it's not needed. bad_submod_port_ps = [ (p, e) | (_, _, (_, port_ps, _, _, _)) <- submod_infos, (p, e) <- port_ps, -- not constant not (isConstVE parent_ps e), -- if it's a variable reference, it must be an input, -- a submod output, or a definition -- (which is everything, so no need to check) -- a const select of a variable is OK as well case (e) of VEVar v -> False VESelect (VEVar v) e1 e2 -> not (isConstVE parent_ps e1 && isConstVE parent_ps e2) VESelect1 (VEVar v) e1 -> not (isConstVE parent_ps e1) VEIndex v e1 -> not (isConstVE parent_ps e1) _ -> True ] -- ---------- -- final item list final_ds = filtered_ds2 -- list the probes first, then the inlined regs, then submodules -- (because they are VMGroup, there will be spaces between them) inst_decl_groups = probe_decls_group ++ rwire_decls_group ++ reg_decl_groups ++ submod_decl_groups -- only non-inlined modules have instantiations -- (everything else is pure decls) -- if verilogDeclareAllFirst flag is off, then the insts will be -- included in the decl groups and this list will be empty inst_def_groups = submod_def_groups in if (not (null bad_submod_ps)) then internalError ("genInstances: bad submod params: " ++ ppReadable bad_submod_ps) else if (not (null bad_submod_port_ps)) then internalError ("genInstances: bad submod instantiation arg: " ++ ppReadable bad_submod_port_ps) else (final_ds, inst_decl_groups, inst_def_groups, (submod_inputs, reg_inputs, rwire_inputs, probe_inputs), extra_submod_decls, inlined_reg_blocks, inout_rewire_map, top_level_comments) -- a wrapper for mkInstGroup, -- this creates the decls and defs for non-inlined submodules; -- if the verilogDeclareAllFirst flag is on, then this is straightforward -- and the wrapper is trivial, but if the flag is off then work is done -- to sort the instantiations and declare any needed signals etc -- (any signals which are pre-declared are returned as the fourth item -- of the tuple) mkSubmodGroups :: Flags -> [AStateOut] -> [VMItem] -> [(Id,[String])] -> [VId] -> [(AId, VMItem, InstInfo)] -> ([(AId, [VId])], [VMItem], [VMItem], [VId]) mkSubmodGroups flags sos defs submod_comments decls_so_far submod_infos = let (submod_decl_groups, submod_def_groups) = apFst catMaybes $ apSnd catMaybes $ unzip $ map (mkInstGroup flags sos defs submod_comments) submod_infos submod_inputs = let getInputs (i, _, (_,_,_,ins,_)) = (i, map snd ins) in map getInputs submod_infos in -- If the verilogDeclareAllFirst flag is on, then we are done; -- if not, then "submod_def_groups" will be an empty list and -- we need to sort the insts etc to allow the intermingling if (verilogDeclareAllFirst flags) then (submod_inputs, submod_decl_groups, submod_def_groups, []) else let (new_submod_groups, extra_submod_decls) = fixupSubmodGroups sos defs submod_infos decls_so_far submod_decl_groups in -- check that the assumption is OK if (null submod_def_groups) then (submod_inputs, new_submod_groups, [], extra_submod_decls) else internalError "AVerilog mkSubmodGroups: defs not null" fixupSubmodGroups :: [AStateOut] -> [VMItem] -> [(AId, VMItem, InstInfo)] -> [VId] -> [VMItem] -> ([VMItem], [VId]) fixupSubmodGroups sos defs submod_infos decls_so_far submod_groups = let -- ---------- -- tsort the instantiations so that insts whose outputs are inputs -- to later insts appear first -- we want to sort the groups, but join them with the inst id -- for uniqueness. -- also, keep some inst info for a later step (adding decls). inst_nodes = zipWith (\(i,_,(_,ps,_,_,_)) g -> (i,g,ps)) submod_infos submod_groups submod_igs = zip submod_infos submod_groups inst_edges = [ ((i2,g2,ports2), (i1,g1,ports1)) | -- for each inst with inst ports (clocks, resets) ((i1, _, (_, ports1, _, _, _)), g1) <- submod_igs, (_, VEVar in_port) <- ports1, -- for each inst with outputs (special or otherwise) ((i2, _, (_, ports2, special, _, outs)), g2) <- submod_igs, (_, out_port) <- special ++ outs, -- if the connection wire matches in_port == out_port ] -- G.topSort doesn't seem to catch errors (or not nicely), -- so we check for cycles ourselves sorted_inst_nodes :: [(AId, VMItem, [(VId,VExpr)])] sorted_inst_nodes = unsafePerformIO $ do inst_graph <- G.makeGraph (reverse inst_nodes) inst_edges cycles <- G.findCycles inst_graph let cycle_ids = map (map fst3) cycles if (null inst_edges) then return (inst_nodes) else if (not (null cycles)) then internalError ("genInstances: instantiations are cyclic: " ++ ppReadable cycle_ids) else return (G.topSort inst_graph) -- ---------- -- insert decls for non-method input port uses -- Some non-method input ports might involve wires that have not -- yet been declared. As a temporary solution, we will simply -- declare the signal prior to the instantiation. Thus, we fold -- along the instantiations (rather than map) to keep track of the -- wires we have declared so far. (final_submod_groups, extra_submod_decls) = apFst reverse $ foldl (addInstPortDecls defs decls_so_far) ([],[]) sorted_inst_nodes in (final_submod_groups, extra_submod_decls) -- create a group of Verilog statements for a submodule instantiation mkInstGroup :: Flags -> [AStateOut] -> [VMItem] -> [(Id,[String])] -> (AId, VMItem, InstInfo) -> (Maybe VMItem, Maybe VMItem) mkInstGroup flags sos defs comments_map (instname, vmi, info) = let (_, _, special, ins, outs) = info -- nub on the output values because there can be permissible overlap between -- "special" clock/reset outputs and method outputs wire_decls = map (mkInstInputDecl defs instname . snd) ins ++ map (mkInstOutputDecl sos instname VDWire . snd) (nub (special ++ outs)) user_comment = case (lookup instname comments_map) of Nothing -> [] Just cs -> concatMap lines cs inst_comment = ("submodule " ++ getIdString instname) : user_comment decl_comment = ["ports of submodule " ++ getIdString instname] -- if we keep decls and instantiation together: unified_group = Just $ VMComment inst_comment (VMGroup False [mergeCommonDecl wire_decls ++ [vmi]]) -- if we separate them (so that decls can all be first): decl_group = if (null wire_decls) then Nothing else Just $ VMComment decl_comment (VMGroup False [mergeCommonDecl wire_decls]) def_group = Just $ VMComment inst_comment vmi in if (verilogDeclareAllFirst flags) then (decl_group, def_group) else (unified_group, Nothing) -- create one group of Verilog wire declarations for all probes -- (each probe has one input, with obvious name, so no need to add a -- comment and empty line for each probe) -- (note that there is no instantiation for Probes, just the wire) mkProbeGroup :: [AStateOut] -> [VMItem] -> [(Id, [String])] -> [(AId, VMItem, InstInfo)] -> ([VId], [VMItem]) mkProbeGroup sos defs comments_map probe_infos = let getProbeInput (instname, vmi, (_, _, _, inps, _)) = map (mkInstInputDecl defs instname . snd) inps -- we drop the vmi and just declare the input port -- (there shouldn't be any outputs) probe_decls = concatMap getProbeInput probe_infos decl_ids = [ i | (VMDecl (VVDecl _ _ [VVar i])) <- probe_decls ] -- one comment for the entire group, not per probe -- (the inst of each probe is obvious from the wire name) -- but include a comment for any attached to a probe mkProbeComment instname = case (lookup instname comments_map) of Nothing -> Nothing Just cs -> Just (("Comments for probe " ++ quote (getIdString instname) ++ ":") : indentLines 2 (concatMap lines cs)) inst_comments = insertBlankLines $ mapMaybe (mkProbeComment . fst3) probe_infos hdr_comment = ["probes"] comment = if (null inst_comments) then hdr_comment else hdr_comment ++ [""] ++ inst_comments ++ [""] group = VMComment comment (VMGroup False [mergeCommonDecl probe_decls]) in if (null probe_decls) then ([], []) else (decl_ids, [group]) -- create a group of Verilog statements for an inlined register "instantiation" mkRegGroup :: [AStateOut] -> [VMItem] -> [(Id, [String])] -> RegInstInfo -> VMItem mkRegGroup sos defs comments_map (inst_vid, def_name, _, inps, (out, out_size)) = let reg_decl = VMDecl (VVDecl VDReg out_size [VVar out]) -- if the EN is 0, then D_IN might not be defined! -- so have a backup in case the signal is not defined inp_decls = map (mkInstInputDeclWithDefault defs) inps all_decls = (reg_decl : mergeCommonDecl inp_decls) comments = lookupWithDefault comments_map [] (vidToId inst_vid) in VMRegGroup inst_vid def_name comments (VMGroup False [all_decls]) mkRWireGroup :: [VMItem] -> [AId] -> ([VId], [VMItem]) mkRWireGroup defs rws = let rw_decls = mapMaybe (mkInstInputDeclMaybe defs . vId) rws decl_ids = [ i | (VMDecl (VVDecl _ _ [VVar i])) <- rw_decls ] comment = ["inlined wires"] group = VMComment comment (VMGroup False [(mergeCommonDecl rw_decls)]) in if (null rw_decls) then ([],[]) else (decl_ids, [group]) -- ----- -- when folded left along a tsort'ed list of instances, it inserts additional -- signal decls for signals used in non-method ports of the instantiations. -- the resulting list of VMItem groups is in reverse order (due to foldl). addInstPortDecls :: [VMItem] -> [VId] -> ([VMItem], [VId]) -> (AId, VMItem, [(VId,VExpr)]) -> ([VMItem], [VId]) addInstPortDecls defs decls (gs, new_decls) (i, inst_g, ps) = let -- variables used in the inst which have not already been declared -- (prior to submod instances or by an earlier submod inst) vs = [ i | (_, VEVar i) <- ps, i `notElem` (decls ++ new_decls) ] -- convert to decl v_decls = map (mkInstInputDecl defs i) vs -- create a group for it decl_g = VMGroup False [mergeCommonDecl v_decls] in if (null vs) then (inst_g:gs, new_decls) else (inst_g : decl_g : gs, vs ++ new_decls) -- ----- -- for a submodule output, lookup the size and create a Verilog decl -- of the appropriate size (with the given type) -- (the type is specified, because inlined Reg outputs are of type VDReg) mkInstOutputDecl :: [AStateOut] -> AId -> VDType -> VId -> VMItem mkInstOutputDecl sos inst_id decl_type out_port_id = let mkDecl i sz = VMDecl (VVDecl decl_type sz [VVar i]) out_port_aid = vidToId out_port_id in case (lookup out_port_aid sos) of Just asz -> mkDecl out_port_id (vSize asz) Nothing -> internalError ("mkInstOutputDecl: instance `" ++ getIdString inst_id ++ "' output port not found: " ++ getVIdString out_port_id) -- for a submodule input, lookup the size and create a Verilog decl -- of the appropriate size and type (some decls are defined with case-stmt) mkInstInputDecl :: [VMItem] -> Id -> VId -> VMItem mkInstInputDecl defs inst_id in_port_id = let mkDecl i sz t = VMDecl (VVDecl t sz [VVar i]) in case (getSizeAndTypeM in_port_id defs) of Just (sz, t) -> mkDecl in_port_id sz t Nothing -> internalError ("mkInstInputDecl: instance `" ++ getIdString inst_id ++ "' input port not found: " ++ getVIdString in_port_id ++ "\n defs = " ++ ppReadable defs) mkInstInputDeclWithDefault :: [VMItem] -> (VId, Maybe VRange) -> VMItem mkInstInputDeclWithDefault defs (in_port_id, in_port_size) = let mkDecl i sz t = VMDecl (VVDecl t sz [VVar i]) in case (getSizeAndTypeM in_port_id defs) of Just (sz, t) -> mkDecl in_port_id sz t Nothing -> mkDecl in_port_id in_port_size VDWire mkInstInputDeclMaybe :: [VMItem] -> VId -> Maybe VMItem mkInstInputDeclMaybe defs in_port_id = let mkDecl i sz t = VMDecl (VVDecl t sz [VVar i]) in case (getSizeAndTypeM in_port_id defs) of Just (sz, t) -> Just (mkDecl in_port_id sz t) Nothing -> Nothing -- ----- -- used to check if instantiation parameter exprs are ok -- (first argument is a list of ids which are known to be constant -- -- that is, incoming parameters) isConstVE :: [Id] -> VExpr -> Bool isConstVE _ (VEConst {}) = True isConstVE _ (VEWConst {}) = True isConstVE _ (VEUnknown {}) = True isConstVE _ (VEString {}) = True isConstVE _ (VEReal {}) = True isConstVE _ (VETriConst {}) = True isConstVE ps (VERepeat e1 e2) = isConstVE ps e1 && isConstVE ps e2 isConstVE ps (VEConcat es) = all (isConstVE ps) es -- these could be assumed to be False, but let's check anyway isConstVE ps (VEUnOp _ _ e) = isConstVE ps e isConstVE ps (VEOp _ e1 _ e2) = isConstVE ps e1 && isConstVE ps e2 isConstVE ps (VESelect e1 e2 e3) = isConstVE ps e1 && isConstVE ps e2 && isConstVE ps e3 isConstVE ps (VESelect1 e1 e2) = isConstVE ps e1 && isConstVE ps e2 isConstVE ps (VEIf e1 e2 e3) = isConstVE ps e1 && isConstVE ps e2 && isConstVE ps e3 -- consider references to Ids to be not constant, unless otherwise told isConstVE ps (VEVar vid) = (vidToId vid `elem` ps) isConstVE ps (VEIndex vid idx) = isConstVE ps (VEVar vid) && isConstVE ps idx isConstVE _ (VEFctCall {}) = False isConstVE _ (VEMacro {}) = True -- ============================== -- utilities for pass-through comments insertBlankLines :: [[String]] -> [String] insertBlankLines [] = [] insertBlankLines xss = foldr1 (\xs ys -> xs ++ [""] ++ ys) xss indentLines :: Int -> [String] -> [String] indentLines n xs = let spaces = replicate n ' ' in map (spaces ++) xs makeTopComments :: String -> [(Id, [String])] -> [String] makeTopComments item_type ics = let -- merge items that have the same comment mergeComment (i,cs) [] = [([i],cs)] mergeComment (i,cs) ((is,cs'):rest) = if (cs' == cs) then (((i:is),cs'):rest) else ((is,cs'):(mergeComment (i,cs) rest)) -- the merged comments list ics_merged = foldr mergeComment [] ics -- split and indent lines splitAndIndent cs = indentLines 2 (concatMap lines cs) -- convert them into top-level comments mkTopComment ([],cs) = internalError ("makeTopComments mkTopComment") mkTopComment ([i],cs) = let hdr = "Comments on the inlined " ++ item_type ++ " " ++ quote (getIdString i) ++ ":" in hdr : splitAndIndent cs mkTopComment (is,cs) = let quoted_items = map (quote . getIdString) is item_list = intercalate ", " quoted_items hdr = "Comments on the inlined " ++ item_type ++ "s " ++ item_list ++ ":" wrapped_hdr = pretty 78 78 (s2par hdr) in (lines wrapped_hdr) ++ splitAndIndent cs in insertBlankLines (map mkTopComment ics_merged) -- ============================== -- buildPrimModules -- extracts the instantiated "Prim" definitions from the verilog and builds verilog modules for these buildPrimModules :: Flags -> [VMItem] -> [VModule] buildPrimModules flags defs = map vMux (muxes `union` primuxes) ++ map vPriMux primuxes ++ map (vPriEnc rstGateF) (priencs `union` primuxes) where rstGateF = False -- rstGate flags -- "gate rule fire signals with reset" ?? muxes = nub (concatMap (getInsts "Mux_") defs) --- XXX hack primuxes = nub (concatMap (getInsts "PriMux_") defs) --- XXX hack priencs = nub (concatMap (getInsts "PriEnc_") defs) --- XXX hack getInsts :: String -> VMItem -> [Integer] getInsts s (VMInst (VId s' _ _) _ _ _) = match s s' where match [] s@(c:_) | isDigit c = [read s] match (c:cs) (d:ds) | c == d = match cs ds match _ _ = [] getInsts s (VMComment _ i) = getInsts s i getInsts s (VMGroup _ iss) = concatMap (concatMap (getInsts s)) iss getInsts _ _ = [] -- ============================== -- Creating test scan ports createScanStructures :: [PProp] -> ([VMItem],[VArg]) createScanStructures pps = (scanDefs, scanArgs) where (doScanInsert, nScans) = let find [] = (False, 0) find (PPscanInsert i : _) = (True, i) find (_ : ps) = find ps in find pps scanDefs = if doScanInsert then createScanDefs nScans else [] scanArgs = if doScanInsert then createScanArgs nScans else [] -- Create a [VArg] which has a input scan_en, scan_in[n-1:0], and output scan_out[n-1:0] createScanArgs :: Integer -> [VArg] createScanArgs n = [ VAInput (mkVId "scan_en") Nothing, VAInput (mkVId "scan_in") (Just (VEConst (n-1), VEConst 0)), VAOutput (mkVId "scan_out") (Just (VEConst (n-1), VEConst 0)) ] -- createScanDefs :: Integer -> [VMItem] createScanDefs n = [ VMDecl (VVDecl VDWire (Just (VEConst (n-1), VEConst 0)) [VVar (mkVId "scan_out")]) ] -- ============================== -- Bit-blasting ports createBitBlast :: [VArg] -> ([VArg],[VMItem]) createBitBlast args = let (ass, dss) = unzip (map blast args) blast a@(VAParameter {}) = ([a], []) blast a@(VAOutput vi@(VId s _ _) msz) = let su = map toUpper s iu = mkVId su -- w = VMDecl (VVDecl VDWire msz [v]) in case msz of Nothing -> if s == su then ([a], []) else ([VAOutput iu msz], [{-w,-} VMAssign (VLId iu) (VEVar vi)]) Just (VEConst n, VEConst 0) -> let vis = vids_sub_name "BBL1" su n mkOutput i = VAOutput i Nothing in (map mkOutput vis, [{-w,-} VMAssign (VLConcat (map VLId vis)) (VEVar vi)]) x -> internalError ("blast 1 " ++ ppReadable x) blast a@(VAInput vi@(VId s _ _) msz) = let su = map toUpper s iu = mkVId su w = VMDecl (VVDecl VDWire msz [VVar vi]) in case msz of Nothing -> if s == su then ([a], []) else ([VAInput iu msz], [w, VMAssign (VLId vi) (VEVar iu)]) Just (VEConst n, VEConst 0) -> let vis = vids_sub_name "BBL2" su n mkOutput i = VAOutput i Nothing in (map mkOutput vis, [w, VMAssign (VLId vi) (VEConcat (map VEVar vis))]) x -> internalError ("blast 2 " ++ ppReadable x) blast a = internalError ("blast 3 " ++ ppReadable a) in (concat ass, concat dss) vids_sub_name :: a -> String -> Integer -> [VId] vids_sub_name tag su n = [mkVId lname | i <- [0..n], let lname = su ++ "_" ++ itos i] unBlast :: [VArg] -> [VMItem] unBlast args = let unblast :: VArg -> [VMItem] unblast a@(VAParameter {}) = [] unblast a@(VAOutput vi@(VId s _ _) msz) = let su = map toUpper s iu = mkVId su in case msz of Nothing -> if s == su then [] else [VMAssign (VLId vi) (VEVar iu)] Just (VEConst n, VEConst 0) -> let vis = vids_sub_name "UBL1" su n in [VMAssign (VLId vi) (VEConcat (map VEVar vis))] x -> internalError ("unblast 1 " ++ ppReadable x) unblast a@(VAInput vi@(VId s _ _) msz) = let su = map toUpper s iu = mkVId su in case msz of Nothing -> if s == su then [] else [VMAssign (VLId iu) (VEVar vi)] Just (VEConst n, VEConst 0) -> let vis = vids_sub_name "UBL1" su n in [VMAssign (VLConcat (map VLId vis)) (VEVar vi)] x -> internalError ("unblast 2 " ++ ppReadable x) unblast a = internalError ("unblast 3 " ++ ppReadable a) in concatMap unblast args -- un blasted ubInst :: [VId] -> VId -> VMItem ubInst ids mod = VMInst { vi_module_name = mod, vi_inst_name = pref verilogInstancePrefix mod, vi_inst_params = Right [], vi_inst_ports = [(i, Just (VEVar i)) | i <- ids] } -- ------------------------------ -- extract out the ids of the port (IO arguments) getArgIds :: [VArg] -> [VId] getArgIds args = concatMap getio args where getio (VAInput i _) = [i] getio (VAOutput i _) = [i] getio (VAParameter {}) = [] getio (VAInout i mi _) = internalError ("getArgIds getio") -- ============================== {- -- Move the declarations to the front of the list declsFirst :: [VMItem] -> [VMItem] declsFirst vss = (mergeCommonDecl ds) ++ (sort ss) where (ds,ss) = partition isDecl vss -} -- Merge common declarations into one to avoid pages of wire declarations mergeCommonDecl :: [VMItem] -> [VMItem] mergeCommonDecl ins = -- group decls with common types and merge them map mergeOneDecl groupss where sins = sort ins -- is this sort needed? groupss = groupBy commonTypes sins -- two decls are the same if they are decls without assignment, -- and the type (reg/wire) matches and the range (bit-size) matches commonTypes :: VMItem -> VMItem -> Bool commonTypes (VMDecl vvd1) (VMDecl vvd2) = commonDeclTypes vvd1 vvd2 commonTypes _ _ = False mergeOneDecl :: [VMItem] -> VMItem mergeOneDecl [] = internalError ("mergeCommonDecl: unexpected empty list!") mergeOneDecl [a] = a mergeOneDecl (a@(VMDecl (VVDecl vt vr vars)):as) = let -- extract the vars from the other decls extrVars :: VMItem -> [VVar] extrVars (VMDecl (VVDecl _ _ vars)) = vars extrVars _ = [] -- all the vars, sorted merged_vars = sort (vars ++ concatMap extrVars as) in VMDecl (VVDecl vt vr merged_vars) mergeOneDecl _ = internalError ("mergeCommonDecl::mergeOneDecl") -- ============================== getSizeAndTypeM :: VId -> [VMItem] -> Maybe (Maybe VRange, VDType) getSizeAndTypeM i [] = Nothing getSizeAndTypeM i (VMDecl (VVDWire sz (VVar i') _) : _) | i == i' = Just (sz, VDWire) getSizeAndTypeM i (VMDecl (VVDecl t sz vs) : _) | (t == VDWire || t == VDReg) && i `elem` [ i | VVar i <- vs ] = Just (sz, t) getSizeAndTypeM i (_ : ms) = getSizeAndTypeM i ms -- assumes that decls haven't been merged yet isDeclFromList :: [VId] -> VMItem -> Bool isDeclFromList is (VMDecl (VVDWire _ vv _)) = elem (vvName vv) is isDeclFromList is (VMDecl (VVDecl _ _ [vv])) = elem (vvName vv) is isDeclFromList _ _ = False -- lookup defs, maintaining their order, and returning the remaining defs findADefs :: [AId] -> M.Map AId ADef -> ([ADef], M.Map AId ADef) findADefs ds defmap = let fn i (ds_accum, defmap_accum) = case (M.lookup i defmap_accum) of Nothing -> (ds_accum, defmap_accum) Just d -> (d:ds_accum, M.delete i defmap_accum) in foldr fn ([], defmap) ds isADefFromList :: [AId] -> ADef -> Bool isADefFromList is (ADef i _ _ _) = i `elem` is -- ============================== -- If the wire connected to the port of an instantiated submodule is unused, -- then remove the wire and leave the port unconnected (it will appear as -- empty parentheses in the Verilog). -- -- Arguments: -- ff_blocks = converted foreign function calls -- defs = module statements which are Not INSTantiationS (defs) -- insts = instantiations -- -- Returns: -- * ids of unused signals (for removing from the defs) -- * the instantiations with the unused ports filtered -- dropUnusedPortWires :: [VMItem] -> [VMItem] -> [VExpr] -> [(AId, VMItem, InstInfo)] -> ([VId], [(AId, VMItem, InstInfo)]) dropUnusedPortWires ff_blocks defs reg_uses inst_infos = let -- the vars used in the defs and the foreign function calls and -- as clk/rst to registers used = S.fromList $ vuses (defs ++ ff_blocks) ++ vuses reg_uses -- the vars defined with values def_ids = S.fromList $ [ v | VMDecl (VVDecl _ _ vs) <- defs, VVar v <- vs ] ++ [ v | VMDecl (VVDWire _ (VVar v) _) <- defs ] -- just the instantiations insts = [ is | (_, is, _) <- inst_infos ] -- the ios of the modules -- XXX this could come from the aspkg field, but we recompute: submod_io = S.fromList $ [ v | (_, _, (_, _, spec, ins, outs)) <- inst_infos, (_,v) <- spec ++ ins ++ outs ] -- unused wires are those which: unused = [ i | -- appear only once in the instantiations -- (that is, they do not connect between two instances) (1, i) <- count (vuses insts), -- are submodule inputs or outputs i `S.member` submod_io, -- (for inputs) are not defined with a value i `S.notMember` def_ids, -- (for outputs) are not used in any defs or ffunc calls i `S.notMember` used ] inst_infos_filtered = if (null unused) then inst_infos else map (dropIds unused) inst_infos in (unused, inst_infos_filtered) notProbe :: (AId, VMItem, InstInfo) -> Bool notProbe (_, VMInst (VId string _ _) _ _ _, _) = (string /= "Probe") notProbe item = True count :: (Ord a) => [a] -> [(Int, a)] count = sort . map (\ xs@(x:_) -> (length xs, x)) . group . sort dropIds :: [VId] -> (AId, VMItem, InstInfo) -> (AId, VMItem, InstInfo) dropIds is (i, vmi, info) = let -- function to drop a port connection from VMInst if it's in is dropInstPort (Just (VEVar i)) | i `elem` is = Nothing dropInstPort e = e -- function to drop ports from the VMInst dropFromVMInst (VMInst m n es ves) = VMInst m n es (mapSnd dropInstPort ves) dropFromVMInst m = internalError ("AVerilog dropIds: " ++ ppReadable m) -- function to drop ports from the InstInfo dropFromInstInfo (ps, port_ps, special, ins, outs) = (ps, port_ps, -- we know that these will not be dropped dropFromIdList special, dropFromIdList ins, dropFromIdList outs) -- drop from a list of pairs dropFromIdList = filter (\ (a,b) -> b `notElem` is) in (i, dropFromVMInst vmi, dropFromInstInfo info) -- ------------------------------ class VUse a where vuses :: a -> [VId] instance VUse VId where vuses x = [x] instance {-# OVERLAPPING #-} VUse String where vuses x = [] instance {-# OVERLAPPABLE #-} (VUse a) => VUse [a] where vuses xs = concatMap vuses xs instance (VUse a) => VUse (Maybe a) where vuses Nothing = [] vuses (Just x) = vuses x instance (VUse a, VUse b) => VUse (a,b) where vuses (x,y) = vuses x ++ vuses y instance (VUse a, VUse b) => VUse (Either a b) where vuses (Left es) = vuses es vuses (Right as) = vuses as instance VUse VMItem where vuses (VMDecl d) = vuses d vuses (VMStmt {vi_body = body}) = vuses body vuses (VMAssign l e) = vuses l ++ vuses e vuses (VMInst _ _ ps as) = vuses ps ++ vuses as vuses (VMGroup _ ll) = concatMap vuses (concat ll) vuses _ = [] instance VUse VStmt where vuses (VAt e s) = vuses e ++ vuses s vuses (Valways s) = vuses s vuses (Vinitial s) = vuses s vuses (VSeq ss) = vuses ss vuses s@(Vcasex {}) = vuses (vs_case_expr s) ++ vuses (vs_case_arms s) vuses s@(Vcase {}) = vuses (vs_case_expr s) ++ vuses (vs_case_arms s) vuses (VAssign l e) = vuses l ++ vuses e vuses (VAssignA l e) = vuses l ++ vuses e vuses (Vif e s) = vuses e ++ vuses s vuses (Vifelse e s s') = vuses e ++ vuses s ++ vuses s' vuses (Vdumpvars _ _) = [] vuses (VTask _ es) = vuses es vuses (VAssert e es) = vuses e ++ vuses es vuses (VZeroDelay) = [] instance VUse VLValue where vuses (VLId i) = [i] vuses (VLConcat ls) = vuses ls vuses (VLSub l e) = vuses l ++ vuses e instance VUse VCaseArm where vuses (VCaseArm es s) = vuses es ++ vuses s vuses (VDefault s) = vuses s instance VUse VVDecl where vuses (VVDecl _ _ _) = [] vuses (VVDWire _ _ e) = vuses e instance VUse VEventExpr where vuses (VEEOr e e') = vuses e ++ vuses e' vuses (VEEposedge e) = vuses e vuses (VEEnegedge e) = vuses e vuses (VEE e) = vuses e vuses (VEEMacro s e) = vuses e instance VUse VExpr where vuses (VEConst _) = [] vuses (VEWConst _ _ _ _) = [] vuses (VEUnknown _ _) = [] vuses (VEReal _) = [] vuses (VEString _) = [] vuses (VETriConst _) = [] vuses (VEUnOp _ _ e) = vuses e vuses (VEOp _ e _ e') = vuses e ++ vuses e' vuses (VEVar i) = [i] vuses (VEConcat es) = vuses es vuses (VEIndex i e) = i : vuses e vuses (VESelect e1 e2 e3) = vuses e1 ++ vuses e2 ++ vuses e3 vuses (VESelect1 e1 e2) = vuses e1 ++ vuses e2 vuses (VERepeat e e') = vuses e ++ vuses e' vuses (VEIf e1 e2 e3) = vuses e1 ++ vuses e2 ++ vuses e3 vuses (VEFctCall _ es) = vuses es vuses (VEMacro {}) = [] -- ==============================