def insertDeviceModules(moduleList, annotateParentsOnly=False): elabAreaConstraints = AreaConstraints(moduleList) # this was constructed upon the original call to load area. elabAreaConstraints.loadAreaConstraintsElaborated() for userAreaGroup in elabAreaConstraints.constraints.values(): if('SYNTH_BOUNDARY' in userAreaGroup.attributes): # Modify parent to know about this child. parentModule = moduleList.modules[userAreaGroup.parentName] # pick up deps from parent. moduleDeps ={} moduleName = userAreaGroup.attributes['MODULE_NAME'] # grab the parent module verilog and convert it. This # is really ugly, and demonstrates whe first class # language constructs are so nice. Eventually, we # should push these new synth boundary objects into # flow earlier. moduleVerilog = None for dep in map(functools.partial(bsv_tool.modify_path_ba, moduleList), model.convertDependencies(moduleList.getAllDependenciesWithPaths('GEN_VERILOGS'))): if (re.search(moduleName, dep)): moduleVerilog = dep if(moduleVerilog is None): print "ERROR: failed to find verilog for area group: " + userAreaGroup.name exit(1) moduleVerilogBlackBox = moduleVerilog.replace('.v', '_stub.v') moduleDeps['GEN_VERILOG_STUB'] = [moduleVerilogBlackBox] # We need to ensure that the second pass glue logic # modules don't look at the black box stubs. The modules # are in the current synth boundaries list, but not in the LI graph. parentList = [parentModule, moduleList.topModule] + [module for module in moduleList.synthBoundaries() if not module.name in elabAreaConstraints.constraints] for parent in parentList: print "BLACK_BOX Annotating: " + parent.name if(parent.getAttribute('BLACK_BOX') is None): parent.putAttribute('BLACK_BOX', {moduleVerilog: moduleVerilogBlackBox}) else: blackBoxDict = parent.getAttribute('BLACK_BOX') blackBoxDict[moduleVerilog] = moduleVerilogBlackBox if(not annotateParentsOnly): moduleList.env.Command([moduleVerilogBlackBox], [moduleVerilog], 'leap-gen-black-box -nohash $SOURCE > $TARGET') m = Module(userAreaGroup.name, [moduleName],\ parentModule.buildPath, parentModule.name,\ [], parentModule.name, [], moduleDeps) m.putAttribute("WRAPPER_NAME", moduleName) m.putAttribute("AREA_GROUP", 1) moduleList.insertModule(m)
def setupTreeBuild(self, moduleList, topo): useBVI = self.parent.USE_BVI env = moduleList.env root_directory = model.rootDir ## ## Load intra-Bluespec dependence already computed. This ## information will ultimately drive the building of Bluespec ## modules. Build tree has a few dependencies which must be ## captured. ## ## If we aren't building the build tree, don't bother with its dependencies env.ParseDepends(get_build_path(moduleList, moduleList.topModule) + '/.depends-build-tree', must_exist = not moduleList.env.GetOption('clean')) tree_base_path = env.Dir(get_build_path(moduleList, moduleList.topModule)) tree_file_synth = tree_base_path.File('build_tree_synth.bsv') tree_file_synth_bo_path = tree_base_path.File(self.parent.TMP_BSC_DIR + '/build_tree_synth.bo') tree_file_wrapper = tree_base_path.File('build_tree_Wrapper.bsv') tree_file_wrapper_bo_path = tree_base_path.File(self.parent.TMP_BSC_DIR + '/build_tree_Wrapper.bo') # Area constraints area_constraints = None try: if (moduleList.getAWBParam('area_group_tool', 'AREA_GROUPS_ENABLE')): area_constraints = area_group_tool.AreaConstraints(moduleList) except: # The area constraints code is not present. pass boundary_logs = [] for module in topo: # Remove any platform modules.. These are special in that # they can have wired interfaces. if (not module.platformModule): for log in module.moduleDependency['BSV_LOG']: boundary_logs += [root_directory.File(log)] ## ## Back to SCons configuration (first) pass... ## top_module_path = get_build_path(moduleList, moduleList.topModule) # Inform object code build of the LI Graph retrieved from the # first pass. Probe firstPassGraph for relevant object codes # (BA/NGC/BSV_SYNTH/BSV_SYNTH_BSH) accessed: # module.objectCache['NGC'] (these already have absolute # paths) I feel like the GEN_BAS/GEN_VERILOGS of the first # pass may be missing. We insert these modules as objects in # the ModuleList. def makeAWBLink(doLink, source, buildPath, uniquifier=''): base_file = os.path.basename(str(source)) link_dir = buildPath + '/.li' link_path = link_dir + '/' + uniquifier + base_file if (doLink): if (os.path.lexists(link_path)): os.remove(link_path) rel = os.path.relpath(str(source), link_dir) print 'Linking: ' + link_path + ' -> ' + rel os.symlink(rel, link_path) return link_path limLinkUserSources = [] limLinkUserTargets = [] limLinkPlatformSources = [] limLinkPlatformTargets = [] importStubs = [] if (not self.getFirstPassLIGraph is None): # Now that we have demanded bluespec builds (for # dependencies), we should now should downgrade synthesis boundaries for the backend. oldStubs = [] for module in topo: if(not module.platformModule): if((not module.name in self.getFirstPassLIGraph.modules) or (self.getFirstPassLIGraph.modules[module.name].getAttribute('RESYNTHESIZE') is None)): module.liIgnore = True # this may not be needed. else: oldStubs += module.moduleDependency['GEN_VERILOG_STUB'] # let's pick up the platform dependencies, since they are also special. env.ParseDepends(get_build_path(moduleList, moduleList.topModule) + '/.depends-platform', must_exist = not moduleList.env.GetOption('clean')) # Due to the way that string files are # generated, they are difficult to rename in # the front-end compilation. This leads to # collisions amoung similarly-typed LI # Modules. We fix it by uniquifying the links. def getModuleName(module): return module.name def getEmpty(module): return '' linkthroughMap = {'BA': getEmpty, 'GEN_BAS': getEmpty, 'GEN_VERILOGS': getEmpty, 'GEN_VERILOG_STUB': getEmpty, 'STR': getModuleName} buildPath = get_build_path(moduleList, moduleList.topModule) for module in self.getFirstPassLIGraph.modules.values(): # do not link through those modules marked for resynthesis. if(not module.getAttribute('RESYNTHESIZE') is None): continue moduleDeps = {} for objType in linkthroughMap: if(objType in module.objectCache): localNames = map(lambda fileName: makeAWBLink(False, fileName.from_bld(), buildPath, uniquifier=linkthroughMap[objType](module)), module.objectCache[objType]) # The previous passes GEN_VERILOGS are not # really generated here, so we can't call them # as such. Tuck them in to 'VERILOG' if(objType == 'GEN_VERILOG_STUB'): oldStubs += localNames moduleDeps[objType] = localNames if (module.getAttribute('PLATFORM_MODULE') is None): limLinkUserTargets += localNames limLinkUserSources += module.objectCache[objType] else: limLinkPlatformTargets += localNames limLinkPlatformSources += module.objectCache[objType] m = Module(module.name, ["mk_" + module.name + "_Wrapper"],\ moduleList.topModule.buildPath, moduleList.topModule.name,\ [], moduleList.topModule.name, [], moduleDeps) moduleList.insertModule(m) else: # The top module/build pipeline only depend on non-platformModules oldStubs = [module.moduleDependency['GEN_VERILOG_STUB'] for module in moduleList.synthBoundaries() if not module.platformModule] ## Enumerate the dependencies created by the build tree. buildTreeDeps = {} ## We have now generated a completely new module. Let's throw it ## into the list. Although we are building it seperately, this ## module is an extension to the build tree. expected_wrapper_count = len(boundary_logs) - 2 importBOs = [] if (not self.getFirstPassLIGraph is None): # we now have platform modules in here. expected_wrapper_count = len(self.getFirstPassLIGraph.modules) - 2 # If we have an LI graph, we need to construct and compile # LI import wrappers for the modules we received from the # first pass. Do that here. include all the dependencies # in the graph in the wrapper. liGraph = LIGraph([]) firstPassGraph = self.getFirstPassLIGraph # We should ignore the 'PLATFORM_MODULE' liGraph.mergeModules([ module for module in bsv_tool.getUserModules(firstPassGraph) if module.getAttribute('RESYNTHESIZE') is None]) for module in sorted(liGraph.graph.nodes(), key=lambda module: module.name): # pull in the dependecies generate by the dependency pass. env.ParseDepends(str(tree_base_path) + '/.depends-' + module.name, must_exist = not moduleList.env.GetOption('clean')) wrapper_path = tree_base_path.File(module.name + '_Wrapper.bsv') wrapper_bo_path = tree_base_path.File(self.parent.TMP_BSC_DIR + '/' + module.name + '_Wrapper.bo') # include commands to build the wrapper .bo/.ba # Here, we won't be using the generated .v (it's garbage), so we intentionally get rid of it. importVDir = env.Dir('.lim_import_verilog') if not os.path.isdir(str(importVDir)): os.mkdir(str(importVDir)) wrapper_command = self.parent.compile_bo_bsc_base([wrapper_bo_path], get_build_path(moduleList, moduleList.topModule), vdir=importVDir) + ' $SOURCES' wrapper_bo = env.Command([wrapper_bo_path], [wrapper_path], wrapper_command) # create BO. importBOs += [wrapper_bo] verilog_deps = [ "__TREE_MODULE__" + str(id) for id in range(expected_wrapper_count)] if(self.parent.BUILD_LOGS_ONLY == 0): buildTreeDeps['GEN_VERILOGS'] = ["mk_" + vlog + '_Wrapper' + ".v" for vlog in verilog_deps] else: buildTreeDeps['GEN_VERILOGS'] = [] buildTreeDeps['GEN_BAS'] = [ "mk_" + vlog + '_Wrapper' + ".ba" for vlog in verilog_deps] buildTreeDeps['BA'] = [] buildTreeDeps['STR'] = [] buildTreeDeps['VERILOG'] = [top_module_path + '/' + self.parent.TMP_BSC_DIR + '/mk_build_tree_Wrapper.v'] buildTreeDeps['GIVEN_BSVS'] = [] buildTreeDeps['VERILOG_STUB'] = model.convertDependencies(oldStubs) tree_module = Module( 'build_tree', ["mkBuildTree"], moduleList.topModule.buildPath,\ moduleList.topModule.name,\ [], moduleList.topModule.name, [], buildTreeDeps, platformModule=True) tree_module.putAttribute('LI_GRAPH_IGNORE', True) moduleList.insertModule(tree_module) wrapper_gen_tool.generateAWBCompileWrapper(moduleList, tree_module) ## This produces the treeNode BSV. It must wait for the ## compilation of the log files, which it will read to form the ## LIM graph ## ## We do two operations during this phase. First, we dump a ## representation of the user program. This representation is ## used by the LIM compiler to create heterogeneous ## executables. We then do a local modification to the build ## tree to reduce Bluespec compilation time. # If I got an LI graph, I don't care about the boundary logs. # In this case, everything comes from the first pass graph. # Usually, we only need logs and BOs to build the build tree. # However, during the second pass build we also need to fill # in information about area group paths (changed by tree build) tree_build_deps = boundary_logs + importBOs tree_build_results = [tree_file_wrapper, tree_file_synth] if (self.getFirstPassLIGraph and area_constraints): tree_build_deps += [area_constraints.areaConstraintsFilePlaced()] tree_build_results += [area_constraints.areaConstraintsFile()] ## ## The cutTreeBuild builder function needs some of the local state ## in the current function. Build a dictionary with the required ## state and partial instance of cutTreeBuild with the state applied. ## cut_tree_state = dict() cut_tree_state['area_constraints'] = area_constraints cut_tree_state['boundary_logs'] = boundary_logs cut_tree_state['moduleList'] = moduleList cut_tree_state['tree_file_synth'] = tree_file_synth cut_tree_state['tree_file_wrapper'] = tree_file_wrapper cut_tree_build = functools.partial(self.cutTreeBuild, cut_tree_state) cut_tree_build.__name__ = 'cutTreeBuild' tree_components = env.Command(tree_build_results, tree_build_deps, cut_tree_build) ## Compiling the build tree wrapper produces several .ba ## files, some that are useful, the TREE_MODULES, and some ## which are not, the _Wrapper.ba. As a result, we dump the ## tree build output to a different directory, so as not to ## pollute the existing build. Here, we link to the relevant ## files in that directory. def linkLIMObjClosure(liModules, buildPath): def linkLIMObj(target, source, env): if (not self.getFirstPassLIGraph is None): # The LIM build has passed us some source and we need # to patch it through. for module in liModules: for objType in linkthroughMap: if(objType in module.objectCache): map(lambda fileName: makeAWBLink(True, fileName.from_bld(), buildPath, uniquifier=linkthroughMap[objType](module)), module.objectCache[objType]) return linkLIMObj ## The top level build depends on the compilation of the tree components ## into bo/ba/v files. # the GEN_BAS attached to the build tree need to be massaged # to reflect their actual path. Perhaps we should be using # some kind of object that makes these sorts of conversions # simpler. producedBAs = map(lambda path: bsv_tool.modify_path_ba(moduleList, path), moduleList.getModuleDependenciesWithPaths(tree_module, 'GEN_BAS')) producedVs = map(lambda path: bsv_tool.modify_path_ba(moduleList, path), moduleList.getModuleDependenciesWithPaths(tree_module, 'GEN_VERILOGS')) + \ buildTreeDeps['VERILOG'] tree_command = self.parent.compile_bo_bsc_base([tree_file_wrapper_bo_path], get_build_path(moduleList, moduleList.topModule)) + ' ' + tree_file_wrapper.path tree_file_wrapper_bo = env.Command([tree_file_wrapper_bo_path] + producedBAs + producedVs, tree_components, tree_command) # If we got a first pass LI graph, we need to link its object codes. if (not self.getFirstPassLIGraph is None): srcs = [s.from_bld() for s in limLinkUserSources] link_lim_user_objs = env.Command(limLinkUserTargets, srcs, linkLIMObjClosure([ module for module in bsv_tool.getUserModules(firstPassGraph) if module.getAttribute('RESYNTHESIZE') is None], tree_base_path.path)) env.Depends(link_lim_user_objs, tree_file_wrapper_bo) # the tree_file_wrapper build needs all the wrapper bo from the user program, # but not the top level build. top_bo = moduleList.topModule.moduleDependency['BSV_BO'] all_bo = moduleList.getAllDependencies('BO') env.Depends(tree_file_wrapper_bo, all_bo) tree_synth_command = self.parent.compile_bo_bsc_base([tree_file_synth_bo_path], get_build_path(moduleList, moduleList.topModule)) + ' ' + tree_file_synth.path tree_file_synth_bo = env.Command([tree_file_synth_bo_path], [tree_file_synth, tree_file_wrapper_bo], tree_synth_command) env.Depends(top_bo, tree_file_synth_bo) env.Depends(moduleList.topModule.moduleDependency['BSV_LOG'], tree_file_synth_bo) #Handle the platform_synth build, which is special cased. platform_synth = get_build_path(moduleList, moduleList.topModule) + "/" + moduleList.localPlatformName + "_platform_synth.bsv" platform_synth_bo_path = get_build_path(moduleList, moduleList.topModule) + "/" + self.parent.TMP_BSC_DIR +"/" + moduleList.localPlatformName + "_platform_synth" # if we are in the lim linking phase, we need to change the # vdir directory to hide the spurious verilog generated by # bluespec. importVDir = None if(not self.getFirstPassLIGraph is None): importVDir = env.Dir('.lim_import_verilog') if not os.path.isdir(str(importVDir)): os.mkdir(str(importVDir)) platform_synth_command = self.parent.compile_bo_bsc_base([platform_synth_bo_path + '.bo'], get_build_path(moduleList, moduleList.topModule), vdir=importVDir) + ' $SOURCE' platform_wrapper_bo = get_build_path(moduleList, moduleList.topModule) + "/" + self.parent.TMP_BSC_DIR + "/" +moduleList.localPlatformName + '_platform_Wrapper.bo' platform_synth_deps = [platform_synth] #if we have a module graph, we don't require the compilation of the platform_wrapper_bo. if (self.getFirstPassLIGraph is None): platform_synth_deps.append(platform_wrapper_bo) platform_synth_bo = env.Command([platform_synth_bo_path + '.bo'], platform_synth_deps, platform_synth_command) # this produces a ba also? env.Depends(moduleList.topModule.moduleDependency['BSV_LOG'], platform_synth_bo) # Platform synth does the same object-bypass dance as tree_module. if(not self.getFirstPassLIGraph is None): srcs = [s.from_bld() for s in limLinkPlatformSources] link_lim_platform_objs = env.Command(limLinkPlatformTargets, srcs, linkLIMObjClosure([ module for module in bsv_tool.getPlatformModules(firstPassGraph) if module.getAttribute('RESYNTHESIZE') is None], tree_base_path.path)) env.Depends(link_lim_platform_objs, platform_synth_bo) # need to generate a stub file for the build tree module. # note that in some cases, there will be only one module in # the graph, usually in a multifpga build. In this case, # the build_tree module will be vestigal, but since we can't # predict this statically we'll have to build it anyway. tree_module.moduleDependency['GEN_VERILOG_STUB'] = [self.parent.stubGenCommand(top_module_path, "build_tree", top_module_path + '/' + self.parent.TMP_BSC_DIR + "/mk_build_tree_Wrapper.v")] # top level only depends on platform modules moduleList.topModule.moduleDependency['VERILOG_STUB'] = model.convertDependencies([module.moduleDependency['GEN_VERILOG_STUB'] for module in moduleList.synthBoundaries() if module.platformModule]) if(not self.getFirstPassLIGraph is None): #Second pass build picks up stub files from the first pass build. moduleList.topModule.moduleDependency['VERILOG_STUB'] += model.convertDependencies(oldStubs)