This commit is contained in:
锦恢 2023-11-21 22:50:07 +08:00
parent 8a480c52af
commit bc08d9a9c4
14 changed files with 1489 additions and 1089 deletions

View File

@ -14,8 +14,8 @@ Bug 修复
Feat Feat
- 增加对 XDCTCL 等脚本的 LSP 支持 - 增加对 XDCTCL 等脚本的 LSP 支持
- 增加 verilog, vhdl, xdc, tcl 等语言的图标 - 增加 verilog, vhdl, xdc, tcl 等语言的图标
- 增加对于 vivado 的支持,用户可以通过添加 vivado 路径的方式来使用 vivado 的仿真和自动纠错 - 增加对于 vivado 的支持,用户可以通过添加 vivado 路径的方式(或者将 bin 文件夹添加到环境变量,默认路径为 C:\Xilinx\Vivado\2018.3\bin来使用 vivado 的仿真和自动纠错
- 增加对于 modelsim 的支持,用户可以通过添加 modelsim 安装路径(或者将 bin 文件夹添加到环境变量,默认路径为 C:\modeltech64_10.4\win64来使用 vivado 的仿真和自动纠错
## [0.1.23] - 2022-12-24 ## [0.1.23] - 2022-12-24
- Finish the css of documentation, see `./css/documentation.css` for detail. - Finish the css of documentation, see `./css/documentation.css` for detail.

File diff suppressed because it is too large Load Diff

View File

@ -40,8 +40,5 @@
"digital-ide.lsp.tool.transformOldPropertyFile.title": "Transform configure file from previous version to new version", "digital-ide.lsp.tool.transformOldPropertyFile.title": "Transform configure file from previous version to new version",
"digital-ide.vhdl2vlog.title": "Translate vhdl code to verilog code", "digital-ide.vhdl2vlog.title": "Translate vhdl code to verilog code",
"digital-ide.fsm.show.title": "Show FSM graph of current file", "digital-ide.fsm.show.title": "Show FSM graph of current file",
"digital-ide.netlist.show.title": "Show netlist of current file", "digital-ide.netlist.show.title": "Show netlist of current file"
"digital-ide.lsp.verilog.linter.title": "type of diagnotor for verilog", }
"digital-ide.lsp.vhdl.linter.title": "type of diagnotor for vhdl",
"digital-ide.lsp.systemverilog.linter.title": "type of diagnotor for systemverilog"
}

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@ -0,0 +1,15 @@
import * as vscode from 'vscode';
interface BaseLinter {
diagnostic: vscode.DiagnosticCollection;
lint(document: vscode.TextDocument): Promise<void>;
}
interface BaseManager {
initialise(): Promise<void>;
}
export {
BaseLinter,
BaseManager
};

View File

@ -0,0 +1,165 @@
import * as vscode from 'vscode';
import { All } from '../../../../resources/hdlParser';
import { isVerilogFile, isVhdlFile } from '../../../hdlFs/file';
import { Position, Range } from '../../../hdlParser/common';
import { hdlSymbolStorage } from '../core';
import { BaseLinter } from './base';
import { LspOutput, ReportType } from '../../../global';
class DefaultVlogLinter implements BaseLinter {
diagnostic: vscode.DiagnosticCollection;
constructor() {
this.diagnostic = vscode.languages.createDiagnosticCollection();
}
async lint(document: vscode.TextDocument): Promise<void> {
const filePath = document.fileName;
const vlogAll = await hdlSymbolStorage.getSymbol(filePath);
// console.log('lint all finish');
if (vlogAll) {
const diagnostics = this.provideDiagnostics(document, vlogAll);
this.diagnostic.set(document.uri, diagnostics);
}
}
private provideDiagnostics(document: vscode.TextDocument, all: All): vscode.Diagnostic[] {
const diagnostics: vscode.Diagnostic[] = [];
if (all.error && all.error.length > 0) {
for (const hdlError of all.error) {
const range = this.makeCorrectRange(document, hdlError.range);
const diag = new vscode.Diagnostic(range, hdlError.message, hdlError.severity);
diag.source = hdlError.source;
diagnostics.push(diag);
}
}
return diagnostics;
}
private makeCorrectRange(document: vscode.TextDocument, range: Position): vscode.Range {
range.line --;
if (range.character === 0 && range.line > 0) {
range.line --;
}
while (range.line > 0) {
const lineContent = document.lineAt(range.line).text;
if (lineContent.trim().length > 0) {
break;
} else {
range.line --;
}
}
const currentLine = document.lineAt(range.line).text;
if (range.character === 0 && currentLine.trim().length > 0) {
range.character = currentLine.trimEnd().length;
}
const position = new vscode.Position(range.line, range.character);
const wordRange = document.getWordRangeAtPosition(position, /[`_0-9a-zA-Z]+/);
if (wordRange) {
return wordRange;
} else {
const errorEnd = new vscode.Position(range.line, range.character + 1);
const errorRange = new vscode.Range(position, errorEnd);
return errorRange;
}
}
async remove(uri: vscode.Uri) {
this.diagnostic.delete(uri);
}
public async initialise() {
for (const doc of vscode.workspace.textDocuments) {
if (isVerilogFile(doc.fileName)) {
// TODO : check performance
await this.lint(doc);
}
}
LspOutput.report('finish initialization of default vlog linter', ReportType.Launch);
}
}
class DefaultVHDLLinter implements BaseLinter {
diagnostic: vscode.DiagnosticCollection;
constructor() {
this.diagnostic = vscode.languages.createDiagnosticCollection();
}
async lint(document: vscode.TextDocument): Promise<void> {
const filePath = document.fileName;
const vhdlAll = await hdlSymbolStorage.getSymbol(filePath);
// console.log('lint all finish');
if (vhdlAll) {
const diagnostics = this.provideDiagnostics(document, vhdlAll);
this.diagnostic.set(document.uri, diagnostics);
}
}
private provideDiagnostics(document: vscode.TextDocument, all: All): vscode.Diagnostic[] {
const diagnostics: vscode.Diagnostic[] = [];
if (all.error && all.error.length > 0) {
for (const hdlError of all.error) {
const range = this.makeCorrectRange(document, hdlError.range);
const diag = new vscode.Diagnostic(range, hdlError.message, hdlError.severity);
diag.source = hdlError.source;
diagnostics.push(diag);
}
}
return diagnostics;
}
private makeCorrectRange(document: vscode.TextDocument, range: Position): vscode.Range {
range.line --;
if (range.character === 0 && range.line > 0) {
range.line --;
}
while (range.line > 0) {
const lineContent = document.lineAt(range.line).text;
if (lineContent.trim().length > 0) {
break;
} else {
range.line --;
}
}
const currentLine = document.lineAt(range.line).text;
if (range.character === 0 && currentLine.trim().length > 0) {
range.character = currentLine.trimEnd().length;
}
const position = new vscode.Position(range.line, range.character);
const wordRange = document.getWordRangeAtPosition(position, /[`_0-9a-zA-Z]+/);
if (wordRange) {
return wordRange;
} else {
const errorEnd = new vscode.Position(range.line, range.character + 1);
const errorRange = new vscode.Range(position, errorEnd);
return errorRange;
}
}
async remove(uri: vscode.Uri) {
this.diagnostic.delete(uri);
}
public async initialise() {
for (const doc of vscode.workspace.textDocuments) {
if (isVhdlFile(doc.fileName)) {
// TODO : check performance
await this.lint(doc);
}
}
LspOutput.report('finish initialization of default vlog linter', ReportType.Launch);
}
}
export {
DefaultVlogLinter,
DefaultVHDLLinter
};

View File

@ -1,5 +1,8 @@
import { vlogLinter } from './vlog'; import { vlogLinterManager } from './vlog';
import { vhdlLinterManager } from './vhdl';
export { export {
vlogLinter vlogLinterManager,
vhdlLinterManager
}; };

View File

@ -0,0 +1,18 @@
import * as vscode from 'vscode';
import { BaseManager } from './base';
class VhdlLinterManager implements BaseManager {
constructor() {
}
async initialise(): Promise<void> {
}
}
const vhdlLinterManager = new VhdlLinterManager();
export {
vhdlLinterManager
};

View File

@ -1,18 +1,248 @@
import * as vscode from "vscode"; import * as vscode from "vscode";
import * as fs from 'fs';
import * as childProcess from 'child_process';
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 VivadoLinter 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[]>();
class VivadoLinter {
diagnostic: vscode.DiagnosticCollection;
constructor() { constructor() {
this.diagnostic = vscode.languages.createDiagnosticCollection(); this.diagnostic = vscode.languages.createDiagnosticCollection();
// configure map for executable file name
this.executableFileMap.set(HdlLangID.Verilog, 'xvlog');
this.executableFileMap.set(HdlLangID.Vhdl, 'xvhdl');
this.executableFileMap.set(HdlLangID.SystemVerilog, 'xvlog');
this.executableFileMap.set(HdlLangID.Unknown, undefined);
// configure map for argruments when lintering
this.linterArgsMap.set(HdlLangID.Verilog, ['--nolog']);
this.linterArgsMap.set(HdlLangID.Vhdl, ['--nolog']);
this.linterArgsMap.set(HdlLangID.SystemVerilog, ['--sv', '--nolog']);
this.linterArgsMap.set(HdlLangID.Unknown, []);
const executablePath = this.selectExecutableFilePath();
this.setExecutablePath(executablePath);
} }
async lint(document: vscode.TextDocument) { async lint(document: vscode.TextDocument) {
const filePath = document.fileName; const filePath = document.fileName;
// acquire install path // acquire install path
const name = "prj.vivado.install.path"; const args = [hdlPath.toSlash(filePath), ...this.linterArgs];
const executor = this.xvlogExecutablePath;
if (executor !== undefined) {
const { stdout, stderr } = 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.vivado.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(/:?\s*(?:\[|\])\s*/);
const headerInfo = tokens[0];
// const standardInfo = tokens[1];
const syntaxInfo = tokens[2];
const parsedPath = tokens[3];
if (headerInfo === 'ERROR') {
const errorInfos = parsedPath.split(':');
const errorLine = parseInt(errorInfos[errorInfos.length - 1]);
const range = this.makeCorrectRange(document, errorLine);
const diag = new vscode.Diagnostic(range, syntaxInfo, vscode.DiagnosticSeverity.Error);
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 selectExecutableFilePath(langID: HdlLangID): string | Path {
// vivado install path stored in prj.vivado.install.path
const vivadoConfig = vscode.workspace.getConfiguration('prj.vivado');
const vivadoInstallPath = vivadoConfig.get('install.path', '');
if (vivadoInstallPath.trim() === '' || !fs.existsSync(vivadoInstallPath)) {
LspOutput.report(`User's Vivado Install Path ${vivadoInstallPath}, which is invalid. Use xvlog in default.`, ReportType.Warn);
LspOutput.report('If you have doubts, check prj.vivado.install.path in setting', ReportType.Warn);
return 'xvlog';
} else {
LspOutput.report(`User's Vivado Install Path ${vivadoInstallPath}, which is invalid`);
const xvlogPath = hdlPath.join(
hdlPath.toSlash(vivadoInstallPath),
// this.selectExecutableBasicName(langID)
);
// prevent path like C://stupid name/xxx/xxx/bin
// blank space
const safeXvlogPath = '"' + xvlogPath + '"';
return safeXvlogPath;
}
}
private selectExecutableBasicName(langID: HdlLangID): string | undefined {
const basicName = this.executableFileMap.get(langID);
if (!basicName) {
return basicName;
}
if (opeParam.os === 'win32') {
// e.g. xvlog.bat
return basicName + '.bat';
} else {
// e.g. xvlog
return basicName;
}
}
public async setExecutablePath(executorPath: string | Path, langID: HdlLangID): Promise<boolean> {
const { stdout, 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 vivado is ready to go!`, ReportType.Launch);
return true;
}
}
}
class XvhdlLinter implements BaseLinter {
diagnostic: vscode.DiagnosticCollection;
xvhdlExecutablePath: string | undefined;
linterArgs: string[] = ['--nolog'];
constructor() {
this.diagnostic = vscode.languages.createDiagnosticCollection();
const executablePath = this.selectExecutableFilePath();
this.setExecutablePath(executablePath);
}
async lint(document: vscode.TextDocument): Promise<void> {
const filePath = document.fileName;
// acquire install path
const args = [hdlPath.toSlash(filePath), ...this.linterArgs];
const executor = this.xvhdlExecutablePath;
if (executor !== undefined) {
const { stdout, stderr } = 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.vivado.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(/:?\s*(?:\[|\])\s*/);
const headerInfo = tokens[0];
// const standardInfo = tokens[1];
const syntaxInfo = tokens[2];
const parsedPath = tokens[3];
if (headerInfo === 'ERROR') {
const errorInfos = parsedPath.split(':');
const errorLine = parseInt(errorInfos[errorInfos.length - 1]);
const range = this.makeCorrectRange(document, errorLine);
const diag = new vscode.Diagnostic(range, syntaxInfo, vscode.DiagnosticSeverity.Error);
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 selectExecutableFilePath(): string | Path {
// vivado install path stored in prj.vivado.install.path
const vivadoConfig = vscode.workspace.getConfiguration('prj.vivado');
const vivadoInstallPath = vivadoConfig.get('install.path', '');
if (vivadoInstallPath.trim() === '' || !fs.existsSync(vivadoInstallPath)) {
LspOutput.report(`User's Vivado Install Path ${vivadoInstallPath}, which is invalid. Use xvhdl in default.`, ReportType.Warn);
LspOutput.report('If you have doubts, check prj.vivado.install.path in setting', ReportType.Warn);
return 'xvhdl';
} else {
LspOutput.report(`User's Vivado Install Path ${vivadoInstallPath}, which is invalid`);
const xvhdlPath = hdlPath.join(
hdlPath.toSlash(vivadoInstallPath),
this.selectXvhdlFilename()
);
// prevent path like C://stupid name/xxx/xxx/bin
// blank space
const safeXvhdlPath = '"' + xvhdlPath + '"';
return safeXvhdlPath;
}
}
private selectXvhdlFilename(): string {
if (opeParam.os === 'win32') {
return 'xvhdl.bat';
} else {
return 'xvhdl';
}
}
public async setExecutablePath(path: string | Path): Promise<boolean> {
const { stdout, stderr } = await easyExec(path, []);
if (stderr.length === 0) {
this.xvhdlExecutablePath = undefined;
LspOutput.report(`fail to execute ${path}! Reason: ${stderr}`, ReportType.Error);
return false;
} else {
this.xvhdlExecutablePath = path;
LspOutput.report(`success to verify ${path}, linter from vivado is ready to go!`, ReportType.Launch);
return true;
}
}
}
export {
VivadoLinter,
XvhdlLinter
};

View File

@ -1,87 +1,19 @@
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { All } from '../../../../resources/hdlParser'; import { BaseManager } from './base';
import { isVerilogFile } from '../../../hdlFs/file';
import { Position, Range } from '../../../hdlParser/common';
import { hdlSymbolStorage } from '../core';
class VlogLinterManager implements BaseManager {
class VlogLinter {
diagnostic: vscode.DiagnosticCollection;
constructor() { constructor() {
this.diagnostic = vscode.languages.createDiagnosticCollection(); const vlogLspConfig = vscode.workspace.getConfiguration('digital-ide.lsp.verilog.linter');
const
} }
async lint(document: vscode.TextDocument) { async initialise(): Promise<void> {
const filePath = document.fileName;
const vlogAll = await hdlSymbolStorage.getSymbol(filePath);
// console.log('lint all finish');
if (vlogAll) {
const diagnostics = this.provideDiagnostics(document, vlogAll);
this.diagnostic.set(document.uri, diagnostics);
}
}
private provideDiagnostics(document: vscode.TextDocument, all: All): vscode.Diagnostic[] {
const diagnostics: vscode.Diagnostic[] = [];
if (all.error && all.error.length > 0) {
for (const hdlError of all.error) {
const range = this.makeCorrectRange(document, hdlError.range);
const diag = new vscode.Diagnostic(range, hdlError.message, hdlError.severity);
diag.source = hdlError.source;
diagnostics.push(diag);
}
}
return diagnostics;
}
private makeCorrectRange(document: vscode.TextDocument, range: Position): vscode.Range {
range.line --;
if (range.character === 0 && range.line > 0) {
range.line --;
}
while (range.line > 0) {
const lineContent = document.lineAt(range.line).text;
if (lineContent.trim().length > 0) {
break;
} else {
range.line --;
}
}
const currentLine = document.lineAt(range.line).text;
if (range.character === 0 && currentLine.trim().length > 0) {
range.character = currentLine.trimEnd().length;
}
const position = new vscode.Position(range.line, range.character);
const wordRange = document.getWordRangeAtPosition(position, /[`_0-9a-zA-Z]+/);
if (wordRange) {
return wordRange;
} else {
const errorEnd = new vscode.Position(range.line, range.character + 1);
const errorRange = new vscode.Range(position, errorEnd);
return errorRange;
}
}
async remove(uri: vscode.Uri) {
this.diagnostic.delete(uri);
}
public async initialise() {
for (const doc of vscode.workspace.textDocuments) {
if (isVerilogFile(doc.fileName)) {
// TODO : check performance
await this.lint(doc);
}
}
} }
} }
const vlogLinter = new VlogLinter(); const vlogLinterManager = new VlogLinterManager();
export { export {
vlogLinter vlogLinterManager
}; };

View File

@ -1,6 +1,6 @@
import { opeParam, OpeParamDefaults } from './opeParam'; import { opeParam, OpeParamDefaults } from './opeParam';
import { PrjInfo, PrjInfoDefaults } from './prjInfo'; import { PrjInfo, PrjInfoDefaults } from './prjInfo';
import { MainOutput, YosysOutput, ReportType } from './outputChannel'; import { MainOutput, LspOutput, YosysOutput, ReportType } from './outputChannel';
import * as Enum from './enum'; import * as Enum from './enum';
import * as Lang from './lang'; import * as Lang from './lang';
@ -20,6 +20,7 @@ export {
AbsPath, AbsPath,
RelPath, RelPath,
MainOutput, MainOutput,
LspOutput,
YosysOutput, YosysOutput,
ReportType, ReportType,
AllowNull AllowNull

View File

@ -74,10 +74,12 @@ class Output {
} }
const MainOutput = new Output('Digital-IDE'); const MainOutput = new Output('Digital-IDE');
const LspOutput = new Output('Digital-IDE Language Server');
const YosysOutput = new Output('Yosys'); const YosysOutput = new Output('Yosys');
export { export {
ReportType, ReportType,
MainOutput, MainOutput,
LspOutput,
YosysOutput YosysOutput
}; };

View File

@ -1,4 +1,5 @@
import * as fs from 'fs'; import * as fs from 'fs';
import * as childProcess from 'child_process';
import { AbsPath } from "."; import { AbsPath } from ".";
@ -34,8 +35,34 @@ function isSameSet<T>(setA: Set<T>, setB: Set<T>): boolean {
return true; return true;
} }
interface ExecutorOutput {
stdout: string
stderr: string
}
/**
* more elegant function to execute command
* @param executor executor
* @param args argruments
* @returns { Promise<ExecutorOutput> }
*/
async function easyExec(executor: string, args: string[]): Promise<ExecutorOutput> {
const allArguments = [executor, ...args];
const command = allArguments.join(' ');
const p = new Promise<ExecutorOutput>( ( resolve, _ ) => {
childProcess.exec(command, ( _, stdout, stderr ) => {
resolve({ stdout, stderr });
});
});
return p;
}
export { export {
PathSet, PathSet,
isSameSet isSameSet,
easyExec
}; };