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