add verilator support

This commit is contained in:
锦恢 2023-11-22 22:45:19 +08:00
parent fdacf3f2e1
commit 010a4b6bec
8 changed files with 371 additions and 38 deletions

View File

@ -60,7 +60,13 @@
"scope": "window", "scope": "window",
"type": "string", "type": "string",
"default": "", "default": "",
"description": "set the xilinx install path. \n e.g. : D:/APP/vivado_18_3/Vivado/2018.3/bin \n Default path is C:/Xilinx/Vivado/2018.3/bin \n This applies only to WIN For other systems, add it to environment variables" "description": "Set the xilinx install path. Ignore this setting if you add relative path to environment variable PATH \n e.g. : D:/APP/vivado_18_3/Vivado/2018.3/bin \n Default path is C:/Xilinx/Vivado/2018.3/bin"
},
"prj.modelsim.install.path": {
"scope": "window",
"type": "string",
"default": "",
"description": "set the modelsim install path. Ignore this setting if you add relative path to environment variable PATH \n Default path is C:/modeltech64_10.4/win64"
}, },
"prj.xilinx.IP.repo.path": { "prj.xilinx.IP.repo.path": {
"scope": "window", "scope": "window",
@ -1046,4 +1052,4 @@
"vscode-textmate": "^9.0.0", "vscode-textmate": "^9.0.0",
"wavedrom": "^2.9.1" "wavedrom": "^2.9.1"
} }
} }

View File

@ -9,7 +9,8 @@ interface BaseManager {
initialise(): Promise<void>; initialise(): Promise<void>;
} }
export { export {
BaseLinter, BaseLinter,
BaseManager BaseManager
}; };

View File

@ -1,7 +1,7 @@
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { All } from '../../../../resources/hdlParser'; import { All } from '../../../../resources/hdlParser';
import { isVerilogFile, isVhdlFile } from '../../../hdlFs/file'; import { isVerilogFile, isVhdlFile } from '../../../hdlFs/file';
import { Position, Range } from '../../../hdlParser/common'; import { Position } from '../../../hdlParser/common';
import { hdlSymbolStorage } from '../core'; import { hdlSymbolStorage } from '../core';
import { BaseLinter } from './base'; import { BaseLinter } from './base';
import { LspOutput, ReportType } from '../../../global'; import { LspOutput, ReportType } from '../../../global';
@ -159,7 +159,12 @@ class DefaultVHDLLinter implements BaseLinter {
} }
} }
const defaultVlogLinter = new DefaultVlogLinter();
const defaultVHDLLinter = new DefaultVHDLLinter();
export { export {
defaultVlogLinter,
defaultVHDLLinter,
DefaultVlogLinter, DefaultVlogLinter,
DefaultVHDLLinter DefaultVHDLLinter
}; };

View File

@ -28,10 +28,6 @@ class ModelsimLinter implements BaseLinter {
this.linterArgsMap.set(HdlLangID.Vhdl, ['-quiet', '-nologo', '-2008']); this.linterArgsMap.set(HdlLangID.Vhdl, ['-quiet', '-nologo', '-2008']);
this.linterArgsMap.set(HdlLangID.SystemVerilog, ['-quiet', '-nolog', '-sv']); this.linterArgsMap.set(HdlLangID.SystemVerilog, ['-quiet', '-nolog', '-sv']);
this.linterArgsMap.set(HdlLangID.Unknown, []); this.linterArgsMap.set(HdlLangID.Unknown, []);
this.initialise(HdlLangID.Verilog);
this.initialise(HdlLangID.Vhdl);
this.initialise(HdlLangID.SystemVerilog);
} }
@ -40,16 +36,22 @@ class ModelsimLinter implements BaseLinter {
const langID = hdlFile.getLanguageId(filePath); const langID = hdlFile.getLanguageId(filePath);
// acquire install path // acquire install path
const args = [hdlPath.toSlash(filePath), ...this.linterArgsMap]; const linterArgs = this.linterArgsMap.get(langID);
if (linterArgs === undefined) {
return;
}
const args = [filePath, ...linterArgs];
const executor = this.executableInvokeNameMap.get(langID); const executor = this.executableInvokeNameMap.get(langID);
if (executor !== undefined) { if (executor !== undefined) {
const { stdout, stderr } = await easyExec(executor, args); const { stdout } = await easyExec(executor, args);
if (stdout.length > 0) { if (stdout.length > 0) {
const diagnostics = this.provideDiagnostics(document, stdout); const diagnostics = this.provideDiagnostics(document, stdout);
this.diagnostic.set(document.uri, diagnostics); this.diagnostic.set(document.uri, diagnostics);
} }
} else { } else {
LspOutput.report('linter is not available, please check prj.vivado.install.path in your setting', ReportType.Error); LspOutput.report('linter is not available, please check prj.modelsim.install.path in your setting', ReportType.Error);
} }
} }
@ -63,7 +65,7 @@ class ModelsimLinter implements BaseLinter {
for (const line of stdout.split('\n')) { for (const line of stdout.split('\n')) {
const tokens = line.split(/(Error|Warning).+?(?: *?(?:.+?(?:\\|\/))+.+?\((\d+?)\):|)(?: *?near "(.+?)":|)(?: *?\((.+?)\)|) +?(.+)/gm); const tokens = line.split(/(Error|Warning).+?(?: *?(?:.+?(?:\\|\/))+.+?\((\d+?)\):|)(?: *?near "(.+?)":|)(?: *?\((.+?)\)|) +?(.+)/gm);
const headerInfo = tokens[0]; const headerInfo = tokens[0];
if (headerInfo === 'Error') { if (headerInfo === 'Error') {
const errorLine = parseInt(tokens[2]) - 1; const errorLine = parseInt(tokens[2]) - 1;
@ -93,26 +95,26 @@ class ModelsimLinter implements BaseLinter {
} }
private getExecutableFilePath(langID: HdlLangID): string | Path | undefined { private getExecutableFilePath(langID: HdlLangID): string | Path | undefined {
// vivado install path stored in prj.vivado.install.path // modelsim install path stored in prj.modelsim.install.path
const vivadoConfig = vscode.workspace.getConfiguration('prj.vivado'); const modelsimConfig = vscode.workspace.getConfiguration('prj.modelsim');
const vivadoInstallPath = vivadoConfig.get('install.path', ''); const modelsimInstallPath = modelsimConfig.get('install.path', '');
const executorName = this.executableFileMap.get(langID); const executorName = this.executableFileMap.get(langID);
if (executorName === undefined) { if (executorName === undefined) {
return undefined; return undefined;
} }
// e.g. xvlog.bat in windows, xvlog in linux // e.g. vlog.exe in windows, vlog in linux
const fullExecutorName = opeParam.os === 'win32' ? executorName + '.bat' : executorName; const fullExecutorName = opeParam.os === 'win32' ? executorName + '.exe' : executorName;
if (vivadoInstallPath.trim() === '' || !fs.existsSync(vivadoInstallPath)) { if (modelsimInstallPath.trim() === '' || !fs.existsSync(modelsimInstallPath)) {
LspOutput.report(`User's Vivado Install Path ${vivadoInstallPath}, which is invalid. Use ${executorName} in default.`, ReportType.Warn); LspOutput.report(`User's modelsim Install Path ${modelsimInstallPath}, which is invalid. Use ${executorName} in default.`, ReportType.Warn);
LspOutput.report('If you have doubts, check prj.vivado.install.path in setting', ReportType.Warn); LspOutput.report('If you have doubts, check prj.modelsim.install.path in setting', ReportType.Warn);
return executorName; return executorName;
} else { } else {
LspOutput.report(`User's Vivado Install Path ${vivadoInstallPath}, which is invalid`); LspOutput.report(`User's modelsim Install Path ${modelsimInstallPath}, which is invalid`);
const executorPath = hdlPath.join( const executorPath = hdlPath.join(
hdlPath.toSlash(vivadoInstallPath), hdlPath.toSlash(modelsimInstallPath),
fullExecutorName fullExecutorName
); );
// prevent path like C://stupid name/xxx/xxx/bin // prevent path like C://stupid name/xxx/xxx/bin
@ -127,14 +129,14 @@ class ModelsimLinter implements BaseLinter {
if (executorPath === undefined) { if (executorPath === undefined) {
return false; return false;
} }
const { stdout, stderr } = await easyExec(executorPath, []); const { stderr } = await easyExec(executorPath, []);
if (stderr.length === 0) { if (stderr.length === 0) {
this.executableInvokeNameMap.set(langID, undefined); this.executableInvokeNameMap.set(langID, undefined);
LspOutput.report(`fail to execute ${executorPath}! Reason: ${stderr}`, ReportType.Error); LspOutput.report(`fail to execute ${executorPath}! Reason: ${stderr}`, ReportType.Error);
return false; return false;
} else { } else {
this.executableInvokeNameMap.set(langID, executorPath); this.executableInvokeNameMap.set(langID, executorPath);
LspOutput.report(`success to verify ${executorPath}, linter from vivado is ready to go!`, ReportType.Launch); LspOutput.report(`success to verify ${executorPath}, linter from modelsim is ready to go!`, ReportType.Launch);
return true; return true;
} }
} }
@ -145,7 +147,9 @@ class ModelsimLinter implements BaseLinter {
} }
} }
const modelsimLinter = new ModelsimLinter()
export { export {
modelsimLinter,
ModelsimLinter ModelsimLinter
}; };

View File

@ -0,0 +1,153 @@
import * as vscode from "vscode";
import * as fs from 'fs';
import { LspOutput, ReportType, opeParam } from "../../../global";
import { Path } from "../../../../resources/hdlParser";
import { hdlFile, hdlPath } from "../../../hdlFs";
import { easyExec } from "../../../global/util";
import { BaseLinter } from "./base";
import { HdlLangID } from "../../../global/enum";
class VerilatorLinter implements BaseLinter {
diagnostic: vscode.DiagnosticCollection;
executableFileMap: Map<HdlLangID, string | undefined> = new Map<HdlLangID, string>();
executableInvokeNameMap: Map<HdlLangID, string | undefined> = new Map<HdlLangID, string>();
linterArgsMap: Map<HdlLangID, string[]> = new Map<HdlLangID, string[]>();
constructor() {
this.diagnostic = vscode.languages.createDiagnosticCollection();
// configure map for executable file name
this.executableFileMap.set(HdlLangID.Verilog, 'verilator');
this.executableFileMap.set(HdlLangID.SystemVerilog, 'verilator');
this.executableFileMap.set(HdlLangID.Unknown, undefined);
// configure map for argruments when lintering
this.linterArgsMap.set(HdlLangID.Verilog, ['--lint-only', '-Wall', '-bbox-sys', '--bbox-unsup', '-DGLBL']);
this.linterArgsMap.set(HdlLangID.SystemVerilog, ['--lint-only', '-sv', '-Wall', '-bbox-sys', '--bbox-unsup', '-DGLBL']);
this.linterArgsMap.set(HdlLangID.Unknown, []);
}
async lint(document: vscode.TextDocument) {
const filePath = hdlPath.toSlash(document.fileName);
const langID = hdlFile.getLanguageId(filePath);
// acquire install path
const linterArgs = this.linterArgsMap.get(langID);
if (linterArgs === undefined) {
return;
}
const args = [filePath, ...linterArgs];
const executor = this.executableInvokeNameMap.get(langID);
if (executor !== undefined) {
const { stdout } = await easyExec(executor, args);
if (stdout.length > 0) {
const diagnostics = this.provideDiagnostics(document, stdout);
this.diagnostic.set(document.uri, diagnostics);
}
} else {
LspOutput.report('linter is not available, please check prj.verilator.install.path in your setting', ReportType.Error);
}
}
/**
* @param document
* @param stdout stdout from xvlog
* @returns { vscode.Diagnostic[] } linter info
*/
private provideDiagnostics(document: vscode.TextDocument, stdout: string): vscode.Diagnostic[] {
const diagnostics = [];
for (const line of stdout.split('\n')) {
const tokens = line.split(/(Error|Warning).+?(?: *?(?:.+?(?:\\|\/))+.+?\((\d+?)\):|)(?: *?near "(.+?)":|)(?: *?\((.+?)\)|) +?(.+)/gm);
// TODO : make parser of output info from verilator
const headerInfo = tokens[0];
if (headerInfo === 'Error') {
const errorLine = parseInt(tokens[2]) - 1;
const syntaxInfo = tokens[5];
const range = this.makeCorrectRange(document, errorLine);
const diag = new vscode.Diagnostic(range, syntaxInfo, vscode.DiagnosticSeverity.Error);
diagnostics.push(diag);
} else if (headerInfo == 'Warning') {
const errorLine = parseInt(tokens[2]) - 1;
const syntaxInfo = tokens[5];
const range = this.makeCorrectRange(document, errorLine);
const diag = new vscode.Diagnostic(range, syntaxInfo, vscode.DiagnosticSeverity.Warning);
diagnostics.push(diag);
}
}
return diagnostics;
}
private makeCorrectRange(document: vscode.TextDocument, line: number): vscode.Range {
const startPosition = new vscode.Position(line, 0);
const wordRange = document.getWordRangeAtPosition(startPosition, /[`_0-9a-zA-Z]+/);
if (wordRange) {
return wordRange;
} else {
return new vscode.Range(startPosition, startPosition);
}
}
private getExecutableFilePath(langID: HdlLangID): string | Path | undefined {
// verilator install path stored in prj.verilator.install.path
const verilatorConfig = vscode.workspace.getConfiguration('prj.verilator');
const verilatorInstallPath = verilatorConfig.get('install.path', '');
const executorName = this.executableFileMap.get(langID);
if (executorName === undefined) {
return undefined;
}
// e.g. vlog.exe in windows, vlog in linux
const fullExecutorName = opeParam.os === 'win32' ? executorName + '.exe' : executorName;
if (verilatorInstallPath.trim() === '' || !fs.existsSync(verilatorInstallPath)) {
LspOutput.report(`User's verilator Install Path ${verilatorInstallPath}, which is invalid. Use ${executorName} in default.`, ReportType.Warn);
LspOutput.report('If you have doubts, check prj.verilator.install.path in setting', ReportType.Warn);
return executorName;
} else {
LspOutput.report(`User's verilator Install Path ${verilatorInstallPath}, which is invalid`);
const executorPath = hdlPath.join(
hdlPath.toSlash(verilatorInstallPath),
fullExecutorName
);
// prevent path like C://stupid name/xxx/xxx/bin
// blank space
const safeExecutorPath = '"' + executorPath + '"';
return safeExecutorPath;
}
}
public async setExecutableFilePath(executorPath: string | Path | undefined, langID: HdlLangID): Promise<boolean> {
if (executorPath === undefined) {
return false;
}
const { stderr } = await easyExec(executorPath, []);
if (stderr.length === 0) {
this.executableInvokeNameMap.set(langID, undefined);
LspOutput.report(`fail to execute ${executorPath}! Reason: ${stderr}`, ReportType.Error);
return false;
} else {
this.executableInvokeNameMap.set(langID, executorPath);
LspOutput.report(`success to verify ${executorPath}, linter from verilator is ready to go!`, ReportType.Launch);
return true;
}
}
public initialise(langID: HdlLangID) {
const executorPath = this.getExecutableFilePath(langID);
this.setExecutableFilePath(executorPath, langID);
}
}
const verilatorLinter = new VerilatorLinter();
export {
verilatorLinter,
VerilatorLinter
};

View File

@ -1,13 +1,92 @@
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { BaseManager } from './base'; import { LspOutput, ReportType } from '../../../global';
import { HdlLangID } from '../../../global/enum';
import { BaseLinter, BaseManager } from './base';
import { defaultVlogLinter } from './default';
import { modelsimLinter } from './modelsim';
import { vivadoLinter } from './vivado';
class VhdlLinterManager implements BaseManager { class VhdlLinterManager implements BaseManager {
constructor() { currentLinter: BaseLinter | undefined;
activateList: Map<string, boolean> = new Map<string, boolean>();
activateLinterName: string;
constructor() {
this.activateList.set('vivado', false);
this.activateList.set('modelsim', false);
this.activateList.set('default', false);
this.activateLinterName = 'default';
this.updateLinter();
// update when user's config is changed
vscode.workspace.onDidChangeConfiguration(() => {
const diagnostor = this.getUserDiagnostorSelection();
const lastDiagnostor = this.activateLinterName;
if (diagnostor !== lastDiagnostor) {
LspOutput.report(`[vhdl lsp manager] detect linter setting changes, switch ${lastDiagnostor} to ${diagnostor}.`, );
this.updateLinter();
}
});
} }
async initialise(): Promise<void> { async initialise(): Promise<void> {
}
public getUserDiagnostorSelection() {
const vlogLspConfig = vscode.workspace.getConfiguration('function.lsp.linter.vlog');
const diagnostor = vlogLspConfig.get('diagnostor', 'default');
return diagnostor;
}
public updateLinter() {
const diagnostor = this.getUserDiagnostorSelection();
switch (diagnostor) {
case 'vivado': this.activateVivado(); break;
case 'modelsim': this.activateModelsim(); break;
case 'default': this.activateDefault(); break;
case default: this.activateDefault(); break;
}
}
public activateVivado() {
const selectedLinter = vivadoLinter;
if (this.activateList.get('vivado') === false) {
selectedLinter.initialise(HdlLangID.Verilog);
this.activateList.set('vivado', true);
LspOutput.report('[vhdl lsp manager] vivado linter has been activated', ReportType.Info);
}
this.currentLinter = selectedLinter;
this.activateLinterName = 'vivado';
}
public activateModelsim() {
const selectedLinter = modelsimLinter;
if (this.activateList.get('modelsim') === false) {
selectedLinter.initialise(HdlLangID.Verilog);
this.activateList.set('modelsim', true);
LspOutput.report('[vhdl lsp manager] modelsim linter has been activated', ReportType.Info);
}
this.currentLinter = selectedLinter;
this.activateLinterName = 'modelsim';
}
public activateDefault() {
const selectedLinter = defaultVlogLinter;
if (this.activateList.get('default') === false) {
this.activateList.set('default', true);
LspOutput.report('[vhdl lsp manager] default build-in linter has been activated', ReportType.Info);
}
this.currentLinter = selectedLinter;
this.activateLinterName = 'default';
} }
} }
@ -15,4 +94,4 @@ const vhdlLinterManager = new VhdlLinterManager();
export { export {
vhdlLinterManager vhdlLinterManager
}; };

View File

@ -29,9 +29,9 @@ class VivadoLinter implements BaseLinter {
this.linterArgsMap.set(HdlLangID.SystemVerilog, ['--sv', '--nolog']); this.linterArgsMap.set(HdlLangID.SystemVerilog, ['--sv', '--nolog']);
this.linterArgsMap.set(HdlLangID.Unknown, []); this.linterArgsMap.set(HdlLangID.Unknown, []);
this.initialise(HdlLangID.Verilog); // this.initialise(HdlLangID.Verilog);
this.initialise(HdlLangID.Vhdl); // this.initialise(HdlLangID.Vhdl);
this.initialise(HdlLangID.SystemVerilog); // this.initialise(HdlLangID.SystemVerilog);
} }
@ -40,10 +40,15 @@ class VivadoLinter implements BaseLinter {
const langID = hdlFile.getLanguageId(filePath); const langID = hdlFile.getLanguageId(filePath);
// acquire install path // acquire install path
const args = [hdlPath.toSlash(filePath), ...this.linterArgsMap]; const linterArgs = this.linterArgsMap.get(langID);
if (linterArgs === undefined) {
return;
}
const args = [filePath, ...linterArgs];
const executor = this.executableInvokeNameMap.get(langID); const executor = this.executableInvokeNameMap.get(langID);
if (executor !== undefined) { if (executor !== undefined) {
const { stdout, stderr } = await easyExec(executor, args); const { stdout } = await easyExec(executor, args);
if (stdout.length > 0) { if (stdout.length > 0) {
const diagnostics = this.provideDiagnostics(document, stdout); const diagnostics = this.provideDiagnostics(document, stdout);
this.diagnostic.set(document.uri, diagnostics); this.diagnostic.set(document.uri, diagnostics);
@ -122,7 +127,7 @@ class VivadoLinter implements BaseLinter {
if (executorPath === undefined) { if (executorPath === undefined) {
return false; return false;
} }
const { stdout, stderr } = await easyExec(executorPath, []); const { stderr } = await easyExec(executorPath, []);
if (stderr.length === 0) { if (stderr.length === 0) {
this.executableInvokeNameMap.set(langID, undefined); this.executableInvokeNameMap.set(langID, undefined);
LspOutput.report(`fail to execute ${executorPath}! Reason: ${stderr}`, ReportType.Error); LspOutput.report(`fail to execute ${executorPath}! Reason: ${stderr}`, ReportType.Error);
@ -140,7 +145,9 @@ class VivadoLinter implements BaseLinter {
} }
} }
const vivadoLinter = new VivadoLinter();
export { export {
vivadoLinter,
VivadoLinter VivadoLinter
}; };

View File

@ -1,14 +1,92 @@
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { BaseManager } from './base'; import { LspOutput, ReportType } from '../../../global';
import { HdlLangID } from '../../../global/enum';
import { BaseLinter, BaseManager } from './base';
import { defaultVlogLinter } from './default';
import { modelsimLinter } from './modelsim';
import { vivadoLinter } from './vivado';
class VlogLinterManager implements BaseManager { class VlogLinterManager implements BaseManager {
currentLinter: BaseLinter | undefined;
activateList: Map<string, boolean> = new Map<string, boolean>();
activateLinterName: string;
constructor() { constructor() {
const vlogLspConfig = vscode.workspace.getConfiguration('digital-ide.lsp.verilog.linter'); this.activateList.set('vivado', false);
const this.activateList.set('modelsim', false);
this.activateList.set('default', false);
this.activateLinterName = 'default';
this.updateLinter();
// update when user's config is changed
vscode.workspace.onDidChangeConfiguration(() => {
const diagnostor = this.getUserDiagnostorSelection();
const lastDiagnostor = this.activateLinterName;
if (diagnostor !== lastDiagnostor) {
LspOutput.report(`detect linter setting changes, switch ${lastDiagnostor} to ${diagnostor}.`, );
this.updateLinter();
}
});
} }
async initialise(): Promise<void> { async initialise(): Promise<void> {
}
public getUserDiagnostorSelection() {
const vlogLspConfig = vscode.workspace.getConfiguration('function.lsp.linter.vlog');
const diagnostor = vlogLspConfig.get('diagnostor', 'default');
return diagnostor;
}
public updateLinter() {
const diagnostor = this.getUserDiagnostorSelection();
switch (diagnostor) {
case 'vivado': this.activateVivado(); break;
case 'modelsim': this.activateModelsim(); break;
case 'default': this.activateDefault(); break;
case default: this.activateDefault(); break;
}
}
public activateVivado() {
const selectedLinter = vivadoLinter;
if (this.activateList.get('vivado') === false) {
selectedLinter.initialise(HdlLangID.Verilog);
this.activateList.set('vivado', true);
LspOutput.report('vivado linter has been activated', ReportType.Info);
}
this.currentLinter = selectedLinter;
this.activateLinterName = 'vivado';
}
public activateModelsim() {
const selectedLinter = modelsimLinter;
if (this.activateList.get('modelsim') === false) {
selectedLinter.initialise(HdlLangID.Verilog);
this.activateList.set('modelsim', true);
LspOutput.report('modelsim linter has been activated', ReportType.Info);
}
this.currentLinter = selectedLinter;
this.activateLinterName = 'modelsim';
}
public activateDefault() {
const selectedLinter = defaultVlogLinter;
if (this.activateList.get('default') === false) {
this.activateList.set('default', true);
LspOutput.report('default build-in linter has been activated', ReportType.Info);
}
this.currentLinter = selectedLinter;
this.activateLinterName = 'default';
} }
} }