yosys2digitaljs 0.8.0 → 0.9.1
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/dist/core.d.ts +140 -0
- package/dist/core.js +1082 -0
- package/dist/index.d.ts +4 -139
- package/dist/index.js +9 -1089
- package/dist/types.d.ts +174 -0
- package/dist/types.js +6 -0
- package/package.json +20 -7
- package/process.js +3 -2
- package/src/core.ts +1210 -0
- package/src/index.ts +6 -1215
- package/src/types.ts +182 -0
- package/test.sv +0 -31
- package/test1.sv +0 -19
- package/test1.v +0 -19
- package/test2.v +0 -19
- package/test3.sv +0 -63
- package/test3.v +0 -19
- package/test4.sv +0 -62
- package/yosyss +0 -4
package/src/index.ts
CHANGED
|
@@ -3,256 +3,11 @@
|
|
|
3
3
|
|
|
4
4
|
import * as tmp from 'tmp-promise';
|
|
5
5
|
import * as child_process from 'child_process';
|
|
6
|
-
import * as assert from 'assert';
|
|
7
6
|
import * as fs from 'fs';
|
|
8
7
|
import * as path from 'path';
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
|
|
12
|
-
import {Vector3vl, Mem3vl} from '3vl';
|
|
13
|
-
|
|
14
|
-
const topsort: <T>(edges:T[][], options?:{continueOnCircularDependency: boolean}) => T[] = require('topsort');
|
|
15
|
-
const sanitize = require("sanitize-filename");
|
|
16
|
-
|
|
17
|
-
const unary_gates = new Set([
|
|
18
|
-
'$not', '$neg', '$pos', '$reduce_and', '$reduce_or', '$reduce_xor',
|
|
19
|
-
'$reduce_xnor', '$reduce_bool', '$logic_not']);
|
|
20
|
-
const binary_gates = new Set([
|
|
21
|
-
'$and', '$or', '$xor', '$xnor',
|
|
22
|
-
'$add', '$sub', '$mul', '$div', '$mod', '$pow',
|
|
23
|
-
'$lt', '$le', '$eq', '$ne', '$ge', '$gt', '$eqx', '$nex',
|
|
24
|
-
'$shl', '$shr', '$sshl', '$sshr', '$shift', '$shiftx',
|
|
25
|
-
'$logic_and', '$logic_or']);
|
|
26
|
-
const gate_subst = new Map([
|
|
27
|
-
['$not', 'Not'],
|
|
28
|
-
['$and', 'And'],
|
|
29
|
-
['$nand', 'Nand'],
|
|
30
|
-
['$or', 'Or'],
|
|
31
|
-
['$nor', 'Nor'],
|
|
32
|
-
['$xor', 'Xor'],
|
|
33
|
-
['$xnor', 'Xnor'],
|
|
34
|
-
['$reduce_and', 'AndReduce'],
|
|
35
|
-
['$reduce_nand', 'NandReduce'],
|
|
36
|
-
['$reduce_or', 'OrReduce'],
|
|
37
|
-
['$reduce_nor', 'NorReduce'],
|
|
38
|
-
['$reduce_xor', 'XorReduce'],
|
|
39
|
-
['$reduce_xnor', 'XnorReduce'],
|
|
40
|
-
['$reduce_bool', 'OrReduce'],
|
|
41
|
-
['$logic_not', 'NorReduce'],
|
|
42
|
-
['$repeater', 'Repeater'],
|
|
43
|
-
['$shl', 'ShiftLeft'],
|
|
44
|
-
['$shr', 'ShiftRight'],
|
|
45
|
-
['$lt', 'Lt'],
|
|
46
|
-
['$le', 'Le'],
|
|
47
|
-
['$eq', 'Eq'],
|
|
48
|
-
['$ne', 'Ne'],
|
|
49
|
-
['$gt', 'Gt'],
|
|
50
|
-
['$ge', 'Ge'],
|
|
51
|
-
['$constant', 'Constant'],
|
|
52
|
-
['$neg', 'Negation'],
|
|
53
|
-
['$pos', 'UnaryPlus'],
|
|
54
|
-
['$add', 'Addition'],
|
|
55
|
-
['$sub', 'Subtraction'],
|
|
56
|
-
['$mul', 'Multiplication'],
|
|
57
|
-
['$div', 'Division'],
|
|
58
|
-
['$mod', 'Modulo'],
|
|
59
|
-
['$pow', 'Power'],
|
|
60
|
-
['$mux', 'Mux'],
|
|
61
|
-
['$pmux', 'Mux1Hot'],
|
|
62
|
-
['$mem', 'Memory'],
|
|
63
|
-
['$mem_v2', 'Memory'],
|
|
64
|
-
['$lut', 'Memory'],
|
|
65
|
-
['$fsm', 'FSM'],
|
|
66
|
-
['$clock', 'Clock'],
|
|
67
|
-
['$button', 'Button'],
|
|
68
|
-
['$lamp', 'Lamp'],
|
|
69
|
-
['$numdisplay', 'NumDisplay'],
|
|
70
|
-
['$numentry', 'NumEntry'],
|
|
71
|
-
['$input', 'Input'],
|
|
72
|
-
['$output', 'Output'],
|
|
73
|
-
['$busgroup', 'BusGroup'],
|
|
74
|
-
['$busungroup', 'BusUngroup'],
|
|
75
|
-
['$busslice', 'BusSlice'],
|
|
76
|
-
['$zeroextend', 'ZeroExtend'],
|
|
77
|
-
['$signextend', 'SignExtend'],
|
|
78
|
-
['$reduce_bool', 'OrReduce'],
|
|
79
|
-
['$eqx', 'Eq'],
|
|
80
|
-
['$nex', 'Ne'],
|
|
81
|
-
['$sshl', 'ShiftLeft'],
|
|
82
|
-
['$sshr', 'ShiftRight'],
|
|
83
|
-
['$shift', 'ShiftRight'],
|
|
84
|
-
['$shiftx', 'ShiftRight'],
|
|
85
|
-
['$logic_and', 'And'],
|
|
86
|
-
['$logic_or', 'Or'],
|
|
87
|
-
['$dff', 'Dff'],
|
|
88
|
-
['$dffe', 'Dff'],
|
|
89
|
-
['$adff', 'Dff'],
|
|
90
|
-
['$adffe', 'Dff'],
|
|
91
|
-
['$sdff', 'Dff'],
|
|
92
|
-
['$sdffe', 'Dff'],
|
|
93
|
-
['$sdffce', 'Dff'],
|
|
94
|
-
['$dlatch', 'Dff'],
|
|
95
|
-
['$adlatch', 'Dff'],
|
|
96
|
-
['$sr', 'Dff'],
|
|
97
|
-
['$dffsr', 'Dff'],
|
|
98
|
-
['$dffsre', 'Dff'],
|
|
99
|
-
['$aldff', 'Dff'],
|
|
100
|
-
['$aldffe', 'Dff']]);
|
|
101
|
-
const gate_negations = new Map([
|
|
102
|
-
['And', 'Nand'],
|
|
103
|
-
['Nand', 'And'],
|
|
104
|
-
['Nor', 'Or'],
|
|
105
|
-
['Or', 'Nor'],
|
|
106
|
-
['Xor', 'Xnor'],
|
|
107
|
-
['Xnor', 'Xor'],
|
|
108
|
-
['AndReduce', 'NandReduce'],
|
|
109
|
-
['NandReduce', 'AndReduce'],
|
|
110
|
-
['NorReduce', 'OrReduce'],
|
|
111
|
-
['OrReduce', 'NorReduce'],
|
|
112
|
-
['XorReduce', 'XnorReduce'],
|
|
113
|
-
['XnorReduce', 'XorReduce']]);
|
|
114
|
-
|
|
115
|
-
namespace Digitaljs {
|
|
116
|
-
|
|
117
|
-
export type FilePosition = {
|
|
118
|
-
line: number,
|
|
119
|
-
column: number
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
export type SourcePosition = {
|
|
123
|
-
name: string,
|
|
124
|
-
from: FilePosition,
|
|
125
|
-
to: FilePosition
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
export type MemReadPort = {
|
|
129
|
-
clock_polarity?: boolean,
|
|
130
|
-
enable_polarity?: boolean,
|
|
131
|
-
arst_polarity?: boolean,
|
|
132
|
-
srst_polarity?: boolean,
|
|
133
|
-
enable_srst?: boolean,
|
|
134
|
-
transparent?: boolean | boolean[],
|
|
135
|
-
collision?: boolean | boolean[],
|
|
136
|
-
init_value?: string,
|
|
137
|
-
arst_value?: string,
|
|
138
|
-
srst_value?: string
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
export type MemWritePort = {
|
|
142
|
-
clock_polarity?: boolean,
|
|
143
|
-
enable_polarity?: boolean,
|
|
144
|
-
no_bit_enable?: boolean
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
export type Device = {
|
|
148
|
-
type: string,
|
|
149
|
-
source_positions?: SourcePosition[],
|
|
150
|
-
[key: string]: any
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
export type Port = {
|
|
154
|
-
id: string,
|
|
155
|
-
port: string
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
export type Connector = {
|
|
159
|
-
from: Port,
|
|
160
|
-
to: Port,
|
|
161
|
-
name?: string,
|
|
162
|
-
source_positions?: SourcePosition[]
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
export type Module = {
|
|
166
|
-
devices: { [key: string]: Device },
|
|
167
|
-
connectors: Connector[]
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
export type TopModule = Module & {
|
|
171
|
-
subcircuits: { [key: string]: Module }
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
namespace Yosys {
|
|
177
|
-
|
|
178
|
-
export const ConstChars = ["0", "1", "x", "z"] as const;
|
|
179
|
-
|
|
180
|
-
export type BitChar = (typeof ConstChars)[number];
|
|
181
|
-
|
|
182
|
-
export type Bit = number | BitChar;
|
|
183
|
-
|
|
184
|
-
export type BitVector = Bit[];
|
|
185
|
-
|
|
186
|
-
export type Port = {
|
|
187
|
-
direction: 'input' | 'output' | 'inout',
|
|
188
|
-
bits: any
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
export type Parameters = {
|
|
192
|
-
WIDTH?: JsonConstant,
|
|
193
|
-
A_WIDTH?: JsonConstant,
|
|
194
|
-
B_WIDTH?: JsonConstant,
|
|
195
|
-
S_WIDTH?: JsonConstant,
|
|
196
|
-
Y_WIDTH?: JsonConstant,
|
|
197
|
-
A_SIGNED?: JsonConstant,
|
|
198
|
-
B_SIGNED?: JsonConstant,
|
|
199
|
-
CLK_POLARITY?: JsonConstant,
|
|
200
|
-
EN_POLARITY?: JsonConstant,
|
|
201
|
-
ARST_POLARITY?: JsonConstant,
|
|
202
|
-
ARST_VALUE: JsonConstant,
|
|
203
|
-
CTRL_IN_WIDTH?: JsonConstant,
|
|
204
|
-
CTRL_OUT_WIDTH?: JsonConstant,
|
|
205
|
-
TRANS_NUM?: JsonConstant,
|
|
206
|
-
STATE_NUM?: JsonConstant,
|
|
207
|
-
STATE_NUM_LOG2?: JsonConstant,
|
|
208
|
-
STATE_RST?: JsonConstant,
|
|
209
|
-
RD_PORTS?: JsonConstant,
|
|
210
|
-
WR_PORTS?: JsonConstant,
|
|
211
|
-
RD_CLK_POLARITY?: JsonConstant,
|
|
212
|
-
RD_CLK_ENABLE?: JsonConstant,
|
|
213
|
-
RD_CLK_TRANSPARENT?: JsonConstant,
|
|
214
|
-
WR_CLK_POLARITY?: JsonConstant,
|
|
215
|
-
WR_CLK_ENABLE?: JsonConstant,
|
|
216
|
-
[key: string]: any
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
export type JsonConstant = number | string;
|
|
220
|
-
|
|
221
|
-
export type Attributes = {
|
|
222
|
-
init: JsonConstant,
|
|
223
|
-
[key: string]: any
|
|
224
|
-
};
|
|
225
|
-
|
|
226
|
-
export type Cell = {
|
|
227
|
-
hide_name: 0 | 1,
|
|
228
|
-
type: string,
|
|
229
|
-
parameters: Parameters,
|
|
230
|
-
attributes: Attributes,
|
|
231
|
-
port_directions: { [key: string]: 'input' | 'output' },
|
|
232
|
-
connections: { [key: string]: BitVector }
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
export type Net = {
|
|
236
|
-
hide_name: 0 | 1,
|
|
237
|
-
bits: BitVector,
|
|
238
|
-
attributes: { [key: string]: string }
|
|
239
|
-
};
|
|
240
|
-
|
|
241
|
-
export type Module = {
|
|
242
|
-
ports: { [key: string]: Port },
|
|
243
|
-
cells: { [key: string]: Cell },
|
|
244
|
-
netnames: { [key: string]: Net }
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
export type Output = {
|
|
248
|
-
modules: { [key: string]: Module }
|
|
249
|
-
};
|
|
250
|
-
|
|
251
|
-
};
|
|
252
|
-
|
|
253
|
-
type ConvertOptions = {
|
|
254
|
-
propagation?: number,
|
|
255
|
-
};
|
|
8
|
+
import { promisify } from 'util';
|
|
9
|
+
import { prepare_yosys_script, prepare_verilator_args, yosys2digitaljs, ConvertOptions, Digitaljs } from './core';
|
|
10
|
+
const sanitize = require('sanitize-filename');
|
|
256
11
|
|
|
257
12
|
type Options = ConvertOptions & {
|
|
258
13
|
optimize?: boolean,
|
|
@@ -270,26 +25,6 @@ type Output = {
|
|
|
270
25
|
lint?: LintMessage[]
|
|
271
26
|
};
|
|
272
27
|
|
|
273
|
-
type Portmap = { [key: string]: string };
|
|
274
|
-
type Portmaps = { [key: string]: Portmap };
|
|
275
|
-
|
|
276
|
-
type Bit = Yosys.Bit | `bit${number}`;
|
|
277
|
-
|
|
278
|
-
type Net = Bit[];
|
|
279
|
-
|
|
280
|
-
type NetInfo = {
|
|
281
|
-
source: undefined | Digitaljs.Port,
|
|
282
|
-
targets: Digitaljs.Port[],
|
|
283
|
-
name: undefined | string,
|
|
284
|
-
source_positions: Digitaljs.SourcePosition[]
|
|
285
|
-
};
|
|
286
|
-
|
|
287
|
-
type BitInfo = {
|
|
288
|
-
id: string,
|
|
289
|
-
port: string,
|
|
290
|
-
num: number
|
|
291
|
-
};
|
|
292
|
-
|
|
293
28
|
type LintMessage = {
|
|
294
29
|
type: string,
|
|
295
30
|
file: string,
|
|
@@ -298,913 +33,13 @@ type LintMessage = {
|
|
|
298
33
|
message: string
|
|
299
34
|
};
|
|
300
35
|
|
|
301
|
-
function chunkArray(a, chunk_size){
|
|
302
|
-
let results = [];
|
|
303
|
-
let ca = a.splice();
|
|
304
|
-
|
|
305
|
-
while (ca.length) {
|
|
306
|
-
results.push(ca.splice(0, chunk_size));
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
return results;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
function module_deps(data: Yosys.Output): [string, string | number][] {
|
|
313
|
-
const out: [string, string | number][] = [];
|
|
314
|
-
for (const [name, mod] of Object.entries(data.modules)) {
|
|
315
|
-
out.push([name, 1/0]);
|
|
316
|
-
for (const cname in mod.cells) {
|
|
317
|
-
const cell = mod.cells[cname];
|
|
318
|
-
if (cell.type in data.modules)
|
|
319
|
-
out.push([cell.type, name]);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
return out;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
function order_ports(data: Yosys.Output): Portmaps {
|
|
326
|
-
const unmap = {A: 'in', Y: 'out'};
|
|
327
|
-
const binmap = {A: 'in1', B: 'in2', Y: 'out'};
|
|
328
|
-
const out = {
|
|
329
|
-
'$mux': {A: 'in0', B: 'in1', S: 'sel', Y: 'out'},
|
|
330
|
-
'$dff': {CLK: 'clk', D: 'in', Q: 'out'},
|
|
331
|
-
'$dffe': {CLK: 'clk', EN: 'en', D: 'in', Q: 'out'},
|
|
332
|
-
'$adff': {CLK: 'clk', ARST: 'arst', D: 'in', Q: 'out'},
|
|
333
|
-
'$adffe': {CLK: 'clk', EN: 'en', ARST: 'arst', D: 'in', Q: 'out'},
|
|
334
|
-
'$sdff': {CLK: 'clk', SRST: 'srst', D: 'in', Q: 'out'},
|
|
335
|
-
'$sdffe': {CLK: 'clk', EN: 'en', SRST: 'srst', D: 'in', Q: 'out'},
|
|
336
|
-
'$sdffce': {CLK: 'clk', EN: 'en', SRST: 'srst', D: 'in', Q: 'out'},
|
|
337
|
-
'$dlatch': {EN: 'en', D: 'in', Q: 'out'},
|
|
338
|
-
'$adlatch': {EN: 'en', ARST: 'arst', D: 'in', Q: 'out'},
|
|
339
|
-
'$dffsr': {CLK: 'clk', SET: 'set', CLR: 'clr', D: 'in', Q: 'out'},
|
|
340
|
-
'$dffsre': {CLK: 'clk', EN: 'en', SET: 'set', CLR: 'clr', D: 'in', Q: 'out'},
|
|
341
|
-
'$aldff': {CLK: 'clk', ALOAD: 'aload', AD: 'ain', D: 'in', Q: 'out'},
|
|
342
|
-
'$aldffe': {CLK: 'clk', EN: 'en', ALOAD: 'aload', AD: 'ain', D: 'in', Q: 'out'},
|
|
343
|
-
'$sr': {SET: 'set', CLR: 'clr', Q: 'out'},
|
|
344
|
-
'$fsm': {ARST: 'arst', CLK: 'clk', CTRL_IN: 'in', CTRL_OUT: 'out'}
|
|
345
|
-
};
|
|
346
|
-
binary_gates.forEach((nm) => out[nm] = binmap);
|
|
347
|
-
unary_gates.forEach((nm) => out[nm] = unmap);
|
|
348
|
-
for (const [name, mod] of Object.entries(data.modules)) {
|
|
349
|
-
const portmap: Portmap = {};
|
|
350
|
-
const ins = [], outs = [];
|
|
351
|
-
for (const pname in mod.ports) {
|
|
352
|
-
portmap[pname] = pname;
|
|
353
|
-
}
|
|
354
|
-
out[name] = portmap;
|
|
355
|
-
}
|
|
356
|
-
return out;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
function decode_json_bigint(param: string | number): bigInt.BigInteger {
|
|
360
|
-
if (typeof param == 'string')
|
|
361
|
-
return bigInt(param, 2)
|
|
362
|
-
else if (typeof param == 'number')
|
|
363
|
-
return bigInt(param)
|
|
364
|
-
else assert(false);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
function decode_json_number(param: Yosys.JsonConstant): number {
|
|
368
|
-
if (typeof param == 'string')
|
|
369
|
-
return Number.parseInt(param, 2);
|
|
370
|
-
else if (typeof param == 'number')
|
|
371
|
-
return param
|
|
372
|
-
else assert(false);
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
function decode_json_bigint_as_array(param: string | number): number[] {
|
|
376
|
-
return decode_json_bigint(param).toArray(2).value;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
function decode_json_constant(param: Yosys.JsonConstant, bits: number, fill : Yosys.BitChar = '0'): string {
|
|
380
|
-
if (typeof param == 'number')
|
|
381
|
-
return bigInt(param).toArray(2).value.map(String).reverse()
|
|
382
|
-
.concat(Array(bits).fill(fill)).slice(0, bits).reverse().join('');
|
|
383
|
-
else
|
|
384
|
-
return param;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
function parse_source_positions(str: string): Digitaljs.SourcePosition[] {
|
|
388
|
-
const ret = [];
|
|
389
|
-
for (const entry of str.split('|')) {
|
|
390
|
-
const colonIdx = entry.lastIndexOf(':');
|
|
391
|
-
const name = entry.slice(0, colonIdx);
|
|
392
|
-
const pos = entry.slice(colonIdx+1);
|
|
393
|
-
const [from, to] = pos.split('-').map(s => s.split('.').map(v => Number(v))).map(([line, column]) => ({line, column}));
|
|
394
|
-
ret.push({name, from, to});
|
|
395
|
-
}
|
|
396
|
-
return ret;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
function yosys_to_digitaljs(data: Yosys.Output, portmaps: Portmaps, options: ConvertOptions = {}): {[key: string]: Digitaljs.Module} {
|
|
400
|
-
const out = {};
|
|
401
|
-
for (const [name, mod] of Object.entries(data.modules)) {
|
|
402
|
-
out[name] = yosys_to_digitaljs_mod(name, mod, portmaps, options);
|
|
403
|
-
}
|
|
404
|
-
return out
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portmaps, options: ConvertOptions = {}): Digitaljs.Module {
|
|
408
|
-
function constbit(bit: Bit) {
|
|
409
|
-
return (Yosys.ConstChars as readonly string[]).includes(bit.toString());
|
|
410
|
-
}
|
|
411
|
-
const nets = new HashMap<Net, NetInfo>();
|
|
412
|
-
const netnames = new HashMap<Net, string[]>();
|
|
413
|
-
const netsrc = new HashMap<Net, Digitaljs.SourcePosition[]>();
|
|
414
|
-
const bits = new Map<Bit, BitInfo>();
|
|
415
|
-
const devnets = new Map<string, Map<string, Net>>();
|
|
416
|
-
let n = 0, pn = 0;
|
|
417
|
-
function gen_name(): string {
|
|
418
|
-
const nm = `dev${n++}`;
|
|
419
|
-
devnets.set(nm, new Map());
|
|
420
|
-
return nm;
|
|
421
|
-
}
|
|
422
|
-
function gen_bitname(): Bit {
|
|
423
|
-
return `bit${pn++}`;
|
|
424
|
-
}
|
|
425
|
-
function get_net(k: Net): NetInfo {
|
|
426
|
-
// create net if does not exist yet
|
|
427
|
-
if (!nets.has(k)) {
|
|
428
|
-
const nms = netnames.get(k);
|
|
429
|
-
const src = netsrc.get(k);
|
|
430
|
-
nets.set(k, {source: undefined, targets: [], name: nms ? nms[0] : undefined, source_positions: src || []});
|
|
431
|
-
}
|
|
432
|
-
return nets.get(k);
|
|
433
|
-
}
|
|
434
|
-
function add_net_source(k: Net, d: string, p: string, primary: boolean = false) {
|
|
435
|
-
if (k.length == 0) return; // for unconnected ports
|
|
436
|
-
const net = get_net(k);
|
|
437
|
-
if(net.source !== undefined) {
|
|
438
|
-
// multiple sources driving one net, disallowed in digitaljs
|
|
439
|
-
throw Error('Multiple sources driving net: ' + net.name);
|
|
440
|
-
}
|
|
441
|
-
net.source = { id: d, port: p };
|
|
442
|
-
if (primary) for (const [nbit, bit] of k.entries()) {
|
|
443
|
-
bits.set(bit, { id: d, port: p, num: nbit });
|
|
444
|
-
}
|
|
445
|
-
devnets.get(d).set(p, k);
|
|
446
|
-
}
|
|
447
|
-
function add_net_target(k: Net, d: string, p: string) {
|
|
448
|
-
if (k.length == 0) return; // for unconnected ports
|
|
449
|
-
const net = get_net(k);
|
|
450
|
-
net.targets.push({ id: d, port: p });
|
|
451
|
-
devnets.get(d).set(p, k);
|
|
452
|
-
}
|
|
453
|
-
const mout = {
|
|
454
|
-
devices: {},
|
|
455
|
-
connectors: []
|
|
456
|
-
}
|
|
457
|
-
function add_device(dev : Digitaljs.Device): string {
|
|
458
|
-
const dname = gen_name();
|
|
459
|
-
if (options.propagation !== undefined)
|
|
460
|
-
dev.propagation = options.propagation;
|
|
461
|
-
mout.devices[dname] = dev;
|
|
462
|
-
return dname;
|
|
463
|
-
}
|
|
464
|
-
function add_busgroup(nbits: Net, groups: Net[]) {
|
|
465
|
-
if (get_net(nbits).source !== undefined)
|
|
466
|
-
return; // the bits were already grouped
|
|
467
|
-
const dname = add_device({
|
|
468
|
-
type: 'BusGroup',
|
|
469
|
-
groups: groups.map(g => g.length)
|
|
470
|
-
});
|
|
471
|
-
add_net_source(nbits, dname, 'out');
|
|
472
|
-
for (const [gn, group] of groups.entries()) {
|
|
473
|
-
add_net_target(group, dname, 'in' + gn);
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
function connect_device(dname: string, cell: Yosys.Cell, portmap: Portmap) {
|
|
477
|
-
for (const [pname, pdir] of Object.entries(cell.port_directions)) {
|
|
478
|
-
const pconn = cell.connections[pname];
|
|
479
|
-
switch (pdir) {
|
|
480
|
-
case 'input':
|
|
481
|
-
add_net_target(pconn, dname, portmap[pname]);
|
|
482
|
-
break;
|
|
483
|
-
case 'output':
|
|
484
|
-
add_net_source(pconn, dname, portmap[pname], true);
|
|
485
|
-
break;
|
|
486
|
-
default:
|
|
487
|
-
throw Error('Invalid port direction: ' + pdir);
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
function connect_pmux(dname: string, cell: Yosys.Cell) {
|
|
492
|
-
add_net_target(cell.connections.A, dname, 'in0');
|
|
493
|
-
add_net_target(cell.connections.S.slice().reverse(), dname, 'sel');
|
|
494
|
-
add_net_source(cell.connections.Y, dname, 'out', true);
|
|
495
|
-
for (const i of Array(decode_json_number(cell.parameters.S_WIDTH)).keys()) {
|
|
496
|
-
const p = (decode_json_number(cell.parameters.S_WIDTH)-i-1) * decode_json_number(cell.parameters.WIDTH);
|
|
497
|
-
add_net_target(cell.connections.B.slice(p, p + decode_json_number(cell.parameters.WIDTH)),
|
|
498
|
-
dname, 'in' + (i+1));
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
function connect_mem(dname: string, cell: Yosys.Cell, dev: Digitaljs.Device) {
|
|
502
|
-
for (const [k, port] of dev.rdports.entries()) {
|
|
503
|
-
const portname = "rd" + k;
|
|
504
|
-
add_net_target(cell.connections.RD_ADDR.slice(dev.abits * k, dev.abits * (k+1)),
|
|
505
|
-
dname, portname + "addr");
|
|
506
|
-
add_net_source(cell.connections.RD_DATA.slice(dev.bits * k, dev.bits * (k+1)),
|
|
507
|
-
dname, portname + "data", true);
|
|
508
|
-
if ('clock_polarity' in port)
|
|
509
|
-
add_net_target([cell.connections.RD_CLK[k]], dname, portname + "clk");
|
|
510
|
-
if ('enable_polarity' in port)
|
|
511
|
-
add_net_target([cell.connections.RD_EN[k]], dname, portname + "en");
|
|
512
|
-
if ('arst_polarity' in port)
|
|
513
|
-
add_net_target([cell.connections.RD_ARST[k]], dname, portname + "arst");
|
|
514
|
-
if ('srst_polarity' in port)
|
|
515
|
-
add_net_target([cell.connections.RD_SRST[k]], dname, portname + "srst");
|
|
516
|
-
}
|
|
517
|
-
for (const [k, port] of dev.wrports.entries()) {
|
|
518
|
-
const portname = "wr" + k;
|
|
519
|
-
add_net_target(cell.connections.WR_ADDR.slice(dev.abits * k, dev.abits * (k+1)),
|
|
520
|
-
dname, portname + "addr");
|
|
521
|
-
add_net_target(cell.connections.WR_DATA.slice(dev.bits * k, dev.bits * (k+1)),
|
|
522
|
-
dname, portname + "data");
|
|
523
|
-
if ('clock_polarity' in port)
|
|
524
|
-
add_net_target([cell.connections.WR_CLK[k]], dname, portname + "clk");
|
|
525
|
-
if ('enable_polarity' in port) {
|
|
526
|
-
if (port.no_bit_enable)
|
|
527
|
-
add_net_target([cell.connections.WR_EN[dev.bits * k]], dname, portname + "en");
|
|
528
|
-
else
|
|
529
|
-
add_net_target(cell.connections.WR_EN.slice(dev.bits * k, dev.bits * (k+1)),
|
|
530
|
-
dname, portname + "en");
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
// Find net names
|
|
535
|
-
for (const [nname, data] of Object.entries(mod.netnames)) {
|
|
536
|
-
if (data.hide_name) continue;
|
|
537
|
-
let l = netnames.get(data.bits);
|
|
538
|
-
if (l === undefined) {
|
|
539
|
-
l = [];
|
|
540
|
-
netnames.set(data.bits, l);
|
|
541
|
-
}
|
|
542
|
-
l.push(nname);
|
|
543
|
-
if (typeof data.attributes == 'object' && data.attributes.src) {
|
|
544
|
-
let l = netsrc.get(data.bits);
|
|
545
|
-
if (l === undefined) {
|
|
546
|
-
l = [];
|
|
547
|
-
netsrc.set(data.bits, l);
|
|
548
|
-
}
|
|
549
|
-
const positions = parse_source_positions(data.attributes.src);
|
|
550
|
-
l.push(...positions);
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
// Add inputs/outputs
|
|
554
|
-
for (const [pname, port] of Object.entries(mod.ports)) {
|
|
555
|
-
const dir = port.direction == "input" ? "Input" :
|
|
556
|
-
port.direction == "output" ? "Output" :
|
|
557
|
-
undefined;
|
|
558
|
-
const dname = add_device({
|
|
559
|
-
type: dir,
|
|
560
|
-
net: pname,
|
|
561
|
-
order: n,
|
|
562
|
-
bits: port.bits.length
|
|
563
|
-
});
|
|
564
|
-
switch (port.direction) {
|
|
565
|
-
case 'input':
|
|
566
|
-
add_net_source(port.bits, dname, 'out', true);
|
|
567
|
-
break;
|
|
568
|
-
case 'output':
|
|
569
|
-
add_net_target(port.bits, dname, 'in');
|
|
570
|
-
break;
|
|
571
|
-
default: throw Error('Invalid port direction: ' + port.direction);
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
// Add gates
|
|
575
|
-
for (const [cname, cell] of Object.entries(mod.cells)) {
|
|
576
|
-
const dev : Digitaljs.Device = {
|
|
577
|
-
label: cname,
|
|
578
|
-
type: gate_subst.get(cell.type)
|
|
579
|
-
};
|
|
580
|
-
if (dev.type == undefined) {
|
|
581
|
-
dev.type = 'Subcircuit';
|
|
582
|
-
dev.celltype = cell.type;
|
|
583
|
-
}
|
|
584
|
-
if (typeof cell.attributes == 'object' && cell.attributes.src) {
|
|
585
|
-
dev.source_positions = parse_source_positions(cell.attributes.src);
|
|
586
|
-
}
|
|
587
|
-
const dname = add_device(dev);
|
|
588
|
-
function match_port(con: Net, nsig: Yosys.JsonConstant, sz: number) {
|
|
589
|
-
const sig = decode_json_number(nsig);
|
|
590
|
-
if (con.length > sz)
|
|
591
|
-
con.splice(sz);
|
|
592
|
-
else if (con.length < sz) {
|
|
593
|
-
const ccon = con.slice();
|
|
594
|
-
const pad = sig ? con.slice(-1)[0] : '0';
|
|
595
|
-
con.splice(con.length, 0, ...Array(sz - con.length).fill(pad));
|
|
596
|
-
if (!con.every(constbit) && get_net(con).source === undefined) {
|
|
597
|
-
// WARNING: potentially troublesome hack for readability
|
|
598
|
-
// handled generally in the grouping phase,
|
|
599
|
-
// but it's hard to add sign extensions there
|
|
600
|
-
const extname = add_device({
|
|
601
|
-
type: sig ? 'SignExtend' : 'ZeroExtend',
|
|
602
|
-
extend: { input: ccon.length, output: con.length }
|
|
603
|
-
});
|
|
604
|
-
add_net_target(ccon, extname, 'in');
|
|
605
|
-
add_net_source(con, extname, 'out');
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
function zero_extend_output(con: Net) {
|
|
610
|
-
if (con.length > 1) {
|
|
611
|
-
const ccon = con.slice();
|
|
612
|
-
con.splice(1);
|
|
613
|
-
const extname = add_device({
|
|
614
|
-
type: 'ZeroExtend',
|
|
615
|
-
extend: { input: con.length, output: ccon.length }
|
|
616
|
-
});
|
|
617
|
-
add_net_source(ccon, extname, 'out');
|
|
618
|
-
add_net_target(con, extname, 'in');
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
if (unary_gates.has(cell.type)) {
|
|
622
|
-
assert(cell.connections.A.length == decode_json_number(cell.parameters.A_WIDTH));
|
|
623
|
-
assert(cell.connections.Y.length == decode_json_number(cell.parameters.Y_WIDTH));
|
|
624
|
-
assert(cell.port_directions.A == 'input');
|
|
625
|
-
assert(cell.port_directions.Y == 'output');
|
|
626
|
-
}
|
|
627
|
-
if (binary_gates.has(cell.type)) {
|
|
628
|
-
assert(cell.connections.A.length == decode_json_number(cell.parameters.A_WIDTH));
|
|
629
|
-
assert(cell.connections.B.length == decode_json_number(cell.parameters.B_WIDTH));
|
|
630
|
-
assert(cell.connections.Y.length == decode_json_number(cell.parameters.Y_WIDTH));
|
|
631
|
-
assert(cell.port_directions.A == 'input');
|
|
632
|
-
assert(cell.port_directions.B == 'input');
|
|
633
|
-
assert(cell.port_directions.Y == 'output');
|
|
634
|
-
}
|
|
635
|
-
if (['$dff', '$dffe', '$adff', '$adffe', '$sdff', '$sdffe', '$sdffce', '$dlatch', '$adlatch', '$dffsr', '$dffsre', '$aldff', '$aldffe'].includes(cell.type)) {
|
|
636
|
-
assert(cell.connections.D.length == decode_json_number(cell.parameters.WIDTH));
|
|
637
|
-
assert(cell.connections.Q.length == decode_json_number(cell.parameters.WIDTH));
|
|
638
|
-
assert(cell.port_directions.D == 'input');
|
|
639
|
-
assert(cell.port_directions.Q == 'output');
|
|
640
|
-
if (cell.type != '$dlatch' && cell.type != '$adlatch') {
|
|
641
|
-
assert(cell.connections.CLK.length == 1);
|
|
642
|
-
assert(cell.port_directions.CLK == 'input');
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
if (['$dffe', '$adffe', '$sdffe', '$sdffce', '$dffsre', '$aldffe', '$dlatch', '$adlatch'].includes(cell.type)) {
|
|
646
|
-
assert(cell.connections.EN.length == 1);
|
|
647
|
-
assert(cell.port_directions.EN == 'input');
|
|
648
|
-
}
|
|
649
|
-
if (['$adff', '$adffe', '$adlatch'].includes(cell.type)) {
|
|
650
|
-
assert(cell.connections.ARST.length == 1);
|
|
651
|
-
assert(cell.port_directions.ARST == 'input');
|
|
652
|
-
}
|
|
653
|
-
if (['$sdff', '$sdffe', '$sdffce'].includes(cell.type)) {
|
|
654
|
-
assert(cell.connections.SRST.length == 1);
|
|
655
|
-
assert(cell.port_directions.SRST == 'input');
|
|
656
|
-
}
|
|
657
|
-
if (['$dffsr', '$dffsre'].includes(cell.type)) {
|
|
658
|
-
assert(cell.connections.SET.length == decode_json_number(cell.parameters.WIDTH));
|
|
659
|
-
assert(cell.connections.CLR.length == decode_json_number(cell.parameters.WIDTH));
|
|
660
|
-
assert(cell.port_directions.SET == 'input');
|
|
661
|
-
assert(cell.port_directions.CLR == 'input');
|
|
662
|
-
}
|
|
663
|
-
switch (cell.type) {
|
|
664
|
-
case '$neg': case '$pos':
|
|
665
|
-
dev.bits = {
|
|
666
|
-
in: cell.connections.A.length,
|
|
667
|
-
out: cell.connections.Y.length
|
|
668
|
-
};
|
|
669
|
-
dev.signed = Boolean(decode_json_number(cell.parameters.A_SIGNED));
|
|
670
|
-
break;
|
|
671
|
-
case '$not':
|
|
672
|
-
match_port(cell.connections.A, cell.parameters.A_SIGNED, cell.connections.Y.length);
|
|
673
|
-
dev.bits = cell.connections.Y.length;
|
|
674
|
-
break;
|
|
675
|
-
case '$add': case '$sub': case '$mul': case '$div': case '$mod': case '$pow':
|
|
676
|
-
dev.bits = {
|
|
677
|
-
in1: cell.connections.A.length,
|
|
678
|
-
in2: cell.connections.B.length,
|
|
679
|
-
out: cell.connections.Y.length
|
|
680
|
-
};
|
|
681
|
-
dev.signed = {
|
|
682
|
-
in1: Boolean(decode_json_number(cell.parameters.A_SIGNED)),
|
|
683
|
-
in2: Boolean(decode_json_number(cell.parameters.B_SIGNED))
|
|
684
|
-
}
|
|
685
|
-
break;
|
|
686
|
-
case '$and': case '$or': case '$xor': case '$xnor':
|
|
687
|
-
match_port(cell.connections.A, cell.parameters.A_SIGNED, cell.connections.Y.length);
|
|
688
|
-
match_port(cell.connections.B, cell.parameters.B_SIGNED, cell.connections.Y.length);
|
|
689
|
-
dev.bits = cell.connections.Y.length;
|
|
690
|
-
break;
|
|
691
|
-
case '$reduce_and': case '$reduce_or': case '$reduce_xor': case '$reduce_xnor':
|
|
692
|
-
case '$reduce_bool': case '$logic_not':
|
|
693
|
-
dev.bits = cell.connections.A.length;
|
|
694
|
-
zero_extend_output(cell.connections.Y);
|
|
695
|
-
if (dev.bits == 1) {
|
|
696
|
-
if (['$reduce_xnor', '$logic_not'].includes(cell.type))
|
|
697
|
-
dev.type = 'Not';
|
|
698
|
-
else
|
|
699
|
-
dev.type = 'Repeater';
|
|
700
|
-
}
|
|
701
|
-
break;
|
|
702
|
-
case '$eq': case '$ne': case '$lt': case '$le': case '$gt': case '$ge':
|
|
703
|
-
case '$eqx': case '$nex':
|
|
704
|
-
dev.bits = {
|
|
705
|
-
in1: cell.connections.A.length,
|
|
706
|
-
in2: cell.connections.B.length
|
|
707
|
-
};
|
|
708
|
-
dev.signed = {
|
|
709
|
-
in1: Boolean(decode_json_number(cell.parameters.A_SIGNED)),
|
|
710
|
-
in2: Boolean(decode_json_number(cell.parameters.B_SIGNED))
|
|
711
|
-
};
|
|
712
|
-
zero_extend_output(cell.connections.Y);
|
|
713
|
-
break;
|
|
714
|
-
case '$shl': case '$shr': case '$sshl': case '$sshr':
|
|
715
|
-
case '$shift': case '$shiftx':
|
|
716
|
-
dev.bits = {
|
|
717
|
-
in1: cell.connections.A.length,
|
|
718
|
-
in2: cell.connections.B.length,
|
|
719
|
-
out: cell.connections.Y.length
|
|
720
|
-
};
|
|
721
|
-
dev.signed = {
|
|
722
|
-
in1: Boolean(decode_json_number(cell.parameters.A_SIGNED)),
|
|
723
|
-
in2: Boolean(decode_json_number(cell.parameters.B_SIGNED) && ['$shift', '$shiftx'].includes(cell.type)),
|
|
724
|
-
out: Boolean(decode_json_number(cell.parameters.A_SIGNED) && ['$sshl', '$sshr'].includes(cell.type))
|
|
725
|
-
};
|
|
726
|
-
dev.fillx = cell.type == '$shiftx';
|
|
727
|
-
break;
|
|
728
|
-
case '$logic_and': case '$logic_or': {
|
|
729
|
-
function reduce_input(con: Net) {
|
|
730
|
-
const ccon = con.slice();
|
|
731
|
-
con.splice(0, con.length, gen_bitname());
|
|
732
|
-
const extname = add_device({
|
|
733
|
-
type: 'OrReduce',
|
|
734
|
-
bits: ccon.length
|
|
735
|
-
});
|
|
736
|
-
add_net_source(con, extname, 'out');
|
|
737
|
-
add_net_target(ccon, extname, 'in');
|
|
738
|
-
}
|
|
739
|
-
if (cell.connections.A.length > 1)
|
|
740
|
-
reduce_input(cell.connections.A);
|
|
741
|
-
if (cell.connections.B.length > 1)
|
|
742
|
-
reduce_input(cell.connections.B);
|
|
743
|
-
zero_extend_output(cell.connections.Y);
|
|
744
|
-
break;
|
|
745
|
-
}
|
|
746
|
-
case '$mux':
|
|
747
|
-
assert(cell.connections.A.length == decode_json_number(cell.parameters.WIDTH));
|
|
748
|
-
assert(cell.connections.B.length == decode_json_number(cell.parameters.WIDTH));
|
|
749
|
-
assert(cell.connections.Y.length == decode_json_number(cell.parameters.WIDTH));
|
|
750
|
-
assert(cell.port_directions.A == 'input');
|
|
751
|
-
assert(cell.port_directions.B == 'input');
|
|
752
|
-
assert(cell.port_directions.Y == 'output');
|
|
753
|
-
dev.bits = {
|
|
754
|
-
in: decode_json_number(cell.parameters.WIDTH),
|
|
755
|
-
sel: 1
|
|
756
|
-
};
|
|
757
|
-
break;
|
|
758
|
-
case '$pmux':
|
|
759
|
-
assert(cell.connections.B.length == decode_json_number(cell.parameters.WIDTH) * decode_json_number(cell.parameters.S_WIDTH));
|
|
760
|
-
assert(cell.connections.A.length == decode_json_number(cell.parameters.WIDTH));
|
|
761
|
-
assert(cell.connections.S.length == decode_json_number(cell.parameters.S_WIDTH));
|
|
762
|
-
assert(cell.connections.Y.length == decode_json_number(cell.parameters.WIDTH));
|
|
763
|
-
assert(cell.port_directions.A == 'input');
|
|
764
|
-
assert(cell.port_directions.B == 'input');
|
|
765
|
-
assert(cell.port_directions.S == 'input');
|
|
766
|
-
assert(cell.port_directions.Y == 'output');
|
|
767
|
-
dev.bits = {
|
|
768
|
-
in: decode_json_number(cell.parameters.WIDTH),
|
|
769
|
-
sel: decode_json_number(cell.parameters.S_WIDTH)
|
|
770
|
-
};
|
|
771
|
-
break;
|
|
772
|
-
case '$dff':
|
|
773
|
-
dev.bits = decode_json_number(cell.parameters.WIDTH);
|
|
774
|
-
dev.polarity = {
|
|
775
|
-
clock: Boolean(decode_json_number(cell.parameters.CLK_POLARITY))
|
|
776
|
-
};
|
|
777
|
-
break;
|
|
778
|
-
case '$dffe':
|
|
779
|
-
dev.bits = decode_json_number(cell.parameters.WIDTH);
|
|
780
|
-
dev.polarity = {
|
|
781
|
-
clock: Boolean(decode_json_number(cell.parameters.CLK_POLARITY)),
|
|
782
|
-
enable: Boolean(decode_json_number(cell.parameters.EN_POLARITY))
|
|
783
|
-
};
|
|
784
|
-
break;
|
|
785
|
-
case '$aldff':
|
|
786
|
-
dev.bits = decode_json_number(cell.parameters.WIDTH);
|
|
787
|
-
dev.polarity = {
|
|
788
|
-
clock: Boolean(decode_json_number(cell.parameters.CLK_POLARITY)),
|
|
789
|
-
aload: Boolean(decode_json_number(cell.parameters.ALOAD_POLARITY))
|
|
790
|
-
};
|
|
791
|
-
break;
|
|
792
|
-
case '$aldffe':
|
|
793
|
-
dev.bits = decode_json_number(cell.parameters.WIDTH);
|
|
794
|
-
dev.polarity = {
|
|
795
|
-
clock: Boolean(decode_json_number(cell.parameters.CLK_POLARITY)),
|
|
796
|
-
enable: Boolean(decode_json_number(cell.parameters.EN_POLARITY)),
|
|
797
|
-
aload: Boolean(decode_json_number(cell.parameters.ALOAD_POLARITY))
|
|
798
|
-
};
|
|
799
|
-
break;
|
|
800
|
-
case '$adff':
|
|
801
|
-
dev.bits = decode_json_number(cell.parameters.WIDTH);
|
|
802
|
-
dev.polarity = {
|
|
803
|
-
clock: Boolean(decode_json_number(cell.parameters.CLK_POLARITY)),
|
|
804
|
-
arst: Boolean(decode_json_number(cell.parameters.ARST_POLARITY))
|
|
805
|
-
};
|
|
806
|
-
dev.arst_value = decode_json_constant(cell.parameters.ARST_VALUE, dev.bits);
|
|
807
|
-
break;
|
|
808
|
-
case '$sdff':
|
|
809
|
-
dev.bits = decode_json_number(cell.parameters.WIDTH);
|
|
810
|
-
dev.polarity = {
|
|
811
|
-
clock: Boolean(decode_json_number(cell.parameters.CLK_POLARITY)),
|
|
812
|
-
srst: Boolean(decode_json_number(cell.parameters.SRST_POLARITY))
|
|
813
|
-
};
|
|
814
|
-
dev.srst_value = decode_json_constant(cell.parameters.SRST_VALUE, dev.bits);
|
|
815
|
-
break;
|
|
816
|
-
case '$adffe':
|
|
817
|
-
dev.bits = decode_json_number(cell.parameters.WIDTH);
|
|
818
|
-
dev.polarity = {
|
|
819
|
-
clock: Boolean(decode_json_number(cell.parameters.CLK_POLARITY)),
|
|
820
|
-
arst: Boolean(decode_json_number(cell.parameters.ARST_POLARITY)),
|
|
821
|
-
enable: Boolean(decode_json_number(cell.parameters.EN_POLARITY))
|
|
822
|
-
};
|
|
823
|
-
dev.arst_value = decode_json_constant(cell.parameters.ARST_VALUE, dev.bits);
|
|
824
|
-
break;
|
|
825
|
-
case '$sdffe':
|
|
826
|
-
dev.bits = decode_json_number(cell.parameters.WIDTH);
|
|
827
|
-
dev.polarity = {
|
|
828
|
-
clock: Boolean(decode_json_number(cell.parameters.CLK_POLARITY)),
|
|
829
|
-
srst: Boolean(decode_json_number(cell.parameters.SRST_POLARITY)),
|
|
830
|
-
enable: Boolean(decode_json_number(cell.parameters.EN_POLARITY))
|
|
831
|
-
};
|
|
832
|
-
dev.srst_value = decode_json_constant(cell.parameters.SRST_VALUE, dev.bits);
|
|
833
|
-
break;
|
|
834
|
-
case '$sdffce':
|
|
835
|
-
dev.bits = decode_json_number(cell.parameters.WIDTH);
|
|
836
|
-
dev.polarity = {
|
|
837
|
-
clock: Boolean(decode_json_number(cell.parameters.CLK_POLARITY)),
|
|
838
|
-
srst: Boolean(decode_json_number(cell.parameters.SRST_POLARITY)),
|
|
839
|
-
enable: Boolean(decode_json_number(cell.parameters.EN_POLARITY))
|
|
840
|
-
};
|
|
841
|
-
dev.enable_srst = true;
|
|
842
|
-
dev.srst_value = decode_json_constant(cell.parameters.SRST_VALUE, dev.bits);
|
|
843
|
-
break;
|
|
844
|
-
case '$dlatch':
|
|
845
|
-
dev.bits = decode_json_number(cell.parameters.WIDTH);
|
|
846
|
-
dev.polarity = {
|
|
847
|
-
enable: Boolean(decode_json_number(cell.parameters.EN_POLARITY))
|
|
848
|
-
};
|
|
849
|
-
break;
|
|
850
|
-
case '$adlatch':
|
|
851
|
-
dev.bits = decode_json_number(cell.parameters.WIDTH);
|
|
852
|
-
dev.polarity = {
|
|
853
|
-
enable: Boolean(decode_json_number(cell.parameters.EN_POLARITY)),
|
|
854
|
-
arst: Boolean(decode_json_number(cell.parameters.ARST_POLARITY))
|
|
855
|
-
};
|
|
856
|
-
dev.arst_value = decode_json_constant(cell.parameters.ARST_VALUE, dev.bits);
|
|
857
|
-
break;
|
|
858
|
-
case '$dffsr':
|
|
859
|
-
dev.bits = decode_json_number(cell.parameters.WIDTH);
|
|
860
|
-
dev.polarity = {
|
|
861
|
-
clock: Boolean(decode_json_number(cell.parameters.CLK_POLARITY)),
|
|
862
|
-
set: Boolean(decode_json_number(cell.parameters.SET_POLARITY)),
|
|
863
|
-
clr: Boolean(decode_json_number(cell.parameters.CLR_POLARITY))
|
|
864
|
-
};
|
|
865
|
-
break;
|
|
866
|
-
case '$dffsre':
|
|
867
|
-
dev.bits = decode_json_number(cell.parameters.WIDTH);
|
|
868
|
-
dev.polarity = {
|
|
869
|
-
clock: Boolean(decode_json_number(cell.parameters.CLK_POLARITY)),
|
|
870
|
-
enable: Boolean(decode_json_number(cell.parameters.EN_POLARITY)),
|
|
871
|
-
set: Boolean(decode_json_number(cell.parameters.SET_POLARITY)),
|
|
872
|
-
clr: Boolean(decode_json_number(cell.parameters.CLR_POLARITY))
|
|
873
|
-
};
|
|
874
|
-
break;
|
|
875
|
-
case '$sr':
|
|
876
|
-
assert(cell.connections.Q.length == decode_json_number(cell.parameters.WIDTH));
|
|
877
|
-
assert(cell.port_directions.Q == 'output');
|
|
878
|
-
dev.no_data = true;
|
|
879
|
-
dev.bits = decode_json_number(cell.parameters.WIDTH);
|
|
880
|
-
dev.polarity = {
|
|
881
|
-
set: Boolean(decode_json_number(cell.parameters.SET_POLARITY)),
|
|
882
|
-
clr: Boolean(decode_json_number(cell.parameters.CLR_POLARITY))
|
|
883
|
-
};
|
|
884
|
-
break;
|
|
885
|
-
case '$fsm': {
|
|
886
|
-
assert(cell.connections.ARST.length == 1);
|
|
887
|
-
assert(cell.connections.CLK.length == 1);
|
|
888
|
-
assert(cell.connections.CTRL_IN.length == decode_json_number(cell.parameters.CTRL_IN_WIDTH));
|
|
889
|
-
assert(cell.connections.CTRL_OUT.length == decode_json_number(cell.parameters.CTRL_OUT_WIDTH));
|
|
890
|
-
const TRANS_NUM = decode_json_number(cell.parameters.TRANS_NUM);
|
|
891
|
-
const STATE_NUM_LOG2 = decode_json_number(cell.parameters.STATE_NUM_LOG2);
|
|
892
|
-
const step = 2*STATE_NUM_LOG2
|
|
893
|
-
+ decode_json_number(cell.parameters.CTRL_IN_WIDTH)
|
|
894
|
-
+ decode_json_number(cell.parameters.CTRL_OUT_WIDTH);
|
|
895
|
-
const tt = typeof(cell.parameters.TRANS_TABLE) == "number"
|
|
896
|
-
? Vector3vl.fromBin(bigInt(cell.parameters.TRANS_TABLE).toString(2), TRANS_NUM * step).toBin() // workaround for yosys silliness
|
|
897
|
-
: cell.parameters.TRANS_TABLE;
|
|
898
|
-
assert(tt.length == TRANS_NUM * step);
|
|
899
|
-
dev.polarity = {
|
|
900
|
-
clock: Boolean(decode_json_number(cell.parameters.CLK_POLARITY)),
|
|
901
|
-
arst: Boolean(decode_json_number(cell.parameters.ARST_POLARITY))
|
|
902
|
-
};
|
|
903
|
-
dev.wirename = cell.parameters.NAME;
|
|
904
|
-
dev.bits = {
|
|
905
|
-
in: decode_json_number(cell.parameters.CTRL_IN_WIDTH),
|
|
906
|
-
out: decode_json_number(cell.parameters.CTRL_OUT_WIDTH)
|
|
907
|
-
};
|
|
908
|
-
dev.states = decode_json_number(cell.parameters.STATE_NUM);
|
|
909
|
-
dev.init_state = decode_json_number(cell.parameters.STATE_RST);
|
|
910
|
-
dev.trans_table = [];
|
|
911
|
-
for (let i = 0; i < TRANS_NUM; i++) {
|
|
912
|
-
let base = i * step;
|
|
913
|
-
const f = (sz) => {
|
|
914
|
-
const ret = tt.slice(base, base + sz);
|
|
915
|
-
base += sz;
|
|
916
|
-
return ret;
|
|
917
|
-
};
|
|
918
|
-
const o = {
|
|
919
|
-
state_in: parseInt(f(STATE_NUM_LOG2), 2),
|
|
920
|
-
ctrl_in: f(decode_json_number(cell.parameters.CTRL_IN_WIDTH)).replace(/-/g, 'x'),
|
|
921
|
-
state_out: parseInt(f(STATE_NUM_LOG2), 2),
|
|
922
|
-
ctrl_out: f(decode_json_number(cell.parameters.CTRL_OUT_WIDTH))
|
|
923
|
-
};
|
|
924
|
-
dev.trans_table.push(o);
|
|
925
|
-
}
|
|
926
|
-
break;
|
|
927
|
-
}
|
|
928
|
-
case '$mem':
|
|
929
|
-
case '$mem_v2': {
|
|
930
|
-
const RD_PORTS = decode_json_number(cell.parameters.RD_PORTS);
|
|
931
|
-
const WR_PORTS = decode_json_number(cell.parameters.WR_PORTS);
|
|
932
|
-
assert(cell.connections.RD_EN.length == RD_PORTS);
|
|
933
|
-
assert(cell.connections.RD_CLK.length == RD_PORTS);
|
|
934
|
-
assert(cell.connections.RD_DATA.length == RD_PORTS * decode_json_number(cell.parameters.WIDTH));
|
|
935
|
-
assert(cell.connections.RD_ADDR.length == RD_PORTS * decode_json_number(cell.parameters.ABITS));
|
|
936
|
-
assert(cell.connections.WR_EN.length == WR_PORTS * decode_json_number(cell.parameters.WIDTH));
|
|
937
|
-
assert(cell.connections.WR_CLK.length == WR_PORTS);
|
|
938
|
-
assert(cell.connections.WR_DATA.length == WR_PORTS * decode_json_number(cell.parameters.WIDTH));
|
|
939
|
-
assert(cell.connections.WR_ADDR.length == WR_PORTS * decode_json_number(cell.parameters.ABITS));
|
|
940
|
-
if (cell.type == "$mem_v2") {
|
|
941
|
-
assert(cell.connections.RD_ARST.length == RD_PORTS);
|
|
942
|
-
assert(cell.connections.RD_SRST.length == RD_PORTS);
|
|
943
|
-
}
|
|
944
|
-
dev.bits = decode_json_number(cell.parameters.WIDTH);
|
|
945
|
-
dev.abits = decode_json_number(cell.parameters.ABITS);
|
|
946
|
-
dev.words = decode_json_number(cell.parameters.SIZE);
|
|
947
|
-
dev.offset = decode_json_number(cell.parameters.OFFSET);
|
|
948
|
-
dev.rdports = [];
|
|
949
|
-
dev.wrports = [];
|
|
950
|
-
const rdpol = decode_json_bigint_as_array(cell.parameters.RD_CLK_POLARITY).reverse();
|
|
951
|
-
const rden = decode_json_bigint_as_array(cell.parameters.RD_CLK_ENABLE).reverse();
|
|
952
|
-
const rdtr = cell.type == "$mem_v2"
|
|
953
|
-
? []
|
|
954
|
-
: decode_json_bigint_as_array(cell.parameters.RD_TRANSPARENT).reverse();
|
|
955
|
-
const wrpol = decode_json_bigint_as_array(cell.parameters.WR_CLK_POLARITY).reverse();
|
|
956
|
-
const wren = decode_json_bigint_as_array(cell.parameters.WR_CLK_ENABLE).reverse();
|
|
957
|
-
const init = typeof(cell.parameters.INIT) == 'number'
|
|
958
|
-
? bigInt(cell.parameters.INIT).toArray(2).value.map(String).reverse()
|
|
959
|
-
: cell.parameters.INIT.split('').reverse();
|
|
960
|
-
const v2_feature = (param) => cell.type == "$mem_v2" ? decode_json_bigint_as_array(param).reverse() : [];
|
|
961
|
-
const v2_feature_const = (param, size) => cell.type == "$mem_v2" ? decode_json_constant(param, size) : "";
|
|
962
|
-
const rdtrmask = v2_feature(cell.parameters.RD_TRANSPARENCY_MASK);
|
|
963
|
-
const rdcolmask = v2_feature(cell.parameters.RD_COLLISION_X_MASK);
|
|
964
|
-
const rdensrst = v2_feature(cell.parameters.RD_CE_OVER_SRST);
|
|
965
|
-
const rdinit = v2_feature_const(cell.parameters.RD_INIT_VALUE, dev.bits * RD_PORTS);
|
|
966
|
-
const rdarst = v2_feature_const(cell.parameters.RD_ARST_VALUE, dev.bits * RD_PORTS);
|
|
967
|
-
const rdsrst = v2_feature_const(cell.parameters.RD_SRST_VALUE, dev.bits * RD_PORTS);
|
|
968
|
-
if (cell.parameters.INIT) {
|
|
969
|
-
const l = init.slice(-1)[0] == 'x' ? 'x' : '0';
|
|
970
|
-
const memdata = new Mem3vl(dev.bits, dev.words);
|
|
971
|
-
for (const k of Array(dev.words).keys()) {
|
|
972
|
-
const wrd = init.slice(dev.bits * k, dev.bits * (k+1));
|
|
973
|
-
while (wrd.length < dev.bits) wrd.push(l);
|
|
974
|
-
memdata.set(k, Vector3vl.fromBin(wrd.reverse().join('')));
|
|
975
|
-
}
|
|
976
|
-
dev.memdata = memdata.toJSON();
|
|
977
|
-
}
|
|
978
|
-
for (const k of Array(RD_PORTS).keys()) {
|
|
979
|
-
const port: Digitaljs.MemReadPort = {
|
|
980
|
-
};
|
|
981
|
-
if (rden[k]) {
|
|
982
|
-
port.clock_polarity = Boolean(rdpol[k]);
|
|
983
|
-
if (cell.connections.RD_EN[k] != '1')
|
|
984
|
-
port.enable_polarity = true;
|
|
985
|
-
};
|
|
986
|
-
if (rdtr[k])
|
|
987
|
-
port.transparent = true;
|
|
988
|
-
if (cell.type == "$mem_v2") {
|
|
989
|
-
if (rdensrst[k])
|
|
990
|
-
port.enable_srst = true;
|
|
991
|
-
function mk_init(s: string, f: (v: string) => void) {
|
|
992
|
-
const v = s.slice(dev.bits * k, dev.bits * (k+1));
|
|
993
|
-
if (!v.split('').every(c => c == 'x'))
|
|
994
|
-
f(v);
|
|
995
|
-
};
|
|
996
|
-
mk_init(rdinit, v => port.init_value = v);
|
|
997
|
-
if (cell.connections.RD_ARST[k] != '0') {
|
|
998
|
-
port.arst_polarity = true;
|
|
999
|
-
mk_init(rdarst, v => port.arst_value = v);
|
|
1000
|
-
}
|
|
1001
|
-
if (cell.connections.RD_SRST[k] != '0') {
|
|
1002
|
-
port.srst_polarity = true;
|
|
1003
|
-
mk_init(rdsrst, v => port.srst_value = v);
|
|
1004
|
-
}
|
|
1005
|
-
function mk_mask(s: number[], f: (v: boolean | boolean[]) => void) {
|
|
1006
|
-
const v = Array(WR_PORTS).fill(0);
|
|
1007
|
-
s.slice(WR_PORTS * k, WR_PORTS * (k+1)).map((c, i) => { v[i] = c });
|
|
1008
|
-
if (v.every(c => c))
|
|
1009
|
-
f(true);
|
|
1010
|
-
else if (v.some(c => c))
|
|
1011
|
-
f(v.map(c => Boolean(c)));
|
|
1012
|
-
}
|
|
1013
|
-
mk_mask(rdtrmask, v => port.transparent = v);
|
|
1014
|
-
mk_mask(rdcolmask, v => port.collision = v);
|
|
1015
|
-
}
|
|
1016
|
-
dev.rdports.push(port);
|
|
1017
|
-
}
|
|
1018
|
-
for (const k of Array(WR_PORTS).keys()) {
|
|
1019
|
-
const port: Digitaljs.MemWritePort = {
|
|
1020
|
-
};
|
|
1021
|
-
if (wren[k]) {
|
|
1022
|
-
port.clock_polarity = Boolean(wrpol[k]);
|
|
1023
|
-
const wr_en_connections = cell.connections.WR_EN.slice(dev.bits * k, dev.bits * (k+1));
|
|
1024
|
-
if (wr_en_connections.some(z => z != '1')) {
|
|
1025
|
-
port.enable_polarity = true;
|
|
1026
|
-
if (wr_en_connections.every(z => z == wr_en_connections[0]))
|
|
1027
|
-
port.no_bit_enable = true;
|
|
1028
|
-
}
|
|
1029
|
-
};
|
|
1030
|
-
dev.wrports.push(port);
|
|
1031
|
-
}
|
|
1032
|
-
break;
|
|
1033
|
-
}
|
|
1034
|
-
case '$lut':
|
|
1035
|
-
assert(cell.connections.A.length == decode_json_number(cell.parameters.WIDTH));
|
|
1036
|
-
assert(cell.connections.Y.length == 1);
|
|
1037
|
-
assert(cell.port_directions.A == 'input');
|
|
1038
|
-
assert(cell.port_directions.Y == 'output');
|
|
1039
|
-
dev.abits = cell.connections.A.length;
|
|
1040
|
-
dev.bits = cell.connections.Y.length;
|
|
1041
|
-
dev.rdports = [{}];
|
|
1042
|
-
dev.wrports = [];
|
|
1043
|
-
dev.memdata = cell.parameters.LUT.split('').reverse();
|
|
1044
|
-
assert(dev.memdata.length == Math.pow(2, dev.abits));
|
|
1045
|
-
|
|
1046
|
-
// Rewrite cell connections to be $mem compatible for port mapping
|
|
1047
|
-
cell.connections.RD_ADDR = cell.connections.A;
|
|
1048
|
-
cell.connections.RD_DATA = cell.connections.Y;
|
|
1049
|
-
delete cell.connections.A;
|
|
1050
|
-
delete cell.connections.Y;
|
|
1051
|
-
break;
|
|
1052
|
-
default:
|
|
1053
|
-
}
|
|
1054
|
-
if (dev.type == 'Dff') {
|
|
1055
|
-
// find register initial value, if exists
|
|
1056
|
-
// Yosys puts initial values in net attributes; there can be many for single actual net!
|
|
1057
|
-
const nms = netnames.get(cell.connections.Q);
|
|
1058
|
-
if (nms !== undefined) {
|
|
1059
|
-
for (const nm of nms) {
|
|
1060
|
-
if (mod.netnames[nm].attributes.init !== undefined)
|
|
1061
|
-
dev.initial = decode_json_constant(mod.netnames[nm].attributes.init, dev.bits);
|
|
1062
|
-
}
|
|
1063
|
-
}
|
|
1064
|
-
}
|
|
1065
|
-
const portmap = portmaps[cell.type];
|
|
1066
|
-
if (portmap) connect_device(dname, cell, portmap);
|
|
1067
|
-
else if (cell.type == '$pmux') connect_pmux(dname, cell);
|
|
1068
|
-
else if (cell.type == '$mem') connect_mem(dname, cell, dev);
|
|
1069
|
-
else if (cell.type == '$mem_v2') connect_mem(dname, cell, dev);
|
|
1070
|
-
else if (cell.type == '$lut') connect_mem(dname, cell, dev);
|
|
1071
|
-
else throw Error('Invalid cell type: ' + cell.type);
|
|
1072
|
-
}
|
|
1073
|
-
// Group bits into nets for complex sources
|
|
1074
|
-
for (const [nbits, net] of nets.entries()) {
|
|
1075
|
-
if (net.source !== undefined) continue;
|
|
1076
|
-
const groups: Net[] = [[]];
|
|
1077
|
-
let pbitinfo: BitInfo | 'const' | undefined = undefined;
|
|
1078
|
-
for (const bit of nbits) {
|
|
1079
|
-
let bitinfo: BitInfo | 'const' = bits.get(bit);
|
|
1080
|
-
if (bitinfo == undefined && constbit(bit))
|
|
1081
|
-
bitinfo = 'const';
|
|
1082
|
-
if (groups.slice(-1)[0].length > 0 &&
|
|
1083
|
-
(typeof bitinfo != typeof pbitinfo ||
|
|
1084
|
-
typeof bitinfo == 'object' &&
|
|
1085
|
-
typeof pbitinfo == 'object' &&
|
|
1086
|
-
(bitinfo.id != pbitinfo.id ||
|
|
1087
|
-
bitinfo.port != pbitinfo.port ||
|
|
1088
|
-
bitinfo.num != pbitinfo.num + 1))) {
|
|
1089
|
-
groups.push([]);
|
|
1090
|
-
}
|
|
1091
|
-
groups.slice(-1)[0].push(bit);
|
|
1092
|
-
pbitinfo = bitinfo;
|
|
1093
|
-
}
|
|
1094
|
-
if (groups.length == 1) continue;
|
|
1095
|
-
if (groups.slice(-1)[0].every(x => x == '0')) {
|
|
1096
|
-
// infer zero-extend
|
|
1097
|
-
const ilen = nbits.length - groups.slice(-1)[0].length;
|
|
1098
|
-
const dname = add_device({
|
|
1099
|
-
type: 'ZeroExtend',
|
|
1100
|
-
extend: { output: nbits.length, input: ilen }
|
|
1101
|
-
});
|
|
1102
|
-
const zbits = nbits.slice(0, ilen);
|
|
1103
|
-
add_net_source(nbits, dname, 'out');
|
|
1104
|
-
add_net_target(zbits, dname, 'in');
|
|
1105
|
-
if (groups.length > 2)
|
|
1106
|
-
add_busgroup(zbits, groups.slice(0, groups.length - 1));
|
|
1107
|
-
} else add_busgroup(nbits, groups);
|
|
1108
|
-
}
|
|
1109
|
-
// Add constants
|
|
1110
|
-
for (const [nbits, net] of nets.entries()) {
|
|
1111
|
-
if (net.source !== undefined) continue;
|
|
1112
|
-
if (!nbits.every(constbit))
|
|
1113
|
-
continue;
|
|
1114
|
-
const dname = add_device({
|
|
1115
|
-
// label: String(val), // TODO
|
|
1116
|
-
type: 'Constant',
|
|
1117
|
-
constant: nbits.slice().reverse().join('')
|
|
1118
|
-
});
|
|
1119
|
-
add_net_source(nbits, dname, 'out');
|
|
1120
|
-
}
|
|
1121
|
-
// Select bits from complex targets
|
|
1122
|
-
for (const [nbits, net] of nets.entries()) {
|
|
1123
|
-
if (net.source !== undefined) continue;
|
|
1124
|
-
// constants should be already handled!
|
|
1125
|
-
assert(nbits.every(x => x > 1));
|
|
1126
|
-
const bitinfos = nbits.map(x => bits.get(x));
|
|
1127
|
-
if (!bitinfos.every(x => typeof x == 'object'))
|
|
1128
|
-
continue; // ignore not fully driven ports
|
|
1129
|
-
// complex sources should be already handled!
|
|
1130
|
-
assert(bitinfos.every(info => info.id == bitinfos[0].id &&
|
|
1131
|
-
info.port == bitinfos[0].port));
|
|
1132
|
-
const cconn = devnets.get(bitinfos[0].id).get(bitinfos[0].port);
|
|
1133
|
-
const dname = add_device({
|
|
1134
|
-
type: 'BusSlice',
|
|
1135
|
-
slice: {
|
|
1136
|
-
first: bitinfos[0].num,
|
|
1137
|
-
count: bitinfos.length,
|
|
1138
|
-
total: cconn.length
|
|
1139
|
-
}
|
|
1140
|
-
});
|
|
1141
|
-
add_net_source(nbits, dname, 'out');
|
|
1142
|
-
add_net_target(cconn, dname, 'in');
|
|
1143
|
-
}
|
|
1144
|
-
// Generate connections between devices
|
|
1145
|
-
for (const [nbits, net] of nets.entries()) {
|
|
1146
|
-
if (net.source === undefined) {
|
|
1147
|
-
console.warn('Undriven net in ' + name + ': ' + nbits);
|
|
1148
|
-
continue;
|
|
1149
|
-
}
|
|
1150
|
-
let first = true;
|
|
1151
|
-
for (const target in net.targets) {
|
|
1152
|
-
const conn: Digitaljs.Connector = {
|
|
1153
|
-
to: net.targets[target],
|
|
1154
|
-
from: net.source
|
|
1155
|
-
};
|
|
1156
|
-
if (net.name) conn.name = net.name;
|
|
1157
|
-
if (net.source_positions) conn.source_positions = net.source_positions;
|
|
1158
|
-
if (!first && mout.devices[conn.from.id].type == "Constant") {
|
|
1159
|
-
// replicate constants for better clarity
|
|
1160
|
-
const dname = add_device({
|
|
1161
|
-
type: 'Constant',
|
|
1162
|
-
constant: mout.devices[conn.from.id].constant
|
|
1163
|
-
});
|
|
1164
|
-
conn.from = {id: dname, port: 'out'};
|
|
1165
|
-
}
|
|
1166
|
-
mout.connectors.push(conn);
|
|
1167
|
-
first = false;
|
|
1168
|
-
}
|
|
1169
|
-
}
|
|
1170
|
-
return mout;
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1173
|
-
function ansi_c_escape_contents(cmd: string): string {
|
|
1174
|
-
function func(ch: string) {
|
|
1175
|
-
if (ch == '\t') return '\\t';
|
|
1176
|
-
if (ch == '\r') return '\\r';
|
|
1177
|
-
if (ch == '\n') return '\\n';
|
|
1178
|
-
return '\\x' + ch.charCodeAt(0).toString(16).padStart(2, '0');
|
|
1179
|
-
}
|
|
1180
|
-
return cmd.replace(/(["'\\])/g,'\\$1')
|
|
1181
|
-
.replace(/[\x00-\x1F\x7F-\x9F]/g, func);
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
|
-
function ansi_c_escape(cmd: string): string {
|
|
1185
|
-
return '"' + ansi_c_escape_contents(cmd) + '"';
|
|
1186
|
-
}
|
|
1187
|
-
|
|
1188
|
-
function shell_escape_contents(cmd: string): string {
|
|
1189
|
-
return cmd.replace(/(["\r\n$`\\])/g,'\\$1');
|
|
1190
|
-
}
|
|
1191
|
-
|
|
1192
|
-
function shell_escape(cmd: string): string {
|
|
1193
|
-
return '"' + shell_escape_contents(cmd) + '"';
|
|
1194
|
-
}
|
|
1195
|
-
|
|
1196
|
-
function process_filename(filename: string): string {
|
|
1197
|
-
const flags = /\.sv$/.test(filename) ? " -sv" : "";
|
|
1198
|
-
return "read_verilog" + flags + " " + ansi_c_escape(filename);
|
|
1199
|
-
}
|
|
1200
|
-
|
|
1201
36
|
const verilator_re = /^%(Warning|Error)[^:]*: ([^:]*):([0-9]+):([0-9]+): (.*)$/;
|
|
1202
37
|
|
|
1203
38
|
export async function verilator_lint(filenames: string[], dirname?: string, options: Options = {}): Promise<LintMessage[]> {
|
|
1204
39
|
try {
|
|
1205
40
|
const output: LintMessage[] = [];
|
|
1206
41
|
const verilator_result: {stdout: string, stderr: string} = await promisify(child_process.exec)(
|
|
1207
|
-
|
|
42
|
+
`timeout -k10s 40s verilator ${prepare_verilator_args(filenames).join(' ')}`,
|
|
1208
43
|
{maxBuffer: 1000000, cwd: dirname || null, timeout: options.timeout || 60000})
|
|
1209
44
|
.catch(exc => exc);
|
|
1210
45
|
for (const line of verilator_result.stderr.split('\n')) {
|
|
@@ -1224,36 +59,16 @@ export async function verilator_lint(filenames: string[], dirname?: string, opti
|
|
|
1224
59
|
}
|
|
1225
60
|
}
|
|
1226
61
|
|
|
1227
|
-
export function yosys2digitaljs(obj: Yosys.Output, options: ConvertOptions = {}): Digitaljs.TopModule {
|
|
1228
|
-
const portmaps = order_ports(obj);
|
|
1229
|
-
const out = yosys_to_digitaljs(obj, portmaps, options);
|
|
1230
|
-
const toporder = topsort(module_deps(obj));
|
|
1231
|
-
toporder.pop();
|
|
1232
|
-
const toplevel = toporder.pop();
|
|
1233
|
-
const output: Digitaljs.TopModule = { subcircuits: {}, ... out[toplevel] };
|
|
1234
|
-
for (const x of toporder)
|
|
1235
|
-
output.subcircuits[x] = out[x];
|
|
1236
|
-
return output;
|
|
1237
|
-
}
|
|
1238
|
-
|
|
1239
62
|
export async function process(filenames: string[], dirname?: string, options: Options = {}): Promise<Output> {
|
|
1240
|
-
const optimize_simp = options.optimize ? "; opt" : "; opt_clean";
|
|
1241
|
-
const optimize = options.optimize ? "; opt -full" : "; opt_clean";
|
|
1242
|
-
const fsmexpand = options.fsmexpand ? " -expand" : "";
|
|
1243
|
-
const fsmpass = options.fsm == "nomap" ? "; fsm -nomap" + fsmexpand
|
|
1244
|
-
: options.fsm ? "; fsm" + fsmexpand
|
|
1245
|
-
: "";
|
|
1246
63
|
const tmpjson = await tmp.tmpName({ postfix: '.json' });
|
|
1247
64
|
let obj = undefined;
|
|
1248
65
|
const yosys_result: {stdout: string, stderr: string, killed?: boolean, code?: number} = await promisify(child_process.exec)(
|
|
1249
|
-
|
|
1250
|
-
'; hierarchy -auto-top; proc' + optimize_simp + fsmpass + '; memory -nomap; wreduce -memx' +
|
|
1251
|
-
optimize + '" -o "' + tmpjson + '"',
|
|
66
|
+
`timeout -k10s 40s yosys -p "${prepare_yosys_script(filenames, options)}" -o ${tmpjson}`,
|
|
1252
67
|
{maxBuffer: 1000000, cwd: dirname || null, timeout: options.timeout || 60000})
|
|
1253
68
|
.catch(exc => exc);
|
|
1254
69
|
try {
|
|
1255
70
|
if (yosys_result instanceof Error) {
|
|
1256
|
-
if (yosys_result.killed)
|
|
71
|
+
if (yosys_result.killed)
|
|
1257
72
|
yosys_result.message = "Yosys killed"
|
|
1258
73
|
else if (yosys_result.code)
|
|
1259
74
|
yosys_result.message = "Yosys failed with code " + yosys_result.code;
|
|
@@ -1281,29 +96,6 @@ export async function process(filenames: string[], dirname?: string, options: Op
|
|
|
1281
96
|
}
|
|
1282
97
|
}
|
|
1283
98
|
|
|
1284
|
-
export function io_ui(output: Digitaljs.Module) {
|
|
1285
|
-
for (const [name, dev] of Object.entries(output.devices)) {
|
|
1286
|
-
if (dev.type == 'Input' || dev.type == 'Output') {
|
|
1287
|
-
dev.label = dev.net;
|
|
1288
|
-
}
|
|
1289
|
-
// use clock for clocky named inputs
|
|
1290
|
-
if (dev.type == 'Input' && dev.bits == 1 && (dev.label == 'clk' || dev.label == 'clock')) {
|
|
1291
|
-
dev.type = 'Clock';
|
|
1292
|
-
dev.propagation = 100;
|
|
1293
|
-
}
|
|
1294
|
-
if (dev.type == 'Input')
|
|
1295
|
-
dev.type = dev.bits == 1 ? 'Button' : 'NumEntry';
|
|
1296
|
-
if (dev.type == 'Output') {
|
|
1297
|
-
if (dev.bits == 1)
|
|
1298
|
-
dev.type = 'Lamp';
|
|
1299
|
-
else if (dev.bits == 8 && (dev.label == 'display7' || dev.label.startsWith('display7_')))
|
|
1300
|
-
dev.type = 'Display7';
|
|
1301
|
-
else
|
|
1302
|
-
dev.type = 'NumDisplay';
|
|
1303
|
-
}
|
|
1304
|
-
}
|
|
1305
|
-
}
|
|
1306
|
-
|
|
1307
99
|
export async function process_files(data: {[key: string]: string}, options: Options = {}): Promise<Output> {
|
|
1308
100
|
const dir = await tmp.dir();
|
|
1309
101
|
const names = [];
|
|
@@ -1332,4 +124,3 @@ export async function process_sv(text: string, options: Options = {}): Promise<O
|
|
|
1332
124
|
tmpsv.cleanup();
|
|
1333
125
|
}
|
|
1334
126
|
}
|
|
1335
|
-
|