yosys2digitaljs 0.9.0 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/core.d.ts +3 -1
- package/dist/core.js +61 -2
- package/dist/index.d.ts +3 -3
- package/dist/index.js +8 -41
- package/dist/types.d.ts +10 -10
- package/package.json +1 -1
- package/run_tests.sh +1 -1
- package/src/core.ts +62 -0
- package/src/index.ts +5 -42
- package/tests/overflow_counter.il +78 -0
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# yosys2digitaljs
|
|
2
|
-
This program converts JSON netlist output generated by [Yosys](https://yosyshq.net/yosys/)
|
|
2
|
+
This program converts JSON netlist output generated by [Yosys](https://yosyshq.net/yosys/)
|
|
3
3
|
circuit synthesis software (Github repo [here](https://github.com/YosysHQ/yosys/)) for use with the
|
|
4
4
|
[DigitalJS](http://github.com/tilk/digitaljs) graphical circuit simulator.
|
|
5
5
|
|
|
@@ -26,8 +26,8 @@ Yosys2digitaljs can be used as a library. The API is promise (or async/await) ba
|
|
|
26
26
|
|
|
27
27
|
- `yosys2digitaljs(json, options)` - converts the Yosys JSON output `json` (passed as an JS object) to a DigitalJS representation of the same circuit.
|
|
28
28
|
- `process_sv(sv_text, options)` - converts a single SystemVerilog source passed as a string.
|
|
29
|
-
- `process_files(texts, options)` - converts multiple Verilog/SystemVerilog sources. The `texts` parameter is an object, with keys being file names, and corresponding values containing the file contents as strings. Example: `{ 'test.sv': 'module test; ...' }`.
|
|
30
|
-
- `process(filenames, dirname, options)` - converts Verilog/SystemVerilog sources saved on the filesystem under names `filenames` in the directory `dirname`.
|
|
29
|
+
- `process_files(texts, options)` - converts multiple Verilog/SystemVerilog/RTLIL sources. The `texts` parameter is an object, with keys being file names, and corresponding values containing the file contents as strings. Example: `{ 'test.sv': 'module test; ...' }`.
|
|
30
|
+
- `process(filenames, dirname, options)` - converts Verilog/SystemVerilog/RTLIL sources saved on the filesystem under names `filenames` in the directory `dirname`.
|
|
31
31
|
|
|
32
32
|
The functions return a promise, which fulfills with an object value with following keys:
|
|
33
33
|
|
package/dist/core.d.ts
CHANGED
|
@@ -130,9 +130,11 @@ declare namespace Yosys {
|
|
|
130
130
|
};
|
|
131
131
|
};
|
|
132
132
|
}
|
|
133
|
-
export type ConvertOptions = {
|
|
133
|
+
export declare type ConvertOptions = {
|
|
134
134
|
propagation?: number;
|
|
135
135
|
};
|
|
136
136
|
export declare function yosys2digitaljs(obj: Yosys.Output, options?: ConvertOptions): Digitaljs.TopModule;
|
|
137
137
|
export declare function io_ui(output: Digitaljs.Module): void;
|
|
138
|
+
export declare function prepare_yosys_script(filenames: string[], options: Options): string;
|
|
139
|
+
export declare function prepare_verilator_args(filenames: string[]): string[];
|
|
138
140
|
export {};
|
package/dist/core.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.yosys2digitaljs =
|
|
5
|
-
exports.io_ui = io_ui;
|
|
4
|
+
exports.prepare_verilator_args = exports.prepare_yosys_script = exports.io_ui = exports.yosys2digitaljs = void 0;
|
|
6
5
|
const HashMap = require("hashmap");
|
|
7
6
|
const bigInt = require("big-integer");
|
|
8
7
|
const _3vl_1 = require("3vl");
|
|
@@ -1012,6 +1011,7 @@ function yosys2digitaljs(obj, options = {}) {
|
|
|
1012
1011
|
output.subcircuits[x] = out[x];
|
|
1013
1012
|
return output;
|
|
1014
1013
|
}
|
|
1014
|
+
exports.yosys2digitaljs = yosys2digitaljs;
|
|
1015
1015
|
function io_ui(output) {
|
|
1016
1016
|
for (const [name, dev] of Object.entries(output.devices)) {
|
|
1017
1017
|
if (dev.type == 'Input' || dev.type == 'Output') {
|
|
@@ -1034,3 +1034,62 @@ function io_ui(output) {
|
|
|
1034
1034
|
}
|
|
1035
1035
|
}
|
|
1036
1036
|
}
|
|
1037
|
+
exports.io_ui = io_ui;
|
|
1038
|
+
function ansi_c_escape_contents(cmd) {
|
|
1039
|
+
function func(ch) {
|
|
1040
|
+
if (ch == '\t')
|
|
1041
|
+
return '\\t';
|
|
1042
|
+
if (ch == '\r')
|
|
1043
|
+
return '\\r';
|
|
1044
|
+
if (ch == '\n')
|
|
1045
|
+
return '\\n';
|
|
1046
|
+
return '\\x' + ch.charCodeAt(0).toString(16).padStart(2, '0');
|
|
1047
|
+
}
|
|
1048
|
+
return cmd.replace(/(["'\\])/g, '\\$1')
|
|
1049
|
+
.replace(/[\x00-\x1F\x7F-\x9F]/g, func);
|
|
1050
|
+
}
|
|
1051
|
+
function ansi_c_escape(cmd) {
|
|
1052
|
+
return '"' + ansi_c_escape_contents(cmd) + '"';
|
|
1053
|
+
}
|
|
1054
|
+
function shell_escape_contents(cmd) {
|
|
1055
|
+
return cmd.replace(/(["\r\n$`\\])/g, '\\$1');
|
|
1056
|
+
}
|
|
1057
|
+
function shell_escape(cmd) {
|
|
1058
|
+
return '"' + shell_escape_contents(cmd) + '"';
|
|
1059
|
+
}
|
|
1060
|
+
function process_filename(filename) {
|
|
1061
|
+
var _a;
|
|
1062
|
+
const ext = (_a = filename.match(/\.[a-z]+$/)) === null || _a === void 0 ? void 0 : _a[0];
|
|
1063
|
+
const commands = {
|
|
1064
|
+
'.il': 'read_rtlil',
|
|
1065
|
+
'.sv': 'read_verilog -sv',
|
|
1066
|
+
'.v': 'read_verilog',
|
|
1067
|
+
'.vh': 'read_verilog'
|
|
1068
|
+
};
|
|
1069
|
+
if (ext && ext in commands) {
|
|
1070
|
+
return `${commands[ext]} ${ansi_c_escape(filename)}`;
|
|
1071
|
+
}
|
|
1072
|
+
else {
|
|
1073
|
+
return '';
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
function prepare_yosys_script(filenames, options) {
|
|
1077
|
+
const optimize_simp = options.optimize ? "opt" : "opt_clean";
|
|
1078
|
+
const optimize = options.optimize ? "opt -full" : "opt_clean";
|
|
1079
|
+
const fsmexpand = options.fsmexpand ? " -expand" : "";
|
|
1080
|
+
const fsmpass = options.fsm == "nomap"
|
|
1081
|
+
? "fsm -nomap" + fsmexpand
|
|
1082
|
+
: options.fsm
|
|
1083
|
+
? "fsm" + fsmexpand
|
|
1084
|
+
: "";
|
|
1085
|
+
const readFilesScript = filenames
|
|
1086
|
+
.map((filename) => process_filename(filename));
|
|
1087
|
+
const yosysScript = [...readFilesScript, 'setattr -mod -unset top', 'hierarchy -auto-top', 'proc', optimize_simp, fsmpass, 'memory -nomap', 'wreduce -memx', optimize];
|
|
1088
|
+
return yosysScript.join('; ');
|
|
1089
|
+
}
|
|
1090
|
+
exports.prepare_yosys_script = prepare_yosys_script;
|
|
1091
|
+
function prepare_verilator_args(filenames) {
|
|
1092
|
+
const processed_filenames = filenames.map(shell_escape);
|
|
1093
|
+
return ['-lint-only', '-Wall', '-Wno-DECLFILENAME', '-Wno-UNOPT', '-Wno-UNOPTFLAT', ...processed_filenames];
|
|
1094
|
+
}
|
|
1095
|
+
exports.prepare_verilator_args = prepare_verilator_args;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { ConvertOptions, Digitaljs } from './core';
|
|
3
|
-
type Options = ConvertOptions & {
|
|
3
|
+
declare type Options = ConvertOptions & {
|
|
4
4
|
optimize?: boolean;
|
|
5
5
|
fsmexpand?: boolean;
|
|
6
6
|
fsm?: boolean | "nomap";
|
|
7
7
|
timeout?: number;
|
|
8
8
|
lint?: boolean;
|
|
9
9
|
};
|
|
10
|
-
type Output = {
|
|
10
|
+
declare type Output = {
|
|
11
11
|
output?: Digitaljs.TopModule;
|
|
12
12
|
yosys_output?: any;
|
|
13
13
|
yosys_stdout: string;
|
|
14
14
|
yosys_stderr: string;
|
|
15
15
|
lint?: LintMessage[];
|
|
16
16
|
};
|
|
17
|
-
type LintMessage = {
|
|
17
|
+
declare type LintMessage = {
|
|
18
18
|
type: string;
|
|
19
19
|
file: string;
|
|
20
20
|
line: number;
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.verilator_lint =
|
|
5
|
-
exports.process = process;
|
|
6
|
-
exports.process_files = process_files;
|
|
7
|
-
exports.process_sv = process_sv;
|
|
4
|
+
exports.process_sv = exports.process_files = exports.process = exports.verilator_lint = void 0;
|
|
8
5
|
const tmp = require("tmp-promise");
|
|
9
6
|
const child_process = require("child_process");
|
|
10
7
|
const fs = require("fs");
|
|
@@ -12,37 +9,11 @@ const path = require("path");
|
|
|
12
9
|
const util_1 = require("util");
|
|
13
10
|
const core_1 = require("./core");
|
|
14
11
|
const sanitize = require('sanitize-filename');
|
|
15
|
-
function ansi_c_escape_contents(cmd) {
|
|
16
|
-
function func(ch) {
|
|
17
|
-
if (ch == '\t')
|
|
18
|
-
return '\\t';
|
|
19
|
-
if (ch == '\r')
|
|
20
|
-
return '\\r';
|
|
21
|
-
if (ch == '\n')
|
|
22
|
-
return '\\n';
|
|
23
|
-
return '\\x' + ch.charCodeAt(0).toString(16).padStart(2, '0');
|
|
24
|
-
}
|
|
25
|
-
return cmd.replace(/(["'\\])/g, '\\$1')
|
|
26
|
-
.replace(/[\x00-\x1F\x7F-\x9F]/g, func);
|
|
27
|
-
}
|
|
28
|
-
function ansi_c_escape(cmd) {
|
|
29
|
-
return '"' + ansi_c_escape_contents(cmd) + '"';
|
|
30
|
-
}
|
|
31
|
-
function shell_escape_contents(cmd) {
|
|
32
|
-
return cmd.replace(/(["\r\n$`\\])/g, '\\$1');
|
|
33
|
-
}
|
|
34
|
-
function shell_escape(cmd) {
|
|
35
|
-
return '"' + shell_escape_contents(cmd) + '"';
|
|
36
|
-
}
|
|
37
|
-
function process_filename(filename) {
|
|
38
|
-
const flags = /\.sv$/.test(filename) ? " -sv" : "";
|
|
39
|
-
return "read_verilog" + flags + " " + ansi_c_escape(filename);
|
|
40
|
-
}
|
|
41
12
|
const verilator_re = /^%(Warning|Error)[^:]*: ([^:]*):([0-9]+):([0-9]+): (.*)$/;
|
|
42
13
|
async function verilator_lint(filenames, dirname, options = {}) {
|
|
43
14
|
try {
|
|
44
15
|
const output = [];
|
|
45
|
-
const verilator_result = await (0, util_1.promisify)(child_process.exec)(
|
|
16
|
+
const verilator_result = await (0, util_1.promisify)(child_process.exec)(`timeout -k10s 40s verilator ${(0, core_1.prepare_verilator_args)(filenames).join(' ')}`, { maxBuffer: 1000000, cwd: dirname || null, timeout: options.timeout || 60000 })
|
|
46
17
|
.catch(exc => exc);
|
|
47
18
|
for (const line of verilator_result.stderr.split('\n')) {
|
|
48
19
|
const result = line.match(verilator_re);
|
|
@@ -62,18 +33,11 @@ async function verilator_lint(filenames, dirname, options = {}) {
|
|
|
62
33
|
return null;
|
|
63
34
|
}
|
|
64
35
|
}
|
|
36
|
+
exports.verilator_lint = verilator_lint;
|
|
65
37
|
async function process(filenames, dirname, options = {}) {
|
|
66
|
-
const optimize_simp = options.optimize ? "; opt" : "; opt_clean";
|
|
67
|
-
const optimize = options.optimize ? "; opt -full" : "; opt_clean";
|
|
68
|
-
const fsmexpand = options.fsmexpand ? " -expand" : "";
|
|
69
|
-
const fsmpass = options.fsm == "nomap" ? "; fsm -nomap" + fsmexpand
|
|
70
|
-
: options.fsm ? "; fsm" + fsmexpand
|
|
71
|
-
: "";
|
|
72
38
|
const tmpjson = await tmp.tmpName({ postfix: '.json' });
|
|
73
39
|
let obj = undefined;
|
|
74
|
-
const yosys_result = await (0, util_1.promisify)(child_process.exec)(
|
|
75
|
-
'; hierarchy -auto-top; proc' + optimize_simp + fsmpass + '; memory -nomap; wreduce -memx' +
|
|
76
|
-
optimize + '" -o "' + tmpjson + '"', { maxBuffer: 1000000, cwd: dirname || null, timeout: options.timeout || 60000 })
|
|
40
|
+
const yosys_result = await (0, util_1.promisify)(child_process.exec)(`timeout -k10s 40s yosys -p "${(0, core_1.prepare_yosys_script)(filenames, options)}" -o ${tmpjson}`, { maxBuffer: 1000000, cwd: dirname || null, timeout: options.timeout || 60000 })
|
|
77
41
|
.catch(exc => exc);
|
|
78
42
|
try {
|
|
79
43
|
if (yosys_result instanceof Error) {
|
|
@@ -106,6 +70,7 @@ async function process(filenames, dirname, options = {}) {
|
|
|
106
70
|
throw exc;
|
|
107
71
|
}
|
|
108
72
|
}
|
|
73
|
+
exports.process = process;
|
|
109
74
|
async function process_files(data, options = {}) {
|
|
110
75
|
const dir = await tmp.dir();
|
|
111
76
|
const names = [];
|
|
@@ -113,7 +78,7 @@ async function process_files(data, options = {}) {
|
|
|
113
78
|
for (const [name, content] of Object.entries(data)) {
|
|
114
79
|
const sname = sanitize(name);
|
|
115
80
|
await (0, util_1.promisify)(fs.writeFile)(path.resolve(dir.path, sname), content);
|
|
116
|
-
if (/\.(v|sv)$/.test(sname))
|
|
81
|
+
if (/\.(v|sv|il)$/.test(sname))
|
|
117
82
|
names.push(sname);
|
|
118
83
|
}
|
|
119
84
|
return await process(names, dir.path, options);
|
|
@@ -125,6 +90,7 @@ async function process_files(data, options = {}) {
|
|
|
125
90
|
dir.cleanup();
|
|
126
91
|
}
|
|
127
92
|
}
|
|
93
|
+
exports.process_files = process_files;
|
|
128
94
|
async function process_sv(text, options = {}) {
|
|
129
95
|
const tmpsv = await tmp.file({ postfix: '.sv' });
|
|
130
96
|
try {
|
|
@@ -136,3 +102,4 @@ async function process_sv(text, options = {}) {
|
|
|
136
102
|
tmpsv.cleanup();
|
|
137
103
|
}
|
|
138
104
|
}
|
|
105
|
+
exports.process_sv = process_sv;
|
package/dist/types.d.ts
CHANGED
|
@@ -129,43 +129,43 @@ declare namespace Yosys {
|
|
|
129
129
|
};
|
|
130
130
|
};
|
|
131
131
|
}
|
|
132
|
-
type ConvertOptions = {
|
|
132
|
+
declare type ConvertOptions = {
|
|
133
133
|
propagation?: number;
|
|
134
134
|
};
|
|
135
|
-
type Options = ConvertOptions & {
|
|
135
|
+
declare type Options = ConvertOptions & {
|
|
136
136
|
optimize?: boolean;
|
|
137
137
|
fsmexpand?: boolean;
|
|
138
138
|
fsm?: boolean | "nomap";
|
|
139
139
|
timeout?: number;
|
|
140
140
|
lint?: boolean;
|
|
141
141
|
};
|
|
142
|
-
type Output = {
|
|
142
|
+
declare type Output = {
|
|
143
143
|
output?: Digitaljs.TopModule;
|
|
144
144
|
yosys_output?: any;
|
|
145
145
|
yosys_stdout: string;
|
|
146
146
|
yosys_stderr: string;
|
|
147
147
|
lint?: LintMessage[];
|
|
148
148
|
};
|
|
149
|
-
type Portmap = {
|
|
149
|
+
declare type Portmap = {
|
|
150
150
|
[key: string]: string;
|
|
151
151
|
};
|
|
152
|
-
type Portmaps = {
|
|
152
|
+
declare type Portmaps = {
|
|
153
153
|
[key: string]: Portmap;
|
|
154
154
|
};
|
|
155
|
-
type Bit = Yosys.Bit | `bit${number}`;
|
|
156
|
-
type Net = Bit[];
|
|
157
|
-
type NetInfo = {
|
|
155
|
+
declare type Bit = Yosys.Bit | `bit${number}`;
|
|
156
|
+
declare type Net = Bit[];
|
|
157
|
+
declare type NetInfo = {
|
|
158
158
|
source: undefined | Digitaljs.Port;
|
|
159
159
|
targets: Digitaljs.Port[];
|
|
160
160
|
name: undefined | string;
|
|
161
161
|
source_positions: Digitaljs.SourcePosition[];
|
|
162
162
|
};
|
|
163
|
-
type BitInfo = {
|
|
163
|
+
declare type BitInfo = {
|
|
164
164
|
id: string;
|
|
165
165
|
port: string;
|
|
166
166
|
num: number;
|
|
167
167
|
};
|
|
168
|
-
type LintMessage = {
|
|
168
|
+
declare type LintMessage = {
|
|
169
169
|
type: string;
|
|
170
170
|
file: string;
|
|
171
171
|
line: number;
|
package/package.json
CHANGED
package/run_tests.sh
CHANGED
package/src/core.ts
CHANGED
|
@@ -1158,3 +1158,65 @@ export function io_ui(output: Digitaljs.Module) {
|
|
|
1158
1158
|
}
|
|
1159
1159
|
}
|
|
1160
1160
|
}
|
|
1161
|
+
|
|
1162
|
+
function ansi_c_escape_contents(cmd: string): string {
|
|
1163
|
+
function func(ch: string) {
|
|
1164
|
+
if (ch == '\t') return '\\t';
|
|
1165
|
+
if (ch == '\r') return '\\r';
|
|
1166
|
+
if (ch == '\n') return '\\n';
|
|
1167
|
+
return '\\x' + ch.charCodeAt(0).toString(16).padStart(2, '0');
|
|
1168
|
+
}
|
|
1169
|
+
return cmd.replace(/(["'\\])/g,'\\$1')
|
|
1170
|
+
.replace(/[\x00-\x1F\x7F-\x9F]/g, func);
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
function ansi_c_escape(cmd: string): string {
|
|
1174
|
+
return '"' + ansi_c_escape_contents(cmd) + '"';
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
function shell_escape_contents(cmd: string): string {
|
|
1178
|
+
return cmd.replace(/(["\r\n$`\\])/g,'\\$1');
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
function shell_escape(cmd: string): string {
|
|
1182
|
+
return '"' + shell_escape_contents(cmd) + '"';
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
function process_filename(filename: string): string {
|
|
1186
|
+
const ext = filename.match(/\.[a-z]+$/)?.[0];
|
|
1187
|
+
|
|
1188
|
+
const commands: { [key: string]: string } = {
|
|
1189
|
+
'.il': 'read_rtlil',
|
|
1190
|
+
'.sv': 'read_verilog -sv',
|
|
1191
|
+
'.v': 'read_verilog',
|
|
1192
|
+
'.vh': 'read_verilog'
|
|
1193
|
+
};
|
|
1194
|
+
|
|
1195
|
+
if (ext && ext in commands) {
|
|
1196
|
+
return `${commands[ext]} ${ansi_c_escape(filename)}`;
|
|
1197
|
+
} else {
|
|
1198
|
+
return ''
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
export function prepare_yosys_script(filenames: string[], options: Options): string {
|
|
1203
|
+
const optimize_simp = options.optimize ? "opt" : "opt_clean";
|
|
1204
|
+
const optimize = options.optimize ? "opt -full" : "opt_clean";
|
|
1205
|
+
const fsmexpand = options.fsmexpand ? " -expand" : "";
|
|
1206
|
+
const fsmpass = options.fsm == "nomap"
|
|
1207
|
+
? "fsm -nomap" + fsmexpand
|
|
1208
|
+
: options.fsm
|
|
1209
|
+
? "fsm" + fsmexpand
|
|
1210
|
+
: "";
|
|
1211
|
+
|
|
1212
|
+
const readFilesScript = filenames
|
|
1213
|
+
.map((filename) => process_filename(filename))
|
|
1214
|
+
|
|
1215
|
+
const yosysScript = [...readFilesScript, 'setattr -mod -unset top', 'hierarchy -auto-top', 'proc', optimize_simp, fsmpass, 'memory -nomap', 'wreduce -memx', optimize]
|
|
1216
|
+
return yosysScript.join('; ');
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
export function prepare_verilator_args(filenames: string[]): string[] {
|
|
1220
|
+
const processed_filenames = filenames.map(shell_escape);
|
|
1221
|
+
return ['-lint-only', '-Wall', '-Wno-DECLFILENAME', '-Wno-UNOPT', '-Wno-UNOPTFLAT', ...processed_filenames];
|
|
1222
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -6,7 +6,7 @@ import * as child_process from 'child_process';
|
|
|
6
6
|
import * as fs from 'fs';
|
|
7
7
|
import * as path from 'path';
|
|
8
8
|
import { promisify } from 'util';
|
|
9
|
-
import { yosys2digitaljs, ConvertOptions, Digitaljs } from './core';
|
|
9
|
+
import { prepare_yosys_script, prepare_verilator_args, yosys2digitaljs, ConvertOptions, Digitaljs } from './core';
|
|
10
10
|
const sanitize = require('sanitize-filename');
|
|
11
11
|
|
|
12
12
|
type Options = ConvertOptions & {
|
|
@@ -33,42 +33,13 @@ type LintMessage = {
|
|
|
33
33
|
message: string
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
function ansi_c_escape_contents(cmd: string): string {
|
|
38
|
-
function func(ch: string) {
|
|
39
|
-
if (ch == '\t') return '\\t';
|
|
40
|
-
if (ch == '\r') return '\\r';
|
|
41
|
-
if (ch == '\n') return '\\n';
|
|
42
|
-
return '\\x' + ch.charCodeAt(0).toString(16).padStart(2, '0');
|
|
43
|
-
}
|
|
44
|
-
return cmd.replace(/(["'\\])/g,'\\$1')
|
|
45
|
-
.replace(/[\x00-\x1F\x7F-\x9F]/g, func);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function ansi_c_escape(cmd: string): string {
|
|
49
|
-
return '"' + ansi_c_escape_contents(cmd) + '"';
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function shell_escape_contents(cmd: string): string {
|
|
53
|
-
return cmd.replace(/(["\r\n$`\\])/g,'\\$1');
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function shell_escape(cmd: string): string {
|
|
57
|
-
return '"' + shell_escape_contents(cmd) + '"';
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function process_filename(filename: string): string {
|
|
61
|
-
const flags = /\.sv$/.test(filename) ? " -sv" : "";
|
|
62
|
-
return "read_verilog" + flags + " " + ansi_c_escape(filename);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
36
|
const verilator_re = /^%(Warning|Error)[^:]*: ([^:]*):([0-9]+):([0-9]+): (.*)$/;
|
|
66
37
|
|
|
67
38
|
export async function verilator_lint(filenames: string[], dirname?: string, options: Options = {}): Promise<LintMessage[]> {
|
|
68
39
|
try {
|
|
69
40
|
const output: LintMessage[] = [];
|
|
70
41
|
const verilator_result: {stdout: string, stderr: string} = await promisify(child_process.exec)(
|
|
71
|
-
|
|
42
|
+
`timeout -k10s 40s verilator ${prepare_verilator_args(filenames).join(' ')}`,
|
|
72
43
|
{maxBuffer: 1000000, cwd: dirname || null, timeout: options.timeout || 60000})
|
|
73
44
|
.catch(exc => exc);
|
|
74
45
|
for (const line of verilator_result.stderr.split('\n')) {
|
|
@@ -89,23 +60,15 @@ export async function verilator_lint(filenames: string[], dirname?: string, opti
|
|
|
89
60
|
}
|
|
90
61
|
|
|
91
62
|
export async function process(filenames: string[], dirname?: string, options: Options = {}): Promise<Output> {
|
|
92
|
-
const optimize_simp = options.optimize ? "; opt" : "; opt_clean";
|
|
93
|
-
const optimize = options.optimize ? "; opt -full" : "; opt_clean";
|
|
94
|
-
const fsmexpand = options.fsmexpand ? " -expand" : "";
|
|
95
|
-
const fsmpass = options.fsm == "nomap" ? "; fsm -nomap" + fsmexpand
|
|
96
|
-
: options.fsm ? "; fsm" + fsmexpand
|
|
97
|
-
: "";
|
|
98
63
|
const tmpjson = await tmp.tmpName({ postfix: '.json' });
|
|
99
64
|
let obj = undefined;
|
|
100
65
|
const yosys_result: {stdout: string, stderr: string, killed?: boolean, code?: number} = await promisify(child_process.exec)(
|
|
101
|
-
|
|
102
|
-
'; hierarchy -auto-top; proc' + optimize_simp + fsmpass + '; memory -nomap; wreduce -memx' +
|
|
103
|
-
optimize + '" -o "' + tmpjson + '"',
|
|
66
|
+
`timeout -k10s 40s yosys -p "${prepare_yosys_script(filenames, options)}" -o ${tmpjson}`,
|
|
104
67
|
{maxBuffer: 1000000, cwd: dirname || null, timeout: options.timeout || 60000})
|
|
105
68
|
.catch(exc => exc);
|
|
106
69
|
try {
|
|
107
70
|
if (yosys_result instanceof Error) {
|
|
108
|
-
if (yosys_result.killed)
|
|
71
|
+
if (yosys_result.killed)
|
|
109
72
|
yosys_result.message = "Yosys killed"
|
|
110
73
|
else if (yosys_result.code)
|
|
111
74
|
yosys_result.message = "Yosys failed with code " + yosys_result.code;
|
|
@@ -140,7 +103,7 @@ export async function process_files(data: {[key: string]: string}, options: Opti
|
|
|
140
103
|
for (const [name, content] of Object.entries(data)) {
|
|
141
104
|
const sname = sanitize(name);
|
|
142
105
|
await promisify(fs.writeFile)(path.resolve(dir.path, sname), content);
|
|
143
|
-
if (/\.(v|sv)$/.test(sname)) names.push(sname);
|
|
106
|
+
if (/\.(v|sv|il)$/.test(sname)) names.push(sname);
|
|
144
107
|
}
|
|
145
108
|
return await process(names, dir.path, options);
|
|
146
109
|
} finally {
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
attribute \generator "Amaranth"
|
|
2
|
+
attribute \src "<exec>:18"
|
|
3
|
+
attribute \top 1
|
|
4
|
+
module \top
|
|
5
|
+
|
|
6
|
+
attribute \src "<exec>:13"
|
|
7
|
+
attribute \init 4'0000
|
|
8
|
+
wire width 4 \count
|
|
9
|
+
|
|
10
|
+
attribute \src "<exec>:8"
|
|
11
|
+
wire width 1 input 0 \en
|
|
12
|
+
|
|
13
|
+
attribute \src "/lib/python3.11/site-packages/amaranth/hdl/_ir.py:283"
|
|
14
|
+
wire width 1 input 1 \clk
|
|
15
|
+
|
|
16
|
+
attribute \src "/lib/python3.11/site-packages/amaranth/hdl/_ir.py:283"
|
|
17
|
+
wire width 1 input 2 \rst
|
|
18
|
+
|
|
19
|
+
attribute \src "<exec>:9"
|
|
20
|
+
wire width 1 output 3 \ovf
|
|
21
|
+
|
|
22
|
+
wire width 5 $1
|
|
23
|
+
|
|
24
|
+
wire width 4 $2
|
|
25
|
+
|
|
26
|
+
attribute \src "<exec>:19"
|
|
27
|
+
cell $eq $3
|
|
28
|
+
parameter \A_SIGNED 0
|
|
29
|
+
parameter \B_SIGNED 0
|
|
30
|
+
parameter \A_WIDTH 4
|
|
31
|
+
parameter \B_WIDTH 4
|
|
32
|
+
parameter \Y_WIDTH 1
|
|
33
|
+
connect \A \count [3:0]
|
|
34
|
+
connect \B 4'1010
|
|
35
|
+
connect \Y \ovf
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
attribute \src "<exec>:24"
|
|
39
|
+
cell $add $4
|
|
40
|
+
parameter \A_SIGNED 0
|
|
41
|
+
parameter \B_SIGNED 0
|
|
42
|
+
parameter \A_WIDTH 4
|
|
43
|
+
parameter \B_WIDTH 1
|
|
44
|
+
parameter \Y_WIDTH 5
|
|
45
|
+
connect \A \count [3:0]
|
|
46
|
+
connect \B 1'1
|
|
47
|
+
connect \Y $1
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
attribute \src "<exec>:13"
|
|
51
|
+
process $5
|
|
52
|
+
assign $2 [3:0] \count [3:0]
|
|
53
|
+
switch \en [0]
|
|
54
|
+
case 1'1
|
|
55
|
+
switch \ovf [0]
|
|
56
|
+
case 1'1
|
|
57
|
+
assign $2 [3:0] 4'0000
|
|
58
|
+
case
|
|
59
|
+
assign $2 [3:0] $1 [3:0]
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
switch \rst [0]
|
|
63
|
+
case 1'1
|
|
64
|
+
assign $2 [3:0] 4'0000
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
attribute \src "<exec>:13"
|
|
69
|
+
cell $dff $6
|
|
70
|
+
parameter \WIDTH 4
|
|
71
|
+
parameter \CLK_POLARITY 1
|
|
72
|
+
connect \D $2 [3:0]
|
|
73
|
+
connect \CLK \clk [0]
|
|
74
|
+
connect \Q \count
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
end
|
|
78
|
+
|