yosys2digitaljs 0.7.0 → 0.9.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 CHANGED
@@ -3,254 +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 * 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
- ['$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 type BitChar = '0' | '1' | 'x';
179
-
180
- export type Bit = number | '0' | '1' | 'x';
181
-
182
- export type BitVector = Bit[];
183
-
184
- export type Port = {
185
- direction: 'input' | 'output' | 'inout',
186
- bits: any
187
- };
188
-
189
- export type Parameters = {
190
- WIDTH?: JsonConstant,
191
- A_WIDTH?: JsonConstant,
192
- B_WIDTH?: JsonConstant,
193
- S_WIDTH?: JsonConstant,
194
- Y_WIDTH?: JsonConstant,
195
- A_SIGNED?: JsonConstant,
196
- B_SIGNED?: JsonConstant,
197
- CLK_POLARITY?: JsonConstant,
198
- EN_POLARITY?: JsonConstant,
199
- ARST_POLARITY?: JsonConstant,
200
- ARST_VALUE: JsonConstant,
201
- CTRL_IN_WIDTH?: JsonConstant,
202
- CTRL_OUT_WIDTH?: JsonConstant,
203
- TRANS_NUM?: JsonConstant,
204
- STATE_NUM?: JsonConstant,
205
- STATE_NUM_LOG2?: JsonConstant,
206
- STATE_RST?: JsonConstant,
207
- RD_PORTS?: JsonConstant,
208
- WR_PORTS?: JsonConstant,
209
- RD_CLK_POLARITY?: JsonConstant,
210
- RD_CLK_ENABLE?: JsonConstant,
211
- RD_CLK_TRANSPARENT?: JsonConstant,
212
- WR_CLK_POLARITY?: JsonConstant,
213
- WR_CLK_ENABLE?: JsonConstant,
214
- [key: string]: any
215
- };
216
-
217
- export type JsonConstant = number | string;
218
-
219
- export type Attributes = {
220
- init: JsonConstant,
221
- [key: string]: any
222
- };
223
-
224
- export type Cell = {
225
- hide_name: 0 | 1,
226
- type: string,
227
- parameters: Parameters,
228
- attributes: Attributes,
229
- port_directions: { [key: string]: 'input' | 'output' },
230
- connections: { [key: string]: BitVector }
231
- };
232
-
233
- export type Net = {
234
- hide_name: 0 | 1,
235
- bits: BitVector,
236
- attributes: { [key: string]: string }
237
- };
238
-
239
- export type Module = {
240
- ports: { [key: string]: Port },
241
- cells: { [key: string]: Cell },
242
- netnames: { [key: string]: Net }
243
- };
244
-
245
- export type Output = {
246
- modules: { [key: string]: Module }
247
- };
248
-
249
- };
250
-
251
- type ConvertOptions = {
252
- propagation?: number,
253
- };
8
+ import { promisify } from 'util';
9
+ import { yosys2digitaljs, ConvertOptions, Digitaljs } from './core';
10
+ const sanitize = require('sanitize-filename');
254
11
 
255
12
  type Options = ConvertOptions & {
256
13
  optimize?: boolean,
@@ -268,26 +25,6 @@ type Output = {
268
25
  lint?: LintMessage[]
269
26
  };
270
27
 
271
- type Portmap = { [key: string]: string };
272
- type Portmaps = { [key: string]: Portmap };
273
-
274
- type Bit = Yosys.Bit | `bit${number}`;
275
-
276
- type Net = Bit[];
277
-
278
- type NetInfo = {
279
- source: undefined | Digitaljs.Port,
280
- targets: Digitaljs.Port[],
281
- name: undefined | string,
282
- source_positions: Digitaljs.SourcePosition[]
283
- };
284
-
285
- type BitInfo = {
286
- id: string,
287
- port: string,
288
- num: number
289
- };
290
-
291
28
  type LintMessage = {
292
29
  type: string,
293
30
  file: string,
@@ -296,891 +33,42 @@ type LintMessage = {
296
33
  message: string
297
34
  };
298
35
 
299
- function chunkArray(a, chunk_size){
300
- let results = [];
301
- let ca = a.splice();
302
-
303
- while (ca.length) {
304
- results.push(ca.splice(0, chunk_size));
305
- }
306
-
307
- return results;
308
- }
309
36
 
310
- function module_deps(data: Yosys.Output): [string, string | number][] {
311
- const out: [string, string | number][] = [];
312
- for (const [name, mod] of Object.entries(data.modules)) {
313
- out.push([name, 1/0]);
314
- for (const cname in mod.cells) {
315
- const cell = mod.cells[cname];
316
- if (cell.type in data.modules)
317
- out.push([cell.type, name]);
318
- }
37
+ function ansi_c_escape_contents(cmd: string): string {
38
+ function func(ch: string) {
39
+ if (ch == '\t') return '\\t';
40
+ if (ch == '\r') return '\\r';
41
+ if (ch == '\n') return '\\n';
42
+ return '\\x' + ch.charCodeAt(0).toString(16).padStart(2, '0');
319
43
  }
320
- return out;
44
+ return cmd.replace(/(["'\\])/g,'\\$1')
45
+ .replace(/[\x00-\x1F\x7F-\x9F]/g, func);
321
46
  }
322
47
 
323
- function order_ports(data: Yosys.Output): Portmaps {
324
- const unmap = {A: 'in', Y: 'out'};
325
- const binmap = {A: 'in1', B: 'in2', Y: 'out'};
326
- const out = {
327
- '$mux': {A: 'in0', B: 'in1', S: 'sel', Y: 'out'},
328
- '$dff': {CLK: 'clk', D: 'in', Q: 'out'},
329
- '$dffe': {CLK: 'clk', EN: 'en', D: 'in', Q: 'out'},
330
- '$adff': {CLK: 'clk', ARST: 'arst', D: 'in', Q: 'out'},
331
- '$adffe': {CLK: 'clk', EN: 'en', ARST: 'arst', D: 'in', Q: 'out'},
332
- '$sdff': {CLK: 'clk', SRST: 'srst', D: 'in', Q: 'out'},
333
- '$sdffe': {CLK: 'clk', EN: 'en', SRST: 'srst', D: 'in', Q: 'out'},
334
- '$sdffce': {CLK: 'clk', EN: 'en', SRST: 'srst', D: 'in', Q: 'out'},
335
- '$dlatch': {EN: 'en', D: 'in', Q: 'out'},
336
- '$adlatch': {EN: 'en', ARST: 'arst', D: 'in', Q: 'out'},
337
- '$dffsr': {CLK: 'clk', SET: 'set', CLR: 'clr', D: 'in', Q: 'out'},
338
- '$dffsre': {CLK: 'clk', EN: 'en', SET: 'set', CLR: 'clr', D: 'in', Q: 'out'},
339
- '$aldff': {CLK: 'clk', ALOAD: 'aload', AD: 'ain', D: 'in', Q: 'out'},
340
- '$aldffe': {CLK: 'clk', EN: 'en', ALOAD: 'aload', AD: 'ain', D: 'in', Q: 'out'},
341
- '$sr': {SET: 'set', CLR: 'clr', Q: 'out'},
342
- '$fsm': {ARST: 'arst', CLK: 'clk', CTRL_IN: 'in', CTRL_OUT: 'out'}
343
- };
344
- binary_gates.forEach((nm) => out[nm] = binmap);
345
- unary_gates.forEach((nm) => out[nm] = unmap);
346
- for (const [name, mod] of Object.entries(data.modules)) {
347
- const portmap: Portmap = {};
348
- const ins = [], outs = [];
349
- for (const pname in mod.ports) {
350
- portmap[pname] = pname;
351
- }
352
- out[name] = portmap;
353
- }
354
- return out;
48
+ function ansi_c_escape(cmd: string): string {
49
+ return '"' + ansi_c_escape_contents(cmd) + '"';
355
50
  }
356
51
 
357
- function decode_json_bigint(param: string | number): bigInt.BigInteger {
358
- if (typeof param == 'string')
359
- return bigInt(param, 2)
360
- else if (typeof param == 'number')
361
- return bigInt(param)
362
- else assert(false);
52
+ function shell_escape_contents(cmd: string): string {
53
+ return cmd.replace(/(["\r\n$`\\])/g,'\\$1');
363
54
  }
364
55
 
365
- function decode_json_number(param: Yosys.JsonConstant): number {
366
- if (typeof param == 'string')
367
- return Number.parseInt(param, 2);
368
- else if (typeof param == 'number')
369
- return param
370
- else assert(false);
371
- }
372
-
373
- function decode_json_bigint_as_array(param: string | number): number[] {
374
- return decode_json_bigint(param).toArray(2).value;
375
- }
376
-
377
- function decode_json_constant(param: Yosys.JsonConstant, bits: number, fill : Yosys.BitChar = '0'): string {
378
- if (typeof param == 'number')
379
- return bigInt(param).toArray(2).value.map(String).reverse()
380
- .concat(Array(bits).fill(fill)).slice(0, bits).reverse().join('');
381
- else
382
- return param;
383
- }
384
-
385
- function parse_source_positions(str: string): Digitaljs.SourcePosition[] {
386
- const ret = [];
387
- for (const entry of str.split('|')) {
388
- const colonIdx = entry.lastIndexOf(':');
389
- const name = entry.slice(0, colonIdx);
390
- const pos = entry.slice(colonIdx+1);
391
- const [from, to] = pos.split('-').map(s => s.split('.').map(v => Number(v))).map(([line, column]) => ({line, column}));
392
- ret.push({name, from, to});
393
- }
394
- return ret;
56
+ function shell_escape(cmd: string): string {
57
+ return '"' + shell_escape_contents(cmd) + '"';
395
58
  }
396
59
 
397
- function yosys_to_digitaljs(data: Yosys.Output, portmaps: Portmaps, options: ConvertOptions = {}): {[key: string]: Digitaljs.Module} {
398
- const out = {};
399
- for (const [name, mod] of Object.entries(data.modules)) {
400
- out[name] = yosys_to_digitaljs_mod(name, mod, portmaps, options);
401
- }
402
- return out
403
- }
404
-
405
- function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portmaps, options: ConvertOptions = {}): Digitaljs.Module {
406
- function constbit(bit: Bit) {
407
- return bit == '0' || bit == '1' || bit == 'x';
408
- }
409
- const nets = new HashMap<Net, NetInfo>();
410
- const netnames = new HashMap<Net, string[]>();
411
- const netsrc = new HashMap<Net, Digitaljs.SourcePosition[]>();
412
- const bits = new Map<Bit, BitInfo>();
413
- const devnets = new Map<string, Map<string, Net>>();
414
- let n = 0, pn = 0;
415
- function gen_name(): string {
416
- const nm = `dev${n++}`;
417
- devnets.set(nm, new Map());
418
- return nm;
419
- }
420
- function gen_bitname(): Bit {
421
- return `bit${pn++}`;
422
- }
423
- function get_net(k: Net): NetInfo {
424
- // create net if does not exist yet
425
- if (!nets.has(k)) {
426
- const nms = netnames.get(k);
427
- const src = netsrc.get(k);
428
- nets.set(k, {source: undefined, targets: [], name: nms ? nms[0] : undefined, source_positions: src || []});
429
- }
430
- return nets.get(k);
431
- }
432
- function add_net_source(k: Net, d: string, p: string, primary: boolean = false) {
433
- if (k.length == 0) return; // for unconnected ports
434
- const net = get_net(k);
435
- if(net.source !== undefined) {
436
- // multiple sources driving one net, disallowed in digitaljs
437
- console.log(k);
438
- console.log(net);
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;
60
+ function process_filename(filename: string): string {
61
+ const flags = /\.sv$/.test(filename) ? " -sv" : "";
62
+ return "read_verilog" + flags + " " + ansi_c_escape(filename);
1171
63
  }
1172
64
 
1173
- function escape_filename(cmd: string): string {
1174
- return '"' + cmd.replace(/(["\s'$`\\])/g,'\\$1') + '"';
1175
- }
1176
-
1177
65
  const verilator_re = /^%(Warning|Error)[^:]*: ([^:]*):([0-9]+):([0-9]+): (.*)$/;
1178
66
 
1179
67
  export async function verilator_lint(filenames: string[], dirname?: string, options: Options = {}): Promise<LintMessage[]> {
1180
68
  try {
1181
69
  const output: LintMessage[] = [];
1182
70
  const verilator_result: {stdout: string, stderr: string} = await promisify(child_process.exec)(
1183
- 'verilator -lint-only -Wall -Wno-DECLFILENAME -Wno-UNOPT -Wno-UNOPTFLAT ' + filenames.map(escape_filename).join(' '),
71
+ 'timeout -k10s 40s verilator -lint-only -Wall -Wno-DECLFILENAME -Wno-UNOPT -Wno-UNOPTFLAT ' + filenames.map(shell_escape).join(' '),
1184
72
  {maxBuffer: 1000000, cwd: dirname || null, timeout: options.timeout || 60000})
1185
73
  .catch(exc => exc);
1186
74
  for (const line of verilator_result.stderr.split('\n')) {
@@ -1200,18 +88,6 @@ export async function verilator_lint(filenames: string[], dirname?: string, opti
1200
88
  }
1201
89
  }
1202
90
 
1203
- export function yosys2digitaljs(obj: Yosys.Output, options: ConvertOptions = {}): Digitaljs.TopModule {
1204
- const portmaps = order_ports(obj);
1205
- const out = yosys_to_digitaljs(obj, portmaps, options);
1206
- const toporder = topsort(module_deps(obj));
1207
- toporder.pop();
1208
- const toplevel = toporder.pop();
1209
- const output: Digitaljs.TopModule = { subcircuits: {}, ... out[toplevel] };
1210
- for (const x of toporder)
1211
- output.subcircuits[x] = out[x];
1212
- return output;
1213
- }
1214
-
1215
91
  export async function process(filenames: string[], dirname?: string, options: Options = {}): Promise<Output> {
1216
92
  const optimize_simp = options.optimize ? "; opt" : "; opt_clean";
1217
93
  const optimize = options.optimize ? "; opt -full" : "; opt_clean";
@@ -1222,8 +98,9 @@ export async function process(filenames: string[], dirname?: string, options: Op
1222
98
  const tmpjson = await tmp.tmpName({ postfix: '.json' });
1223
99
  let obj = undefined;
1224
100
  const yosys_result: {stdout: string, stderr: string, killed?: boolean, code?: number} = await promisify(child_process.exec)(
1225
- 'yosys -p "hierarchy -auto-top; proc' + optimize_simp + fsmpass + '; memory -nomap; wreduce -memx' +
1226
- optimize + '" -o "' + tmpjson + '" ' + filenames.map(escape_filename).join(' '),
101
+ 'timeout -k10s 40s yosys -p "' + shell_escape_contents(filenames.map(process_filename).join('; ')) +
102
+ '; hierarchy -auto-top; proc' + optimize_simp + fsmpass + '; memory -nomap; wreduce -memx' +
103
+ optimize + '" -o "' + tmpjson + '"',
1227
104
  {maxBuffer: 1000000, cwd: dirname || null, timeout: options.timeout || 60000})
1228
105
  .catch(exc => exc);
1229
106
  try {
@@ -1256,29 +133,6 @@ export async function process(filenames: string[], dirname?: string, options: Op
1256
133
  }
1257
134
  }
1258
135
 
1259
- export function io_ui(output: Digitaljs.Module) {
1260
- for (const [name, dev] of Object.entries(output.devices)) {
1261
- if (dev.type == 'Input' || dev.type == 'Output') {
1262
- dev.label = dev.net;
1263
- }
1264
- // use clock for clocky named inputs
1265
- if (dev.type == 'Input' && dev.bits == 1 && (dev.label == 'clk' || dev.label == 'clock')) {
1266
- dev.type = 'Clock';
1267
- dev.propagation = 100;
1268
- }
1269
- if (dev.type == 'Input')
1270
- dev.type = dev.bits == 1 ? 'Button' : 'NumEntry';
1271
- if (dev.type == 'Output') {
1272
- if (dev.bits == 1)
1273
- dev.type = 'Lamp';
1274
- else if (dev.bits == 8 && (dev.label == 'display7' || dev.label.startsWith('display7_')))
1275
- dev.type = 'Display7';
1276
- else
1277
- dev.type = 'NumDisplay';
1278
- }
1279
- }
1280
- }
1281
-
1282
136
  export async function process_files(data: {[key: string]: string}, options: Options = {}): Promise<Output> {
1283
137
  const dir = await tmp.dir();
1284
138
  const names = [];
@@ -1307,4 +161,3 @@ export async function process_sv(text: string, options: Options = {}): Promise<O
1307
161
  tmpsv.cleanup();
1308
162
  }
1309
163
  }
1310
-