606 lines
20 KiB
TypeScript
606 lines
20 KiB
TypeScript
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<HdlModule> = new Set<HdlModule>();
|
|
private readonly srcTopModules : Set<HdlModule> = new Set<HdlModule>();
|
|
private readonly simTopModules : Set<HdlModule> = new Set<HdlModule>();
|
|
private readonly pathToHdlFiles : Map<AbsPath, HdlFile> = new Map<AbsPath, HdlFile>();
|
|
private readonly modules : Set<HdlModule> = new Set<HdlModule>();
|
|
private readonly unhandleInstances : Set<HdlInstance> = new Set<HdlInstance>();
|
|
|
|
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<HdlModule> {
|
|
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<AbsPath>) {
|
|
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<AbsPath>) {
|
|
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<string, HdlInstance>;
|
|
private unhandleInstances: Set<HdlInstance>;
|
|
private globalRefers: Set<HdlInstance>;
|
|
private localRefers: Set<HdlInstance>;
|
|
|
|
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<string, HdlInstance>();
|
|
|
|
// 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<HdlInstance>();
|
|
|
|
// 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<HdlInstance>();
|
|
|
|
// make unhandleInstances
|
|
this.unhandleInstances = new Set<HdlInstance>();
|
|
}
|
|
|
|
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<HdlInstance> {
|
|
return this.globalRefers;
|
|
}
|
|
|
|
public getAllLocalRefers(): Set<HdlInstance> {
|
|
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<string, HdlModule>;
|
|
|
|
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<string, HdlModule>();
|
|
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
|
|
}; |