finish reconstruct of basic function
This commit is contained in:
parent
4fd79de4e4
commit
23ca078e73
24
package.json
24
package.json
@ -174,6 +174,26 @@
|
||||
"description": "Indentation",
|
||||
"type": "number",
|
||||
"default": 4
|
||||
},
|
||||
"function.lsp.completion.vlog.autoAddInclude": {
|
||||
"description": "`include \"xxx.v\" will be added to the top of the file automatically",
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"function.lsp.completion.vlog.completeWholeInstante": {
|
||||
"description": "complete everything invoking a module needs including paramters and ports",
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"function.instantiation.addComment": {
|
||||
"description": "add comment like // ports, // input, // output when doing instantiation, including completion for module invoking",
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"function.instantiation.autoNetOutputDeclaration": {
|
||||
"description": "auto declare output type nets in the scope when instantiation happens.",
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -385,6 +405,10 @@
|
||||
},
|
||||
"category": "tool",
|
||||
"title": "%digital-ide.fsm.title%"
|
||||
},
|
||||
{
|
||||
"command": "digital-ide.lsp.tool.insertTextToUri",
|
||||
"title": "%digital-ide.lsp.tool.insertTextToUri.title%"
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
|
@ -35,5 +35,6 @@
|
||||
"digital-ide.pl.addFile.title": "add file",
|
||||
"digital-ide.pl.delFile.title": "del file",
|
||||
"digital-ide.netlist.title": "netlist",
|
||||
"digital-ide.fsm.title": "finite state machine"
|
||||
"digital-ide.fsm.title": "finite state machine",
|
||||
"digital-ide.lsp.tool.insertTextToUri.title": "insert text to uri"
|
||||
}
|
@ -35,5 +35,6 @@
|
||||
"digital-ide.pl.addFile.title": "添加文件",
|
||||
"digital-ide.pl.delFile.title": "d删除文件",
|
||||
"digital-ide.netlist.title": "netlist",
|
||||
"digital-ide.fsm.title": "有限状态机"
|
||||
"digital-ide.fsm.title": "有限状态机",
|
||||
"digital-ide.lsp.tool.insertTextToUri.title": "插入文本uri"
|
||||
}
|
@ -28,12 +28,13 @@
|
||||
"digital-ide.hard.gui.title": "打開界面",
|
||||
"digital-ide.hard.exit.title": "退出當前項目",
|
||||
"digital-ide.pickLibrary.title": "從自定義選擇自由和普遍",
|
||||
"digital-ide.pl.setSrcTop.title": "設置為src的文件",
|
||||
"digital-ide.pl.setSimTop.title": "設置為文件的sim卡",
|
||||
"digital-ide.pl.addDevice.title": "添加設備",
|
||||
"digital-ide.pl.delDevice.title": "德爾設備",
|
||||
"digital-ide.pl.setSrcTop.title": "設置為src的top",
|
||||
"digital-ide.pl.setSimTop.title": "設置為sim的top",
|
||||
"digital-ide.pl.addDevice.title": "添加device",
|
||||
"digital-ide.pl.delDevice.title": "刪除device",
|
||||
"digital-ide.pl.addFile.title": "添加文件",
|
||||
"digital-ide.pl.delFile.title": "del文件",
|
||||
"digital-ide.netlist.title": "網表",
|
||||
"digital-ide.fsm.title": "有限狀態機"
|
||||
"digital-ide.pl.delFile.title": "d刪除文件",
|
||||
"digital-ide.netlist.title": "netlist",
|
||||
"digital-ide.fsm.title": "有限狀態機",
|
||||
"digital-ide.lsp.tool.insertTextToUri.title": "插入文本uri"
|
||||
}
|
2
resources/hdlParser/index.d.ts
vendored
2
resources/hdlParser/index.d.ts
vendored
@ -7,7 +7,7 @@ type Path = AbsPath | RelPath;
|
||||
|
||||
interface Fast {
|
||||
content: RawHdlModule[]
|
||||
languageId: HdlLangID
|
||||
languageId: string
|
||||
macro: Macro
|
||||
}
|
||||
|
||||
|
@ -2,19 +2,24 @@ import * as vscode from 'vscode';
|
||||
|
||||
import { opeParam, MainOutput, ReportType } from './global';
|
||||
import { hdlParam } from './hdlParser';
|
||||
import { prjManage, registerManagerCommands } from './manager';
|
||||
import { registerFunctionCommands, registerLsp } from './function';
|
||||
import * as manager from './manager';
|
||||
import * as func from './function';
|
||||
import { hdlMonitor } from './monitor';
|
||||
|
||||
async function registerCommand(context: vscode.ExtensionContext) {
|
||||
registerFunctionCommands(context);
|
||||
registerManagerCommands(context);
|
||||
registerLsp(context);
|
||||
manager.registerManagerCommands(context);
|
||||
|
||||
func.registerFunctionCommands(context);
|
||||
func.registerLsp(context);
|
||||
func.registerToolCommands(context);
|
||||
}
|
||||
|
||||
async function launch(context: vscode.ExtensionContext) {
|
||||
await prjManage.initialise(context);
|
||||
await manager.prjManage.initialise(context);
|
||||
await registerCommand(context);
|
||||
hdlMonitor.start();
|
||||
|
||||
console.log(hdlParam);
|
||||
|
||||
MainOutput.report('Digital-IDE has launched, Version: 0.3.0');
|
||||
MainOutput.report('OS: ' + opeParam.os);
|
||||
|
@ -26,7 +26,7 @@ class ExportFunctionItem {
|
||||
};
|
||||
|
||||
function registerFileDocExport(context: vscode.ExtensionContext) {
|
||||
vscode.commands.registerCommand('digital-ide.hdlDoc.exportFile', () => {
|
||||
vscode.commands.registerCommand('digital-ide.hdlDoc.exportFile', async () => {
|
||||
const option = {
|
||||
placeHolder: 'Select an Export Format'
|
||||
};
|
||||
@ -36,16 +36,15 @@ function registerFileDocExport(context: vscode.ExtensionContext) {
|
||||
new ExportFunctionItem('html', ' html', 'only support light theme', exportCurrentFileDocAsHTML)
|
||||
];
|
||||
|
||||
vscode.window.showQuickPick(items, option).then(item => {
|
||||
const item = await vscode.window.showQuickPick(items, option);
|
||||
if (item) {
|
||||
item.exportFunc();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function registerProjectDocExport(context: vscode.ExtensionContext) {
|
||||
vscode.commands.registerCommand('digital-ide.hdlDoc.exportProject', () => {
|
||||
vscode.commands.registerCommand('digital-ide.hdlDoc.exportProject', async () => {
|
||||
const option = {
|
||||
placeHolder: 'Select an Export Format'
|
||||
};
|
||||
@ -55,12 +54,11 @@ function registerProjectDocExport(context: vscode.ExtensionContext) {
|
||||
new ExportFunctionItem('html', ' html', 'only support light theme', exportProjectDocAsHTML)
|
||||
];
|
||||
|
||||
vscode.window.showQuickPick(items, option).then(item => {
|
||||
const item = await vscode.window.showQuickPick(items, option);
|
||||
if (item) {
|
||||
item.exportFunc();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export {
|
||||
|
@ -9,6 +9,9 @@ import * as lspDocSymbol from './lsp/docSymbol';
|
||||
import * as lspDefinition from './lsp/definition';
|
||||
import * as lspHover from './lsp/hover';
|
||||
import * as lspFormatter from '../../resources/formatter';
|
||||
import * as lspDocSemantic from './lsp/docSemantic';
|
||||
|
||||
import * as tool from './tool';
|
||||
|
||||
function registerDocumentation(context: vscode.ExtensionContext) {
|
||||
vscode.commands.registerCommand('digital-ide.hdlDoc.showWebview', hdlDoc.showDocWebview);
|
||||
@ -65,11 +68,18 @@ function registerLsp(context: vscode.ExtensionContext) {
|
||||
vscode.languages.registerCompletionItemProvider(vlogSelector, lspCompletion.vlogMacroCompletionProvider, '`');
|
||||
vscode.languages.registerCompletionItemProvider(vlogSelector, lspCompletion.vlogPositionPortProvider, '.');
|
||||
vscode.languages.registerCompletionItemProvider(vlogSelector, lspCompletion.vlogCompletionProvider);
|
||||
// vhdl lsp
|
||||
vscode.languages.registerDocumentSemanticTokensProvider(vlogSelector, lspDocSemantic.vlogDocSenmanticProvider, lspDocSemantic.vlogLegend);
|
||||
|
||||
|
||||
// vhdl lsp
|
||||
}
|
||||
|
||||
function registerToolCommands(context: vscode.ExtensionContext) {
|
||||
vscode.commands.registerCommand('digital-ide.lsp.tool.insertTextToUri', tool.insertTextToUri);
|
||||
}
|
||||
|
||||
export {
|
||||
registerFunctionCommands,
|
||||
registerLsp
|
||||
registerLsp,
|
||||
registerToolCommands
|
||||
};
|
@ -4,10 +4,11 @@ import * as fs from 'fs';
|
||||
import * as util from '../util';
|
||||
import { hdlFile, hdlPath } from '../../../hdlFs';
|
||||
import { hdlParam, HdlSymbol } from '../../../hdlParser';
|
||||
import { AbsPath, MainOutput, ReportType } from '../../../global';
|
||||
import { AbsPath, MainOutput, RelPath, ReportType } from '../../../global';
|
||||
import { Define, Include, RawSymbol } from '../../../hdlParser/common';
|
||||
import { HdlInstance, HdlModule } from '../../../hdlParser/core';
|
||||
import { vlogKeyword } from '../util/keyword';
|
||||
import { instanceVlogCode } from '../../sim/instance';
|
||||
|
||||
class VlogIncludeCompletionProvider implements vscode.CompletionItemProvider {
|
||||
public provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext): vscode.ProviderResult<vscode.CompletionItem[] | vscode.CompletionList<vscode.CompletionItem>> {
|
||||
@ -176,7 +177,9 @@ class VlogCompletionProvider implements vscode.CompletionItemProvider {
|
||||
const filePath = hdlPath.toSlash(document.fileName);
|
||||
|
||||
// 1. provide keyword
|
||||
const completions = this.getKeyWordItem();
|
||||
const completions = this.makeKeywordItems();
|
||||
completions.push(...this.makeCompilerKeywordItems());
|
||||
completions.push(...this.makeSystemKeywordItems());
|
||||
|
||||
const symbolResult = await HdlSymbol.all(filePath);
|
||||
if (!symbolResult) {
|
||||
@ -199,7 +202,7 @@ class VlogCompletionProvider implements vscode.CompletionItemProvider {
|
||||
}
|
||||
|
||||
// 3. provide modules
|
||||
const suggestModulesPromise = this.provideModules(filePath, symbolResult.macro.includes);
|
||||
const suggestModulesPromise = this.provideModules(document, position, filePath, symbolResult.macro.includes);
|
||||
|
||||
// 4. provide params and ports of wrapper module
|
||||
const suggestParamsPortsPromise = this.provideParamsPorts(currentModule);
|
||||
@ -219,7 +222,7 @@ class VlogCompletionProvider implements vscode.CompletionItemProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private getKeyWordItem(): vscode.CompletionItem[] {
|
||||
private makeKeywordItems(): vscode.CompletionItem[] {
|
||||
const vlogKeywordItem = [];
|
||||
for (const keyword of vlogKeyword.keys()) {
|
||||
const clItem = this.makekeywordCompletionItem(keyword);
|
||||
@ -229,6 +232,29 @@ class VlogCompletionProvider implements vscode.CompletionItemProvider {
|
||||
return vlogKeywordItem;
|
||||
}
|
||||
|
||||
private makeCompilerKeywordItems(): vscode.CompletionItem[] {
|
||||
const items = [];
|
||||
for (const keyword of vlogKeyword.compilerKeys()) {
|
||||
const clItem = new vscode.CompletionItem(keyword, vscode.CompletionItemKind.Keyword);
|
||||
clItem.insertText = new vscode.SnippetString('`' + keyword);
|
||||
clItem.detail = 'compiler directive';
|
||||
items.push(clItem);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
private makeSystemKeywordItems(): vscode.CompletionItem[] {
|
||||
const items = [];
|
||||
for (const keyword of vlogKeyword.systemKeys()) {
|
||||
const clItem = new vscode.CompletionItem(keyword, vscode.CompletionItemKind.Method);
|
||||
clItem.insertText = new vscode.SnippetString('\\$' + keyword + '($1);');
|
||||
clItem.detail = 'system task';
|
||||
items.push(clItem);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
|
||||
private makekeywordCompletionItem(keyword: string): vscode.CompletionItem {
|
||||
const clItem = new vscode.CompletionItem(keyword, vscode.CompletionItemKind.Keyword);
|
||||
clItem.detail = 'keyword';
|
||||
@ -240,12 +266,53 @@ class VlogCompletionProvider implements vscode.CompletionItemProvider {
|
||||
return clItem;
|
||||
}
|
||||
|
||||
private async provideModules(filePath: AbsPath, includes: Include[]): Promise<vscode.CompletionItem[]> {
|
||||
private async provideModules(document: vscode.TextDocument, position: vscode.Position, filePath: AbsPath, includes: Include[]): Promise<vscode.CompletionItem[]> {
|
||||
const suggestModules: vscode.CompletionItem[] = [];
|
||||
|
||||
// TODO : add `include xxx automatically
|
||||
const lspVlogConfig = vscode.workspace.getConfiguration('function.lsp.completion.vlog');
|
||||
const autoAddInclude: boolean = lspVlogConfig.get('autoAddInclude', true);
|
||||
const completeWholeInstante: boolean = lspVlogConfig.get('completeWholeInstante', true);
|
||||
|
||||
const includePaths = new Set<AbsPath>();
|
||||
let lastIncludeLine = 0;
|
||||
for (const include of includes) {
|
||||
const absIncludePath = hdlPath.rel2abs(filePath, include.path);
|
||||
includePaths.add(absIncludePath);
|
||||
lastIncludeLine = Math.max(include.range.end.line, lastIncludeLine);
|
||||
}
|
||||
const insertPosition = new vscode.Position(lastIncludeLine, 0);
|
||||
const insertRange = new vscode.Range(insertPosition, insertPosition);
|
||||
const fileFolder = hdlPath.resolve(filePath, '..');
|
||||
|
||||
// used only when completeWholeInstante is true
|
||||
let completePrefix = '';
|
||||
if (completeWholeInstante) {
|
||||
const wordRange = document.getWordRangeAtPosition(position);
|
||||
const countStart = wordRange ? wordRange.start.character : position.character;
|
||||
const spaceNumber = Math.floor(countStart / 4) * 4;
|
||||
console.log(wordRange, countStart, spaceNumber);
|
||||
|
||||
completePrefix = ' '.repeat(spaceNumber);
|
||||
}
|
||||
|
||||
|
||||
for (const module of hdlParam.getAllHdlModules()) {
|
||||
const clItem = new vscode.CompletionItem(module.name, vscode.CompletionItemKind.Class);
|
||||
|
||||
// feature 1 : auto add include path if there's no corresponding include path
|
||||
if (autoAddInclude && !includePaths.has(module.path)) {
|
||||
const relPath: RelPath = hdlPath.relative(fileFolder, module.path);
|
||||
const includeString = '`include "' + relPath + '"\n';
|
||||
const textEdit = new vscode.TextEdit(insertRange, includeString);
|
||||
clItem.additionalTextEdits = [textEdit];
|
||||
}
|
||||
|
||||
// feature 2 : auto complete instance
|
||||
if (completeWholeInstante) {
|
||||
const snippetString = instanceVlogCode(module, '', true);
|
||||
clItem.insertText = new vscode.SnippetString(snippetString);
|
||||
}
|
||||
|
||||
clItem.detail = 'module';
|
||||
suggestModules.push(clItem);
|
||||
}
|
||||
@ -281,6 +348,7 @@ class VlogCompletionProvider implements vscode.CompletionItemProvider {
|
||||
for (const symbol of symbols) {
|
||||
if (symbol.type === 'wire' || symbol.type === 'reg') {
|
||||
const clItem = new vscode.CompletionItem(symbol.name, vscode.CompletionItemKind.Variable);
|
||||
clItem.sortText = '';
|
||||
clItem.detail = symbol.type;
|
||||
suggestNets.push(clItem);
|
||||
}
|
||||
|
@ -52,12 +52,13 @@ class VlogDefinitionProvider implements vscode.DefinitionProvider {
|
||||
|
||||
// match `include
|
||||
const includeResult = util.matchInclude(document, position, all.macro.includes);
|
||||
|
||||
if (includeResult) {
|
||||
const absPath = hdlPath.rel2abs(filePath, includeResult.name);
|
||||
const targetFile = vscode.Uri.file(absPath);
|
||||
const targetPosition = new vscode.Position(0, 0);
|
||||
const targetRange = new vscode.Range(targetPosition, targetPosition);
|
||||
const originSelectionRange = document.getWordRangeAtPosition(position, /[\."_0-9a-zA-Z]+/);
|
||||
const originSelectionRange = document.getWordRangeAtPosition(position, /["\.\\\/_0-9A-Za-z]+/);
|
||||
const link: vscode.LocationLink = { targetUri: targetFile, targetRange, originSelectionRange };
|
||||
return [link];
|
||||
}
|
||||
@ -143,9 +144,6 @@ class VlogDefinitionProvider implements vscode.DefinitionProvider {
|
||||
const normalResult = util.matchNormalSymbol(targetWord, scopeSymbols.symbols);
|
||||
if (normalResult) {
|
||||
const targetRange = util.transformRange(normalResult.range, -1, 0);
|
||||
|
||||
console.log(targetRange, normalResult);
|
||||
|
||||
const link: vscode.LocationLink = { targetUri: document.uri, targetRange };
|
||||
return [link];
|
||||
}
|
||||
|
6
src/function/lsp/docSemantic/index.ts
Normal file
6
src/function/lsp/docSemantic/index.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { vlogDocSenmanticProvider, vlogLegend } from './vlog';
|
||||
|
||||
export {
|
||||
vlogDocSenmanticProvider,
|
||||
vlogLegend
|
||||
};
|
21
src/function/lsp/docSemantic/vlog.ts
Normal file
21
src/function/lsp/docSemantic/vlog.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import { HdlSymbol } from '../../../hdlParser';
|
||||
|
||||
const tokenTypes = ['class', 'interface', 'enum', 'function', 'variable'];
|
||||
const tokenModifiers = ['declaration', 'documentation'];
|
||||
const vlogLegend = new vscode.SemanticTokensLegend(tokenTypes, tokenModifiers);
|
||||
|
||||
class VlogDocSenmanticProvider implements vscode.DocumentSemanticTokensProvider {
|
||||
public async provideDocumentSemanticTokens(document: vscode.TextDocument, token: vscode.CancellationToken): Promise<vscode.SemanticTokens | null | undefined> {
|
||||
const tokensBuilder = new vscode.SemanticTokensBuilder(vlogLegend);
|
||||
return tokensBuilder.build();
|
||||
}
|
||||
}
|
||||
|
||||
const vlogDocSenmanticProvider = new VlogDocSenmanticProvider();
|
||||
|
||||
export {
|
||||
vlogDocSenmanticProvider,
|
||||
vlogLegend
|
||||
};
|
@ -2,7 +2,7 @@ import * as vscode from 'vscode';
|
||||
|
||||
import { AllowNull } from '../../../global';
|
||||
import { HdlSymbol } from '../../../hdlParser';
|
||||
import { RawSymbol, makeVscodePosition, Range } from '../../../hdlParser/common';
|
||||
import { RawSymbol, Range } from '../../../hdlParser/common';
|
||||
|
||||
import { positionAfterEqual } from '../util';
|
||||
|
||||
|
@ -160,9 +160,6 @@ class VlogHoverProvider implements vscode.HoverProvider {
|
||||
const normalComment = await util.searchCommentAround(filePath, normalResult.range);
|
||||
const normalDesc = util.makeNormalDesc(normalResult);
|
||||
|
||||
console.log(normalResult);
|
||||
|
||||
|
||||
content.appendCodeblock(normalDesc, HdlLangID.Verilog);
|
||||
if (normalComment) {
|
||||
content.appendCodeblock(normalComment, HdlLangID.Verilog);
|
||||
|
@ -123,7 +123,7 @@ function isInComment(document: vscode.TextDocument, position: Position, comments
|
||||
|
||||
|
||||
function matchInclude(document: vscode.TextDocument, position: vscode.Position, includes: Include[]) : AllowNull<MatchedSymbol> {
|
||||
const selectFileRange = document.getWordRangeAtPosition(position, /[\._0-9A-Za-z]+/);
|
||||
const selectFileRange = document.getWordRangeAtPosition(position, /[\.\\\/_0-9A-Za-z]+/);
|
||||
const selectFileName = document.getText(selectFileRange);
|
||||
|
||||
if (!includes) {
|
||||
@ -318,7 +318,8 @@ function makeParamDesc(param: HdlModuleParam): string {
|
||||
}
|
||||
|
||||
function makeNormalDesc(normal: RawSymbol): string {
|
||||
const desc = normal.type + ' ' + normal.width + ' ' + normal.name;
|
||||
const width = normal.width ? normal.width : '';
|
||||
const desc = normal.type + ' ' + width + ' ' + normal.name;
|
||||
return desc;
|
||||
}
|
||||
|
||||
|
@ -2,19 +2,12 @@ import * as vscode from 'vscode';
|
||||
|
||||
|
||||
class Keywords {
|
||||
keywords: Set<string>;
|
||||
keywordItems: vscode.CompletionItem[];
|
||||
compilerKeywords: string[]; // start with `
|
||||
systemKeywords: string[]; // start with $
|
||||
private keywords: Set<string>;
|
||||
private compilerKeywords: string[]; // start with `
|
||||
private systemKeywords: string[]; // start with $
|
||||
constructor(keywords: string[], compilerKeywords: string[], systemKeywords: string[]) {
|
||||
this.keywords = new Set(keywords);
|
||||
const keywordItems = [];
|
||||
for (const keyword of keywords) {
|
||||
const clItem = new vscode.CompletionItem(keyword, vscode.CompletionItemKind.Keyword);
|
||||
clItem.detail = "keyword";
|
||||
keywordItems.push(clItem);
|
||||
}
|
||||
this.keywordItems = keywordItems;
|
||||
this.compilerKeywords = compilerKeywords;
|
||||
this.systemKeywords = systemKeywords;
|
||||
}
|
||||
@ -23,6 +16,14 @@ class Keywords {
|
||||
return this.keywords;
|
||||
}
|
||||
|
||||
public compilerKeys(): string[] {
|
||||
return this.compilerKeywords;
|
||||
}
|
||||
|
||||
public systemKeys(): string[] {
|
||||
return this.systemKeywords;
|
||||
}
|
||||
|
||||
public isKeyword(word: string): boolean {
|
||||
return this.keywords.has(word);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { HdlLangID } from '../../global/enum';
|
||||
import { hdlParam } from '../../hdlParser';
|
||||
import { HdlModulePort, HdlModuleParam } from '../../hdlParser/common';
|
||||
import { HdlModulePort, HdlModuleParam, HdlModulePortType } from '../../hdlParser/common';
|
||||
import { HdlModule } from '../../hdlParser/core';
|
||||
|
||||
class ModuleInfoItem {
|
||||
@ -28,23 +28,33 @@ class ModuleInfoItem {
|
||||
* @description verilog模式下生成整个例化的内容
|
||||
* @param module 模块信息
|
||||
*/
|
||||
function instanceVlogCode(module: HdlModule) {
|
||||
let vlogPortStr = vlogPort(module.ports);
|
||||
let vlogParamStr = vlogParam(module.params);
|
||||
function instanceVlogCode(module: HdlModule, prefix: string = '', returnSnippetString: boolean = false): string {
|
||||
const instantiationConfig = vscode.workspace.getConfiguration('function.instantiation');
|
||||
const needComment = instantiationConfig.get('addComment', true);
|
||||
const autoNetOutputDeclaration = instantiationConfig.get('autoNetOutputDeclaration', true);
|
||||
|
||||
let instContent = '';
|
||||
instContent += vlogPortStr.wireStr;
|
||||
instContent += module.name + ' ';
|
||||
const content = new vscode.SnippetString();
|
||||
|
||||
if (vlogParamStr !== '') {
|
||||
instContent += `#(\n${vlogParamStr})\n`;
|
||||
// make net declaration if needed
|
||||
if (autoNetOutputDeclaration) {
|
||||
const netDeclarationString = makeNetOutputDeclaration(module.ports, prefix, needComment);
|
||||
if (netDeclarationString) {
|
||||
content.appendText(netDeclarationString);
|
||||
}
|
||||
}
|
||||
|
||||
instContent += `u_${module.name}(\n`;
|
||||
instContent += vlogPortStr.portStr;
|
||||
instContent += ');\n';
|
||||
content.appendText(prefix + module.name + ' ');
|
||||
if (returnSnippetString) {
|
||||
content.appendPlaceholder('u_' + module.name);
|
||||
} else {
|
||||
content.appendText('u_' + module.name);
|
||||
}
|
||||
|
||||
return instContent;
|
||||
makeVlogParamAssignments(content, module.params, prefix, returnSnippetString, needComment);
|
||||
makeVlogPortAssignments(content, module.ports, prefix, returnSnippetString, needComment);
|
||||
|
||||
const instanceString = content.value;
|
||||
return instanceString;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -67,69 +77,95 @@ function instanceVhdlCode(module: HdlModule) {
|
||||
return instContent;
|
||||
}
|
||||
|
||||
function makeNetOutputDeclaration(ports: HdlModulePort[], prefix: string, needComment: boolean): string | null {
|
||||
const maxWidthLength = Math.max(...ports.map(p => p.width.length));
|
||||
|
||||
let netOutputDeclaration = prefix + (needComment ? '// outports wire\n' : '');
|
||||
let haveOutput = false;
|
||||
for (const port of ports) {
|
||||
if (port.type === HdlModulePortType.Output) {
|
||||
haveOutput = true;
|
||||
let portWidth = port.width ? port.width : '';
|
||||
portWidth += ' '.repeat(maxWidthLength - portWidth.length + 1);
|
||||
const netDeclaration = prefix + `wire ${portWidth}\t${port.name};\n`;
|
||||
netOutputDeclaration += netDeclaration;
|
||||
}
|
||||
}
|
||||
|
||||
if (!haveOutput) {
|
||||
return null;
|
||||
} else {
|
||||
netOutputDeclaration += '\n';
|
||||
return netOutputDeclaration;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description verilog模式下对端口信息生成要例化的内容
|
||||
* @param ports 端口信息列表
|
||||
*/
|
||||
function vlogPort(ports: HdlModulePort[]) : { wireStr: string, portStr: string} {
|
||||
let nmax = getlmax(ports, 'name');
|
||||
let wmax = getlmax(ports, 'width');
|
||||
function makeVlogPortAssignments(content: vscode.SnippetString, ports: HdlModulePort[], prefix: string = '', returnSnippetString: boolean, needComment: boolean) {
|
||||
if (ports.length === 0) {
|
||||
content.appendText('();');
|
||||
return;
|
||||
}
|
||||
|
||||
let portStr = `\t// ports\n`;
|
||||
let wireStr = '// outports wire\n';
|
||||
for (let i = 0; i < ports.length; i++) {
|
||||
const maxNameLength = Math.max(...ports.map(p => p.name.length));
|
||||
|
||||
content.appendText('(\n');
|
||||
|
||||
for (let i = 0; i < ports.length; ++ i) {
|
||||
const port = ports[i];
|
||||
const paddingName = port.name + ' '.repeat(maxNameLength - port.name.length + 1);
|
||||
|
||||
if (port.type === 'output') {
|
||||
let width = port.width;
|
||||
let wpadding = wmax - width.length + 1;
|
||||
width += ' '.repeat(wpadding);
|
||||
// TODO: vhdl type
|
||||
wireStr += `wire ${width}\t${port.name};\n`;
|
||||
content.appendText(prefix + '\t.' + paddingName + '\t( ');
|
||||
if (returnSnippetString) {
|
||||
content.appendPlaceholder(port.name);
|
||||
} else {
|
||||
content.appendText(port.name);
|
||||
}
|
||||
content.appendText(' '.repeat(maxNameLength - port.name.length + 1) + ' )');
|
||||
if (i < ports.length - 1) {
|
||||
content.appendText(',');
|
||||
}
|
||||
content.appendText('\n');
|
||||
}
|
||||
|
||||
let name = port.name;
|
||||
let npadding = nmax - name.length + 1;
|
||||
name += ' '.repeat(npadding);
|
||||
portStr += `\t.${name}\t( ${name} )`;
|
||||
if (i !== ports.length - 1) {
|
||||
portStr += ',';
|
||||
}
|
||||
portStr += '\n';
|
||||
content.appendText(prefix + ');\n');
|
||||
}
|
||||
|
||||
return { wireStr, portStr };
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @description verilog模式下对参数信息生成要例化的内容
|
||||
* @param params 参数信息列表
|
||||
*/
|
||||
function vlogParam(params: HdlModuleParam[]): string {
|
||||
let paramStr = '';
|
||||
let nmax = getlmax(params, 'name');
|
||||
let imax = getlmax(params, 'init');
|
||||
function makeVlogParamAssignments(content: vscode.SnippetString, params: HdlModuleParam[], prefix: string = '', returnSnippetString: boolean, needComment: boolean) {
|
||||
if (params.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const maxNameLength = Math.max(...params.map(p => p.name.length));
|
||||
const maxInitLength = Math.max(...params.map(p => p.init.length));
|
||||
|
||||
content.appendText('#(\n');
|
||||
|
||||
// .NAME ( INIT ),
|
||||
for (let i = 0; i < params.length; i++) {
|
||||
let name = params[i].name;
|
||||
let init = params[i].init;
|
||||
|
||||
let namePadding = nmax - name.length + 1;
|
||||
let initPadding = imax - init.length + 1;
|
||||
|
||||
name +=' '.repeat(namePadding);
|
||||
init +=' '.repeat(initPadding);
|
||||
|
||||
paramStr += `\t.${name}\t( ${init} )`;
|
||||
if (i !== (params.length - 1)) {
|
||||
paramStr += ',';
|
||||
paramStr += '\n';
|
||||
for (let i = 0; i < params.length; ++ i) {
|
||||
const param = params[i];
|
||||
const paddingName = param.name + ' '.repeat(maxNameLength - param.name.length + 1);
|
||||
content.appendText(prefix + '\t.' + paddingName + '\t( ');
|
||||
if (returnSnippetString) {
|
||||
content.appendPlaceholder(param.init);
|
||||
} else {
|
||||
content.appendText(param.init);
|
||||
}
|
||||
content.appendText(' '.repeat(maxInitLength - param.init.length + 1) + ' )');
|
||||
if (i < params.length - 1) {
|
||||
content.appendText(',');
|
||||
}
|
||||
|
||||
return paramStr;
|
||||
content.appendText('\n');
|
||||
}
|
||||
content.appendText(prefix + ')\n');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -262,6 +298,8 @@ function instanceByLangID(module: HdlModule): string {
|
||||
async function instantiation() {
|
||||
const module = await selectModuleFromAll();
|
||||
if (module) {
|
||||
console.log(module);
|
||||
|
||||
const code = instanceByLangID(module);
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
if (editor) {
|
||||
@ -271,6 +309,7 @@ async function instantiation() {
|
||||
}
|
||||
|
||||
export {
|
||||
instanceVlogCode,
|
||||
instantiation,
|
||||
instanceByLangID,
|
||||
getSelectItem
|
||||
|
@ -62,6 +62,8 @@ async function testbench() {
|
||||
if (!hdlFile.isHDLFile(path)) {
|
||||
return;
|
||||
}
|
||||
console.log(path);
|
||||
|
||||
const currentHdlFile = hdlParam.getHdlFile(path);
|
||||
if (!currentHdlFile) {
|
||||
vscode.window.showErrorMessage('There is no hdlFile respect to ' + path);
|
||||
|
18
src/function/tool.ts
Normal file
18
src/function/tool.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
async function insertTextToUri(uri: vscode.Uri, text: string, position?: vscode.Position) {
|
||||
if (!position) {
|
||||
position = new vscode.Position(0, 0);
|
||||
}
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
if (editor) {
|
||||
const edit = new vscode.WorkspaceEdit();
|
||||
edit.insert(uri, position, text);
|
||||
vscode.workspace.applyEdit(edit);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export {
|
||||
insertTextToUri
|
||||
};
|
@ -21,7 +21,7 @@ function openFileByUri(path: string, range: Range) {
|
||||
}
|
||||
}
|
||||
|
||||
function refreshArchTree(element: ModuleDataItem) {
|
||||
function refreshArchTree(element?: ModuleDataItem) {
|
||||
// TODO : diff and optimize
|
||||
moduleTreeProvider.refresh(element);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import * as assert from 'assert';
|
||||
import * as fs from 'fs';
|
||||
|
||||
import { Arch, PrjInfo, RawPrjInfo, resolve, toSlash } from './prjInfo';
|
||||
import { Arch, PrjInfo, RawPrjInfo, resolve } from './prjInfo';
|
||||
|
||||
type AbsPath = string;
|
||||
type RelPath = string;
|
||||
@ -70,14 +70,23 @@ class OpeParam {
|
||||
return this._prjInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* path of property.json
|
||||
*/
|
||||
public get propertyJsonPath(): AbsPath {
|
||||
return this._propertyJsonPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* path of property-schema.json
|
||||
*/
|
||||
public get propertySchemaPath() : AbsPath {
|
||||
return this._propertySchemaPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* path of property-init.json
|
||||
*/
|
||||
public get propertyInitPath() : AbsPath {
|
||||
return this._propertyInitPath;
|
||||
}
|
||||
@ -156,19 +165,16 @@ class OpeParam {
|
||||
* get User's property.json
|
||||
*/
|
||||
public getUserPrjInfo(): PrjInfo {
|
||||
const propertyJsonPath = this.propertyJsonPath;
|
||||
const userPrjInfo = new PrjInfo();
|
||||
if (fs.existsSync(propertyJsonPath)) {
|
||||
const rawPrjInfo = readJSON(propertyJsonPath);
|
||||
const rawPrjInfo = this.getRawUserPrjInfo();
|
||||
userPrjInfo.merge(rawPrjInfo);
|
||||
} else {
|
||||
// use default config instead
|
||||
const rawPrjInfo = readJSON(this.propertyInitPath);
|
||||
userPrjInfo.merge(rawPrjInfo);
|
||||
}
|
||||
return userPrjInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* get content from property.json (disk IO)
|
||||
* @returns
|
||||
*/
|
||||
public getRawUserPrjInfo(): RawPrjInfo {
|
||||
const propertyJsonPath = this.propertyJsonPath;
|
||||
if (fs.existsSync(propertyJsonPath)) {
|
||||
|
@ -203,7 +203,7 @@ class PrjInfo implements PrjInfoMeta {
|
||||
*/
|
||||
public uniformisePath(path: AbsPath): AbsPath {
|
||||
const slashPath = toSlash(path);
|
||||
const replacedPath = this.replacePathToken(path);
|
||||
const replacedPath = this.replacePathToken(slashPath);
|
||||
return replacedPath;
|
||||
}
|
||||
|
||||
@ -240,7 +240,7 @@ class PrjInfo implements PrjInfoMeta {
|
||||
public updateToolChain(toolChain?: ToolChainType) {
|
||||
if (toolChain) {
|
||||
if (!validToolChainType(toolChain)) {
|
||||
vscode.window.showErrorMessage('expect toolChain to be "xilinx"');
|
||||
vscode.window.showErrorMessage('expect toolChain to be "xilinx", "intel", "custom"');
|
||||
return;
|
||||
}
|
||||
this._toolChain = toolChain;
|
||||
@ -267,6 +267,8 @@ class PrjInfo implements PrjInfoMeta {
|
||||
obj[attr] = actualPath;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
obj[attr] = '';
|
||||
}
|
||||
}
|
||||
|
||||
@ -353,7 +355,6 @@ class PrjInfo implements PrjInfoMeta {
|
||||
|
||||
public updateArch(arch?: Arch) {
|
||||
const workspacePath = this._workspacePath;
|
||||
|
||||
if (arch) {
|
||||
this.updatePathWisely(this.arch, 'prjPath', arch.prjPath);
|
||||
if (arch.hardware) {
|
||||
@ -382,9 +383,9 @@ class PrjInfo implements PrjInfoMeta {
|
||||
this.arch.software.data = join(softwarePath, 'data');
|
||||
}
|
||||
|
||||
// if path is '', set as workspace
|
||||
// // if path is '', set as workspace
|
||||
this.setDefaultValue(this.arch.hardware, 'src', workspacePath);
|
||||
this.setDefaultValue(this.arch.hardware, 'sim', workspacePath);
|
||||
this.setDefaultValue(this.arch.hardware, 'sim', this.arch.hardware.src);
|
||||
this.setDefaultValue(this.arch.hardware, 'data', workspacePath);
|
||||
this.setDefaultValue(this.arch.software, 'src', workspacePath);
|
||||
this.setDefaultValue(this.arch.software, 'data', workspacePath);
|
||||
@ -491,6 +492,24 @@ class PrjInfo implements PrjInfoMeta {
|
||||
return libPath;
|
||||
}
|
||||
|
||||
public get hardwareSimPath(): AbsPath {
|
||||
const simPath = this._arch.hardware.sim;
|
||||
if (fspath.isAbsolute(simPath)) {
|
||||
return simPath;
|
||||
}
|
||||
const workspace = this._workspacePath;
|
||||
return hdlPath.join(workspace, simPath);
|
||||
}
|
||||
|
||||
public get hardwareSrcPath(): AbsPath {
|
||||
const srcPath = this._arch.hardware.src;
|
||||
if (fspath.isAbsolute(srcPath)) {
|
||||
return srcPath;
|
||||
}
|
||||
const workspace = this._workspacePath;
|
||||
return hdlPath.join(workspace, srcPath);
|
||||
}
|
||||
|
||||
public json(): RawPrjInfo {
|
||||
return {
|
||||
toolChain: this._toolChain,
|
||||
@ -505,9 +524,6 @@ class PrjInfo implements PrjInfoMeta {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
export {
|
||||
PrjInfo,
|
||||
PrjInfoDefaults,
|
||||
|
@ -16,7 +16,26 @@ class PathSet {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tell if two set are element-wise equal
|
||||
* @param setA
|
||||
* @param setB
|
||||
*/
|
||||
function isSameSet<T>(setA: Set<T>, setB: Set<T>): boolean {
|
||||
if (setA.size !== setB.size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const el of setB) {
|
||||
if (!setA.has(el)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
export {
|
||||
PathSet
|
||||
PathSet,
|
||||
isSameSet
|
||||
};
|
@ -98,7 +98,7 @@ function pickFileRecursive(path: AbsPath | AbsPath[] | Set<AbsPath>, ignores?: A
|
||||
for (const file of fs.readdirSync(path)) {
|
||||
const filePath = hdlPath.join(path, file);
|
||||
if (isDir(filePath)) {
|
||||
const subHdlFiles = pickFileRecursive(filePath, ignores);
|
||||
const subHdlFiles = pickFileRecursive(filePath, ignores, condition);
|
||||
if (subHdlFiles.length > 0) {
|
||||
hdlFiles.push(...subHdlFiles);
|
||||
}
|
||||
@ -179,8 +179,6 @@ function writeFile(path: AbsPath, content: string): boolean {
|
||||
|
||||
function readJSON(path: AbsPath): object {
|
||||
try {
|
||||
console.log(path);
|
||||
|
||||
const context = fs.readFileSync(path, 'utf-8');
|
||||
return JSON.parse(context);
|
||||
} catch (err) {
|
||||
|
@ -26,6 +26,15 @@ function rel2abs(curPath: AbsPath, relPath: RelPath): AbsPath {
|
||||
return toSlash(absPath);
|
||||
}
|
||||
|
||||
|
||||
function relative(from: AbsPath, to: AbsPath): RelPath {
|
||||
let rel = fspath.relative(from, to);
|
||||
if (!rel.startsWith('.') && !rel.startsWith('./')) {
|
||||
rel = './' + rel;
|
||||
}
|
||||
return toSlash(rel);
|
||||
}
|
||||
|
||||
/**
|
||||
* cat paths with '/'
|
||||
* @param paths
|
||||
@ -87,6 +96,7 @@ function exist(path: AbsPath | undefined): boolean {
|
||||
export {
|
||||
toSlash,
|
||||
rel2abs,
|
||||
relative,
|
||||
join,
|
||||
resolve,
|
||||
filename,
|
||||
|
@ -25,7 +25,7 @@ enum HdlModulePortType {
|
||||
Inout = 'inout',
|
||||
Output = 'output',
|
||||
Input = 'input',
|
||||
Unknown = 'Unknown'
|
||||
Unknown = 'unknown'
|
||||
};
|
||||
|
||||
enum HdlModuleParamType {LocalParam, Parameter, Unknown};
|
||||
|
@ -11,7 +11,7 @@ class HdlParam {
|
||||
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>();
|
||||
public readonly modules : Set<HdlModule> = new Set<HdlModule>();
|
||||
private readonly unhandleInstances : Set<HdlInstance> = new Set<HdlInstance>();
|
||||
|
||||
public hasHdlFile(path: AbsPath): boolean {
|
||||
@ -34,11 +34,34 @@ class HdlParam {
|
||||
return hdlFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* used only in initialization stage
|
||||
* @param hdlFile
|
||||
*/
|
||||
public addHdlFile(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.initHdlFiles([path]);
|
||||
const hdlFile = this.getHdlFile(path);
|
||||
if (!hdlFile) {
|
||||
MainOutput.report('error happen when we attempt to add file by path: ' + path, 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;
|
||||
@ -184,12 +207,28 @@ class HdlParam {
|
||||
this.unhandleInstances.delete(inst);
|
||||
}
|
||||
|
||||
/**
|
||||
* vlog -> HdlLangID.Verilog
|
||||
* svlog -> HdlLangID.SystemVerilog
|
||||
* vhdl -> HdlLangID.Vhdl
|
||||
* @param langID
|
||||
*/
|
||||
private alignLanguageId(langID: string) : HdlLangID {
|
||||
switch (langID) {
|
||||
case 'vhdl': return HdlLangID.Vhdl;
|
||||
case 'vlog': return HdlLangID.Verilog;
|
||||
case 'svlog': return HdlLangID.SystemVerilog;
|
||||
default: return HdlLangID.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
private async doHdlFast(path: AbsPath) {
|
||||
try {
|
||||
const fast = await HdlSymbol.fast(path);
|
||||
if (fast) {
|
||||
const languageId = this.alignLanguageId(fast.languageId);
|
||||
new HdlFile(path,
|
||||
fast.languageId,
|
||||
languageId,
|
||||
fast.macro,
|
||||
fast.content);
|
||||
}
|
||||
@ -254,6 +293,18 @@ class HdlParam {
|
||||
}
|
||||
return moduleFiles;
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const hdlParam = new HdlParam();
|
||||
@ -324,6 +375,13 @@ class HdlInstance {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public update(newInstance: common.RawHdlInstance) {
|
||||
this.type = newInstance.type;
|
||||
this.range = newInstance.range;
|
||||
this.instparams = newInstance.instparams;
|
||||
this.instports = newInstance.instports;
|
||||
}
|
||||
};
|
||||
|
||||
class HdlModule {
|
||||
@ -348,22 +406,10 @@ class HdlModule {
|
||||
this.file = file;
|
||||
this.name = name;
|
||||
this.range = range;
|
||||
this.params = params;
|
||||
this.ports = ports;
|
||||
this.params = params ? params : [];
|
||||
this.ports = ports ? ports : [];
|
||||
|
||||
if (!this.params) {
|
||||
this.params = [];
|
||||
}
|
||||
|
||||
if (!this.ports) {
|
||||
this.ports = [];
|
||||
}
|
||||
|
||||
// make instance
|
||||
this.rawInstances = instances;
|
||||
if (!this.rawInstances) {
|
||||
this.rawInstances = [];
|
||||
}
|
||||
this.nameToInstances = new Map<string, HdlInstance>();
|
||||
|
||||
// add in hdlParam data structure
|
||||
@ -432,13 +478,12 @@ class HdlModule {
|
||||
}
|
||||
|
||||
public makeNameToInstances() {
|
||||
|
||||
if (this.rawInstances) {
|
||||
if (this.rawInstances !== undefined) {
|
||||
this.nameToInstances.clear();
|
||||
for (const inst of this.rawInstances) {
|
||||
this.createHdlInstance(inst);
|
||||
}
|
||||
this.rawInstances = undefined;
|
||||
// this.rawInstances = undefined;
|
||||
} else {
|
||||
MainOutput.report('call makeNameToInstances but this.rawInstances is undefined',
|
||||
ReportType.Warn);
|
||||
@ -450,7 +495,7 @@ class HdlModule {
|
||||
this.deleteInstance(inst);
|
||||
}
|
||||
|
||||
public deleteInstance(inst: HdlInstance | undefined) {
|
||||
public deleteInstance(inst?: HdlInstance) {
|
||||
if (inst) {
|
||||
this.deleteUnhandleInstance(inst);
|
||||
hdlParam.deleteUnhandleInstance(inst);
|
||||
@ -585,13 +630,40 @@ class HdlModule {
|
||||
inst.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 Set<string>();
|
||||
for (const inst of this.getAllInstances()) {
|
||||
uncheckedInstanceNames.add(inst.name);
|
||||
}
|
||||
|
||||
for (const newInst of newModule.instances) {
|
||||
if (uncheckedInstanceNames.has(newInst.name)) {
|
||||
// match exist instance, compare and update
|
||||
const originalInstance = this.getInstance(newInst.name);
|
||||
originalInstance?.update(newInst);
|
||||
uncheckedInstanceNames.delete(newInst.name);
|
||||
} else {
|
||||
// unknown instance, create it
|
||||
this.createHdlInstance(newInst);
|
||||
}
|
||||
}
|
||||
// delete Instance that not visited
|
||||
for (const instName of uncheckedInstanceNames) {
|
||||
this.deleteInstanceByName(instName);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class HdlFile {
|
||||
path: string;
|
||||
languageId: HdlLangID;
|
||||
type: common.HdlFileType;
|
||||
macro: common.Macro;
|
||||
public path: string;
|
||||
public languageId: HdlLangID;
|
||||
public type: common.HdlFileType;
|
||||
public macro: common.Macro;
|
||||
private readonly nameToModule: Map<string, HdlModule>;
|
||||
|
||||
constructor(path: string,
|
||||
@ -652,7 +724,27 @@ class HdlFile {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -661,6 +753,10 @@ class HdlFile {
|
||||
module.makeNameToInstances();
|
||||
}
|
||||
}
|
||||
|
||||
public updateMacro(macro: common.Macro) {
|
||||
this.macro = macro;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -87,6 +87,10 @@ class PrjManage {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* get all the hdl files that to be parsed in the project
|
||||
* @returns
|
||||
*/
|
||||
public getPrjHardwareFiles(): AbsPath[] {
|
||||
const searchPathSet = new PathSet();
|
||||
const prjInfo = opeParam.prjInfo;
|
||||
@ -97,24 +101,32 @@ class PrjManage {
|
||||
MainOutput.report(`libManage finish process, add ${fileChange.add.length} files, del ${fileChange.del.length} files`, ReportType.Info);
|
||||
|
||||
// add possible folder to search
|
||||
searchPathSet.checkAdd(hardwareInfo.src);
|
||||
searchPathSet.checkAdd(prjInfo.hardwareSrcPath);
|
||||
searchPathSet.checkAdd(prjInfo.hardwareSimPath);
|
||||
searchPathSet.checkAdd(hardwareInfo.sim);
|
||||
searchPathSet.checkAdd(prjInfo.getLibraryCommonPaths());
|
||||
searchPathSet.checkAdd(prjInfo.getLibraryCustomPaths());
|
||||
|
||||
|
||||
MainOutput.report('<getPrjHardwareFiles> search folders: ', ReportType.Debug);
|
||||
searchPathSet.files.forEach(p => MainOutput.report(p, ReportType.Debug));
|
||||
|
||||
// TODO : make something like .gitignore
|
||||
const ignores = this.getIgnoreFiles();
|
||||
|
||||
// do search
|
||||
const searchPaths = searchPathSet.files;
|
||||
return hdlFile.getHDLFiles(searchPaths, ignores);
|
||||
|
||||
const hdlFiles = hdlFile.getHDLFiles(searchPaths, ignores);
|
||||
return hdlFiles;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async initialise(context: vscode.ExtensionContext, countTimeCost: boolean = true) {
|
||||
if (countTimeCost) {
|
||||
console.time('launch');
|
||||
}
|
||||
|
||||
await this.initOpeParam(context);
|
||||
MainOutput.report('finish initialise opeParam', ReportType.Info);
|
||||
|
||||
|
238
src/monitor/event.ts
Normal file
238
src/monitor/event.ts
Normal file
@ -0,0 +1,238 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import assert = require('assert');
|
||||
import * as chokidar from 'chokidar';
|
||||
import * as vscode from 'vscode';
|
||||
import { refreshArchTree } from '../function/treeView';
|
||||
|
||||
import { AbsPath, MainOutput, opeParam, RelPath, ReportType } from '../global';
|
||||
import { isSameSet } from '../global/util';
|
||||
import { hdlFile, hdlPath } from '../hdlFs';
|
||||
import { hdlParam, HdlSymbol } from '../hdlParser';
|
||||
import { prjManage } from '../manager';
|
||||
|
||||
import type { HdlMonitor } from './index';
|
||||
|
||||
enum Event {
|
||||
Add = 'add', // emit when add file
|
||||
AddDir = 'addDir', // emit when add folder
|
||||
Unlink = 'unlink', // emit when delete file
|
||||
UnlinkDir = 'unlinkDir', // emit when delete folder
|
||||
Change = 'change', // emit when file changed
|
||||
All = 'all', // all the change above
|
||||
Ready = 'ready',
|
||||
Raw = 'raw',
|
||||
Error = 'error'
|
||||
};
|
||||
|
||||
|
||||
abstract class BaseAction {
|
||||
public listenChange(m: HdlMonitor) {
|
||||
const fSWatcher = this.selectFSWatcher(m);
|
||||
if (!fSWatcher) {
|
||||
MainOutput.report("FSWatcher hasn't been made!", ReportType.Error);
|
||||
return;
|
||||
}
|
||||
fSWatcher.on(Event.Change, path => this.change(path, m));
|
||||
}
|
||||
|
||||
public listenAdd(m: HdlMonitor) {
|
||||
const fSWatcher = this.selectFSWatcher(m);
|
||||
if (!fSWatcher) {
|
||||
MainOutput.report("FSWatcher hasn't been made!", ReportType.Error);
|
||||
return;
|
||||
}
|
||||
fSWatcher.on(Event.Add, path => this.add(path, m));
|
||||
}
|
||||
|
||||
public listenUnlink(m: HdlMonitor) {
|
||||
const fSWatcher = this.selectFSWatcher(m);
|
||||
if (!fSWatcher) {
|
||||
MainOutput.report("FSWatcher hasn't been made!", ReportType.Error);
|
||||
return;
|
||||
}
|
||||
fSWatcher.on(Event.Unlink, path => this.unlink(path, m));
|
||||
}
|
||||
|
||||
abstract selectFSWatcher(m: HdlMonitor): chokidar.FSWatcher | undefined;
|
||||
abstract change(path: AbsPath, m: HdlMonitor): Promise<void>;
|
||||
abstract add(path: AbsPath, m: HdlMonitor): Promise<void>;
|
||||
abstract unlink(path: AbsPath, m: HdlMonitor): Promise<void>;
|
||||
}
|
||||
|
||||
class HdlAction extends BaseAction {
|
||||
selectFSWatcher(m: HdlMonitor): chokidar.FSWatcher | undefined {
|
||||
return m.hdlMonitor;
|
||||
}
|
||||
|
||||
async add(path: string, m: HdlMonitor): Promise<void> {
|
||||
console.log('HdlAction add');
|
||||
|
||||
path = hdlPath.toSlash(path);
|
||||
// create corresponding moduleFile
|
||||
hdlParam.initHdlFiles([path]);
|
||||
|
||||
const moduleFile = hdlParam.getHdlFile(path);
|
||||
if (!moduleFile) {
|
||||
console.log('error happen when create moduleFile', path);
|
||||
} else {
|
||||
moduleFile.makeInstance();
|
||||
for (const module of moduleFile.getAllHdlModules()) {
|
||||
module.solveUnhandleInstance();
|
||||
}
|
||||
}
|
||||
refreshArchTree();
|
||||
}
|
||||
|
||||
async unlink(path: string, m: HdlMonitor): Promise<void> {
|
||||
console.log('HdlAction unlink');
|
||||
|
||||
path = hdlPath.toSlash(path);
|
||||
hdlParam.deleteHdlFile(path);
|
||||
refreshArchTree();
|
||||
}
|
||||
|
||||
async change(path: string, m: HdlMonitor): Promise<void> {
|
||||
console.log('HdlAction change');
|
||||
|
||||
path = hdlPath.toSlash(path);
|
||||
const moduleFile = hdlParam.getHdlFile(path);
|
||||
|
||||
if (!moduleFile) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fast = await HdlSymbol.fast(path);
|
||||
if (!fast) {
|
||||
vscode.window.showErrorMessage('error happen when parse ' + path + '\nFail to update');
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. update marco directly
|
||||
moduleFile.updateMacro(fast.macro);
|
||||
|
||||
// 2. update modules one by one
|
||||
const uncheckedModuleNames = new Set<string>();
|
||||
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. delete module not visited yet
|
||||
for (const moduleName of uncheckedModuleNames) {
|
||||
moduleFile.deleteHdlModule(moduleName);
|
||||
}
|
||||
|
||||
refreshArchTree();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PpyAction extends BaseAction {
|
||||
selectFSWatcher(m: HdlMonitor): chokidar.FSWatcher | undefined {
|
||||
return m.ppyMonitor;
|
||||
}
|
||||
|
||||
async add(path: string, m: HdlMonitor): Promise<void> {
|
||||
console.log('PpyAction add');
|
||||
assert.equal(hdlPath.toSlash(path), opeParam.propertyJsonPath);
|
||||
this.updateProperty(m);
|
||||
}
|
||||
|
||||
async unlink(path: string, m: HdlMonitor): Promise<void> {
|
||||
console.log('PpyAction unlink');
|
||||
assert.equal(hdlPath.toSlash(path), opeParam.propertyJsonPath);
|
||||
this.updateProperty(m);
|
||||
}
|
||||
|
||||
async change(path: string, m: HdlMonitor): Promise<void> {
|
||||
console.log('PpyAction change');
|
||||
assert.equal(hdlPath.toSlash(path), opeParam.propertyJsonPath);
|
||||
this.updateProperty(m);
|
||||
}
|
||||
|
||||
// get path set from opeParam that used to tell if need to remake HdlMonitor
|
||||
private getImportantPathSet(): Set<AbsPath | RelPath> {
|
||||
const pathSet = new Set<AbsPath | RelPath>();
|
||||
pathSet.add(opeParam.prjInfo.arch.hardware.sim);
|
||||
pathSet.add(opeParam.prjInfo.arch.hardware.src);
|
||||
pathSet.add(opeParam.prjInfo.libCommonPath);
|
||||
pathSet.add(opeParam.prjInfo.libCustomPath);
|
||||
return pathSet;
|
||||
}
|
||||
|
||||
public async updateProperty(m: HdlMonitor) {
|
||||
const originalPathSet = this.getImportantPathSet();
|
||||
const originalHdlFiles = prjManage.getPrjHardwareFiles();
|
||||
|
||||
const rawPrjInfo = opeParam.getRawUserPrjInfo();
|
||||
opeParam.mergePrjInfo(rawPrjInfo);
|
||||
|
||||
const currentPathSet = this.getImportantPathSet();
|
||||
if (isSameSet(originalPathSet, currentPathSet)) {
|
||||
return;
|
||||
}
|
||||
const options: vscode.ProgressOptions = { location: vscode.ProgressLocation.Notification, title: 'modify the project' };
|
||||
vscode.window.withProgress(options, async () => await this.refreshHdlMonitor(m, originalHdlFiles));
|
||||
}
|
||||
|
||||
public async refreshHdlMonitor(m: HdlMonitor, originalHdlFiles: AbsPath[]) {
|
||||
m.remakeHdlMonitor();
|
||||
|
||||
// update pl
|
||||
const currentHdlFiles = prjManage.getPrjHardwareFiles();
|
||||
await this.updatePL(originalHdlFiles, currentHdlFiles);
|
||||
|
||||
refreshArchTree();
|
||||
}
|
||||
|
||||
public async updatePL(oldFiles: AbsPath[], newFiles: AbsPath[]) {
|
||||
if (prjManage.pl) {
|
||||
const uncheckHdlFileSet = new Set<AbsPath>(oldFiles);
|
||||
const addFiles: AbsPath[] = [];
|
||||
const delFiles: AbsPath[] = [];
|
||||
|
||||
for (const path of newFiles) {
|
||||
if (!uncheckHdlFileSet.has(path)) {
|
||||
await hdlParam.addHdlPath(path);
|
||||
addFiles.push(path);
|
||||
} else {
|
||||
uncheckHdlFileSet.delete(path);
|
||||
}
|
||||
}
|
||||
const vivadoAddPromise = prjManage.pl.addFiles(addFiles);
|
||||
|
||||
for (const path of uncheckHdlFileSet) {
|
||||
hdlParam.deleteHdlFile(path);
|
||||
delFiles.push(path);
|
||||
}
|
||||
const vivadoDelPromise = prjManage.pl.delFiles(delFiles);
|
||||
|
||||
await vivadoAddPromise;
|
||||
await vivadoDelPromise;
|
||||
} else {
|
||||
MainOutput.report('PL is not registered', ReportType.Warn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const hdlAction = new HdlAction();
|
||||
const ppyAction = new PpyAction();
|
||||
|
||||
export {
|
||||
hdlAction,
|
||||
ppyAction
|
||||
};
|
103
src/monitor/index.ts
Normal file
103
src/monitor/index.ts
Normal file
@ -0,0 +1,103 @@
|
||||
import * as chokidar from 'chokidar';
|
||||
import { MainOutput, opeParam, ReportType } from '../global';
|
||||
import { hdlExts } from '../global/lang';
|
||||
import { PathSet } from '../global/util';
|
||||
import { hdlPath } from '../hdlFs';
|
||||
|
||||
import * as Event from './event';
|
||||
|
||||
class HdlMonitor{
|
||||
private monitorConfig: chokidar.WatchOptions;
|
||||
public hdlMonitor?: chokidar.FSWatcher;
|
||||
public ppyMonitor?: chokidar.FSWatcher;
|
||||
|
||||
constructor() {
|
||||
// public config for monitor
|
||||
this.monitorConfig = {
|
||||
persistent: true,
|
||||
usePolling: false,
|
||||
ignoreInitial: true,
|
||||
};
|
||||
}
|
||||
|
||||
public makeMonitor(paths: string | string[], config?: chokidar.WatchOptions): chokidar.FSWatcher {
|
||||
if (!config) {
|
||||
config = this.monitorConfig;
|
||||
}
|
||||
return chokidar.watch(paths, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description get monitor for property.json
|
||||
*/
|
||||
public getPpyMonitor() {
|
||||
const watcherPath = opeParam.propertyJsonPath;
|
||||
return this.makeMonitor(watcherPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description get monitor for HDLParam update
|
||||
*/
|
||||
public getHdlMonitor() {
|
||||
const hdlExtsGlob = `**/*.{${hdlExts.join(',')}}`;
|
||||
const prjInfo = opeParam.prjInfo;
|
||||
|
||||
const monitorPathSet = new PathSet();
|
||||
monitorPathSet.checkAdd(prjInfo.hardwareSimPath);
|
||||
monitorPathSet.checkAdd(prjInfo.hardwareSrcPath);
|
||||
monitorPathSet.checkAdd(prjInfo.libCommonPath);
|
||||
monitorPathSet.checkAdd(prjInfo.libCustomPath);
|
||||
|
||||
const monitorFoldersWithGlob = [];
|
||||
for (const folder of monitorPathSet.files) {
|
||||
const globPath = hdlPath.join(folder, hdlExtsGlob);
|
||||
monitorFoldersWithGlob.push(globPath);
|
||||
}
|
||||
MainOutput.report('Following folders are tracked: ');
|
||||
monitorPathSet.files.forEach(p => MainOutput.report(p));
|
||||
|
||||
return this.makeMonitor(monitorFoldersWithGlob);
|
||||
}
|
||||
|
||||
public close() {
|
||||
this.hdlMonitor?.close();
|
||||
this.ppyMonitor?.close();
|
||||
}
|
||||
|
||||
public start() {
|
||||
// make monitor
|
||||
this.hdlMonitor = this.getHdlMonitor();
|
||||
this.ppyMonitor = this.getPpyMonitor();
|
||||
|
||||
this.registerHdlMonitorListener();
|
||||
this.registerPpyMonitorListener();
|
||||
}
|
||||
|
||||
public remakeHdlMonitor() {
|
||||
if (this.hdlMonitor) {
|
||||
this.hdlMonitor.close();
|
||||
this.hdlMonitor = this.getHdlMonitor();
|
||||
this.registerHdlMonitorListener();
|
||||
}
|
||||
}
|
||||
|
||||
public registerHdlMonitorListener() {
|
||||
Event.hdlAction.listenAdd(this);
|
||||
Event.hdlAction.listenChange(this);
|
||||
Event.hdlAction.listenUnlink(this);
|
||||
}
|
||||
|
||||
public registerPpyMonitorListener() {
|
||||
Event.ppyAction.listenAdd(this);
|
||||
Event.ppyAction.listenChange(this);
|
||||
Event.ppyAction.listenUnlink(this);
|
||||
}
|
||||
};
|
||||
|
||||
const hdlMonitor = new HdlMonitor();
|
||||
|
||||
export {
|
||||
hdlMonitor,
|
||||
};
|
||||
|
||||
export type { HdlMonitor };
|
17
src/test/monitor/.vscode/property.json
vendored
Normal file
17
src/test/monitor/.vscode/property.json
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"toolChain": "xilinx",
|
||||
"prjName": {
|
||||
"PL": "template"
|
||||
},
|
||||
"soc": {
|
||||
"core": "none"
|
||||
},
|
||||
"enableShowLog": false,
|
||||
"device": "none",
|
||||
"arch": {
|
||||
"hardware": {
|
||||
"src": "./src1",
|
||||
"sim": "./sim1"
|
||||
}
|
||||
}
|
||||
}
|
50
src/test/monitor/sim1/testbench.v
Normal file
50
src/test/monitor/sim1/testbench.v
Normal file
@ -0,0 +1,50 @@
|
||||
module testbench();
|
||||
|
||||
parameter DATA_WIDTH = 32;
|
||||
parameter ADDR_WIDTH = 32;
|
||||
parameter MAIN_FRE = 100; //unit MHz
|
||||
reg sys_clk = 0;
|
||||
reg sys_rst = 1;
|
||||
reg [DATA_WIDTH-1:0] data = 0;
|
||||
reg [ADDR_WIDTH-1:0] addr = 0;
|
||||
|
||||
always begin
|
||||
#(500/MAIN_FRE) sys_clk = ~sys_clk;
|
||||
end
|
||||
|
||||
always begin
|
||||
#50 sys_rst = 0;
|
||||
end
|
||||
|
||||
always @(posedge sys_clk) begin
|
||||
if (sys_rst)
|
||||
addr = 0;
|
||||
else
|
||||
addr = addr + 1;
|
||||
end
|
||||
always @(posedge sys_clk) begin
|
||||
if (sys_rst)
|
||||
data = 0;
|
||||
else
|
||||
data = data + 1;
|
||||
end
|
||||
|
||||
//Instance
|
||||
// outports wire
|
||||
wire [8:0] c;
|
||||
|
||||
SimpleAdd_1 u_SimpleAdd_1(
|
||||
.a ( a ),
|
||||
.b ( b ),
|
||||
.c ( c )
|
||||
);
|
||||
|
||||
|
||||
|
||||
initial begin
|
||||
$dumpfile("wave.vcd");
|
||||
$dumpvars(0, testbench);
|
||||
#50000 $finish;
|
||||
end
|
||||
|
||||
endmodule //TOP
|
11
src/test/monitor/src1/add.v
Normal file
11
src/test/monitor/src1/add.v
Normal file
@ -0,0 +1,11 @@
|
||||
module SimpleAdd_1(
|
||||
input [8:0] a, b,
|
||||
output [8:0] c
|
||||
);
|
||||
|
||||
assign c = a + b;
|
||||
|
||||
|
||||
endmodule //SimpleAdd
|
||||
|
||||
|
9
src/test/monitor/src2/add.v
Normal file
9
src/test/monitor/src2/add.v
Normal file
@ -0,0 +1,9 @@
|
||||
module SimpleAdd_2(
|
||||
input [8:0] a, b,
|
||||
output [8:0] c
|
||||
);
|
||||
|
||||
assign c = a + b;
|
||||
|
||||
|
||||
endmodule //SimpleAdd
|
Loading…
x
Reference in New Issue
Block a user