import * as vscode from 'vscode'; import * as os from 'os'; import { AbsPath, IProgress, opeParam } from '../global'; import { HdlLangID } from '../global/enum'; import { MainOutput, ReportType } from '../global/outputChannel'; import * as common from './common'; import { hdlFile, hdlPath } from '../hdlFs'; import { defaultMacro, defaultRange, doPrimitivesJudgeApi, HdlSymbol } from './util'; import { DoFastFileType } from '../global/lsp'; import { t } from '../i18n'; class HdlParam { private readonly topModules : Set = new Set(); private readonly srcTopModules : Set = new Set(); private readonly simTopModules : Set = new Set(); private readonly pathToHdlFiles : Map = new Map(); public readonly modules : Set = new Set(); private readonly unhandleInstances : Set = new Set(); public hasHdlFile(path: AbsPath): boolean { const moduleFile = this.getHdlFile(path); if (!moduleFile) { return false; } return true; } public getHdlFile(path: AbsPath): HdlFile | undefined { return this.pathToHdlFiles.get(path); } public getAllHdlFiles(): HdlFile[] { const hdlFiles = []; for (const [_, hdlFile] of this.pathToHdlFiles) { hdlFiles.push(hdlFile); } return hdlFiles; } /** * used only in initialization stage * @param hdlFile */ public setHdlFile(hdlFile: HdlFile) { const path = hdlFile.path; this.pathToHdlFiles.set(path, hdlFile); } /** * add a file by path and create context * @param path absolute path of the file to be added */ public async addHdlPath(path: AbsPath) { path = hdlPath.toSlash(path); await this.doHdlFast(path, 'common'); const hdlFile = this.getHdlFile(path); if (!hdlFile) { MainOutput.report('error happen when we attempt to add file by path: ' + path, { level: ReportType.Error }); } else { hdlFile.makeInstance(); // when a new file is added, retry the solution of dependency for (const hdlModule of hdlFile.getAllHdlModules()) { hdlModule.solveUnhandleInstance(); } } } public hasHdlModule(path: AbsPath | undefined, name: string): boolean { if (!path) { return false; } const hdlFile = this.getHdlFile(path); if (!hdlFile) { return false; } const hdlModule = hdlFile.getHdlModule(name); if (!hdlModule) { return false; } return true; } public getHdlModule(path: AbsPath, name: string): HdlModule | undefined { const hdlFile = this.getHdlFile(path); if (!hdlFile) { return undefined; } return hdlFile.getHdlModule(name); } public getAllHdlModules(): HdlModule[] { const hdlModules: HdlModule[] = []; this.modules.forEach(m => hdlModules.push(m)); return hdlModules; } public addHdlModule(hdlModule: HdlModule) { this.modules.add(hdlModule); } public addTopModule(hdlModule: HdlModule) { this.topModules.add(hdlModule); } public deleteTopModule(hdlModule: HdlModule) { this.topModules.delete(hdlModule); } public getAllTopModules(global :boolean = false): HdlModule[] { const topModules: HdlModule[] = []; if (global) { this.topModules.forEach(m => topModules.push(m)); } else { this.srcTopModules.forEach(m => topModules.push(m)); this.simTopModules.forEach(m => topModules.push(m)); } return topModules; } public isTopModule(path: AbsPath, name: string, global = false): boolean { const module = this.getHdlModule(path, name); if (!module) { return false; } if (global) { return this.topModules.has(module); } else { const sourceTopModule = this.selectTopModuleSourceByFileType(module); return sourceTopModule.has(module); } } public selectTopModuleSourceByFileType(hdlModule: HdlModule): Set { switch (hdlModule.file.projectType) { case common.HdlFileProjectType.Src: return this.srcTopModules; case common.HdlFileProjectType.Sim: return this.simTopModules; case common.HdlFileProjectType.LocalLib: return this.srcTopModules; case common.HdlFileProjectType.RemoteLib: return this.srcTopModules; default: return this.srcTopModules; } } /** * add module to top modules of a certain source (sim or src) * @param hdlModule */ public addTopModuleToSource(hdlModule: HdlModule) { const topModuleSource = this.selectTopModuleSourceByFileType(hdlModule); topModuleSource.add(hdlModule); } /** * @description 根据输入的 module 把它从所属的 src 或者 sim 的 topmodules 中去除 * @param hdlModule */ public deleteTopModuleToSource(hdlModule: HdlModule) { const topModuleSource = this.selectTopModuleSourceByFileType(hdlModule); topModuleSource.delete(hdlModule); } public getAllDependences(path: AbsPath, name: string): common.HdlDependence | undefined { const module = this.getHdlModule(path, name); if (!module) { return undefined; } if (this.isTopModule(path, name)) { console.log(module); } const dependencies : common.HdlDependence = { current: [], include: [], others: [] }; for (const inst of module.getAllInstances()) { if (!inst.module) { continue; } const status = inst.instModPathStatus; if (status === common.InstModPathStatus.Current && inst.instModPath) { dependencies.current.push(inst.instModPath); } else if (status === common.InstModPathStatus.Include && inst.instModPath) { dependencies.include.push(inst.instModPath); } else if (status === common.InstModPathStatus.Others && inst.instModPath) { dependencies.others.push(inst.instModPath); } const instDependencies = this.getAllDependences(inst.module.path, inst.module.name); if (instDependencies) { dependencies.current.push(...instDependencies.current); dependencies.include.push(...instDependencies.include); dependencies.others.push(...instDependencies.others); } } return dependencies; } public getUnhandleInstanceNumber(): number { return this.unhandleInstances.size; } /** * @description 获取所有 scope 内存在 unhandle instance 的 module */ public getAllUnhandleInstanceBelongedModule(): HdlModule[] { const modules = new Set(); for (const instance of this.unhandleInstances) { modules.add(instance.parentMod); } return [...modules]; } /** * @description 输入 moduleName,找到这个 module 的所有置于 unhandleInstances 中的例化,并去 solve 它们 * 因为一个 module 对应的 unsolved instance 可能不止一个。比如现在项目中,有两个地方存在 AGC 模块的例化, * 但是当前项目并没有引入 AGC 的 module 申明,那么当引入 AGC 后,外部就应该调用 getUnhandleInstancesByModuleName("AGC") * 并返回两个 AGC 的例化模块,然后去 solve 它们 * @param moduleName * @returns */ public getUnhandleInstancesByModuleName(moduleName: string): HdlInstance[] { const unsolvedInstances = []; for (const inst of this.unhandleInstances) { if (inst.type === moduleName) { unsolvedInstances.push(inst); } } return unsolvedInstances; } public addUnhandleInstance(inst: HdlInstance) { this.unhandleInstances.add(inst); } public deleteUnhandleInstance(inst: HdlInstance) { this.unhandleInstances.delete(inst); } private async doHdlFast(path: AbsPath, fileType: DoFastFileType) { try { const fast = await HdlSymbol.fast(path, fileType); if (fast) { const languageId = this.getRealLanguageId(path, fast.fileType); const fileProjectType = this.getHdlFileProjectType(path, fast.fileType); new HdlFile(path, languageId, fast.macro, fast.content, fileProjectType, fast.fileType); } } catch (error) { MainOutput.report('Error happen when parse ' + path, { level: ReportType.Error }); MainOutput.report('Reason: ' + error, { level: ReportType.Error }); } } private getRealLanguageId(path: string, fileType: DoFastFileType): HdlLangID { if (fileType === 'ip' && opeParam.prjInfo.toolChain === 'xilinx') { return HdlLangID.Vhdl; } return hdlFile.getLanguageId(path); } public getHdlFileProjectType(path: string, fileType: DoFastFileType): common.HdlFileProjectType { switch (fileType) { case 'common': // 根据前缀来判断对应的类型 path = hdlPath.toSlash(path); const prjInfo = opeParam.prjInfo; if (path.startsWith(prjInfo.hardwareSrcPath)) { return common.HdlFileProjectType.Src; } else if (path.startsWith(prjInfo.hardwareSimPath)) { return common.HdlFileProjectType.Sim; } else if (path.startsWith(prjInfo.ipPath)) { return common.HdlFileProjectType.IP; } else if (path.startsWith(prjInfo.localLibPath)) { return common.HdlFileProjectType.LocalLib; } else if (path.startsWith(prjInfo.remoteLibPath)) { return common.HdlFileProjectType.RemoteLib; } else { return common.HdlFileProjectType.Unknown; } case 'ip': return common.HdlFileProjectType.IP; case 'primitives': return common.HdlFileProjectType.Primitive; } } public async initializeHdlFiles(hdlFiles: AbsPath[], progress: vscode.Progress) { let count: number = 0; let fileNum = hdlFiles.length; const parallelChunk = Math.min(os.cpus().length, 32); console.log("use cpu: " + parallelChunk); const pools: { id: number, promise: Promise, path: string }[] = []; const reportTitle = t('info.progress.build-module-tree'); progress.report({ message: reportTitle + ` ${1}/${fileNum}`, increment: 0 }); async function consumePools() { for (const p of pools) { const increment = Math.floor(p.id / fileNum * 100); await p.promise; // console.log("handle id " + p.id + ' increment: ' + increment); progress?.report({ message: reportTitle + ` ${p.id}/${fileNum}`, increment }); } pools.length = 0; } for (const path of hdlFiles) { count ++; const p = this.doHdlFast(path, 'common'); pools.push({ id: count, promise: p, path }); if (pools.length % parallelChunk === 0) { // 消费并发池 await consumePools(); } } if (pools.length > 0) { await consumePools(); } } public async initializeIPsPath(IPsPath: string[], progress: vscode.Progress) { let count: number = 0; let fileNum = IPsPath.length; const parallelChunk = Math.min(os.cpus().length, 32); console.log("use cpu: " + parallelChunk); const pools: { id: number, promise: Promise, path: string }[] = []; const reportTitle = t('info.progress.build-ip-module-tree'); progress.report({ message: reportTitle + ` ${1}/${fileNum}`, increment: 0 }); async function consumePools() { for (const p of pools) { const increment = Math.floor(p.id / fileNum * 100); await p.promise; // console.log("handle id " + p.id + ' increment: ' + increment); progress?.report({ message: reportTitle + ` ${p.id}/${fileNum}`, increment }); } pools.length = 0; } for (const path of IPsPath) { count ++; const p = this.doHdlFast(path, 'ip'); pools.push({ id: count, promise: p, path }); if (pools.length % parallelChunk === 0) { // 消费并发池 await consumePools(); } } if (pools.length > 0) { await consumePools(); } } public async makeAllInstance() { for (const hdlFile of this.getAllHdlFiles()) { hdlFile.makeInstance(); } } public getTopModulesByType(type: string): HdlModule[] { const hardware = opeParam.prjInfo.arch.hardware; if (hardware.sim === hardware.src) { return this.getAllTopModules(); } switch (type) { case common.HdlFileProjectType.Src: return this.getSrcTopModules(); case common.HdlFileProjectType.Sim: return this.getSimTopModules(); default: return []; } } public getSrcTopModules(): HdlModule[] { const srcTopModules = this.srcTopModules; if (!srcTopModules) { return []; } const moduleFiles = []; for (const module of srcTopModules) { moduleFiles.push(module); } return moduleFiles; } public getSimTopModules(): HdlModule[] { const simTopModules = this.simTopModules; if (!simTopModules) { return []; } const moduleFiles = []; for (const module of simTopModules) { moduleFiles.push(module); } return moduleFiles; } public removeFromHdlFile(hdlFile: HdlFile) { this.pathToHdlFiles.delete(hdlFile.path); } public deleteHdlFile(path: AbsPath) { path = hdlPath.toSlash(path); const moduleFile = this.getHdlFile(path); if (moduleFile) { for (const name of moduleFile.getAllModuleNames()) { moduleFile.deleteHdlModule(name); } this.pathToHdlFiles.delete(path); } } /** * @description 往 hdlparam 中添加路径中的若干模块 * @param path */ public async addHdlFile(path: AbsPath) { path = hdlPath.toSlash(path); // 解析并构建 await this.doHdlFast(path, 'common'); // 初始化 const moduleFile = this.getHdlFile(path); if (!moduleFile) { MainOutput.report('error happen when create moduleFile ' + path, { level: ReportType.Warn }); } else { moduleFile.makeInstance(); for (const module of moduleFile.getAllHdlModules()) { module.solveUnhandleInstance(); } } } public updateFast(path: string, fast: common.Fast) { const moduleFile = this.getHdlFile(path); if (moduleFile === undefined) { return; } // 1. 更新 macro moduleFile.updateMacro(fast.macro); // 2. 增量更新所有 module const uncheckedModuleNames = new Set(); for (const name of moduleFile.getAllModuleNames()) { uncheckedModuleNames.add(name); } for (const rawHdlModule of fast.content) { const moduleName = rawHdlModule.name; if (uncheckedModuleNames.has(moduleName)) { // match the same module, check then const originalModule = moduleFile.getHdlModule(moduleName); uncheckedModuleNames.delete(moduleName); originalModule?.update(rawHdlModule); } else { // no matched, create it const newModule = moduleFile.createHdlModule(rawHdlModule); newModule.makeNameToInstances(); newModule.solveUnhandleInstance(); } } // 3. 删除没有被访问到的 module for (const moduleName of uncheckedModuleNames) { moduleFile.deleteHdlModule(moduleName); } } public async updateByMonitor(addFiles: AbsPath[], delFiles: AbsPath[]) { for (const path of addFiles) { await this.addHdlFile(path); } for (const path of delFiles) { this.deleteHdlFile(path); } } }; const hdlParam = new HdlParam(); class HdlInstance { /** * @description 例化的名字 * * 对于下面的例子,唯一例化 的 name 就是 `u_tool` * @example * module hello() * tool u_tool(); * endmodule * */ name: string; /** * @description 例化的模块名 * * 对于下面的例子,唯一例化 的 type 就是 `tool` * @example * module hello() * tool u_tool(); * endmodule * */ type: string; /** * @description 例化的 range */ range: common.Range; // range of instance /** * @description 例化的模块的定义路径 * * 对于下面的例子,唯一例化 的 instModPath 就是 `tool` 这个模块 `module tool` 申明所在的文件的路径 * @example * module hello() * tool u_tool(); * endmodule * */ instModPath: AbsPath | undefined; /** * @description 用于描述当前例化是如何被引入的,以下是三类枚举 * - current: 例化对应的模块就在当前文件中 * - include: 通过 `include * - others: 其他 */ instModPathStatus: common.InstModPathStatus; /** * @description 例化 params 部分的 range. * 如果是 vhdl,则是 generic map 部分的 range */ instparams: common.InstRange; /** * @description 例化 ports 部分的 range. * 如果是 vhdl,则是 port map 部分的 range */ instports: common.InstRange; /** * @description 例化模块例化地点的外层 module * * 对于下面的例子, 例化 `u_tool` 的 parentMod 就是 `hello` * @example * module hello() * tool u_tool(); * endmodule * */ parentMod: HdlModule; /** * @description 例化模块的定义模块 * * 对于下面的例子, 例化 `u_tool` 的 `module` 就是 tool * @example * module hello(); * tool u_tool(); * endmodule * * module tool(); * ... * endmodule */ module: HdlModule | undefined; constructor(name: string, type: string, instModPath: string | undefined, instModPathStatus: common.InstModPathStatus, instparams: common.InstRange, instports: common.InstRange, range: common.Range, parentMod: HdlModule ) { this.name = name; this.type = type; this.parentMod = parentMod; this.instparams = instparams; this.instports = instports; this.instModPath = instModPath; this.instModPathStatus = instModPathStatus; this.range = range; this.module = undefined; // solve dependency this.locateHdlModule(); } /** * @description 定位出当前 instance 的模块是什么,并将模块对应的 HdlModule (普通 HDL、IP、原语) 赋值到 this.module 上 * 对于存在于结构树中的 HdlModule (普通 HDL & IP),改变这些 HdlModule 的 ref 并修改顶层模块相关的属性 */ public locateHdlModule() { const instModPath = this.instModPath; const instModName = this.type; if (instModPath) { const module = hdlParam.getHdlModule(instModPath, instModName); if (module) { this.module = module; // 增加当前模块的 global ref this.module.addGlobalReferedInstance(this); // 如果当前 instance 对应的例化是同源例化,则 // 增加当前 instance 的 local ref,并从对应类型的顶层模块中剔除 if (this.isSameSourceInstantiation()) { this.module?.addLocalReferedInstance(this); } } } else { doPrimitivesJudgeApi(instModName).then(isPrimitive => { if (isPrimitive) { // 构造 fake hdlfile if (opeParam.prjInfo.toolChain === 'xilinx') { const fakeModule = new HdlModule( XilinxPrimitivesHdlFile, instModName, defaultRange, [], [], []); this.module = fakeModule; // 原语在任何情况下都不是顶层模块 hdlParam.deleteTopModule(fakeModule); hdlParam.deleteTopModuleToSource(fakeModule); } } }); } } /** * @description 判断当前的 `instance` 对应的例化行为是否为一个同源例化 (SSI, same source instantiation) * * - 对于标准项目结构,也就是 src + sim ,如果在 moduleA 中完成了 moduleB 的例化,且 moduleA 和 moduleB 都是 src 文件夹下的, * 那么这个例化就是一个同源例化;如果 moduleB 在 sim 下, moduleA 在 src 下,那么当前的例化就是一个非同源例化。 * - 对于 library 和 IP 这两种类型的 module,对于它们的例化一律视为同源引用。 * - 同源例化造成的引用为 local ref,非同源例化 + 同源例化造成的引用为 global ref。在模块树下, src 文件夹下的只有 local ref 为空的 module 才是顶层模块 * 换句话说,非同源例化一定不会造成顶层模块的变化,但是同源例化有可能会。 * @returns */ public isSameSourceInstantiation(): boolean { const parentModule = this.parentMod; const belongModule = this.module; // 当前 instance 仍然是 unsolved 状态,返回 false 不参与后续的 ref 计算 if (!belongModule) { return false; } // instance 模块本身是 library / IP / 原语,一律视为 SSI if (belongModule.file.projectType === common.HdlFileProjectType.IP || belongModule.file.projectType === common.HdlFileProjectType.Primitive || belongModule.file.projectType === common.HdlFileProjectType.LocalLib || belongModule.file.projectType === common.HdlFileProjectType.RemoteLib ) { return true; } // 剩余情况下,一律根据 type 判断 return parentModule.file.projectType === belongModule.file.projectType; } /** * @description 更新当前的 instance * @param newInstance */ public update(newInstance: common.RawHdlInstance) { this.type = newInstance.type; this.range = newInstance.range; this.instparams = newInstance.instparams; this.instports = newInstance.instports; this.instModPath = this.module?.path || ''; this.updateInstModPathStatus(); } /** * @description 用于解决例化的路径引入状态,对于 A 模块,它的两个例化 u_A1 和 u_A2 * 使用 u_A1 和 u_A2 的模块需要知道 u_A1 是如何被引入的,此时需要调用 u_A1.updateInstModPathStatus() 来更新 * u_A1 的 instModPathStatus */ public updateInstModPathStatus() { const module = this.module; if (module) { const userModule = this.parentMod; if (userModule.path === module.path) { this.instModPathStatus = common.InstModPathStatus.Current; } else { const userIncludePaths = userModule.file.macro.includes.map( include => hdlPath.rel2abs(userModule.path, include.path) ); if (userIncludePaths.includes(module.path)) { this.instModPathStatus = common.InstModPathStatus.Include; } else { this.instModPathStatus = common.InstModPathStatus.Others; } } } else { this.instModPathStatus = common.InstModPathStatus.Unknown; } } public get getDoFastFileType(): DoFastFileType | undefined { return this.module?.file.doFastType; } }; class HdlModule { file: HdlFile; name: string; range: common.Range; params: common.HdlModuleParam[]; ports: common.HdlModulePort[]; public rawInstances: common.RawHdlInstance[] | undefined; // TODO: 此处无法采用 instance name 作为主键,是因为 verilog 允许 instance name 同名出现 private nameToInstances: Map; private unhandleInstances: Set; private globalRefers: Set; private localRefers: Set; constructor(file: HdlFile, name: string, range: common.Range, params: common.HdlModuleParam[], ports: common.HdlModulePort[], instances: common.RawHdlInstance[]) { this.file = file; this.name = name; this.range = range; this.params = params ? params : []; this.ports = ports ? ports : []; this.rawInstances = instances; this.nameToInstances = new Map(); // add in hdlParam data structure // default both top module in top module and local top module (sim/src) hdlParam.addTopModule(this); hdlParam.addTopModuleToSource(this); hdlParam.addHdlModule(this); // log reference (its instance) // represents all the instance from this this.globalRefers = new Set(); // represents all the instance from this created in the same scope // scope: src or sim (lib belongs to src) // localRefers subset to refers this.localRefers = new Set(); // make unhandleInstances this.unhandleInstances = new Set(); } public get path() : string { return this.file.path; } public get languageId(): HdlLangID { return this.file.languageId; } public makeInstanceKey(instanceName: string, moduleName: string): string { return instanceName + '-' + moduleName; } public getInstance(name: string, moduleName: string): HdlInstance | undefined { return this.nameToInstances.get(this.makeInstanceKey(name, moduleName)); } public getAllInstances(): HdlInstance[] { const hdlInstances: HdlInstance[] = []; for (const inst of this.nameToInstances.values()) { hdlInstances.push(inst); } return hdlInstances; } public getInstanceNum(): number { return this.nameToInstances.size; } public createHdlInstance(rawHdlInstance: common.RawHdlInstance): HdlInstance { const instModName = rawHdlInstance.type; if (this.languageId === HdlLangID.Verilog || this.languageId === HdlLangID.SystemVerilog) { const searchResult = this.searchInstModPath(instModName); const hdlInstance = new HdlInstance(rawHdlInstance.name, rawHdlInstance.type, searchResult.path, searchResult.status, rawHdlInstance.instparams, rawHdlInstance.instports, rawHdlInstance.range, this); if (!searchResult.path) { hdlParam.addUnhandleInstance(hdlInstance); this.addUnhandleInstance(hdlInstance); } if (this.nameToInstances) { const key = this.makeInstanceKey(rawHdlInstance.name, rawHdlInstance.type); this.nameToInstances.set(key, hdlInstance); } return hdlInstance; } else if (this.languageId === HdlLangID.Vhdl) { const searchResult = this.searchInstModPath(instModName); const hdlInstance = new HdlInstance(rawHdlInstance.name, rawHdlInstance.type, searchResult.path, searchResult.status, rawHdlInstance.instparams, rawHdlInstance.instports, rawHdlInstance.range, this); if (hdlInstance.module === undefined) { hdlInstance.module = this; } if (this.nameToInstances) { const key = this.makeInstanceKey(rawHdlInstance.name, rawHdlInstance.type); this.nameToInstances.set(key, hdlInstance); } return hdlInstance; } else { vscode.window.showErrorMessage(`Unknown Language :${this.languageId} exist in our core program`); const hdlInstance = new HdlInstance(rawHdlInstance.name, rawHdlInstance.type, this.path, common.InstModPathStatus.Unknown, rawHdlInstance.instparams, this.ports[0].range, rawHdlInstance.range, this); return hdlInstance; } } public makeNameToInstances() { if (this.rawInstances !== undefined) { this.nameToInstances.clear(); for (const inst of this.rawInstances) { const instance = this.createHdlInstance(inst); } // this.rawInstances = undefined; } else { MainOutput.report('call makeNameToInstances but this.rawInstances is undefined', { level: ReportType.Warn }); } } public deleteInstanceByName(instanceName: string, moduleName: string) { const inst = this.getInstance(instanceName, moduleName); this.deleteInstance(inst); } public deleteInstance(inst?: HdlInstance) { if (inst) { this.deleteUnhandleInstance(inst); hdlParam.deleteUnhandleInstance(inst); if (this.nameToInstances) { const key = this.makeInstanceKey(inst.name, inst.type); this.nameToInstances.delete(key); } // delete reference from instance's instMod const instMod = inst.module; if (instMod) { instMod.deleteGlobalReferedInstance(inst); if (inst.isSameSourceInstantiation()) { instMod.deleteLocalReferedInstance(inst); } } } } /** * @description 计算出当前 instance 和父组件的 包含关系 * @param instModName instance 的名字 * @returns InstModPathSearchResult */ private searchInstModPath(instModName: string): common.InstModPathSearchResult { // search path of instance // priority: "current file" -> "included files" -> "other hdls in the project" // prepare for "other hdls in the project" const excludeFile = new Set([this.file]); // search all the modules in the current file for (const name of this.file.getAllModuleNames()) { if (instModName === name) { return {path : this.path, status: common.InstModPathStatus.Current}; } } // search included file for (const include of this.file.macro.includes) { const absIncludePath = hdlPath.rel2abs(this.path, include.path); const includeFile = hdlParam.getHdlFile(absIncludePath); if (includeFile) { excludeFile.add(includeFile); if (includeFile.hasHdlModule(instModName)) { return {path: includeFile.path, status: common.InstModPathStatus.Include}; } } } // search other files in the project for (const hdlFile of hdlParam.getAllHdlFiles()) { if (!excludeFile.has(hdlFile) && hdlFile.hasHdlModule(instModName)) { return { path: hdlFile.path, status: common.InstModPathStatus.Others }; } } return {path: '', status: common.InstModPathStatus.Unknown}; } public addUnhandleInstance(inst: HdlInstance) { this.unhandleInstances.add(inst); } public deleteUnhandleInstance(inst: HdlInstance) { this.unhandleInstances.delete(inst); } public getAllGlobalRefers(): Set { return this.globalRefers; } public getAllLocalRefers(): Set { return this.localRefers; } public addGlobalReferedInstance(inst: HdlInstance) { const globalRefers = this.globalRefers; globalRefers.add(inst); // it is refered in global scope, so delete this from top module if (globalRefers.size > 0) { hdlParam.deleteTopModule(this); } } public deleteGlobalReferedInstance(inst: HdlInstance) { const globalRefers = this.globalRefers; globalRefers.delete(inst); if (globalRefers.size === 0) { hdlParam.addTopModule(this); } } public addLocalReferedInstance(inst: HdlInstance) { const localRefers = this.localRefers; localRefers.add(inst); // it is refered in local scope, so delete this from top module if (localRefers.size > 0) { hdlParam.deleteTopModuleToSource(this); } } public deleteLocalReferedInstance(inst: HdlInstance) { const localRefers = this.localRefers; localRefers.delete(inst); if (localRefers.size === 0) { hdlParam.addTopModuleToSource(this); } } /** * @description 从全局寻找这个 module 的例化,并尝试修改它的状态 */ public solveUnhandleInstance() { const instances = hdlParam.getUnhandleInstancesByModuleName(this.name); for (const instance of instances) { const belongScopeModule = instance.parentMod; // 先从 unsolved 堆中删除当前的 instance hdlParam.deleteUnhandleInstance(instance); belongScopeModule.deleteUnhandleInstance(instance); // 解决 instance.instModPath = this.path; instance.updateInstModPathStatus(); // 找寻这个 instance 对应的真正的 module(也有可能是原语) // 并将这个 instance 加入这个 module 的计数器中 instance.locateHdlModule(); } } public update(newModule: common.RawHdlModule) { this.ports = newModule.ports; this.params = newModule.params; this.range = newModule.range; // compare and make change to instance const uncheckedInstanceNames = new Map(); for (const inst of this.getAllInstances()) { const key = this.makeInstanceKey(inst.name, inst.type); uncheckedInstanceNames.set(key, inst); } for (const newInst of newModule.instances) { const newInstKey = this.makeInstanceKey(newInst.name, newInst.type); if (uncheckedInstanceNames.has(newInstKey)) { // match exist instance, compare and update const originalInstance = this.getInstance(newInst.name, newInst.type); originalInstance?.update(newInst); uncheckedInstanceNames.delete(newInstKey); } else { // unknown instance, create it this.createHdlInstance(newInst); } } // delete Instance that not visited for (const inst of uncheckedInstanceNames.values()) { this.deleteInstanceByName(inst.name, inst.type); } } }; export class HdlFile { // 标准化的文件绝对路径 public path: string; // 对应的 HDL 语言 ID public languageId: HdlLangID; // 文件的项目类型 public projectType: common.HdlFileProjectType; // 文件的解析模式 public doFastType: DoFastFileType; // 当前文件的宏 public macro: common.Macro; // 维护当前文件内部 module 的 map private readonly nameToModule: Map; constructor(path: string, languageId: HdlLangID, macro: common.Macro, modules: common.RawHdlModule[], projectType: common.HdlFileProjectType, doFastType: DoFastFileType) { this.path = path; this.languageId = languageId; this.macro = macro; this.projectType = projectType; this.doFastType = doFastType; // add to global hdlParam hdlParam.setHdlFile(this); // make nameToModule this.nameToModule = new Map(); for (const rawHdlModule of modules) { this.createHdlModule(rawHdlModule); } } public createHdlModule(rawHdlModule: common.RawHdlModule): HdlModule { const module: HdlModule = new HdlModule(this, rawHdlModule.name, rawHdlModule.range, rawHdlModule.params, rawHdlModule.ports, rawHdlModule.instances); this.nameToModule.set(rawHdlModule.name, module); return module; } public hasHdlModule(name: string): boolean { return this.nameToModule.has(name); } public getHdlModule(name: string): HdlModule | undefined { return this.nameToModule.get(name); } public getAllModuleNames(): string[] { const names: string[] = []; for (const [name, _] of this.nameToModule) { names.push(name); } return names; } public getAllHdlModules(): HdlModule[] { const hdlModules: HdlModule[] = []; for (const hdlModule of this.nameToModule.values()) { hdlModules.push(hdlModule); } return hdlModules; } public deleteHdlModule(name: string) { const hdlModule = this.getHdlModule(name); if (hdlModule) { // delete child reference in the module which use this for (const childInst of hdlModule.getAllGlobalRefers()) { const userModule = childInst.parentMod; childInst.module = undefined; childInst.instModPath = undefined; childInst.instModPathStatus = common.InstModPathStatus.Unknown; hdlParam.addUnhandleInstance(childInst); userModule.addUnhandleInstance(childInst); } // delete all the instance in the module for (const inst of hdlModule.getAllInstances()) { hdlModule.deleteInstance(inst); } // delete any variables containing module hdlParam.deleteTopModule(hdlModule); hdlParam.deleteTopModuleToSource(hdlModule); hdlParam.modules.delete(hdlModule); this.nameToModule.delete(hdlModule.name); } } public makeInstance() { for (const module of this.getAllHdlModules()) { module.makeNameToInstances(); } } public updateMacro(macro: common.Macro) { this.macro = macro; } } export const XilinxPrimitivesHdlFile = new HdlFile( 'xilinx-primitives', HdlLangID.Verilog, defaultMacro, [], common.HdlFileProjectType.Primitive, 'primitives'); export { hdlParam, HdlModule, HdlInstance, hdlFile };