import { AbsPath } from '../global'; import { HdlLangID } from '../global/enum'; import { MainOutput, ReportType } from '../global/outputChannel'; import * as common from './common'; import { hdlFile, hdlPath } from '../hdlFs'; import { HdlSymbol } from './util'; 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(); private 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; } public addHdlFile(hdlFile: HdlFile) { const path = hdlFile.path; this.pathToHdlFiles.set(path, hdlFile); } public hasHdlModule(path: AbsPath, name: string): boolean { 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); } /** * add module to global top modules * @param 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.type) { case common.HdlFileType.Src: return this.srcTopModules; case common.HdlFileType.Sim: return this.simTopModules; case common.HdlFileType.LocalLib: return this.srcTopModules; case common.HdlFileType.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); } 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; } 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 getUnhandleInstanceByType(typeName: string): HdlInstance | undefined { for (const inst of this.unhandleInstances) { if (inst.type === typeName) { return inst; } } return undefined; } public addUnhandleInstance(inst: HdlInstance) { this.unhandleInstances.add(inst); } public deleteUnhandleInstance(inst: HdlInstance) { this.unhandleInstances.delete(inst); } public async initHdlFiles(hdlFiles: AbsPath[] | Generator) { for (const path of hdlFiles) { // TODO : only support verilog now const langID = hdlFile.getLanguageId(path); if (langID === HdlLangID.Verilog) { const fast = await HdlSymbol.fast(path); if (fast) { new HdlFile(path, fast.languageId, fast.macro, fast.content.modules); } } } } public async initialize(hdlFiles: AbsPath[] | Generator) { await this.initHdlFiles(hdlFiles); for (const hdlFile of this.getAllHdlFiles()) { hdlFile.makeInstance(); } } }; const hdlParam = new HdlParam(); class HdlInstance { name: string; // name of the instance type: string; // type range: common.Range; // range of instance instModPath: AbsPath | undefined; // path of the definition instModPathStatus: common.InstModPathStatus; // status of the instance (current, include, others) instparams: common.InstRange; // range of params instports: common.InstRange; // range of ports parentMod: HdlModule; // HdlModule that the instance serves module: HdlModule | undefined; // module 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(); } public locateHdlModule() { const instModPath = this.instModPath; const instModName = this.type; if (instModPath) { this.module = hdlParam.getHdlModule(instModPath, instModName); // add refer for module this.module?.addGlobalReferedInstance(this); // if module and parent module share the same source (e.g both in src folder) if (this.isSameSource()) { this.module?.addLocalReferedInstance(this); } } } /** * judge if the instance is a cross source reference * e.g. this.module is from src, this.parentMod is from sim, then * isSameSource will return false, meaning that the instance is a cross source reference * * a cross source reference won't affect the top module reference of this.module, * meaning that a top module in one source can have its instance in other source */ public isSameSource(): boolean { const parentMod = this.parentMod; const instMod = this.module; if (instMod) { return parentMod.file.type === instMod.file.type; } return false; } }; class HdlModule { file: HdlFile; name: string; range: common.Range; params: common.HdlModuleParam[]; ports: common.HdlModulePort[]; private rawInstances: common.RawHdlInstance[] | undefined; 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; this.ports = ports; // make instance 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 getInstance(name: string): HdlInstance | undefined { return this.nameToInstances.get(name); } 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; 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) { this.nameToInstances.set(rawHdlInstance.name, hdlInstance); } return hdlInstance; } public makeNameToInstances() { if (this.rawInstances) { this.nameToInstances.clear(); for (const inst of this.rawInstances) { this.createHdlInstance(inst); } this.rawInstances = undefined; } else { MainOutput.report('call makeNameToInstances but this.rawInstances is undefined', ReportType.Warn); } } public deleteInstanceByName(name: string) { const inst = this.getInstance(name); this.deleteInstance(inst); } public deleteInstance(inst: HdlInstance | undefined) { if (inst) { this.deleteUnhandleInstance(inst); hdlParam.deleteUnhandleInstance(inst); if (this.nameToInstances) { this.nameToInstances.delete(inst.name); } // delete reference from instance's instMod const instMod = inst.module; if (instMod) { instMod.deleteGlobalReferedInstance(inst); if (inst.isSameSource()) { instMod.deleteLocalReferedInstance(inst); } } } } 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); 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); 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); } } public solveUnhandleInstance() { const inst = hdlParam.getUnhandleInstanceByType(this.name); if (inst) { const userModule = inst.parentMod; // match a inst with the same type name of the module // remove from unhandle list hdlParam.deleteUnhandleInstance(inst); userModule.deleteUnhandleInstance(inst); // assign instModPath inst.instModPath = this.path; // judge the type of instModPathStatus if (userModule.path === this.path) { inst.instModPathStatus = common.InstModPathStatus.Current; } else { const userIncludePaths = userModule.file.macro.includes.map( include => hdlPath.rel2abs(userModule.path, include.path)); if (userIncludePaths.includes(this.path)) { inst.instModPathStatus = common.InstModPathStatus.Include; } else { inst.instModPathStatus = common.InstModPathStatus.Others; } } // assign module in the instance inst.locateHdlModule(); } } }; class HdlFile { path: string; languageId: HdlLangID; type: common.HdlFileType; macro: common.Macro; private readonly nameToModule: Map; constructor(path: string, languageId: HdlLangID, macro: common.Macro, modules: common.RawHdlModule[]) { this.path = path; this.languageId = languageId; this.macro = macro; this.type = hdlFile.getHdlFileType(path); // add to global hdlParam hdlParam.addHdlFile(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) { } } public makeInstance() { for (const module of this.getAllHdlModules()) { module.makeNameToInstances(); } } } export { hdlParam, HdlModule, HdlInstance, hdlFile };