finish reconstruct of basic function

This commit is contained in:
锦恢 2023-07-05 23:23:12 +08:00
parent 4fd79de4e4
commit 23ca078e73
34 changed files with 1000 additions and 224 deletions

View File

@ -174,6 +174,26 @@
"description": "Indentation", "description": "Indentation",
"type": "number", "type": "number",
"default": 4 "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", "category": "tool",
"title": "%digital-ide.fsm.title%" "title": "%digital-ide.fsm.title%"
},
{
"command": "digital-ide.lsp.tool.insertTextToUri",
"title": "%digital-ide.lsp.tool.insertTextToUri.title%"
} }
], ],
"menus": { "menus": {

View File

@ -35,5 +35,6 @@
"digital-ide.pl.addFile.title": "add file", "digital-ide.pl.addFile.title": "add file",
"digital-ide.pl.delFile.title": "del file", "digital-ide.pl.delFile.title": "del file",
"digital-ide.netlist.title": "netlist", "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"
} }

View File

@ -35,5 +35,6 @@
"digital-ide.pl.addFile.title": "添加文件", "digital-ide.pl.addFile.title": "添加文件",
"digital-ide.pl.delFile.title": "d删除文件", "digital-ide.pl.delFile.title": "d删除文件",
"digital-ide.netlist.title": "netlist", "digital-ide.netlist.title": "netlist",
"digital-ide.fsm.title": "有限状态机" "digital-ide.fsm.title": "有限状态机",
"digital-ide.lsp.tool.insertTextToUri.title": "插入文本uri"
} }

View File

@ -28,12 +28,13 @@
"digital-ide.hard.gui.title": "打開界面", "digital-ide.hard.gui.title": "打開界面",
"digital-ide.hard.exit.title": "退出當前項目", "digital-ide.hard.exit.title": "退出當前項目",
"digital-ide.pickLibrary.title": "從自定義選擇自由和普遍", "digital-ide.pickLibrary.title": "從自定義選擇自由和普遍",
"digital-ide.pl.setSrcTop.title": "設置為src的文件", "digital-ide.pl.setSrcTop.title": "設置為src的top",
"digital-ide.pl.setSimTop.title": "設置為文件的sim卡", "digital-ide.pl.setSimTop.title": "設置為sim的top",
"digital-ide.pl.addDevice.title": "添加設備", "digital-ide.pl.addDevice.title": "添加device",
"digital-ide.pl.delDevice.title": "德爾設備", "digital-ide.pl.delDevice.title": "刪除device",
"digital-ide.pl.addFile.title": "添加文件", "digital-ide.pl.addFile.title": "添加文件",
"digital-ide.pl.delFile.title": "del文件", "digital-ide.pl.delFile.title": "d刪除文件",
"digital-ide.netlist.title": "網表", "digital-ide.netlist.title": "netlist",
"digital-ide.fsm.title": "有限狀態機" "digital-ide.fsm.title": "有限狀態機",
"digital-ide.lsp.tool.insertTextToUri.title": "插入文本uri"
} }

View File

@ -7,7 +7,7 @@ type Path = AbsPath | RelPath;
interface Fast { interface Fast {
content: RawHdlModule[] content: RawHdlModule[]
languageId: HdlLangID languageId: string
macro: Macro macro: Macro
} }

View File

@ -2,19 +2,24 @@ import * as vscode from 'vscode';
import { opeParam, MainOutput, ReportType } from './global'; import { opeParam, MainOutput, ReportType } from './global';
import { hdlParam } from './hdlParser'; import { hdlParam } from './hdlParser';
import { prjManage, registerManagerCommands } from './manager'; import * as manager from './manager';
import { registerFunctionCommands, registerLsp } from './function'; import * as func from './function';
import { hdlMonitor } from './monitor';
async function registerCommand(context: vscode.ExtensionContext) { async function registerCommand(context: vscode.ExtensionContext) {
registerFunctionCommands(context); manager.registerManagerCommands(context);
registerManagerCommands(context);
registerLsp(context); func.registerFunctionCommands(context);
func.registerLsp(context);
func.registerToolCommands(context);
} }
async function launch(context: vscode.ExtensionContext) { async function launch(context: vscode.ExtensionContext) {
await prjManage.initialise(context); await manager.prjManage.initialise(context);
await registerCommand(context); await registerCommand(context);
hdlMonitor.start();
console.log(hdlParam);
MainOutput.report('Digital-IDE has launched, Version: 0.3.0'); MainOutput.report('Digital-IDE has launched, Version: 0.3.0');
MainOutput.report('OS: ' + opeParam.os); MainOutput.report('OS: ' + opeParam.os);

View File

@ -26,7 +26,7 @@ class ExportFunctionItem {
}; };
function registerFileDocExport(context: vscode.ExtensionContext) { function registerFileDocExport(context: vscode.ExtensionContext) {
vscode.commands.registerCommand('digital-ide.hdlDoc.exportFile', () => { vscode.commands.registerCommand('digital-ide.hdlDoc.exportFile', async () => {
const option = { const option = {
placeHolder: 'Select an Export Format' placeHolder: 'Select an Export Format'
}; };
@ -36,16 +36,15 @@ function registerFileDocExport(context: vscode.ExtensionContext) {
new ExportFunctionItem('html', ' html', 'only support light theme', exportCurrentFileDocAsHTML) 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) { if (item) {
item.exportFunc(); item.exportFunc();
} }
}); });
});
} }
function registerProjectDocExport(context: vscode.ExtensionContext) { function registerProjectDocExport(context: vscode.ExtensionContext) {
vscode.commands.registerCommand('digital-ide.hdlDoc.exportProject', () => { vscode.commands.registerCommand('digital-ide.hdlDoc.exportProject', async () => {
const option = { const option = {
placeHolder: 'Select an Export Format' placeHolder: 'Select an Export Format'
}; };
@ -55,12 +54,11 @@ function registerProjectDocExport(context: vscode.ExtensionContext) {
new ExportFunctionItem('html', ' html', 'only support light theme', exportProjectDocAsHTML) 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) { if (item) {
item.exportFunc(); item.exportFunc();
} }
}); });
});
} }
export { export {

View File

@ -9,6 +9,9 @@ import * as lspDocSymbol from './lsp/docSymbol';
import * as lspDefinition from './lsp/definition'; import * as lspDefinition from './lsp/definition';
import * as lspHover from './lsp/hover'; import * as lspHover from './lsp/hover';
import * as lspFormatter from '../../resources/formatter'; import * as lspFormatter from '../../resources/formatter';
import * as lspDocSemantic from './lsp/docSemantic';
import * as tool from './tool';
function registerDocumentation(context: vscode.ExtensionContext) { function registerDocumentation(context: vscode.ExtensionContext) {
vscode.commands.registerCommand('digital-ide.hdlDoc.showWebview', hdlDoc.showDocWebview); 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.vlogMacroCompletionProvider, '`');
vscode.languages.registerCompletionItemProvider(vlogSelector, lspCompletion.vlogPositionPortProvider, '.'); vscode.languages.registerCompletionItemProvider(vlogSelector, lspCompletion.vlogPositionPortProvider, '.');
vscode.languages.registerCompletionItemProvider(vlogSelector, lspCompletion.vlogCompletionProvider); 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 { export {
registerFunctionCommands, registerFunctionCommands,
registerLsp registerLsp,
registerToolCommands
}; };

View File

@ -4,10 +4,11 @@ import * as fs from 'fs';
import * as util from '../util'; import * as util from '../util';
import { hdlFile, hdlPath } from '../../../hdlFs'; import { hdlFile, hdlPath } from '../../../hdlFs';
import { hdlParam, HdlSymbol } from '../../../hdlParser'; 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 { Define, Include, RawSymbol } from '../../../hdlParser/common';
import { HdlInstance, HdlModule } from '../../../hdlParser/core'; import { HdlInstance, HdlModule } from '../../../hdlParser/core';
import { vlogKeyword } from '../util/keyword'; import { vlogKeyword } from '../util/keyword';
import { instanceVlogCode } from '../../sim/instance';
class VlogIncludeCompletionProvider implements vscode.CompletionItemProvider { 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>> { 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); const filePath = hdlPath.toSlash(document.fileName);
// 1. provide keyword // 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); const symbolResult = await HdlSymbol.all(filePath);
if (!symbolResult) { if (!symbolResult) {
@ -199,7 +202,7 @@ class VlogCompletionProvider implements vscode.CompletionItemProvider {
} }
// 3. provide modules // 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 // 4. provide params and ports of wrapper module
const suggestParamsPortsPromise = this.provideParamsPorts(currentModule); const suggestParamsPortsPromise = this.provideParamsPorts(currentModule);
@ -219,7 +222,7 @@ class VlogCompletionProvider implements vscode.CompletionItemProvider {
} }
} }
private getKeyWordItem(): vscode.CompletionItem[] { private makeKeywordItems(): vscode.CompletionItem[] {
const vlogKeywordItem = []; const vlogKeywordItem = [];
for (const keyword of vlogKeyword.keys()) { for (const keyword of vlogKeyword.keys()) {
const clItem = this.makekeywordCompletionItem(keyword); const clItem = this.makekeywordCompletionItem(keyword);
@ -229,6 +232,29 @@ class VlogCompletionProvider implements vscode.CompletionItemProvider {
return vlogKeywordItem; 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 { private makekeywordCompletionItem(keyword: string): vscode.CompletionItem {
const clItem = new vscode.CompletionItem(keyword, vscode.CompletionItemKind.Keyword); const clItem = new vscode.CompletionItem(keyword, vscode.CompletionItemKind.Keyword);
clItem.detail = 'keyword'; clItem.detail = 'keyword';
@ -240,12 +266,53 @@ class VlogCompletionProvider implements vscode.CompletionItemProvider {
return clItem; 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[] = []; 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()) { for (const module of hdlParam.getAllHdlModules()) {
const clItem = new vscode.CompletionItem(module.name, vscode.CompletionItemKind.Class); 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'; clItem.detail = 'module';
suggestModules.push(clItem); suggestModules.push(clItem);
} }
@ -281,6 +348,7 @@ class VlogCompletionProvider implements vscode.CompletionItemProvider {
for (const symbol of symbols) { for (const symbol of symbols) {
if (symbol.type === 'wire' || symbol.type === 'reg') { if (symbol.type === 'wire' || symbol.type === 'reg') {
const clItem = new vscode.CompletionItem(symbol.name, vscode.CompletionItemKind.Variable); const clItem = new vscode.CompletionItem(symbol.name, vscode.CompletionItemKind.Variable);
clItem.sortText = '';
clItem.detail = symbol.type; clItem.detail = symbol.type;
suggestNets.push(clItem); suggestNets.push(clItem);
} }

View File

@ -52,12 +52,13 @@ class VlogDefinitionProvider implements vscode.DefinitionProvider {
// match `include // match `include
const includeResult = util.matchInclude(document, position, all.macro.includes); const includeResult = util.matchInclude(document, position, all.macro.includes);
if (includeResult) { if (includeResult) {
const absPath = hdlPath.rel2abs(filePath, includeResult.name); const absPath = hdlPath.rel2abs(filePath, includeResult.name);
const targetFile = vscode.Uri.file(absPath); const targetFile = vscode.Uri.file(absPath);
const targetPosition = new vscode.Position(0, 0); const targetPosition = new vscode.Position(0, 0);
const targetRange = new vscode.Range(targetPosition, targetPosition); 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 }; const link: vscode.LocationLink = { targetUri: targetFile, targetRange, originSelectionRange };
return [link]; return [link];
} }
@ -143,9 +144,6 @@ class VlogDefinitionProvider implements vscode.DefinitionProvider {
const normalResult = util.matchNormalSymbol(targetWord, scopeSymbols.symbols); const normalResult = util.matchNormalSymbol(targetWord, scopeSymbols.symbols);
if (normalResult) { if (normalResult) {
const targetRange = util.transformRange(normalResult.range, -1, 0); const targetRange = util.transformRange(normalResult.range, -1, 0);
console.log(targetRange, normalResult);
const link: vscode.LocationLink = { targetUri: document.uri, targetRange }; const link: vscode.LocationLink = { targetUri: document.uri, targetRange };
return [link]; return [link];
} }

View File

@ -0,0 +1,6 @@
import { vlogDocSenmanticProvider, vlogLegend } from './vlog';
export {
vlogDocSenmanticProvider,
vlogLegend
};

View 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
};

View File

@ -2,7 +2,7 @@ import * as vscode from 'vscode';
import { AllowNull } from '../../../global'; import { AllowNull } from '../../../global';
import { HdlSymbol } from '../../../hdlParser'; import { HdlSymbol } from '../../../hdlParser';
import { RawSymbol, makeVscodePosition, Range } from '../../../hdlParser/common'; import { RawSymbol, Range } from '../../../hdlParser/common';
import { positionAfterEqual } from '../util'; import { positionAfterEqual } from '../util';

View File

@ -160,9 +160,6 @@ class VlogHoverProvider implements vscode.HoverProvider {
const normalComment = await util.searchCommentAround(filePath, normalResult.range); const normalComment = await util.searchCommentAround(filePath, normalResult.range);
const normalDesc = util.makeNormalDesc(normalResult); const normalDesc = util.makeNormalDesc(normalResult);
console.log(normalResult);
content.appendCodeblock(normalDesc, HdlLangID.Verilog); content.appendCodeblock(normalDesc, HdlLangID.Verilog);
if (normalComment) { if (normalComment) {
content.appendCodeblock(normalComment, HdlLangID.Verilog); content.appendCodeblock(normalComment, HdlLangID.Verilog);

View File

@ -123,7 +123,7 @@ function isInComment(document: vscode.TextDocument, position: Position, comments
function matchInclude(document: vscode.TextDocument, position: vscode.Position, includes: Include[]) : AllowNull<MatchedSymbol> { 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); const selectFileName = document.getText(selectFileRange);
if (!includes) { if (!includes) {
@ -318,7 +318,8 @@ function makeParamDesc(param: HdlModuleParam): string {
} }
function makeNormalDesc(normal: RawSymbol): 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; return desc;
} }

View File

@ -2,19 +2,12 @@ import * as vscode from 'vscode';
class Keywords { class Keywords {
keywords: Set<string>; private keywords: Set<string>;
keywordItems: vscode.CompletionItem[]; private compilerKeywords: string[]; // start with `
compilerKeywords: string[]; // start with ` private systemKeywords: string[]; // start with $
systemKeywords: string[]; // start with $
constructor(keywords: string[], compilerKeywords: string[], systemKeywords: string[]) { constructor(keywords: string[], compilerKeywords: string[], systemKeywords: string[]) {
this.keywords = new Set(keywords); this.keywords = new Set(keywords);
const keywordItems = []; 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.compilerKeywords = compilerKeywords;
this.systemKeywords = systemKeywords; this.systemKeywords = systemKeywords;
} }
@ -23,6 +16,14 @@ class Keywords {
return this.keywords; return this.keywords;
} }
public compilerKeys(): string[] {
return this.compilerKeywords;
}
public systemKeys(): string[] {
return this.systemKeywords;
}
public isKeyword(word: string): boolean { public isKeyword(word: string): boolean {
return this.keywords.has(word); return this.keywords.has(word);
} }

View File

@ -1,7 +1,7 @@
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { HdlLangID } from '../../global/enum'; import { HdlLangID } from '../../global/enum';
import { hdlParam } from '../../hdlParser'; import { hdlParam } from '../../hdlParser';
import { HdlModulePort, HdlModuleParam } from '../../hdlParser/common'; import { HdlModulePort, HdlModuleParam, HdlModulePortType } from '../../hdlParser/common';
import { HdlModule } from '../../hdlParser/core'; import { HdlModule } from '../../hdlParser/core';
class ModuleInfoItem { class ModuleInfoItem {
@ -28,23 +28,33 @@ class ModuleInfoItem {
* @description verilog模式下生成整个例化的内容 * @description verilog模式下生成整个例化的内容
* @param module * @param module
*/ */
function instanceVlogCode(module: HdlModule) { function instanceVlogCode(module: HdlModule, prefix: string = '', returnSnippetString: boolean = false): string {
let vlogPortStr = vlogPort(module.ports); const instantiationConfig = vscode.workspace.getConfiguration('function.instantiation');
let vlogParamStr = vlogParam(module.params); const needComment = instantiationConfig.get('addComment', true);
const autoNetOutputDeclaration = instantiationConfig.get('autoNetOutputDeclaration', true);
let instContent = ''; const content = new vscode.SnippetString();
instContent += vlogPortStr.wireStr;
instContent += module.name + ' ';
if (vlogParamStr !== '') { // make net declaration if needed
instContent += `#(\n${vlogParamStr})\n`; if (autoNetOutputDeclaration) {
const netDeclarationString = makeNetOutputDeclaration(module.ports, prefix, needComment);
if (netDeclarationString) {
content.appendText(netDeclarationString);
}
} }
instContent += `u_${module.name}(\n`; content.appendText(prefix + module.name + ' ');
instContent += vlogPortStr.portStr; if (returnSnippetString) {
instContent += ');\n'; 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; 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模式下对端口信息生成要例化的内容 * @description verilog模式下对端口信息生成要例化的内容
* @param ports * @param ports
*/ */
function vlogPort(ports: HdlModulePort[]) : { wireStr: string, portStr: string} { function makeVlogPortAssignments(content: vscode.SnippetString, ports: HdlModulePort[], prefix: string = '', returnSnippetString: boolean, needComment: boolean) {
let nmax = getlmax(ports, 'name'); if (ports.length === 0) {
let wmax = getlmax(ports, 'width'); content.appendText('();');
return;
}
let portStr = `\t// ports\n`; const maxNameLength = Math.max(...ports.map(p => p.name.length));
let wireStr = '// outports wire\n';
for (let i = 0; i < ports.length; i++) { content.appendText('(\n');
for (let i = 0; i < ports.length; ++ i) {
const port = ports[i]; const port = ports[i];
const paddingName = port.name + ' '.repeat(maxNameLength - port.name.length + 1);
if (port.type === 'output') { content.appendText(prefix + '\t.' + paddingName + '\t( ');
let width = port.width; if (returnSnippetString) {
let wpadding = wmax - width.length + 1; content.appendPlaceholder(port.name);
width += ' '.repeat(wpadding); } else {
// TODO: vhdl type content.appendText(port.name);
wireStr += `wire ${width}\t${port.name};\n`; }
content.appendText(' '.repeat(maxNameLength - port.name.length + 1) + ' )');
if (i < ports.length - 1) {
content.appendText(',');
}
content.appendText('\n');
} }
let name = port.name; content.appendText(prefix + ');\n');
let npadding = nmax - name.length + 1;
name += ' '.repeat(npadding);
portStr += `\t.${name}\t( ${name} )`;
if (i !== ports.length - 1) {
portStr += ',';
}
portStr += '\n';
} }
return { wireStr, portStr };
}
/** /**
* @description verilog模式下对参数信息生成要例化的内容 * @description verilog模式下对参数信息生成要例化的内容
* @param params * @param params
*/ */
function vlogParam(params: HdlModuleParam[]): string { function makeVlogParamAssignments(content: vscode.SnippetString, params: HdlModuleParam[], prefix: string = '', returnSnippetString: boolean, needComment: boolean) {
let paramStr = ''; if (params.length === 0) {
let nmax = getlmax(params, 'name'); return;
let imax = getlmax(params, 'init'); }
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 ), // .NAME ( INIT ),
for (let i = 0; i < params.length; i++) { for (let i = 0; i < params.length; ++ i) {
let name = params[i].name; const param = params[i];
let init = params[i].init; const paddingName = param.name + ' '.repeat(maxNameLength - param.name.length + 1);
content.appendText(prefix + '\t.' + paddingName + '\t( ');
let namePadding = nmax - name.length + 1; if (returnSnippetString) {
let initPadding = imax - init.length + 1; content.appendPlaceholder(param.init);
} else {
name +=' '.repeat(namePadding); content.appendText(param.init);
init +=' '.repeat(initPadding);
paramStr += `\t.${name}\t( ${init} )`;
if (i !== (params.length - 1)) {
paramStr += ',';
paramStr += '\n';
} }
content.appendText(' '.repeat(maxInitLength - param.init.length + 1) + ' )');
if (i < params.length - 1) {
content.appendText(',');
} }
content.appendText('\n');
return paramStr; }
content.appendText(prefix + ')\n');
} }
/** /**
@ -262,6 +298,8 @@ function instanceByLangID(module: HdlModule): string {
async function instantiation() { async function instantiation() {
const module = await selectModuleFromAll(); const module = await selectModuleFromAll();
if (module) { if (module) {
console.log(module);
const code = instanceByLangID(module); const code = instanceByLangID(module);
const editor = vscode.window.activeTextEditor; const editor = vscode.window.activeTextEditor;
if (editor) { if (editor) {
@ -271,6 +309,7 @@ async function instantiation() {
} }
export { export {
instanceVlogCode,
instantiation, instantiation,
instanceByLangID, instanceByLangID,
getSelectItem getSelectItem

View File

@ -62,6 +62,8 @@ async function testbench() {
if (!hdlFile.isHDLFile(path)) { if (!hdlFile.isHDLFile(path)) {
return; return;
} }
console.log(path);
const currentHdlFile = hdlParam.getHdlFile(path); const currentHdlFile = hdlParam.getHdlFile(path);
if (!currentHdlFile) { if (!currentHdlFile) {
vscode.window.showErrorMessage('There is no hdlFile respect to ' + path); vscode.window.showErrorMessage('There is no hdlFile respect to ' + path);

18
src/function/tool.ts Normal file
View 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
};

View File

@ -21,7 +21,7 @@ function openFileByUri(path: string, range: Range) {
} }
} }
function refreshArchTree(element: ModuleDataItem) { function refreshArchTree(element?: ModuleDataItem) {
// TODO : diff and optimize // TODO : diff and optimize
moduleTreeProvider.refresh(element); moduleTreeProvider.refresh(element);
} }

View File

@ -1,7 +1,7 @@
import * as assert from 'assert'; import * as assert from 'assert';
import * as fs from 'fs'; import * as fs from 'fs';
import { Arch, PrjInfo, RawPrjInfo, resolve, toSlash } from './prjInfo'; import { Arch, PrjInfo, RawPrjInfo, resolve } from './prjInfo';
type AbsPath = string; type AbsPath = string;
type RelPath = string; type RelPath = string;
@ -70,14 +70,23 @@ class OpeParam {
return this._prjInfo; return this._prjInfo;
} }
/**
* path of property.json
*/
public get propertyJsonPath(): AbsPath { public get propertyJsonPath(): AbsPath {
return this._propertyJsonPath; return this._propertyJsonPath;
} }
/**
* path of property-schema.json
*/
public get propertySchemaPath() : AbsPath { public get propertySchemaPath() : AbsPath {
return this._propertySchemaPath; return this._propertySchemaPath;
} }
/**
* path of property-init.json
*/
public get propertyInitPath() : AbsPath { public get propertyInitPath() : AbsPath {
return this._propertyInitPath; return this._propertyInitPath;
} }
@ -156,19 +165,16 @@ class OpeParam {
* get User's property.json * get User's property.json
*/ */
public getUserPrjInfo(): PrjInfo { public getUserPrjInfo(): PrjInfo {
const propertyJsonPath = this.propertyJsonPath;
const userPrjInfo = new PrjInfo(); const userPrjInfo = new PrjInfo();
if (fs.existsSync(propertyJsonPath)) { const rawPrjInfo = this.getRawUserPrjInfo();
const rawPrjInfo = readJSON(propertyJsonPath);
userPrjInfo.merge(rawPrjInfo); userPrjInfo.merge(rawPrjInfo);
} else {
// use default config instead
const rawPrjInfo = readJSON(this.propertyInitPath);
userPrjInfo.merge(rawPrjInfo);
}
return userPrjInfo; return userPrjInfo;
} }
/**
* get content from property.json (disk IO)
* @returns
*/
public getRawUserPrjInfo(): RawPrjInfo { public getRawUserPrjInfo(): RawPrjInfo {
const propertyJsonPath = this.propertyJsonPath; const propertyJsonPath = this.propertyJsonPath;
if (fs.existsSync(propertyJsonPath)) { if (fs.existsSync(propertyJsonPath)) {

View File

@ -203,7 +203,7 @@ class PrjInfo implements PrjInfoMeta {
*/ */
public uniformisePath(path: AbsPath): AbsPath { public uniformisePath(path: AbsPath): AbsPath {
const slashPath = toSlash(path); const slashPath = toSlash(path);
const replacedPath = this.replacePathToken(path); const replacedPath = this.replacePathToken(slashPath);
return replacedPath; return replacedPath;
} }
@ -240,7 +240,7 @@ class PrjInfo implements PrjInfoMeta {
public updateToolChain(toolChain?: ToolChainType) { public updateToolChain(toolChain?: ToolChainType) {
if (toolChain) { if (toolChain) {
if (!validToolChainType(toolChain)) { if (!validToolChainType(toolChain)) {
vscode.window.showErrorMessage('expect toolChain to be "xilinx"'); vscode.window.showErrorMessage('expect toolChain to be "xilinx", "intel", "custom"');
return; return;
} }
this._toolChain = toolChain; this._toolChain = toolChain;
@ -267,6 +267,8 @@ class PrjInfo implements PrjInfoMeta {
obj[attr] = actualPath; obj[attr] = actualPath;
} }
} }
} else {
obj[attr] = '';
} }
} }
@ -353,7 +355,6 @@ class PrjInfo implements PrjInfoMeta {
public updateArch(arch?: Arch) { public updateArch(arch?: Arch) {
const workspacePath = this._workspacePath; const workspacePath = this._workspacePath;
if (arch) { if (arch) {
this.updatePathWisely(this.arch, 'prjPath', arch.prjPath); this.updatePathWisely(this.arch, 'prjPath', arch.prjPath);
if (arch.hardware) { if (arch.hardware) {
@ -382,9 +383,9 @@ class PrjInfo implements PrjInfoMeta {
this.arch.software.data = join(softwarePath, 'data'); 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, '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.hardware, 'data', workspacePath);
this.setDefaultValue(this.arch.software, 'src', workspacePath); this.setDefaultValue(this.arch.software, 'src', workspacePath);
this.setDefaultValue(this.arch.software, 'data', workspacePath); this.setDefaultValue(this.arch.software, 'data', workspacePath);
@ -491,6 +492,24 @@ class PrjInfo implements PrjInfoMeta {
return libPath; 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 { public json(): RawPrjInfo {
return { return {
toolChain: this._toolChain, toolChain: this._toolChain,
@ -505,9 +524,6 @@ class PrjInfo implements PrjInfoMeta {
} }
}; };
export { export {
PrjInfo, PrjInfo,
PrjInfoDefaults, PrjInfoDefaults,

View File

@ -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 { export {
PathSet PathSet,
isSameSet
}; };

View File

@ -98,7 +98,7 @@ function pickFileRecursive(path: AbsPath | AbsPath[] | Set<AbsPath>, ignores?: A
for (const file of fs.readdirSync(path)) { for (const file of fs.readdirSync(path)) {
const filePath = hdlPath.join(path, file); const filePath = hdlPath.join(path, file);
if (isDir(filePath)) { if (isDir(filePath)) {
const subHdlFiles = pickFileRecursive(filePath, ignores); const subHdlFiles = pickFileRecursive(filePath, ignores, condition);
if (subHdlFiles.length > 0) { if (subHdlFiles.length > 0) {
hdlFiles.push(...subHdlFiles); hdlFiles.push(...subHdlFiles);
} }
@ -179,8 +179,6 @@ function writeFile(path: AbsPath, content: string): boolean {
function readJSON(path: AbsPath): object { function readJSON(path: AbsPath): object {
try { try {
console.log(path);
const context = fs.readFileSync(path, 'utf-8'); const context = fs.readFileSync(path, 'utf-8');
return JSON.parse(context); return JSON.parse(context);
} catch (err) { } catch (err) {

View File

@ -26,6 +26,15 @@ function rel2abs(curPath: AbsPath, relPath: RelPath): AbsPath {
return toSlash(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 '/' * cat paths with '/'
* @param paths * @param paths
@ -87,6 +96,7 @@ function exist(path: AbsPath | undefined): boolean {
export { export {
toSlash, toSlash,
rel2abs, rel2abs,
relative,
join, join,
resolve, resolve,
filename, filename,

View File

@ -25,7 +25,7 @@ enum HdlModulePortType {
Inout = 'inout', Inout = 'inout',
Output = 'output', Output = 'output',
Input = 'input', Input = 'input',
Unknown = 'Unknown' Unknown = 'unknown'
}; };
enum HdlModuleParamType {LocalParam, Parameter, Unknown}; enum HdlModuleParamType {LocalParam, Parameter, Unknown};

View File

@ -11,7 +11,7 @@ class HdlParam {
private readonly srcTopModules : Set<HdlModule> = new Set<HdlModule>(); private readonly srcTopModules : Set<HdlModule> = new Set<HdlModule>();
private readonly simTopModules : 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 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>(); private readonly unhandleInstances : Set<HdlInstance> = new Set<HdlInstance>();
public hasHdlFile(path: AbsPath): boolean { public hasHdlFile(path: AbsPath): boolean {
@ -34,11 +34,34 @@ class HdlParam {
return hdlFiles; return hdlFiles;
} }
/**
* used only in initialization stage
* @param hdlFile
*/
public addHdlFile(hdlFile: HdlFile) { public addHdlFile(hdlFile: HdlFile) {
const path = hdlFile.path; const path = hdlFile.path;
this.pathToHdlFiles.set(path, hdlFile); 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 { public hasHdlModule(path: AbsPath | undefined, name: string): boolean {
if (!path) { if (!path) {
return false; return false;
@ -184,12 +207,28 @@ class HdlParam {
this.unhandleInstances.delete(inst); 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) { private async doHdlFast(path: AbsPath) {
try { try {
const fast = await HdlSymbol.fast(path); const fast = await HdlSymbol.fast(path);
if (fast) { if (fast) {
const languageId = this.alignLanguageId(fast.languageId);
new HdlFile(path, new HdlFile(path,
fast.languageId, languageId,
fast.macro, fast.macro,
fast.content); fast.content);
} }
@ -254,6 +293,18 @@ class HdlParam {
} }
return moduleFiles; 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(); const hdlParam = new HdlParam();
@ -324,6 +375,13 @@ class HdlInstance {
} }
return false; 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 { class HdlModule {
@ -348,22 +406,10 @@ class HdlModule {
this.file = file; this.file = file;
this.name = name; this.name = name;
this.range = range; this.range = range;
this.params = params; this.params = params ? params : [];
this.ports = ports; this.ports = ports ? ports : [];
if (!this.params) {
this.params = [];
}
if (!this.ports) {
this.ports = [];
}
// make instance
this.rawInstances = instances; this.rawInstances = instances;
if (!this.rawInstances) {
this.rawInstances = [];
}
this.nameToInstances = new Map<string, HdlInstance>(); this.nameToInstances = new Map<string, HdlInstance>();
// add in hdlParam data structure // add in hdlParam data structure
@ -432,13 +478,12 @@ class HdlModule {
} }
public makeNameToInstances() { public makeNameToInstances() {
if (this.rawInstances !== undefined) {
if (this.rawInstances) {
this.nameToInstances.clear(); this.nameToInstances.clear();
for (const inst of this.rawInstances) { for (const inst of this.rawInstances) {
this.createHdlInstance(inst); this.createHdlInstance(inst);
} }
this.rawInstances = undefined; // this.rawInstances = undefined;
} else { } else {
MainOutput.report('call makeNameToInstances but this.rawInstances is undefined', MainOutput.report('call makeNameToInstances but this.rawInstances is undefined',
ReportType.Warn); ReportType.Warn);
@ -450,7 +495,7 @@ class HdlModule {
this.deleteInstance(inst); this.deleteInstance(inst);
} }
public deleteInstance(inst: HdlInstance | undefined) { public deleteInstance(inst?: HdlInstance) {
if (inst) { if (inst) {
this.deleteUnhandleInstance(inst); this.deleteUnhandleInstance(inst);
hdlParam.deleteUnhandleInstance(inst); hdlParam.deleteUnhandleInstance(inst);
@ -585,13 +630,40 @@ class HdlModule {
inst.locateHdlModule(); 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 { class HdlFile {
path: string; public path: string;
languageId: HdlLangID; public languageId: HdlLangID;
type: common.HdlFileType; public type: common.HdlFileType;
macro: common.Macro; public macro: common.Macro;
private readonly nameToModule: Map<string, HdlModule>; private readonly nameToModule: Map<string, HdlModule>;
constructor(path: string, constructor(path: string,
@ -652,7 +724,27 @@ class HdlFile {
public deleteHdlModule(name: string) { public deleteHdlModule(name: string) {
const hdlModule = this.getHdlModule(name); const hdlModule = this.getHdlModule(name);
if (hdlModule) { 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(); module.makeNameToInstances();
} }
} }
public updateMacro(macro: common.Macro) {
this.macro = macro;
}
} }

View File

@ -87,6 +87,10 @@ class PrjManage {
return []; return [];
} }
/**
* get all the hdl files that to be parsed in the project
* @returns
*/
public getPrjHardwareFiles(): AbsPath[] { public getPrjHardwareFiles(): AbsPath[] {
const searchPathSet = new PathSet(); const searchPathSet = new PathSet();
const prjInfo = opeParam.prjInfo; 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); MainOutput.report(`libManage finish process, add ${fileChange.add.length} files, del ${fileChange.del.length} files`, ReportType.Info);
// add possible folder to search // add possible folder to search
searchPathSet.checkAdd(hardwareInfo.src); searchPathSet.checkAdd(prjInfo.hardwareSrcPath);
searchPathSet.checkAdd(prjInfo.hardwareSimPath);
searchPathSet.checkAdd(hardwareInfo.sim); searchPathSet.checkAdd(hardwareInfo.sim);
searchPathSet.checkAdd(prjInfo.getLibraryCommonPaths()); searchPathSet.checkAdd(prjInfo.getLibraryCommonPaths());
searchPathSet.checkAdd(prjInfo.getLibraryCustomPaths()); 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 // TODO : make something like .gitignore
const ignores = this.getIgnoreFiles(); const ignores = this.getIgnoreFiles();
// do search // do search
const searchPaths = searchPathSet.files; 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) { public async initialise(context: vscode.ExtensionContext, countTimeCost: boolean = true) {
if (countTimeCost) { if (countTimeCost) {
console.time('launch'); console.time('launch');
} }
await this.initOpeParam(context); await this.initOpeParam(context);
MainOutput.report('finish initialise opeParam', ReportType.Info); MainOutput.report('finish initialise opeParam', ReportType.Info);

238
src/monitor/event.ts Normal file
View 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
View 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
View File

@ -0,0 +1,17 @@
{
"toolChain": "xilinx",
"prjName": {
"PL": "template"
},
"soc": {
"core": "none"
},
"enableShowLog": false,
"device": "none",
"arch": {
"hardware": {
"src": "./src1",
"sim": "./sim1"
}
}
}

View 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

View File

@ -0,0 +1,11 @@
module SimpleAdd_1(
input [8:0] a, b,
output [8:0] c
);
assign c = a + b;
endmodule //SimpleAdd

View File

@ -0,0 +1,9 @@
module SimpleAdd_2(
input [8:0] a, b,
output [8:0] c
);
assign c = a + b;
endmodule //SimpleAdd