yosys2digitaljs 0.2.3 → 0.6.0
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/ChangeLog.md +33 -0
- package/README.md +4 -4
- package/dist/index.d.ts +164 -0
- package/dist/index.js +1132 -0
- package/package.json +12 -4
- package/process.js +13 -2
- package/src/index.ts +1284 -0
- package/tests/cycleadder.sv +1 -1
- package/tests/cycleadder_arst.sv +1 -1
- package/tests/dff_masterslave.sv +1 -1
- package/tests/dlatch_gate.sv +1 -1
- package/tests/fsm.sv +4 -3
- package/tsconfig.json +15 -0
- package/.travis.yml +0 -8
- package/index.js +0 -878
package/index.js
DELETED
|
@@ -1,878 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
|
|
4
|
-
const tmp = require('tmp-promise');
|
|
5
|
-
const child_process = require('child_process');
|
|
6
|
-
const assert = require('assert');
|
|
7
|
-
const topsort = require('topsort');
|
|
8
|
-
const fs = require('fs');
|
|
9
|
-
const sanitize = require("sanitize-filename");
|
|
10
|
-
const path = require('path');
|
|
11
|
-
const HashMap = require('hashmap');
|
|
12
|
-
const bigInt = require('big-integer');
|
|
13
|
-
const {promisify} = require('util');
|
|
14
|
-
const {Vector3vl, Mem3vl} = require('3vl');
|
|
15
|
-
|
|
16
|
-
const unary_gates = new Set([
|
|
17
|
-
'$not', '$neg', '$pos', '$reduce_and', '$reduce_or', '$reduce_xor',
|
|
18
|
-
'$reduce_xnor', '$reduce_bool', '$logic_not']);
|
|
19
|
-
const binary_gates = new Set([
|
|
20
|
-
'$and', '$or', '$xor', '$xnor',
|
|
21
|
-
'$add', '$sub', '$mul', '$div', '$mod', '$pow',
|
|
22
|
-
'$lt', '$le', '$eq', '$ne', '$ge', '$gt', '$eqx', '$nex',
|
|
23
|
-
'$shl', '$shr', '$sshl', '$sshr', '$shift', '$shiftx',
|
|
24
|
-
'$logic_and', '$logic_or']);
|
|
25
|
-
const gate_subst = new Map([
|
|
26
|
-
['$not', 'Not'],
|
|
27
|
-
['$and', 'And'],
|
|
28
|
-
['$nand', 'Nand'],
|
|
29
|
-
['$or', 'Or'],
|
|
30
|
-
['$nor', 'Nor'],
|
|
31
|
-
['$xor', 'Xor'],
|
|
32
|
-
['$xnor', 'Xnor'],
|
|
33
|
-
['$reduce_and', 'AndReduce'],
|
|
34
|
-
['$reduce_nand', 'NandReduce'],
|
|
35
|
-
['$reduce_or', 'OrReduce'],
|
|
36
|
-
['$reduce_nor', 'NorReduce'],
|
|
37
|
-
['$reduce_xor', 'XorReduce'],
|
|
38
|
-
['$reduce_xnor', 'XnorReduce'],
|
|
39
|
-
['$reduce_bool', 'OrReduce'],
|
|
40
|
-
['$logic_not', 'NorReduce'],
|
|
41
|
-
['$repeater', 'Repeater'],
|
|
42
|
-
['$shl', 'ShiftLeft'],
|
|
43
|
-
['$shr', 'ShiftRight'],
|
|
44
|
-
['$lt', 'Lt'],
|
|
45
|
-
['$le', 'Le'],
|
|
46
|
-
['$eq', 'Eq'],
|
|
47
|
-
['$ne', 'Ne'],
|
|
48
|
-
['$gt', 'Gt'],
|
|
49
|
-
['$ge', 'Ge'],
|
|
50
|
-
['$constant', 'Constant'],
|
|
51
|
-
['$neg', 'Negation'],
|
|
52
|
-
['$pos', 'UnaryPlus'],
|
|
53
|
-
['$add', 'Addition'],
|
|
54
|
-
['$sub', 'Subtraction'],
|
|
55
|
-
['$mul', 'Multiplication'],
|
|
56
|
-
['$div', 'Division'],
|
|
57
|
-
['$mod', 'Modulo'],
|
|
58
|
-
['$pow', 'Power'],
|
|
59
|
-
['$mux', 'Mux'],
|
|
60
|
-
['$pmux', 'Mux1Hot'],
|
|
61
|
-
['$dff', 'Dff'],
|
|
62
|
-
['$mem', 'Memory'],
|
|
63
|
-
['$fsm', 'FSM'],
|
|
64
|
-
['$clock', 'Clock'],
|
|
65
|
-
['$button', 'Button'],
|
|
66
|
-
['$lamp', 'Lamp'],
|
|
67
|
-
['$numdisplay', 'NumDisplay'],
|
|
68
|
-
['$numentry', 'NumEntry'],
|
|
69
|
-
['$input', 'Input'],
|
|
70
|
-
['$output', 'Output'],
|
|
71
|
-
['$busgroup', 'BusGroup'],
|
|
72
|
-
['$busungroup', 'BusUngroup'],
|
|
73
|
-
['$busslice', 'BusSlice'],
|
|
74
|
-
['$zeroextend', 'ZeroExtend'],
|
|
75
|
-
['$signextend', 'SignExtend'],
|
|
76
|
-
['$reduce_bool', 'OrReduce'],
|
|
77
|
-
['$eqx', 'Eq'],
|
|
78
|
-
['$nex', 'Ne'],
|
|
79
|
-
['$sshl', 'ShiftLeft'],
|
|
80
|
-
['$sshr', 'ShiftRight'],
|
|
81
|
-
['$shift', 'ShiftRight'],
|
|
82
|
-
['$shiftx', 'ShiftRight'],
|
|
83
|
-
['$logic_and', 'And'],
|
|
84
|
-
['$logic_or', 'Or'],
|
|
85
|
-
['$dffe', 'Dff'],
|
|
86
|
-
['$adff', 'Dff'],
|
|
87
|
-
['$dlatch', 'Dff']]);
|
|
88
|
-
const gate_negations = new Map([
|
|
89
|
-
['And', 'Nand'],
|
|
90
|
-
['Nand', 'And'],
|
|
91
|
-
['Nor', 'Or'],
|
|
92
|
-
['Or', 'Nor'],
|
|
93
|
-
['Xor', 'Xnor'],
|
|
94
|
-
['Xnor', 'Xor'],
|
|
95
|
-
['AndReduce', 'NandReduce'],
|
|
96
|
-
['NandReduce', 'AndReduce'],
|
|
97
|
-
['NorReduce', 'OrReduce'],
|
|
98
|
-
['OrReduce', 'NorReduce'],
|
|
99
|
-
['XorReduce', 'XnorReduce'],
|
|
100
|
-
['XnorReduce', 'XorReduce']]);
|
|
101
|
-
|
|
102
|
-
function chunkArray(a, chunk_size){
|
|
103
|
-
let results = [];
|
|
104
|
-
let ca = a.splice();
|
|
105
|
-
|
|
106
|
-
while (ca.length) {
|
|
107
|
-
results.push(ca.splice(0, chunk_size));
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return results;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function module_deps(data) {
|
|
114
|
-
const out = [];
|
|
115
|
-
for (const [name, mod] of Object.entries(data.modules)) {
|
|
116
|
-
out.push([name, 1/0]);
|
|
117
|
-
for (const cname in mod.cells) {
|
|
118
|
-
const cell = mod.cells[cname];
|
|
119
|
-
if (cell.type in data.modules)
|
|
120
|
-
out.push([cell.type, name]);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
return out;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function order_ports(data) {
|
|
127
|
-
const unmap = {A: 'in', Y: 'out'};
|
|
128
|
-
const binmap = {A: 'in1', B: 'in2', Y: 'out'};
|
|
129
|
-
const out = {
|
|
130
|
-
'$mux': {A: 'in0', B: 'in1', S: 'sel', Y: 'out'},
|
|
131
|
-
'$dff': {CLK: 'clk', D: 'in', Q: 'out'},
|
|
132
|
-
'$dffe': {CLK: 'clk', EN: 'en', D: 'in', Q: 'out'},
|
|
133
|
-
'$adff': {CLK: 'clk', ARST: 'arst', D: 'in', Q: 'out'},
|
|
134
|
-
'$dlatch': {EN: 'en', D: 'in', Q: 'out'},
|
|
135
|
-
'$fsm': {ARST: 'arst', CLK: 'clk', CTRL_IN: 'in', CTRL_OUT: 'out'}
|
|
136
|
-
};
|
|
137
|
-
binary_gates.forEach((nm) => out[nm] = binmap);
|
|
138
|
-
unary_gates.forEach((nm) => out[nm] = unmap);
|
|
139
|
-
for (const [name, mod] of Object.entries(data.modules)) {
|
|
140
|
-
const portmap = {};
|
|
141
|
-
const ins = [], outs = [];
|
|
142
|
-
for (const pname in mod.ports) {
|
|
143
|
-
portmap[pname] = pname;
|
|
144
|
-
}
|
|
145
|
-
out[name] = portmap;
|
|
146
|
-
}
|
|
147
|
-
return out;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
function decode_json_bigint(param) {
|
|
151
|
-
if (typeof param == 'string')
|
|
152
|
-
return bigInt(param, 2)
|
|
153
|
-
else if (typeof param == 'number')
|
|
154
|
-
return bigInt(param)
|
|
155
|
-
else assert(false);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
function decode_json_bigint_as_array(param) {
|
|
159
|
-
return decode_json_bigint(param).toArray(2).value;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function decode_json_constant(param, bits) {
|
|
163
|
-
if (typeof param == 'number')
|
|
164
|
-
return bigInt(param).toArray(2).value.map(String).reverse()
|
|
165
|
-
.concat(Array(bits).fill('0')).slice(0, bits).reverse().join('');
|
|
166
|
-
else
|
|
167
|
-
return param;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function yosys_to_digitaljs(data, portmaps, options = {}) {
|
|
171
|
-
const out = {};
|
|
172
|
-
for (const [name, mod] of Object.entries(data.modules)) {
|
|
173
|
-
out[name] = yosys_to_digitaljs_mod(name, mod, portmaps, options);
|
|
174
|
-
}
|
|
175
|
-
return out
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function yosys_to_digitaljs_mod(name, mod, portmaps, options = {}) {
|
|
179
|
-
function constbit(bit) {
|
|
180
|
-
return bit == '0' || bit == '1' || bit == 'x';
|
|
181
|
-
}
|
|
182
|
-
const nets = new HashMap();
|
|
183
|
-
const netnames = new HashMap();
|
|
184
|
-
const bits = new Map();
|
|
185
|
-
const devnets = new Map();
|
|
186
|
-
let n = 0, pn = 0;
|
|
187
|
-
function gen_name() {
|
|
188
|
-
const nm = 'dev' + n++;
|
|
189
|
-
devnets.set(nm, new Map());
|
|
190
|
-
return nm;
|
|
191
|
-
}
|
|
192
|
-
function gen_bitname() {
|
|
193
|
-
return 'bit' + pn++;
|
|
194
|
-
}
|
|
195
|
-
function get_net(k) {
|
|
196
|
-
// create net if does not exist yet
|
|
197
|
-
if (!nets.has(k)) {
|
|
198
|
-
const nms = netnames.get(k);
|
|
199
|
-
nets.set(k, {source: undefined, targets: [], name: nms ? nms[0] : undefined});
|
|
200
|
-
}
|
|
201
|
-
return nets.get(k);
|
|
202
|
-
}
|
|
203
|
-
function add_net_source(k, d, p, primary) {
|
|
204
|
-
if (k.length == 0) return; // for unconnected ports
|
|
205
|
-
const net = get_net(k);
|
|
206
|
-
if(net.source !== undefined) {
|
|
207
|
-
// multiple sources driving one net, disallowed in digitaljs
|
|
208
|
-
console.log(k);
|
|
209
|
-
console.log(net);
|
|
210
|
-
throw Error('Multiple sources driving net: ' + net.name);
|
|
211
|
-
}
|
|
212
|
-
net.source = { id: d, port: p };
|
|
213
|
-
if (primary) for (const [nbit, bit] of k.entries()) {
|
|
214
|
-
bits.set(bit, { id: d, port: p, num: nbit });
|
|
215
|
-
}
|
|
216
|
-
devnets.get(d).set(p, k);
|
|
217
|
-
}
|
|
218
|
-
function add_net_target(k, d, p) {
|
|
219
|
-
if (k.length == 0) return; // for unconnected ports
|
|
220
|
-
const net = get_net(k);
|
|
221
|
-
net.targets.push({ id: d, port: p });
|
|
222
|
-
devnets.get(d).set(p, k);
|
|
223
|
-
}
|
|
224
|
-
const mout = {
|
|
225
|
-
devices: {},
|
|
226
|
-
connectors: []
|
|
227
|
-
}
|
|
228
|
-
function add_device(dev) {
|
|
229
|
-
const dname = gen_name();
|
|
230
|
-
if (options.propagation !== undefined)
|
|
231
|
-
dev.propagation = options.propagation;
|
|
232
|
-
mout.devices[dname] = dev;
|
|
233
|
-
return dname;
|
|
234
|
-
}
|
|
235
|
-
function add_busgroup(nbits, groups) {
|
|
236
|
-
if (get_net(nbits).source !== undefined)
|
|
237
|
-
return; // the bits were already grouped
|
|
238
|
-
const dname = add_device({
|
|
239
|
-
type: 'BusGroup',
|
|
240
|
-
groups: groups.map(g => g.length)
|
|
241
|
-
});
|
|
242
|
-
add_net_source(nbits, dname, 'out');
|
|
243
|
-
for (const [gn, group] of groups.entries()) {
|
|
244
|
-
add_net_target(group, dname, 'in' + gn);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
function connect_device(dname, cell, portmap) {
|
|
248
|
-
for (const [pname, pdir] of Object.entries(cell.port_directions)) {
|
|
249
|
-
const pconn = cell.connections[pname];
|
|
250
|
-
switch (pdir) {
|
|
251
|
-
case 'input':
|
|
252
|
-
add_net_target(pconn, dname, portmap[pname]);
|
|
253
|
-
break;
|
|
254
|
-
case 'output':
|
|
255
|
-
add_net_source(pconn, dname, portmap[pname], true);
|
|
256
|
-
break;
|
|
257
|
-
default:
|
|
258
|
-
throw Error('Invalid port direction: ' + pdir);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
function connect_pmux(dname, cell) {
|
|
263
|
-
add_net_target(cell.connections.A, dname, 'in0');
|
|
264
|
-
add_net_target(cell.connections.S.slice().reverse(), dname, 'sel');
|
|
265
|
-
add_net_source(cell.connections.Y, dname, 'out', true);
|
|
266
|
-
for (const i of Array(cell.parameters.S_WIDTH).keys()) {
|
|
267
|
-
const p = (cell.parameters.S_WIDTH-i-1) * cell.parameters.WIDTH;
|
|
268
|
-
add_net_target(cell.connections.B.slice(p, p + cell.parameters.WIDTH),
|
|
269
|
-
dname, 'in' + (i+1));
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
function connect_mem(dname, cell, dev) {
|
|
273
|
-
for (const [k, port] of dev.rdports.entries()) {
|
|
274
|
-
const portname = "rd" + k;
|
|
275
|
-
add_net_target(cell.connections.RD_ADDR.slice(dev.abits * k, dev.abits * (k+1)),
|
|
276
|
-
dname, portname + "addr");
|
|
277
|
-
add_net_source(cell.connections.RD_DATA.slice(dev.bits * k, dev.bits * (k+1)),
|
|
278
|
-
dname, portname + "data", true);
|
|
279
|
-
if ('clock_polarity' in port)
|
|
280
|
-
add_net_target([cell.connections.RD_CLK[k]], dname, portname + "clk");
|
|
281
|
-
if ('enable_polarity' in port)
|
|
282
|
-
add_net_target([cell.connections.RD_EN[k]], dname, portname + "en");
|
|
283
|
-
}
|
|
284
|
-
for (const [k, port] of dev.wrports.entries()) {
|
|
285
|
-
const portname = "wr" + k;
|
|
286
|
-
add_net_target(cell.connections.WR_ADDR.slice(dev.abits * k, dev.abits * (k+1)),
|
|
287
|
-
dname, portname + "addr");
|
|
288
|
-
add_net_target(cell.connections.WR_DATA.slice(dev.bits * k, dev.bits * (k+1)),
|
|
289
|
-
dname, portname + "data");
|
|
290
|
-
if ('clock_polarity' in port)
|
|
291
|
-
add_net_target([cell.connections.WR_CLK[k]], dname, portname + "clk");
|
|
292
|
-
if ('enable_polarity' in port)
|
|
293
|
-
add_net_target(cell.connections.WR_EN.slice(dev.bits * k, dev.bits * (k+1)),
|
|
294
|
-
dname, portname + "en");
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
// Find net names
|
|
298
|
-
for (const [nname, data] of Object.entries(mod.netnames)) {
|
|
299
|
-
if (data.hide_name) continue;
|
|
300
|
-
let l = netnames.get(data.bits);
|
|
301
|
-
if (l === undefined) {
|
|
302
|
-
l = [];
|
|
303
|
-
netnames.set(data.bits, l);
|
|
304
|
-
}
|
|
305
|
-
l.push(nname);
|
|
306
|
-
}
|
|
307
|
-
// Add inputs/outputs
|
|
308
|
-
for (const [pname, port] of Object.entries(mod.ports)) {
|
|
309
|
-
const dir = port.direction == "input" ? "Input" :
|
|
310
|
-
port.direction == "output" ? "Output" :
|
|
311
|
-
undefined;
|
|
312
|
-
const dname = add_device({
|
|
313
|
-
type: dir,
|
|
314
|
-
net: pname,
|
|
315
|
-
order: n,
|
|
316
|
-
bits: port.bits.length
|
|
317
|
-
});
|
|
318
|
-
switch (port.direction) {
|
|
319
|
-
case 'input':
|
|
320
|
-
add_net_source(port.bits, dname, 'out', true);
|
|
321
|
-
break;
|
|
322
|
-
case 'output':
|
|
323
|
-
add_net_target(port.bits, dname, 'in');
|
|
324
|
-
break;
|
|
325
|
-
default: throw Error('Invalid port direction: ' + port.direction);
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
// Add gates
|
|
329
|
-
for (const [cname, cell] of Object.entries(mod.cells)) {
|
|
330
|
-
const dev = {
|
|
331
|
-
label: cname,
|
|
332
|
-
type: gate_subst.get(cell.type)
|
|
333
|
-
};
|
|
334
|
-
if (dev.type == undefined) {
|
|
335
|
-
dev.type = 'Subcircuit';
|
|
336
|
-
dev.celltype = cell.type;
|
|
337
|
-
}
|
|
338
|
-
const dname = add_device(dev);
|
|
339
|
-
function match_port(con, sig, sz) {
|
|
340
|
-
if (con.length > sz)
|
|
341
|
-
con.splice(sz);
|
|
342
|
-
else if (con.length < sz) {
|
|
343
|
-
const ccon = con.slice();
|
|
344
|
-
const pad = sig ? con.slice(-1)[0] : '0';
|
|
345
|
-
con.splice(con.length, 0, ...Array(sz - con.length).fill(pad));
|
|
346
|
-
if (!con.every(constbit) && get_net(con).source === undefined) {
|
|
347
|
-
// WARNING: potentially troublesome hack for readability
|
|
348
|
-
// handled generally in the grouping phase,
|
|
349
|
-
// but it's hard to add sign extensions there
|
|
350
|
-
const extname = add_device({
|
|
351
|
-
type: sig ? 'SignExtend' : 'ZeroExtend',
|
|
352
|
-
extend: { input: ccon.length, output: con.length }
|
|
353
|
-
});
|
|
354
|
-
add_net_target(ccon, extname, 'in');
|
|
355
|
-
add_net_source(con, extname, 'out');
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
function zero_extend_output(con) {
|
|
360
|
-
if (con.length > 1) {
|
|
361
|
-
const ccon = con.slice();
|
|
362
|
-
con.splice(1);
|
|
363
|
-
const extname = add_device({
|
|
364
|
-
type: 'ZeroExtend',
|
|
365
|
-
extend: { input: con.length, output: ccon.length }
|
|
366
|
-
});
|
|
367
|
-
add_net_source(ccon, extname, 'out');
|
|
368
|
-
add_net_target(con, extname, 'in');
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
if (unary_gates.has(cell.type)) {
|
|
372
|
-
assert(cell.connections.A.length == cell.parameters.A_WIDTH);
|
|
373
|
-
assert(cell.connections.Y.length == cell.parameters.Y_WIDTH);
|
|
374
|
-
assert(cell.port_directions.A == 'input');
|
|
375
|
-
assert(cell.port_directions.Y == 'output');
|
|
376
|
-
}
|
|
377
|
-
if (binary_gates.has(cell.type)) {
|
|
378
|
-
assert(cell.connections.A.length == cell.parameters.A_WIDTH);
|
|
379
|
-
assert(cell.connections.B.length == cell.parameters.B_WIDTH);
|
|
380
|
-
assert(cell.connections.Y.length == cell.parameters.Y_WIDTH);
|
|
381
|
-
assert(cell.port_directions.A == 'input');
|
|
382
|
-
assert(cell.port_directions.B == 'input');
|
|
383
|
-
assert(cell.port_directions.Y == 'output');
|
|
384
|
-
}
|
|
385
|
-
if (['$dff', '$dffe', '$adff', '$dlatch'].includes(cell.type)) {
|
|
386
|
-
assert(cell.connections.D.length == cell.parameters.WIDTH);
|
|
387
|
-
assert(cell.connections.Q.length == cell.parameters.WIDTH);
|
|
388
|
-
assert(cell.port_directions.D == 'input');
|
|
389
|
-
assert(cell.port_directions.Q == 'output');
|
|
390
|
-
if (cell.type != '$dlatch') {
|
|
391
|
-
assert(cell.connections.CLK.length == 1);
|
|
392
|
-
assert(cell.port_directions.CLK == 'input');
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
switch (cell.type) {
|
|
396
|
-
case '$neg': case '$pos':
|
|
397
|
-
dev.bits = {
|
|
398
|
-
in: cell.connections.A.length,
|
|
399
|
-
out: cell.connections.Y.length
|
|
400
|
-
};
|
|
401
|
-
dev.signed = Boolean(cell.parameters.A_SIGNED);
|
|
402
|
-
break;
|
|
403
|
-
case '$not':
|
|
404
|
-
match_port(cell.connections.A, cell.parameters.A_SIGNED, cell.connections.Y.length);
|
|
405
|
-
dev.bits = cell.connections.Y.length;
|
|
406
|
-
break;
|
|
407
|
-
case '$add': case '$sub': case '$mul': case '$div': case '$mod': case '$pow':
|
|
408
|
-
dev.bits = {
|
|
409
|
-
in1: cell.connections.A.length,
|
|
410
|
-
in2: cell.connections.B.length,
|
|
411
|
-
out: cell.connections.Y.length
|
|
412
|
-
};
|
|
413
|
-
dev.signed = {
|
|
414
|
-
in1: Boolean(cell.parameters.A_SIGNED),
|
|
415
|
-
in2: Boolean(cell.parameters.B_SIGNED)
|
|
416
|
-
}
|
|
417
|
-
break;
|
|
418
|
-
case '$and': case '$or': case '$xor': case '$xnor':
|
|
419
|
-
match_port(cell.connections.A, cell.parameters.A_SIGNED, cell.connections.Y.length);
|
|
420
|
-
match_port(cell.connections.B, cell.parameters.B_SIGNED, cell.connections.Y.length);
|
|
421
|
-
dev.bits = cell.connections.Y.length;
|
|
422
|
-
break;
|
|
423
|
-
case '$reduce_and': case '$reduce_or': case '$reduce_xor': case '$reduce_xnor':
|
|
424
|
-
case '$reduce_bool': case '$logic_not':
|
|
425
|
-
dev.bits = cell.connections.A.length;
|
|
426
|
-
zero_extend_output(cell.connections.Y);
|
|
427
|
-
if (dev.bits == 1) {
|
|
428
|
-
if (['$reduce_xnor', '$logic_not'].includes(cell.type))
|
|
429
|
-
dev.type = 'Not';
|
|
430
|
-
else
|
|
431
|
-
dev.type = 'Repeater';
|
|
432
|
-
}
|
|
433
|
-
break;
|
|
434
|
-
case '$eq': case '$ne': case '$lt': case '$le': case '$gt': case '$ge':
|
|
435
|
-
case '$eqx': case '$nex':
|
|
436
|
-
dev.bits = {
|
|
437
|
-
in1: cell.connections.A.length,
|
|
438
|
-
in2: cell.connections.B.length
|
|
439
|
-
};
|
|
440
|
-
dev.signed = {
|
|
441
|
-
in1: Boolean(cell.parameters.A_SIGNED),
|
|
442
|
-
in2: Boolean(cell.parameters.B_SIGNED)
|
|
443
|
-
};
|
|
444
|
-
zero_extend_output(cell.connections.Y);
|
|
445
|
-
break;
|
|
446
|
-
case '$shl': case '$shr': case '$sshl': case '$sshr':
|
|
447
|
-
case '$shift': case '$shiftx':
|
|
448
|
-
dev.bits = {
|
|
449
|
-
in1: cell.connections.A.length,
|
|
450
|
-
in2: cell.connections.B.length,
|
|
451
|
-
out: cell.connections.Y.length
|
|
452
|
-
};
|
|
453
|
-
dev.signed = {
|
|
454
|
-
in1: Boolean(cell.parameters.A_SIGNED),
|
|
455
|
-
in2: Boolean(cell.parameters.B_SIGNED && ['$shift', '$shiftx'].includes(cell.type)),
|
|
456
|
-
out: Boolean(cell.parameters.A_SIGNED && ['$sshl', '$sshr'].includes(cell.type))
|
|
457
|
-
};
|
|
458
|
-
dev.fillx = cell.type == '$shiftx';
|
|
459
|
-
break;
|
|
460
|
-
case '$logic_and': case '$logic_or': {
|
|
461
|
-
function reduce_input(con) {
|
|
462
|
-
const ccon = con.slice();
|
|
463
|
-
con.splice(0, con.length, gen_bitname());
|
|
464
|
-
const extname = add_device({
|
|
465
|
-
type: 'OrReduce',
|
|
466
|
-
bits: ccon.length
|
|
467
|
-
});
|
|
468
|
-
add_net_source(con, extname, 'out');
|
|
469
|
-
add_net_target(ccon, extname, 'in');
|
|
470
|
-
}
|
|
471
|
-
if (cell.connections.A.length > 1)
|
|
472
|
-
reduce_input(cell.connections.A);
|
|
473
|
-
if (cell.connections.B.length > 1)
|
|
474
|
-
reduce_input(cell.connections.B);
|
|
475
|
-
zero_extend_output(cell.connections.Y);
|
|
476
|
-
break;
|
|
477
|
-
}
|
|
478
|
-
case '$mux':
|
|
479
|
-
assert(cell.connections.A.length == cell.parameters.WIDTH);
|
|
480
|
-
assert(cell.connections.B.length == cell.parameters.WIDTH);
|
|
481
|
-
assert(cell.connections.Y.length == cell.parameters.WIDTH);
|
|
482
|
-
assert(cell.port_directions.A == 'input');
|
|
483
|
-
assert(cell.port_directions.B == 'input');
|
|
484
|
-
assert(cell.port_directions.Y == 'output');
|
|
485
|
-
dev.bits = {
|
|
486
|
-
in: cell.parameters.WIDTH,
|
|
487
|
-
sel: 1
|
|
488
|
-
};
|
|
489
|
-
break;
|
|
490
|
-
case '$pmux':
|
|
491
|
-
assert(cell.connections.B.length == cell.parameters.WIDTH * cell.parameters.S_WIDTH);
|
|
492
|
-
assert(cell.connections.A.length == cell.parameters.WIDTH);
|
|
493
|
-
assert(cell.connections.S.length == cell.parameters.S_WIDTH);
|
|
494
|
-
assert(cell.connections.Y.length == cell.parameters.WIDTH);
|
|
495
|
-
assert(cell.port_directions.A == 'input');
|
|
496
|
-
assert(cell.port_directions.B == 'input');
|
|
497
|
-
assert(cell.port_directions.S == 'input');
|
|
498
|
-
assert(cell.port_directions.Y == 'output');
|
|
499
|
-
dev.bits = {
|
|
500
|
-
in: cell.parameters.WIDTH,
|
|
501
|
-
sel: cell.parameters.S_WIDTH
|
|
502
|
-
};
|
|
503
|
-
break;
|
|
504
|
-
case '$dff':
|
|
505
|
-
dev.bits = cell.parameters.WIDTH;
|
|
506
|
-
dev.polarity = {
|
|
507
|
-
clock: Boolean(cell.parameters.CLK_POLARITY)
|
|
508
|
-
};
|
|
509
|
-
break;
|
|
510
|
-
case '$dffe':
|
|
511
|
-
assert(cell.connections.EN.length == 1);
|
|
512
|
-
assert(cell.port_directions.EN == 'input');
|
|
513
|
-
dev.bits = cell.parameters.WIDTH;
|
|
514
|
-
dev.polarity = {
|
|
515
|
-
clock: Boolean(cell.parameters.CLK_POLARITY),
|
|
516
|
-
enable: Boolean(cell.parameters.EN_POLARITY)
|
|
517
|
-
};
|
|
518
|
-
break;
|
|
519
|
-
case '$adff':
|
|
520
|
-
assert(cell.connections.ARST.length == 1);
|
|
521
|
-
assert(cell.port_directions.ARST == 'input');
|
|
522
|
-
dev.bits = cell.parameters.WIDTH;
|
|
523
|
-
dev.polarity = {
|
|
524
|
-
clock: Boolean(cell.parameters.CLK_POLARITY),
|
|
525
|
-
arst: Boolean(cell.parameters.ARST_POLARITY)
|
|
526
|
-
};
|
|
527
|
-
dev.arst_value = decode_json_constant(cell.parameters.ARST_VALUE, dev.bits);
|
|
528
|
-
break;
|
|
529
|
-
case '$dlatch':
|
|
530
|
-
assert(cell.connections.EN.length == 1);
|
|
531
|
-
assert(cell.port_directions.EN == 'input');
|
|
532
|
-
dev.bits = cell.parameters.WIDTH;
|
|
533
|
-
dev.polarity = {
|
|
534
|
-
enable: Boolean(cell.parameters.EN_POLARITY)
|
|
535
|
-
};
|
|
536
|
-
break;
|
|
537
|
-
case '$fsm': {
|
|
538
|
-
assert(cell.connections.ARST.length == 1);
|
|
539
|
-
assert(cell.connections.CLK.length == 1);
|
|
540
|
-
assert(cell.connections.CTRL_IN.length == cell.parameters.CTRL_IN_WIDTH);
|
|
541
|
-
assert(cell.connections.CTRL_OUT.length == cell.parameters.CTRL_OUT_WIDTH);
|
|
542
|
-
const step = 2*cell.parameters.STATE_NUM_LOG2
|
|
543
|
-
+ cell.parameters.CTRL_IN_WIDTH
|
|
544
|
-
+ cell.parameters.CTRL_OUT_WIDTH;
|
|
545
|
-
const tt = typeof(cell.parameters.TRANS_TABLE) == "number"
|
|
546
|
-
? Vector3vl.fromBin(bigInt(cell.parameters.TRANS_TABLE).toString(2), cell.parameters.TRANS_NUM * step).toBin() // workaround for yosys silliness
|
|
547
|
-
: cell.parameters.TRANS_TABLE;
|
|
548
|
-
assert(tt.length == cell.parameters.TRANS_NUM * step);
|
|
549
|
-
dev.polarity = {
|
|
550
|
-
clock: Boolean(cell.parameters.CLK_POLARITY),
|
|
551
|
-
arst: Boolean(cell.parameters.ARST_POLARITY)
|
|
552
|
-
};
|
|
553
|
-
dev.wirename = cell.parameters.NAME;
|
|
554
|
-
dev.bits = {
|
|
555
|
-
in: cell.parameters.CTRL_IN_WIDTH,
|
|
556
|
-
out: cell.parameters.CTRL_OUT_WIDTH
|
|
557
|
-
};
|
|
558
|
-
dev.states = cell.parameters.STATE_NUM;
|
|
559
|
-
dev.init_state = cell.parameters.STATE_RST;
|
|
560
|
-
dev.trans_table = [];
|
|
561
|
-
for (let i = 0; i < cell.parameters.TRANS_NUM; i++) {
|
|
562
|
-
let base = i * step;
|
|
563
|
-
const o = {};
|
|
564
|
-
const f = (sz) => {
|
|
565
|
-
const ret = tt.slice(base, base + sz);
|
|
566
|
-
base += sz;
|
|
567
|
-
return ret;
|
|
568
|
-
};
|
|
569
|
-
o.state_in = parseInt(f(cell.parameters.STATE_NUM_LOG2), 2);
|
|
570
|
-
o.ctrl_in = f(cell.parameters.CTRL_IN_WIDTH).replace(/-/g, 'x');
|
|
571
|
-
o.state_out = parseInt(f(cell.parameters.STATE_NUM_LOG2), 2);
|
|
572
|
-
o.ctrl_out = f(cell.parameters.CTRL_OUT_WIDTH);
|
|
573
|
-
dev.trans_table.push(o);
|
|
574
|
-
}
|
|
575
|
-
break;
|
|
576
|
-
}
|
|
577
|
-
case '$mem': {
|
|
578
|
-
assert(cell.connections.RD_EN.length == cell.parameters.RD_PORTS);
|
|
579
|
-
assert(cell.connections.RD_CLK.length == cell.parameters.RD_PORTS);
|
|
580
|
-
assert(cell.connections.RD_DATA.length == cell.parameters.RD_PORTS * cell.parameters.WIDTH);
|
|
581
|
-
assert(cell.connections.RD_ADDR.length == cell.parameters.RD_PORTS * cell.parameters.ABITS);
|
|
582
|
-
assert(cell.connections.WR_EN.length == cell.parameters.WR_PORTS * cell.parameters.WIDTH);
|
|
583
|
-
assert(cell.connections.WR_CLK.length == cell.parameters.WR_PORTS);
|
|
584
|
-
assert(cell.connections.WR_DATA.length == cell.parameters.WR_PORTS * cell.parameters.WIDTH);
|
|
585
|
-
assert(cell.connections.WR_ADDR.length == cell.parameters.WR_PORTS * cell.parameters.ABITS);
|
|
586
|
-
dev.bits = cell.parameters.WIDTH;
|
|
587
|
-
dev.abits = cell.parameters.ABITS;
|
|
588
|
-
dev.words = cell.parameters.SIZE;
|
|
589
|
-
dev.offset = cell.parameters.OFFSET;
|
|
590
|
-
dev.rdports = [];
|
|
591
|
-
dev.wrports = [];
|
|
592
|
-
const rdpol = decode_json_bigint_as_array(cell.parameters.RD_CLK_POLARITY).reverse();
|
|
593
|
-
const rden = decode_json_bigint_as_array(cell.parameters.RD_CLK_ENABLE).reverse();
|
|
594
|
-
const rdtr = decode_json_bigint_as_array(cell.parameters.RD_TRANSPARENT).reverse();
|
|
595
|
-
const wrpol = decode_json_bigint_as_array(cell.parameters.WR_CLK_POLARITY).reverse();
|
|
596
|
-
const wren = decode_json_bigint_as_array(cell.parameters.WR_CLK_ENABLE).reverse();
|
|
597
|
-
const init = typeof(cell.parameters.INIT) == 'number'
|
|
598
|
-
? bigInt(cell.parameters.INIT).toArray(2).value.map(String).reverse()
|
|
599
|
-
: cell.parameters.INIT.split('').reverse();
|
|
600
|
-
if (cell.parameters.INIT) {
|
|
601
|
-
const l = init.slice(-1)[0] == 'x' ? 'x' : '0';
|
|
602
|
-
const memdata = new Mem3vl(dev.bits, dev.words);
|
|
603
|
-
for (const k of Array(dev.words).keys()) {
|
|
604
|
-
const wrd = init.slice(dev.bits * k, dev.bits * (k+1));
|
|
605
|
-
while (wrd.length < dev.bits) wrd.push(l);
|
|
606
|
-
memdata.set(k, Vector3vl.fromBin(wrd.reverse().join('')));
|
|
607
|
-
}
|
|
608
|
-
dev.memdata = memdata.toJSON();
|
|
609
|
-
}
|
|
610
|
-
for (const k of Array(cell.parameters.RD_PORTS).keys()) {
|
|
611
|
-
const port = {
|
|
612
|
-
};
|
|
613
|
-
if (rden[k]) {
|
|
614
|
-
port.clock_polarity = Boolean(rdpol[k]);
|
|
615
|
-
if (cell.connections.RD_EN[k] != '1')
|
|
616
|
-
port.enable_polarity = true;
|
|
617
|
-
};
|
|
618
|
-
if (rdtr[k])
|
|
619
|
-
port.transparent = true;
|
|
620
|
-
dev.rdports.push(port);
|
|
621
|
-
}
|
|
622
|
-
for (const k of Array(cell.parameters.WR_PORTS).keys()) {
|
|
623
|
-
const port = {
|
|
624
|
-
};
|
|
625
|
-
if (wren[k]) {
|
|
626
|
-
port.clock_polarity = Boolean(wrpol[k]);
|
|
627
|
-
if (cell.connections.WR_EN.slice(dev.bits * k, dev.bits * (k+1))
|
|
628
|
-
.some(z => z != '1'))
|
|
629
|
-
port.enable_polarity = true;
|
|
630
|
-
};
|
|
631
|
-
dev.wrports.push(port);
|
|
632
|
-
}
|
|
633
|
-
break;
|
|
634
|
-
}
|
|
635
|
-
default:
|
|
636
|
-
}
|
|
637
|
-
if (dev.type == 'Dff') {
|
|
638
|
-
// find register initial value, if exists
|
|
639
|
-
// Yosys puts initial values in net attributes; there can be many for single actual net!
|
|
640
|
-
const nms = netnames.get(cell.connections.Q);
|
|
641
|
-
if (nms !== undefined) {
|
|
642
|
-
for (const nm of nms) {
|
|
643
|
-
if (mod.netnames[nm].attributes.init !== undefined)
|
|
644
|
-
dev.initial = decode_json_constant(mod.netnames[nm].attributes.init, dev.bits);
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
const portmap = portmaps[cell.type];
|
|
649
|
-
if (portmap) connect_device(dname, cell, portmap);
|
|
650
|
-
else if (cell.type == '$pmux') connect_pmux(dname, cell);
|
|
651
|
-
else if (cell.type == '$mem') connect_mem(dname, cell, dev);
|
|
652
|
-
else throw Error('Invalid cell type: ' + cell.type);
|
|
653
|
-
}
|
|
654
|
-
// Group bits into nets for complex sources
|
|
655
|
-
for (const [nbits, net] of nets.entries()) {
|
|
656
|
-
if (net.source !== undefined) continue;
|
|
657
|
-
const groups = [[]];
|
|
658
|
-
let group = [];
|
|
659
|
-
let pbitinfo = undefined;
|
|
660
|
-
for (const bit of nbits) {
|
|
661
|
-
let bitinfo = bits.get(bit);
|
|
662
|
-
if (bitinfo == undefined && constbit(bit))
|
|
663
|
-
bitinfo = 'const';
|
|
664
|
-
if (groups.slice(-1)[0].length > 0 &&
|
|
665
|
-
(typeof bitinfo != typeof pbitinfo ||
|
|
666
|
-
typeof bitinfo == 'object' &&
|
|
667
|
-
typeof pbitinfo == 'object' &&
|
|
668
|
-
(bitinfo.id != pbitinfo.id ||
|
|
669
|
-
bitinfo.port != pbitinfo.port ||
|
|
670
|
-
bitinfo.num != pbitinfo.num + 1))) {
|
|
671
|
-
groups.push([]);
|
|
672
|
-
}
|
|
673
|
-
groups.slice(-1)[0].push(bit);
|
|
674
|
-
pbitinfo = bitinfo;
|
|
675
|
-
}
|
|
676
|
-
if (groups.length == 1) continue;
|
|
677
|
-
if (groups.slice(-1)[0].every(x => x == '0')) {
|
|
678
|
-
// infer zero-extend
|
|
679
|
-
const ilen = nbits.length - groups.slice(-1)[0].length;
|
|
680
|
-
const dname = add_device({
|
|
681
|
-
type: 'ZeroExtend',
|
|
682
|
-
extend: { output: nbits.length, input: ilen }
|
|
683
|
-
});
|
|
684
|
-
const zbits = nbits.slice(0, ilen);
|
|
685
|
-
add_net_source(nbits, dname, 'out');
|
|
686
|
-
add_net_target(zbits, dname, 'in');
|
|
687
|
-
if (groups.length > 2)
|
|
688
|
-
add_busgroup(zbits, groups.slice(0, groups.length - 1));
|
|
689
|
-
} else add_busgroup(nbits, groups);
|
|
690
|
-
}
|
|
691
|
-
// Add constants
|
|
692
|
-
for (const [nbits, net] of nets.entries()) {
|
|
693
|
-
if (net.source !== undefined) continue;
|
|
694
|
-
if (!nbits.every(constbit))
|
|
695
|
-
continue;
|
|
696
|
-
const dname = add_device({
|
|
697
|
-
// label: String(val), // TODO
|
|
698
|
-
type: 'Constant',
|
|
699
|
-
constant: nbits.slice().reverse().join('')
|
|
700
|
-
});
|
|
701
|
-
add_net_source(nbits, dname, 'out');
|
|
702
|
-
}
|
|
703
|
-
// Select bits from complex targets
|
|
704
|
-
for (const [nbits, net] of nets.entries()) {
|
|
705
|
-
if (net.source !== undefined) continue;
|
|
706
|
-
// constants should be already handled!
|
|
707
|
-
assert(nbits.every(x => x > 1));
|
|
708
|
-
const bitinfos = nbits.map(x => bits.get(x));
|
|
709
|
-
if (!bitinfos.every(x => typeof x == 'object'))
|
|
710
|
-
continue; // ignore not fully driven ports
|
|
711
|
-
// complex sources should be already handled!
|
|
712
|
-
assert(bitinfos.every(info => info.id == bitinfos[0].id &&
|
|
713
|
-
info.port == bitinfos[0].port));
|
|
714
|
-
const cconn = devnets.get(bitinfos[0].id).get(bitinfos[0].port);
|
|
715
|
-
const dname = add_device({
|
|
716
|
-
type: 'BusSlice',
|
|
717
|
-
slice: {
|
|
718
|
-
first: bitinfos[0].num,
|
|
719
|
-
count: bitinfos.length,
|
|
720
|
-
total: cconn.length
|
|
721
|
-
}
|
|
722
|
-
});
|
|
723
|
-
add_net_source(nbits, dname, 'out');
|
|
724
|
-
add_net_target(cconn, dname, 'in');
|
|
725
|
-
}
|
|
726
|
-
// Make some simplifications in the graph
|
|
727
|
-
// TODO multiple passes, less ad-hoc
|
|
728
|
-
for (const [nbits, net] of nets.entries()) {
|
|
729
|
-
if (net.source === undefined || net.targets.length != 1 ||
|
|
730
|
-
net.source.id == net.targets[0].id) continue;
|
|
731
|
-
const srcdev = mout.devices[net.source.id];
|
|
732
|
-
const tgtdev = mout.devices[net.targets[0].id];
|
|
733
|
-
// eliminate repeaters, TODO nets with multiple targets
|
|
734
|
-
if (tgtdev.type == 'Repeater') {
|
|
735
|
-
delete mout.devices[net.targets[0].id];
|
|
736
|
-
const t_nbits = devnets.get(net.targets[0].id).get('out');
|
|
737
|
-
const t_net = nets.get(t_nbits);
|
|
738
|
-
t_net.source = net.source;
|
|
739
|
-
nets.delete(nbits);
|
|
740
|
-
}
|
|
741
|
-
// eliminate negations
|
|
742
|
-
if (tgtdev.type == 'Not' && gate_negations.has(srcdev.type)) {
|
|
743
|
-
srcdev.type = gate_negations.get(srcdev.type);
|
|
744
|
-
delete mout.devices[net.targets[0].id];
|
|
745
|
-
const t_nbits = devnets.get(net.targets[0].id).get('out');
|
|
746
|
-
const t_net = nets.get(t_nbits);
|
|
747
|
-
t_net.source = net.source;
|
|
748
|
-
nets.delete(nbits);
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
// Generate connections between devices
|
|
752
|
-
for (const [nbits, net] of nets.entries()) {
|
|
753
|
-
if (net.source === undefined) {
|
|
754
|
-
console.warn('Undriven net in ' + name + ': ' + nbits);
|
|
755
|
-
continue;
|
|
756
|
-
}
|
|
757
|
-
let first = true;
|
|
758
|
-
for (const target in net.targets) {
|
|
759
|
-
const conn = {
|
|
760
|
-
to: net.targets[target],
|
|
761
|
-
from: net.source
|
|
762
|
-
};
|
|
763
|
-
if (net.name) conn.name = net.name;
|
|
764
|
-
if (!first && mout.devices[conn.from.id].type == "Constant") {
|
|
765
|
-
// replicate constants for better clarity
|
|
766
|
-
const dname = add_device({
|
|
767
|
-
type: 'Constant',
|
|
768
|
-
constant: mout.devices[conn.from.id].constant
|
|
769
|
-
});
|
|
770
|
-
conn.from = {id: dname, port: 'out'};
|
|
771
|
-
}
|
|
772
|
-
mout.connectors.push(conn);
|
|
773
|
-
first = false;
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
return mout;
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
async function process(filenames, dirname, options = {}) {
|
|
780
|
-
const optimize_simp = options.optimize ? "; opt" : "; opt_clean";
|
|
781
|
-
const optimize = options.optimize ? "; opt -full" : "; opt_clean";
|
|
782
|
-
const fsmexpand = options.fsmexpand ? " -expand" : "";
|
|
783
|
-
const fsmpass = options.fsm == "nomap" ? "; fsm -nomap" + fsmexpand
|
|
784
|
-
: options.fsm ? "; fsm" + fsmexpand
|
|
785
|
-
: "";
|
|
786
|
-
const tmpjson = await tmp.tmpName({ postfix: '.json' });
|
|
787
|
-
let obj = undefined;
|
|
788
|
-
const yosys_result = await promisify(child_process.exec)(
|
|
789
|
-
'yosys -p "hierarchy -auto-top; proc' + optimize_simp + fsmpass + '; memory -nomap; dff2dffe; wreduce -memx' +
|
|
790
|
-
optimize + '" -o "' + tmpjson + '" ' +
|
|
791
|
-
filenames.map(cmd => '"' + cmd.replace(/(["\s'$`\\])/g,'\\$1') + '"').join(' '),
|
|
792
|
-
{maxBuffer: 1000000, cwd: dirname || null, timeout: options.timeout || 60000})
|
|
793
|
-
.catch(exc => exc);
|
|
794
|
-
try {
|
|
795
|
-
if (yosys_result instanceof Error) {
|
|
796
|
-
if (yosys_result.killed)
|
|
797
|
-
yosys_result.message = "Yosys killed"
|
|
798
|
-
else if (yosys_result.code)
|
|
799
|
-
yosys_result.message = "Yosys failed with code " + yosys_result.code;
|
|
800
|
-
else
|
|
801
|
-
yosys_result.message = "Yosys failed";
|
|
802
|
-
throw yosys_result;
|
|
803
|
-
}
|
|
804
|
-
obj = JSON.parse(fs.readFileSync(tmpjson, 'utf8'));
|
|
805
|
-
await promisify(fs.unlink)(tmpjson);
|
|
806
|
-
const portmaps = order_ports(obj);
|
|
807
|
-
const out = yosys_to_digitaljs(obj, portmaps, options);
|
|
808
|
-
const toporder = topsort(module_deps(obj));
|
|
809
|
-
toporder.pop();
|
|
810
|
-
const toplevel = toporder.pop();
|
|
811
|
-
const output = out[toplevel];
|
|
812
|
-
output.subcircuits = {};
|
|
813
|
-
for (const x of toporder) output.subcircuits[x] = out[x];
|
|
814
|
-
return {
|
|
815
|
-
output: output,
|
|
816
|
-
yosys_output: obj,
|
|
817
|
-
yosys_stdout: yosys_result.stdout,
|
|
818
|
-
yosys_stderr: yosys_result.stderr
|
|
819
|
-
};
|
|
820
|
-
} catch (exc) {
|
|
821
|
-
if (obj !== undefined) exc.yosys_output = obj;
|
|
822
|
-
exc.yosys_stdout = yosys_result.stdout;
|
|
823
|
-
exc.yosys_stderr = yosys_result.stderr;
|
|
824
|
-
throw exc;
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
function io_ui(output) {
|
|
829
|
-
for (const [name, dev] of Object.entries(output.devices)) {
|
|
830
|
-
if (dev.type == 'Input' || dev.type == 'Output') {
|
|
831
|
-
dev.label = dev.net;
|
|
832
|
-
}
|
|
833
|
-
// use clock for clocky named inputs
|
|
834
|
-
if (dev.type == 'Input' && dev.bits == 1 && (dev.label == 'clk' || dev.label == 'clock')) {
|
|
835
|
-
dev.type = 'Clock';
|
|
836
|
-
dev.propagation = 100;
|
|
837
|
-
}
|
|
838
|
-
if (dev.type == 'Input')
|
|
839
|
-
dev.type = dev.bits == 1 ? 'Button' : 'NumEntry';
|
|
840
|
-
if (dev.type == 'Output')
|
|
841
|
-
dev.type = dev.bits == 1 ? 'Lamp' : 'NumDisplay';
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
|
|
845
|
-
async function process_files(data, options) {
|
|
846
|
-
const dir = await tmp.dir();
|
|
847
|
-
const names = [];
|
|
848
|
-
try {
|
|
849
|
-
for (const [name, content] of Object.entries(data)) {
|
|
850
|
-
const sname = sanitize(name);
|
|
851
|
-
await promisify(fs.writeFile)(path.resolve(dir.path, sname), content);
|
|
852
|
-
if (/\.(v|sv)$/.test(sname)) names.push(sname);
|
|
853
|
-
}
|
|
854
|
-
return await process(names, dir.path, options);
|
|
855
|
-
} finally {
|
|
856
|
-
for (const name of Object.keys(data)) {
|
|
857
|
-
await promisify(fs.unlink)(path.resolve(dir.path, name));
|
|
858
|
-
}
|
|
859
|
-
dir.cleanup();
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
async function process_sv(text) {
|
|
864
|
-
const tmpsv = await tmp.file({ postfix: '.sv' });
|
|
865
|
-
try {
|
|
866
|
-
await promisify(fs.write)(tmpsv.fd, text);
|
|
867
|
-
await promisify(fs.close)(tmpsv.fd);
|
|
868
|
-
return await process([tmpsv.path]);
|
|
869
|
-
} finally {
|
|
870
|
-
tmpsv.cleanup();
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
exports.process = process;
|
|
875
|
-
exports.process_files = process_files;
|
|
876
|
-
exports.process_sv = process_sv;
|
|
877
|
-
exports.io_ui = io_ui;
|
|
878
|
-
|