Skip to content

Commit 30dd622

Browse files
authored
Improve iverilog linting (#379)
* add includePath option * add options to specify standard rule * refactor
1 parent ea667fc commit 30dd622

File tree

4 files changed

+148
-59
lines changed

4 files changed

+148
-59
lines changed

CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
66

7+
## [1.x.y] - Unreleased
8+
9+
- Added experimental options for Icarus Verilog linting.
10+
- `verilog.linting.iverilog.includePath` is to specify include directory.
11+
- `verilog.linting.iverilog.verilogHDL.standard` is to specify standard rules for Verilog-HDL files.
12+
- `verilog.linting.iverilog.systemVerilog.standard` is to specify standard rules for SystemVerilog files.
13+
- \[Caution\] I've only tested on Ubuntu on WSL2 platform. If you find any problems about Icarus Verilog linting, please let me know.
14+
715
## [1.8.1] - 2022-12-26
816

917
- Fixed `Instantiate Module` not working issue. [#376](https://github.com/mshr-h/vscode-verilog-hdl-support/issues/376)

README.md

+6
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,12 @@ Use the following settings to configure the extension to your needs.
113113

114114
Add custom arguments to Icarus Verilog for linting, like `-Wall` . The argument `-t null` will be added by the linter automatically.
115115

116+
- `verilog.linting.iverilog.includePath` (Default: nothing)
117+
118+
A list of directory paths to use while Icarus Verilog linting.
119+
All the paths are passed as arguments `-I <directory_path>`.
120+
Paths can be specified either an absolute or a relate to the workspace directory.
121+
116122
- `verilog.linting.iverilog.runAtFileLocation` (Default: `false` )
117123

118124
By default, the linter will be run at the workspace directory. Enable this option to run at the file location. If enabled, `` ` include`` directives should contain file paths relative to the current file.

package.json

+41
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,47 @@
235235
"default": "",
236236
"description": "Add Icarus Verilog arguments here (like macros). They will be added to Icarus Verilog while linting (The command \"-t null\" will be added by the linter by default)."
237237
},
238+
"verilog.linting.iverilog.includePath": {
239+
"scope": "window",
240+
"type": "array",
241+
"items": {
242+
"type": "string"
243+
},
244+
"uniqueItems": true,
245+
"description": "A list of directory paths to use while Icarus Verilog linting."
246+
},
247+
"verilog.linting.iverilog.verilogHDL.standard": {
248+
"scope": "window",
249+
"type": "string",
250+
"enum": [
251+
"Verilog-95",
252+
"Verilog-2001",
253+
"Verilog-2005"
254+
],
255+
"enumDescriptions": [
256+
"IEEE1364-1995, specified by -g1995 option in Icarus Verilog",
257+
"IEEE1364-2001, specified by -g2001 option in Icarus Verilog",
258+
"IEEE1364-2005, specified by -g2005 option in Icarus Verilog"
259+
],
260+
"default": "Verilog-2005",
261+
"description": "Select the standard rule to be used when Icarus Verilog linting for Verilog-HDL files."
262+
},
263+
"verilog.linting.iverilog.systemVerilog.standard": {
264+
"scope": "window",
265+
"type": "string",
266+
"enum": [
267+
"SystemVerilog2005",
268+
"SystemVerilog2009",
269+
"SystemVerilog2012"
270+
],
271+
"enumDescriptions": [
272+
"IEEE1800-2005, specified by -g2005-sv option in Icarus Verilog",
273+
"IEEE1800-2009, specified by -g2009 option in Icarus Verilog",
274+
"IEEE1800-2012, specified by -g2012 option in Icarus Verilog"
275+
],
276+
"default": "SystemVerilog2012",
277+
"description": "Select the standard rule to be used when Icarus Verilog linting for SystemVerilog files."
278+
},
238279
"verilog.linting.iverilog.runAtFileLocation": {
239280
"scope": "window",
240281
"type": "boolean",

src/linter/IcarusLinter.ts

+93-59
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,125 @@
11
import * as vscode from 'vscode';
22
import * as child from 'child_process';
3+
import * as path from 'path';
34
import BaseLinter from './BaseLinter';
45

5-
var isWindows = process.platform === 'win32';
6+
let standardToArg: Map<string, string> = new Map<string, string>([
7+
['Verilog-95', '-g1995'],
8+
['Verilog-2001', '-g2001'],
9+
['Verilog-2005', '-g2005'],
10+
['SystemVerilog2005', '-g2005-sv'],
11+
['SystemVerilog2009', '-g2009'],
12+
['SystemVerilog2012', '-g2012'],
13+
]);
614

715
export default class IcarusLinter extends BaseLinter {
8-
private iverilogPath: string;
9-
private iverilogArgs: string;
16+
private configuration: vscode.WorkspaceConfiguration;
17+
private linterInstalledPath: string;
18+
private arguments: string;
19+
private includePath: string[];
20+
private standards: Map<string, string>;
1021
private runAtFileLocation: boolean;
1122

1223
constructor(diagnosticCollection: vscode.DiagnosticCollection, logger: vscode.LogOutputChannel) {
1324
super('iverilog', diagnosticCollection, logger);
1425
vscode.workspace.onDidChangeConfiguration(() => {
15-
this.getConfig();
26+
this.updateConfig();
1627
});
17-
this.getConfig();
28+
this.updateConfig();
1829
}
1930

20-
private getConfig() {
21-
this.iverilogPath = <string>vscode.workspace.getConfiguration().get('verilog.linting.path');
22-
this.iverilogArgs = <string>(
23-
vscode.workspace.getConfiguration().get('verilog.linting.iverilog.arguments')
24-
);
25-
this.runAtFileLocation = <boolean>(
26-
vscode.workspace.getConfiguration().get('verilog.linting.iverilog.runAtFileLocation')
31+
private updateConfig() {
32+
this.linterInstalledPath = <string>(
33+
vscode.workspace.getConfiguration().get('verilog.linting.path')
2734
);
35+
this.configuration = vscode.workspace.getConfiguration('verilog.linting.iverilog');
36+
this.arguments = <string>this.configuration.get('arguments');
37+
let path = <string[]>this.configuration.get('includePath');
38+
this.includePath = path.map((includePath: string) => this.resolvePath(includePath));
39+
this.standards = new Map<string, string>([
40+
['verilog', this.configuration.get('verilogHDL.standard')],
41+
['systemverilog', this.configuration.get('systemVerilog.standard')],
42+
]);
43+
this.runAtFileLocation = <boolean>this.configuration.get('runAtFileLocation');
44+
}
45+
46+
// returns absolute path
47+
private resolvePath(inputPath: string): string {
48+
if (!path || path.isAbsolute(inputPath) || !vscode.workspace.workspaceFolders[0]) {
49+
return '';
50+
}
51+
return path.join(vscode.workspace.workspaceFolders[0].uri.fsPath, inputPath);
2852
}
2953

3054
protected lint(doc: vscode.TextDocument) {
31-
this.logger.info('[iverilog-lint] iverilog lint requested');
32-
let docUri: string = doc.uri.fsPath; //path of current doc
33-
let lastIndex: number = isWindows == true ? docUri.lastIndexOf('\\') : docUri.lastIndexOf('/');
34-
let docFolder = docUri.substr(0, lastIndex); //folder of current doc
35-
let runLocation: string =
36-
this.runAtFileLocation == true ? docFolder : vscode.workspace.rootPath; //choose correct location to run
37-
let svArgs: string = doc.languageId == 'systemverilog' ? '-g2012' : ''; //SystemVerilog args
38-
let command: string =
39-
this.iverilogPath +
40-
'iverilog ' +
41-
svArgs +
42-
' -t null ' +
43-
this.iverilogArgs +
44-
' "' +
45-
doc.fileName +
46-
'"'; //command to execute
47-
this.logger.info('[iverilog-lint] Execute command: ' + command);
55+
let binPath: string = path.join(this.linterInstalledPath, 'iverilog');
56+
57+
let args: string[] = [];
58+
args.push('-t null');
59+
60+
args.push(standardToArg.get(this.standards.get(doc.languageId)));
61+
args = args.concat(this.includePath.map((path: string) => '-I ' + path));
62+
args.push(this.arguments);
63+
args.push(doc.uri.fsPath);
4864

49-
var foo: child.ChildProcess = child.exec(
65+
let command: string = binPath + ' ' + args.join(' ');
66+
67+
let cwd: string = this.runAtFileLocation
68+
? path.dirname(doc.uri.fsPath)
69+
: vscode.workspace.workspaceFolders[0].uri.fsPath;
70+
71+
this.logger.info('[iverilog-lint] Execute');
72+
this.logger.info('[iverilog-lint] command: ' + command);
73+
this.logger.info('[iverilog-lint] cwd : ' + cwd);
74+
75+
var _: child.ChildProcess = child.exec(
5076
command,
51-
{ cwd: runLocation },
77+
{ cwd: cwd },
5278
(_error: Error, _stdout: string, stderr: string) => {
5379
let diagnostics: vscode.Diagnostic[] = [];
54-
let lines = stderr.split(/\r?\n/g);
5580
// Parse output lines
56-
lines.forEach((line, _) => {
57-
if (line.startsWith(doc.fileName)) {
58-
line = line.replace(doc.fileName, '');
59-
let terms = line.split(':');
60-
let lineNum = parseInt(terms[1].trim()) - 1;
61-
if (terms.length == 3) {
62-
diagnostics.push({
63-
severity: vscode.DiagnosticSeverity.Error,
64-
range: new vscode.Range(lineNum, 0, lineNum, Number.MAX_VALUE),
65-
message: terms[2].trim(),
66-
code: 'iverilog',
67-
source: 'iverilog',
68-
});
69-
} else if (terms.length >= 4) {
70-
let sev: vscode.DiagnosticSeverity;
71-
if (terms[2].trim() == 'error') {
81+
// the message is something like this
82+
// /home/ubuntu/project1/module_1.sv:3: syntax error"
83+
// /home/ubuntu/project1/property_1.sv:3: error: Invalid module instantiation"
84+
stderr.split(/\r?\n/g).forEach((line, _) => {
85+
if (!line.startsWith(doc.fileName)) {
86+
return;
87+
}
88+
line = line.replace(doc.fileName, '');
89+
let terms = line.split(':');
90+
let lineNum = parseInt(terms[1].trim()) - 1;
91+
if (terms.length === 3) {
92+
diagnostics.push({
93+
severity: vscode.DiagnosticSeverity.Error,
94+
range: new vscode.Range(lineNum, 0, lineNum, Number.MAX_VALUE),
95+
message: terms[2].trim(),
96+
code: 'iverilog',
97+
source: 'iverilog',
98+
});
99+
} else if (terms.length >= 4) {
100+
let sev: vscode.DiagnosticSeverity;
101+
switch (terms[2].trim()) {
102+
case 'error':
72103
sev = vscode.DiagnosticSeverity.Error;
73-
} else if (terms[2].trim() == 'warning') {
104+
break;
105+
case 'warning':
74106
sev = vscode.DiagnosticSeverity.Warning;
75-
} else {
107+
break;
108+
default:
76109
sev = vscode.DiagnosticSeverity.Information;
77-
}
78-
diagnostics.push({
79-
severity: sev,
80-
range: new vscode.Range(lineNum, 0, lineNum, Number.MAX_VALUE),
81-
message: terms[3].trim(),
82-
code: 'iverilog',
83-
source: 'iverilog',
84-
});
85110
}
111+
diagnostics.push({
112+
severity: sev,
113+
range: new vscode.Range(lineNum, 0, lineNum, Number.MAX_VALUE),
114+
message: terms[3].trim(),
115+
code: 'iverilog',
116+
source: 'Icarus Verilog',
117+
});
86118
}
87119
});
88-
this.logger.info('[iverilog-lint] ' + diagnostics.length + ' errors/warnings returned');
120+
if (diagnostics.length > 0) {
121+
this.logger.info('[iverilog-lint] ' + diagnostics.length + ' errors/warnings returned');
122+
}
89123
this.diagnosticCollection.set(doc.uri, diagnostics);
90124
}
91125
);

0 commit comments

Comments
 (0)