-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathChip.java
5826 lines (5064 loc) · 316 KB
/
Chip.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//------------------------------------------------------------------------------
// Design, simulate and layout a binary tree on a silicon chip.
// Philip R Brenan at appaapps dot com, Appa Apps Ltd Inc., 2024
//------------------------------------------------------------------------------
package com.AppaApps.Silicon; // Design, simulate and layout digital a binary tree on a silicon chip.
/*
Essere o non essere, questo e il problema:
se sia piu nobile d'animo sopportare gli oltraggi,
i sassi e i dardi dell'iniqua fortuna,
o prender l'armi contro un mare di triboli e combattendo disperderli.
Morire, dormire, nulla di piu, e con un sonno dirsi che poniamo
fine al cordoglio e alle infinite miserie naturale retaggio della carne,
e soluzione da accogliere a mani giunte.
Morire, dormire, sognare forse: ma qui e l'ostacolo,
quali sogni possano assalirci in quel sonno di morte
quando siamo gia sdipanati dal groviglio mortale, ci trattiene:
e la remora questa che di tanto prolunga la vita ai nostri tormenti.
Chi vorrebbe, se no, sopportar le frustate e gli insulti del tempo,
le angherie del tiranno, il disprezzo dell'uomo borioso,
le angosce del respinto amore, gli indugi della legge,
la tracotanza dei grandi, i calci in faccia che il merito paziente riceve dai mediocri,
quando di mano propria potrebbe saldare il suo conto con due dita di pugnale?
Chi vorrebbe caricarsi i grossi fardelli, imprecando e sudando sotto il peso di tutta una vita stracca,
se non fosse il timore di qualche cosa, dopo la morte,
la terra inesplorata donde mai non torno alcun viaggiatore, a sgomentare la nostra volonta
e a persuaderci di sopportare i nostri mali piuttosto che correre in cerca d'altri che non conosciamo?
Cosi ci fa vigliacchi la coscienza;
cosi l'incarnato naturale della determinazione si scolora al cospetto del pallido pensiero.
E cosi imprese di grande importanza e rilievo sono distratte dal loro naturale corso:
e dell'azione perdono anche il nome
*/
import java.util.*;
// Adding constants by optimizing Brent-Kung or Kogge-Stone against known values
// Anneal should remove all unused gates
//D1 Construct // Construct a silicon chip using standard logic gates combined via buses.
public class Chip // Describe a chip and emulate its operation.
{final static boolean drawLayouts = false; // Whether we should draw layouts at all
final static boolean github_actions = // Whether we are on a github
"true".equals(System.getenv("GITHUB_ACTIONS"));
final static long start = System.nanoTime(); // Start time
final String name; // Name of chip
Integer layoutLTGates = 100; // Always draw the layout if it has less than this many gates in it or if there is no limit specified
final int defaultMaxSimulationSteps = github_actions ? 1000 : 100; // Default maximum simulation steps
final int defaultMinSimulationSteps = 0; // Default minimum simulation steps - we keep going at least this long even if there have been no changes to allow clocked circuits to evolve.
Integer maxSimulationSteps = null; // Maximum simulation steps
Integer minSimulationSteps = null; // Minimum simulation steps - we keep going at least this long even if there have been no changes to allow clocked circuits to evolve.
int singleLevelLayoutLimit = 16; // Limit on gate scaling dimensions during layout.
final static int pixelsPerCell = 4; // Pixels per cell
final static int layersPerLevel = 4; // There are 4 layers in each level: insulation, x cross bars, x-y connectors and insulation, y cross bars
final static String perlFolder = "perl", perlFile = "gds2.pl"; // Folder and file for Perl code to represent a layout in GDS2.
final static Stack<String> gdsPerl = new Stack<>(); // Perl code to create GDS2 output files
final static TreeSet<String> errors = new TreeSet<>(); // Unique error messages during the compilation of a chip
final Map<String, Gate> gates = new TreeMap<>(); // Gates by name
final Map<String, Bits> bitBuses = new TreeMap<>(); // Several bits make a bit bus
final Map<String, Words> wordBuses = new TreeMap<>(); // Several bit buses make a word bus
final Set<String> outputGates = new TreeSet<>(); // Output gates
final Map<String, TreeSet<Gate.WhichPin>> // Pending gate definitions
pending = new TreeMap<>();
final Map<String, Gate> // Gates that are connected to an output gate
connectedToOutput = new TreeMap<>();
final Map<String, InputUnit> inputs = new TreeMap<>(); // Input devices connected to the chip
final Map<String, OutputUnit> // Output devices connected to the chip
outputs = new TreeMap<>();
final Map<String, Pulse> pulses = new TreeMap<>(); // Bits that are externally driven by periodic pulses of a specified duty cycle
ExecutionTrace executionTrace = null; // Execution trace goes here
int gateSeq = 0; // Sequence numbers for gates
int steps = 0; // Simulation step time
int maximumDistanceToNearestOutput = 0; // Maximum distance from nearest output
int maximumDistance = 0; // Maximum distance from furthest output
int countAtMostCountedDistance; // The distance at which we have the most gates from an output
int mostCountedDistance; // The number of gates at the distance from the drives that has the most gates
final TreeMap<Integer, TreeSet<String>> // Classify gates by distance to output
distanceToOutput = new TreeMap<>();
final static TreeMap<String,Integer> // Classify chips by maximum distance
classifyByDistanceToOutput = new TreeMap<>();
final TreeMap<Integer, Stack<String>> // Each column of gates ordered by Y coordinate in layout
columns = new TreeMap<>();
final static TreeMap<String, Diagram>
diagramsDrawn = new TreeMap<>(); // Avoid redrawing the same layout multiple times by only redrawing a new layout if it has a smaller number of levels or is closer to a square
int gsx, gsy; // The global scaling factors to be applied to the dimensions of the gates during layout
int layoutX, layoutY; // Dimensions of chip
Stack<Connection> connections; // Pairs of gates to be connected
Diagram diagram; // Diagram specifying the layout of the chip
Chip(String Name) {name = Name;} // Create a new L<chip>.
Chip() {this(currentTestNameSuffix());} // Create a new chip while testing.
static Chip chip() {return new Chip();} // Create a new chip while testing.
static Chip chip(String name) {return new Chip(name);} // Create a new chip while testing.
Integer layoutLTGates(Integer LayoutLTGates) {return layoutLTGates = LayoutLTGates;} // Always draw the layout if it has less than this many gates in it
int maxSimulationSteps(int MaxSimulationSteps) {return maxSimulationSteps = MaxSimulationSteps;} // Maximum simulation steps
int minSimulationSteps(int MinSimulationSteps) {return minSimulationSteps = MinSimulationSteps;} // Minimum simulation steps
int singleLevelLayoutLimit(int SingleLevelLayoutLimit) {return singleLevelLayoutLimit = SingleLevelLayoutLimit;} // Limit on gate scaling dimensions during layout.
void simulationSteps(int min, int max) {minSimulationSteps(min); maxSimulationSteps(max);} // Stop cleanly between the specified minimum and maximum number of steps
void simulationSteps(int steps) {minSimulationSteps(steps); maxSimulationSteps(steps);} // Stop cleanly at this number of steps
enum Operator // Gate operations
{And, Continue, FanOut, Gt, Input, Lt, My,
Nand, Ngt, Nlt, Nor, Not, Nxor,
One, Or, Output, Xor, Zero;
}
boolean commutative(Operator op) // Whether the pin order matters on the gate or not
{return op != Operator.Gt && op != Operator.Lt && op != Operator.My &&
op != Operator.Ngt && op != Operator.Nlt;
}
boolean zerad(Operator op) // Whether the gate takes zero inputs
{return op == Operator.Input || op == Operator.One || op == Operator.Zero;
}
boolean monad(Operator op) // Whether the gate takes a single input or not
{return op == Operator.Continue || op == Operator.Not || op == Operator.Output;
}
boolean dyad(Operator op) {return !(zerad(op) || monad(op));} // Whether the gate takes two inputs or not
int gates () {return gates.size();} // Number of gates in this chip
boolean definedGate(Bit bit) // Check whether a gate has been defined yet
{final Gate g = gates.get(bit.name);
return g != null; // A bit can be forward defined without a definite gate type, although eventually they must be backed by a gate.
}
Gate getGate(String name) // Get details of named gate. Gates that have not been created yet will return null even though their details are pending.
{if (name == null) err("No gate name provided");
final Gate g = gates.get(name);
if (g == null) err("No gate named:", name);
return g;
}
Gate getGate(Bit bit) {return getGate(bit.name);} // Get details of named gate. Gates that have not been created yet will return null even though their details are pending.
public String toString() // Convert chip to string
{final StringBuilder b = new StringBuilder();
b.append("Chip: " + name);
b.append(" Step: " + (steps-1));
b.append(" # Gates: " + gates.size());
b.append(" Maximum distance: " + maximumDistanceToNearestOutput);
b.append(" MostCountedDistance: " + mostCountedDistance);
b.append(" CountAtMostCountedDistance: " + countAtMostCountedDistance);
b.append("\n");
b.append("Seq Name____________________________ S " +
"Operator # 11111111111111111111111111111111=#"+
" 22222222222222222222222222222222=# "+
"Chng Fell Frst Last Dist Nearest Px__,Py__"+
" Drives these gates\n");
for (Gate g : gates.values()) b.append(g.print()); // Print each gate
if (bitBuses.size() > 0) // Size of bit buses
{b.append(""+bitBuses.size()+" Bit buses\n");
b.append("Bits Bus_____________________________ Value\n");
for (String n : bitBuses.keySet())
{b.append(String.format("%4d %32.32s", bitBuses.get(n).bits(), n));
final Integer v = bitBuses.get(n).Int();
if (v != null) b.append(String.format(" %d\n", v));
b.append(System.lineSeparator());
}
}
if (wordBuses.size() > 0) // Size of word buses
{b.append(""+wordBuses.size()+" Word buses\n");
b.append("Words Bits Bus_____________________________ Values\n");
for (String n : wordBuses.keySet())
{final Words w = wordBuses.get(n);
b.append(String.format("%4d %4d %32.32s ", w.words(), w.bits(), n));
final Integer[]v = w.Int();
if (v != null) for(int i = 0; i < v.length; ++i) b.append(""+v[i]+", ");
if (b.length() > 1) b.delete(b.length() - 2, b.length());
b.append("\n");
}
}
if (pending.size() > 0) // Write pending gates
{b.append(""+pending.size()+" pending gates\n");
b.append("Source__________________________ "+
"Target__________________________\n");
for (String n : pending.keySet())
for (Gate.WhichPin d : pending.get(n))
b.append(String.format("%32.32s %32.32s %c\n",
n, d.drives, d.pin == null ? ' ' : d.pin ? '1' : '2'));
}
return b.toString(); // String describing chip
}
class Bit implements CharSequence, Comparable<Bit> // Name of a bit that will eventually be generated by a gate
{final String name; // Name of the bit. This is also the name of the gate and the output of the gate. FanOut gates have a second output which is the name suffixed by the secondary marker. The secondary marker is not normally used in a gate name so a name that ends in ! is easily recognized as the second output of a fan out gate.
Bit(CharSequence Name) // Named bit
{name = validateName(Name.toString()); // Validate name
}
Chip chip() {return Chip.this;} // The chip implementing this bit
String validateName(String name) // Confirm that a component name looks like a variable name and has not already been used
{if (name == null) err("Name cannot be null");
final String[]words = name.split("_");
for (int i = 0; i < words.length; i++)
{final String w = words[i];
if (!w.matches("\\A([a-zA-Z][a-zA-Z0-9_.:]*|\\d+)\\Z"))
stop("Invalid gate name:", name, "in word", w);
}
return name;
}
public int compareTo(Bit a) {return name.compareTo(a.name);} // So we can create sets if bits
Boolean value() // Value of bit
{final Gate g = getGate(name);
if (g == null) stop("No gate associated with bit:", name);
return g.value;
}
void ok(Boolean expected) // Confirm that a bit has the expected value
{final Boolean value = value();
int fails = 0;
if (false) {} // The various combinations with null
else if (value == null && expected == null) {}
else if (value == null && expected != null) ++fails;
else if (value != null && expected == null) ++fails;
else if (value != expected) ++fails;
if (fails > 0) // Show error location with a trace back so we know where the failure occurred
{err("Bit value:", value, "does not match expected", expected);
++testsFailed;
}
else ++testsPassed; // Passed
}
Bit anneal() {Output(n(name, "anneal"), this); return this;} // Anneal bit if it is not required for some reason although this would waste surface area and power on a real chip.
public String toString() {return name;} // Name of bit
public char charAt(int index) {return name.charAt(index);}
public int length() {return name.length();}
public CharSequence subSequence(int start, int end)
{return name.substring(start, end);
}
void set(Boolean value) // Set the value of a bit by setting the gate that drives it
{final Gate g = getGate(name); // Gate providing bit
if (g == null) stop("No such gate named:", name); // Find gate driving bit
g.value = value; // Set bit
}
}
Bit bit(CharSequence Name) {return new Bit(Name);} // Get a named bit or define such a bit if it does not already exist
class Gate extends Bit // Description of a gate that produces a bit
{final Operator op; // Operation performed by gate
final int seq; // Sequence number for gate
Gate iGate1, iGate2; // Gates driving the inputs of this gate as during simulation but not during layout
Bit soGate1, soGate2, tiGate1, tiGate2; // Pin assignments on source and target gates used during layout but not during simulation
final TreeSet<WhichPin>
drives = new TreeSet<>(); // The names of the gates that are driven by the output of this gate with a possible pin selection attached
Gate drives1, drives2; // Drives these gates during simulation
boolean systemGate = false; // System gate if true. Suppresses checking that this gate is driven by something as it is driven externally by the surrounding environment.
Boolean value; // Current output value of this gate
Boolean nextValue; // Next value to be set
int fellStep = 0; // Last step in which the value of the gate fell
int changeStep = 0; // Last step in which the value of the gate changed
Integer distanceToOutput; // Distance to nearest output. Used during layout to position gate on silicon surface.
int firstStepChanged; // First step at which we changed - used to help position gate on silicon surface during layout.
int lastStepChanged; // Last step at which we changed - used to help position gate on silicon surface during layout.
String nearestOutput; // The name of the nearest output so we can sort each layer to position each gate vertically so that it is on approximately the same Y value as its nearest output.
int px, py; // Position in x and y of gate in latest layout
public Gate(Operator Op, CharSequence Name, Bit Input1, Bit Input2) // User created gate with a user supplied name and inputs
{super(Name);
op = Op;
seq = ++gateSeq; // Gate sequence number
gates.put(name, this);
if (commutative(op)) // Any input pin will do
{if (Input1 != null) impinge(Input1);
if (Input2 != null) impinge(Input2);
}
else // Input pin order is important
{if (Input1 == null || Input2 == null)
stop("Non commutative gates must have two inputs",
Name, Op, Input1, Input2);
impinge(Input1, true);
impinge(Input2, false);
}
final TreeSet<WhichPin> d = pending.get(name); // Add any pending gate references to this gate definition
if (d != null)
{pending.remove(name);
for (WhichPin p : d)
{drives.add(new WhichPin(p.drives, p.pin));
}
}
}
public Gate(Operator Op, CharSequence Name) {this(Op, Name, null, null);} // User created gate with a user supplied name
void setSystemGate() {systemGate = true;} // Mark as a system gate so that it does not get reported as lacking a driver
String drives() // Convert drives to a printable string
{final StringBuilder b = new StringBuilder();
for (WhichPin s : drives) b.append(s + ", ");
if (b.length() > 0) b.delete(b.length()-2, b.length());
//if (drives1 != null) b.append(" drives1="+drives1.name+"=");
//if (drives2 != null) b.append(" drives2="+drives2.name+"=");
return b.toString();
}
class WhichPin implements Comparable<WhichPin> // Shows that this gate drives another gate either on a specific pin or on any pin if the gate is commutative
{final Bit drives; // Drives this named gate
final Boolean pin; // Null can drive any pin on target, true - must drive input pin 1, false - must drive input pin 2
WhichPin(Bit Drives, Boolean Pin) {drives = Drives; pin = Pin;} // Construct a pin drive specification targeting a specified input pin
WhichPin(Bit Drives) {this(Drives, null);} // Construct a pin drive specification targeting any available input pin
Gate gate() {return getGate(drives);} // Details of gate being driven
public int compareTo(WhichPin a) // So we can add and remove entries from the set of driving gates
{return drives.name.compareTo(a.drives.name);
}
public String toString() // Convert drive to string
{if (pin == null) return drives.name;
if (pin) return drives.name+">1";
return drives.name+">2";
}
boolean ok1() {return pin == null || pin;} // Can drive the first pin
boolean ok2() {return pin == null || !pin;} // Can drive the second pin
}
String print() // Print gate
{final char v = value == null ? '.' : value ? '1' : '0'; // Value of gate
final char s = systemGate ? 's' : ' '; // System gate
if (op == Operator.Input)
return String.format("%4d %32.32s %c %8s %c"+" ".repeat(127)+"%4d,%4d ",
seq, name, s, op, v, px, py) + drives() + "\n";
final Boolean pin1 = iGate1 != null ? whichPinDrivesPin1() : null;
final Boolean pin2 = iGate2 != null ? whichPinDrivesPin2() : null;
return String.format("%4d %32.32s %c %8s %c %32.32s=%c %32.32s=%c %4d %4d"+
" %4d %4d %4d %32.32s %4d,%4d ",
seq, name, s, op, v,
iGate1 == null ? "" : iGate1.name,
iGate1 == null ? '.' : iGate1.value == null ? '.' : iGate1.value ? '1' : '0',
iGate2 == null ? "" : iGate2.name,
iGate2 == null ? '.' : iGate2.value == null ? '.' : iGate2.value ? '1' : '0',
changeStep, fellStep,
firstStepChanged,
lastStepChanged,
distanceToOutput,
nearestOutput != null ? nearestOutput : "",
px, py
) + drives() + "\n";
}
public String toString() // Convert to string
{return "" + (value == null ? '.' : value ? '1' : '0'); // Value of gate
}
void impinge(Bit Input) // Go to the named gate (which must therefore already exist) and show that it drives this gate on any input pin
{if (definedGate(Input))
{final Gate s = getGate(Input);
s.drives.add(new WhichPin(this)); // Show that the source gate drives this gate
}
else // The source gates have not been defined yet so add the impinging definitions to pending
{TreeSet<WhichPin> d = pending.get(Input.name);
if (d == null) {d = new TreeSet<>(); pending.put(Input.name, d);}
d.add(new WhichPin(this));
}
}
void impinge(Bit Input, Boolean pin) // Go to the named gate (which must therefore already exist) and show that it drives this gate on the specified input pin
{if (definedGate(Input))
{final Gate s = getGate(Input);
s.drives.add(new WhichPin(this, pin)); // Show that the source gate drives this gate
}
else // The source gates have not been defined yet so add the impinging definitions to pending
{TreeSet<WhichPin> d = pending.get(Input.name);
if (d == null) {d = new TreeSet<>(); pending.put(Input.name, d);}
d.add(new WhichPin(this, pin));
}
}
boolean whichPinDrivesPin(String driving) // True if the driving gate is using pin 1 to drive this gate, else false meaning it is using pin 2
{final Gate d = getGate(driving); // Driving gate
if (d == null) stop("Invalid gate name:", driving); // No driving gate
if (d.drives.size() == 0)
{stop("Gate name:", driving, "is referenced as driving gate:", name,
"but does not drive it");
return false;
}
return name.equals(d.drives.first()); // First/Second output pin drives first input pin
}
boolean whichPinDrivesPin1() // True if output pin 1 drives input pin 1, false if output pin 2 drives pin 1, else null if not known
{return whichPinDrivesPin(iGate1.name); // Gate driving pin 1
}
boolean whichPinDrivesPin2() // True if output pin 1 drives input pin 2, false if output pin 2 drives pin 2, else null if not known
{return whichPinDrivesPin(iGate2.name); // Gate driving pin 2
}
void compileGate() // Locate inputs for gate so we do not have to look them up every time
{final int N = drives.size(); // Size of actual drives
if (N == 0 && !systemGate) // Check for user defined gates that do not drive any other gate
{if (op == Operator.Output || op == Operator.FanOut) return;
err(op, "gate", name, "does not drive any gate");
return;
}
if (N > 2) stop("Gate", name, "drives", N, // At this point each gate should have no more than two inputs
"gates, but a gate can drive no more than 2 other gates");
for (WhichPin t : drives) // Connect targeted gates back to this gate
{if (t != null) // Fan out is wider than strictly necessary but tolerated to produce a complete tree.
{final Gate T = t.gate();
if (T.iGate1 == this || T.iGate2 == this) {} // Already set
else if (T.iGate1 == null && t.ok1()) T.iGate1 = this; // Use input pin 1
else if (T.iGate2 == null && t.ok2()) T.iGate2 = this; // Use input pin 2
else // No input pin available
{if (t.pin == null) stop("Gate name:", T.name, "type", T.op,
"driven by too many other gates, including one from gate:", name);
else stop("Gate:", T.name,
"does not have enough pins to be driven by:", t, "from", name);
}
}
}
drives1 = drives2 = null; // In a subsequent pass, record which gate each gate drives
}
void checkGate() // Check that the gate is being driven or is an input gate
{if (iGate1 == null && iGate2 == null && !zerad(op)) // All gates except input gates require at least one input
stop("Gate name:", name, "type:", op, "is not being driven by any other gate");
}
void drivenBy() // Record gates that drive this gate
{if (iGate1 != null)
{if (iGate1.drives1 == null) iGate1.drives1 = this; else if (iGate1.drives2 == null) iGate1.drives2 = this; else stop("Drives too many gates");
}
if (iGate2 != null)
{if (iGate2.drives1 == null) iGate2.drives1 = this; else if (iGate2.drives2 == null) iGate2.drives2 = this; else stop("Drives too many gates");
}
}
void fanOut() // Fan out when more than two gates are driven by this gate
{final int N = drives.size(), N2 = N / 2; // Number of pins driven by this gate
if (op == Operator.Output) return; // Output gates do not fan out
if (N <= 2) return; // No fan out required because we have maintained the rule that no gate may drive more than two gates directly.
final WhichPin[]D = drives.toArray(new WhichPin[N]); // The input pins driven by this output spread across the fan
final Gate g = new Gate(Operator.FanOut, n(1, name, "f")); // Even and greater than 2
final Gate f = new Gate(Operator.FanOut, n(2, name, "f"));
final int P = logTwo(N); // Length of fan out path - we want to make all the fanout paths the same length to simplify timing issues
final int Q = powerTwo(P-1); // Size of a full half
for (int i = 0; i < Q; ++i) // Lower half full tree
{final WhichPin d = D[i];
g.drives.add(d); // Transfer drive to the new lower gate
drives.remove(d); // Remove drive from current gate
}
drives.add(new WhichPin(g)); // The old gate drives the new gate
g.fanOut(); // The lower half gate might need further fan out
for (int i = Q; i < N; ++i) // Upper half - might not be a complete tree
{final WhichPin d = D[i];
f.drives.add(d); // Transfer drive to new lower gate
drives.remove(d); // Remove drive from current gate
}
if (2 * N >= 3 * Q) // Can fill out most of the second half of the tree
{drives.add(new WhichPin(f)); // The old gate drives the new gate
f.fanOut(); // Fan out lower gate
}
else // To few entries to fill more than half of the leaves so push the sub tree down one level
{final Gate e = new Gate(Operator.FanOut, n(name, "f")); // Extend the path
drives.add(new WhichPin(e)); // The old gate drives the extension gate
e.drives.add(new WhichPin(f)); // The extension gate drives the new gate
f.fanOut(); // Fanout the smaller sub tree
}
}
static void reverse(Gate[]G) // Reverse an array of gates
{for (int i = 0; i < G.length/2; i++)
{final Gate g = G[i];
G[i] = G[G.length-1-i];
G[G.length-1-i] = g;
}
}
void nextValue() // Next value for each gate
{final Boolean g = iGate1 != null ? iGate1.value : null,
G = iGate2 != null ? iGate2.value : null;
nextValue = switch(op) // Null means we do not know what the value is. In some cases involving dyadic commutative gates we only need to know one input to be able to deduce the output. However, if the gate output cannot be computed then its result must be null meaning "could be true or false".
{case And ->{if (g != null && G != null) yield g && G; else if ((g != null && !g) || (G != null && !G)) yield false; else yield null;}
case Nand ->{if (g != null && G != null) yield !(g && G); else if ((g != null && !g) || (G != null && !G)) yield true; else yield null;}
case Or ->{if (g != null && G != null) yield g || G; else if ((g != null && g) || (G != null && G)) yield true; else yield null;}
case Nor ->{if (g != null && G != null) yield !(g || G); else if ((g != null && g) || (G != null && G)) yield false; else yield null;}
case Gt ->{if (g != null && G != null) yield g && !G; else yield null;}
case Lt ->{if (g != null && G != null) yield !g && G; else yield null;}
case My -> iGate1.fellStep == steps - 1 ? G : value; // Memory gates retain their current value unless driven by a falling edge in the previous step
case Ngt ->{if (g != null && G != null) yield !g || G; else yield null;}
case Nlt ->{if (g != null && G != null) yield g || !G; else yield null;}
case Not ->{if (g != null) yield !g; else yield null;}
case Nxor ->{if (g != null && G != null) yield !(g ^ G); else yield null;}
case Xor ->{if (g != null && G != null) yield g ^ G; else yield null;}
case One -> true;
case Zero -> false;
case Input -> value;
case Continue, FanOut, Output -> g;
default -> {stop("Unexpected gate type", op); yield null;}
};
}
void setNextValue() // Propagate drive to next gates
{if (value != nextValue) // Record change in value
{changeStep = steps;
if (value != null && value && nextValue != null && !nextValue) // Record falling edge
fellStep = steps;
}
value = nextValue; // Update value
}
} // Gate
Gate FanIn(Operator Op, CharSequence Named, Bit...Input) // Normal gate - not a fan out gate
{final String Name = Named.toString();
final int N = Input.length;
if (N == 0) // Zerad
{if (!zerad(Op)) stop(Op, "gate:", Name, "does not accept zero inputs");
return new Gate(Op, Name, null, null);
}
if (N == 1) // Monad
{if (!monad(Op)) stop(Op, "gate:", Name, "does not accept just one input");
return new Gate(Op, Name, Input[0], null);
}
if (N == 2) // Dyad
{if (!dyad(Op)) stop(Op, "gate:", Name, "does not accept two inputs");
return new Gate(Op, Name, Input[0], Input[1]);
}
final int P = logTwo(N); // Length of fan out path - we want to make all the fanout paths the same length to simplify timing issues
final int Q = powerTwo(P-1); // Size of a full half
final Operator l = switch(Op) // Lower operator
{case Nand -> Operator.And;
case Nor -> Operator.Or;
default -> Op;
};
final Operator u = switch(Op) // Upper operator
{case And, Nand -> {yield N > Q + 1 ? Operator.And : Operator.Continue;}
case Or, Nor -> {yield N > Q + 1 ? Operator.Or : Operator.Continue;}
default -> Op;
};
final Gate f = FanIn(l, n(1, Name, "F"), Arrays.copyOfRange(Input, 0, Q)); // Lower half is a full sub tree
final Gate g = FanIn(u, n(2, Name, "F"), Arrays.copyOfRange(Input, Q, N)); // Upper half might not be full
if (2 * N >= 3 * Q) return new Gate(Op, Name, f, g); // No need to extend path to balance it
final Gate e = FanIn(Operator.Continue, n(Name, "F"), g); // Extension gate to make the upper half paths the same length as the lower half paths
return new Gate(Op, Name, f, e);
}
boolean neq(Bit i, Bit j) // Two bits are considered to be the same bit if they have the same name allowing some gates driven by identically named inputs to be simplified. Trying to use two bits in different places with the same name will led to "too many gates drive" messages.
{return !i.name.equals(j.name);
}
Gate Input (CharSequence n) {return FanIn(Operator.Input, n);}
Gate One (CharSequence n) {return FanIn(Operator.One, n);}
Gate Zero (CharSequence n) {return FanIn(Operator.Zero, n);}
Gate Output (CharSequence n, Bit i) {return FanIn(Operator.Output, n, i);}
Gate Continue(CharSequence n, Bit i) {return FanIn(Operator.Continue, n, i);}
Gate Not (CharSequence n, Bit i) {return FanIn(Operator.Not, n, i);}
Gate Nxor (CharSequence n, Bit i, Bit j) {return neq(i, j) ? FanIn(Operator.Nxor, n, i, j) : One (n);}
Gate Xor (CharSequence n, Bit i, Bit j) {return neq(i, j) ? FanIn(Operator.Xor, n, i, j) : Zero(n);}
Gate Gt (CharSequence n, Bit i, Bit j) {return neq(i, j) ? FanIn(Operator.Gt, n, i, j) : Zero(n);}
Gate Ngt (CharSequence n, Bit i, Bit j) {return neq(i, j) ? FanIn(Operator.Ngt, n, i, j) : One (n);}
Gate Lt (CharSequence n, Bit i, Bit j) {return neq(i, j) ? FanIn(Operator.Lt, n, i, j) : Zero(n);}
Gate Nlt (CharSequence n, Bit i, Bit j) {return neq(i, j) ? FanIn(Operator.Nlt, n, i, j) : One (n);}
Gate My (CharSequence n, Bit i, Bit j) {return FanIn(Operator.My, n, i, j);} // When pin 1 falls from 1 to 0 record the value of pin 2
Gate And (CharSequence n, Bit...i) {return FanIn(Operator.And, n, i);}
Gate Nand (CharSequence n, Bit...i) {return FanIn(Operator.Nand, n, i);}
Gate Or (CharSequence n, Bit...i) {return FanIn(Operator.Or, n, i);}
Gate Nor (CharSequence n, Bit...i) {return FanIn(Operator.Nor, n, i);}
void distanceToNearestOutput() // Distance to the nearest output
{outputGates.clear();
for (Gate g : gates.values()) // Start at the output gates
{g.distanceToOutput = null; // Reset distance
if (g.op == Operator.Output) // Locate drives
{outputGates.add(g.name);
g.nearestOutput = g.name;
}
}
connectedToOutput.clear(); // Gates that are connected to an output gate
Set<String> check = outputGates; // Search backwards from the output gates
for (int d = 0; d < gates.size() && check.size() > 0; ++d) // Set distance to nearest output
{final TreeSet<String> next = new TreeSet<>();
maximumDistanceToNearestOutput = d; // Maximum distance from an output == the number of time we actually went around this loop
final TreeSet<String> to = new TreeSet<>(); // Gates at this distance from drives
distanceToOutput.put(d, to);
for (String name : check) // Expand search one level
{final Gate g = getGate(name);
if (g.distanceToOutput == null) // New gate
{to.add(name); // Classify gate by distance from gate
connectedToOutput.put(g.name, g); // Gates that have connections to output gates
g.distanceToOutput = d; // Distance to nearest output
if (g.iGate1 != null)
{next.add(g.iGate1.name);
g.iGate1.nearestOutput = g.nearestOutput;
}
if (g.iGate2 != null)
{next.add(g.iGate2.name);
g.iGate2.nearestOutput = g.nearestOutput;
}
}
}
check = next;
}
countAtMostCountedDistance = 0; // The distance at which we have the most gates from an output
mostCountedDistance = 0; // The number of gates at the distance from the drives that has the most gates
for (Integer s : distanceToOutput.keySet())
{final TreeSet<String> d = distanceToOutput.get(s);
if (d.size() > countAtMostCountedDistance)
{countAtMostCountedDistance = d.size();
mostCountedDistance = s;
}
}
//say("Maximum path length from nearest output gate:", maximumDistance);
}
int distanceToFurthestOutput() // Distance to the furthest output: loops endlessly when there is a register loop as in class Cpu in Ban.java.
{int md = 0;
Set<Gate> last = new TreeSet<>();
for (Gate o : gates.values()) if (o.op == Operator.Output) last.add(o); // The first layer is the an output gates because the objective of each set of gates is to produce some output
for (int d = 0; d < gates.size() && last.size() > 0; ++d) // Move away from last layer by looking at the gates driving the gates in the current layer
{final TreeSet<Gate> next = new TreeSet<>(); // Next layer obtained from last layer
for (Gate g : last) // Expand search one level
{if (g.iGate1 != null) next.add(g.iGate1);
if (g.iGate2 != null) next.add(g.iGate2);
}
last = next; // latest layer becomes the new start layer
md = d; // Maximum distance from an output
}
return md; // We claim that this is the maximum distance in the chip - which should be equivalent to the maximum number of steps needed to stabilize it.
}
// Simulation // Simulate the behavior of the chip
void printErrors() // Print any errors and stop if there are some
{if (errors.size() > 0) // Print any recorded errors and stop.
{say(errors.size(), "errors during compilation of chip:");
for (String s : errors) say(s);
stop("Stopping because of chip compile errors");
}
}
void compileChip() // Check that an input value has been provided for every input pin on the chip.
{final Gate[]G = gates.values().toArray(new Gate[0]);
for (Gate g : G) g.fanOut(); // Fan the output of each gate if necessary which might introduce more gates
//for (Pulse p : pulses.values()) p.checkGate(); // Compile each pulse
for (Gate g : gates.values()) g.compileGate(); // Compile each gate on chip
for (Gate g : gates.values()) g.checkGate(); // Check each gate is being driven
for (Bit b : gates.values()) getGate(b); // Check that each bit is realized as a gate
for (Gate g : gates.values()) g.drivenBy(); // Record which gates each gate drives
for (Gate g : gates.values()) // Cross check connections
{if (g.iGate1 != null)
{if (g.iGate1.drives1 == g || g.iGate1.drives2 == g) {}
else stop("XXXX11");
}
if (g.iGate2 != null)
{if (g.iGate2.drives1 == g || g.iGate2.drives2 == g) {}
else stop("XXXX22");
}
if (g.drives1 != null)
{if (g.drives1.iGate1 == g || g.drives1.iGate2 == g) {}
else stop("XXXX33");
}
if (g.drives2 != null)
{if (g.drives2.iGate1 == g || g.drives2.iGate2 == g) {}
else stop("XXXX44");
}
}
printErrors();
distanceToNearestOutput(); // Distance to nearest output gate
// maximumDistance = distanceToFurthestOutput(); // Distance to furthest output gate
}
boolean changes() // Check whether any changes were made
{for (Gate g : gates.values()) if (g.changeStep == steps) return true;
return false;
}
void initializeGates(Inputs inputs) // Initialize input gates
{for (Gate g : gates.values()) // Initialize each gate
{g.value = null; g.lastStepChanged = 0; // Make the value of the gate unknown. An unknown value should not be used to compute a known value.
if (g.op == Operator.Input && ! g.systemGate) // User input gate
{if (inputs != null) // Initialize
{final Boolean v = inputs.get(g);
if (v != null) g.value = v; // The input gate has the same value throughout the simulation
else stop("No input value provided for gate:", g.name);
}
else stop("Input gate \""+g.name+"\" has no initial value");
}
}
}
class Inputs // Set values on input gates prior to simulation
{private final Map<String,Boolean> inputs = new TreeMap<>();
Inputs set(Bit s, boolean value) // Set the value of an input
{inputs.put(s.name, value);
return this;
}
Inputs set(Bits input, int value) // Set the value of an input bit bus
{final int bits = input.bits(); // Get the size of the input bit bus
final boolean[]b = bitStack(bits, value); // Bits to set
for (int i = 1; i <= bits; i++) set(input.b(i), b[i-1]); // Load the bits into the input bit bus
return this;
}
Inputs set(Words wb, int...values) // Set the value of an input word bus
{for (int w = 1; w <= wb.words(); w++) // Each word
{final boolean[]b = bitStack(wb.bits(), values[w-1]); // Bits from current word
for (int i = 1; i <= wb.bits(); i++) set(wb.b(w, i), b[i-1]); // Load the bits into the input bit bus
}
return this;
}
Boolean get(Bit s) {return inputs.get(s.name);} // Get the value of an input
}
Inputs inputs() {return new Inputs();} // Create a new set of inputs.
abstract class ExecutionTrace // Trace simulation
{final Stack<String> trace = new Stack<>(); // Execution trace
void add() {trace.push(trace());} // Add a trace record
abstract String title(); // Trace required at each step
abstract String trace(); // Trace required at each step
String print(boolean summary) // Print execution trace
{final StringBuilder b = new StringBuilder();
b.append("Step "+title()); b.append('\n');
b.append(String.format("%4d %s\n", 1, trace.firstElement()));
for(int i = 1; i < trace.size(); ++i)
{final String s = trace.elementAt(i-1);
final String t = trace.elementAt(i);
if (!summary || !s.equals(t)) // Remove duplicate entries if summaryion requested
b.append(String.format("%4d %s\n", i+1, trace.elementAt(i)));
}
return b.toString();
}
void ok(String expected) // Confirm expected trace
{final String s = print(true), t = print(false);
if (s.equals(expected)) Chip.ok(s, expected);
else Chip.ok(t, expected);
}
}
void printExecutionTrace () {say(executionTrace.print(false));} // Print execution trace
void printExecutionSymmary() {say(executionTrace.print(true));} // Print execution trace summary
class Stop extends RuntimeException // Raise this exception to stop the current simulation
{private static final long serialVersionUID = 1L;
}
void simulate() {simulate(null);} // Simulate the operation of a chip with no input pins. If the chip has in fact got input pins an error will be reported.
void simulate(Inputs Inputs) // Simulate the operation of a chip
{compileChip(); // Check that the inputs to each gate are defined
initializeGates(Inputs); // Set the value of each input gate
for (InputUnit i : inputs .values()) i.start(); // Each input peripheral on chip
for (OutputUnit o : outputs.values()) o.start(); // Each output peripheral on chip
final int actualMaxSimulationSteps = // Actual limit on number of steps
maxSimulationSteps != null ? maxSimulationSteps : defaultMaxSimulationSteps;
final boolean miss = minSimulationSteps != null; // Minimum simulation steps set
final Gate []G = gates .values().toArray(new Gate[0]); // Currently active gates
final Pulse []P = pulses .values().toArray(new Pulse [0]); // Pulses
final InputUnit []I = inputs .values().toArray(new InputUnit [0]); // Input peripherals
final OutputUnit[]O = outputs .values().toArray(new OutputUnit[0]); // Output peripherals
randomizeArray(G); // To prevent the Zeta problem. In theory the gate execution order does not matter. "In theory, theory and practice are the same. In practice they are not".
atStart();
for (steps = 1; steps <= actualMaxSimulationSteps; ++steps) // Steps in time
{for (Pulse p : P) p.setState (); // Load all the pulses
for (Gate g : G) g.nextValue(); // Compute next value for each gate
for (Gate g : G) g.setNextValue(); // Compute next value for each gate
for (InputUnit i : I) i.inputUnitAction(); // Action on each input peripheral affected by a falling edge
for (OutputUnit o : O) o.outputUnitAction(); // Action on each output peripheral affected by a falling edge
if (executionTrace != null) executionTrace.add(); // Trace requested
try {eachStep();} // Routine to do something after each step
catch(Stop e) {return;} // Stop was requested during end of step processing
if (!changes() && (!miss || steps >= minSimulationSteps)) // No changes occurred and we are beyond the minimum simulation time or no such time was set
{//if (!miss && steps > maximumDistance+2) // Tests that might invalidate the maximum distance theory.
// {err(steps, maximumDistanceToNearestOutput, maximumDistance);
// }
//classifyByDistanceToOutput.put(name, maximumDistance); // Track maximum distance by chip name
return;
}
}
if (maxSimulationSteps == null) // Not enough steps available by default
stop("Out of time after", actualMaxSimulationSteps, "steps");
}
void run() {} // Override this method to define and run a simulation
void atStart () {} // Executed at start
void eachStep() {} // Override this method to do something on each step of the simulation
//D1 Circuits // Some useful circuits
//D2 Bits // Operations on bits
static boolean[]bitStack(int width, long...values) // Create a stack of bits
{final int N = width*values.length;
if (width <= 0) stop("Width must be one or more, not", width);
//if (width >= Long.SIZE) stop("Width must be less than", Long.SIZE, "not", width);
final boolean[]b = new boolean[N];
for(int i = 0; i<N; ++i) b[i] = (values[i/width] & (1l<<(i % width))) != 0;
return b;
}
Boolean getBit(String name) // Get the current value of the named gate.
{final Gate g = getGate(name); // Gate providing bit
if (g == null) stop("No such gate named:", name);
return g.value; // Bit state
}
void setBit(String name, Boolean value) // Set the value of a bit
{final Gate g = getGate(name); // Gate providing bit
if (g == null) stop("No such gate named:", name); // Find gate driving bit
g.value = value; // Set bit
}
//D2 Buses // A bus is an array of bits or an array of arrays of bits
//D3 Bits // An array of bits that can be manipulated via one name.
static String n(String...C) // Gate name from no index probably to make a bus connected to a name
{return concatenateNames(concatenateNames((Object[])C));
}
static String n(int i, String...C) // Gate name from single index.
{return concatenateNames(concatenateNames((Object[])C), i);
}
static String n(int i, int j, String...c) // Gate name from double index.
{return concatenateNames(concatenateNames((Object[])c), i, j);
}
static String concatenateNames(Object...O) // Concatenate names to construct a gate name
{final StringBuilder b = new StringBuilder();
for (Object o: O) {b.append("_"); b.append(o);}
return O.length > 0 ? b.substring(1) : "";
}
class Bits // A bus made out of bits
{final String name; String name() {return name;} // Name of bus
final int bits; int bits() {return bits;} // Number of bits of bus - the width of the bus
Bit b(int i) {return new Bit(n(i, name));} // Name of a bit in the bus
boolean implemented = false; // When false this is a forward declaration, when true the forward declaration has been backed by real bits
Bits anneal() // Anneal this bit bus so that the annealed gates are not reported as driving anything. Such gates should be avoided in real chips as they waste surface area and power while doing nothing, but anneal often simplifies testing by allowing us to ignore such gates for the duration of the test.
{for (int i = 1; i <= bits; i++) b(i).anneal();
return this;
}
Bits(String Name, int Bits) // Declare the size of a named bit bus
{name = Name; bits = Bits;
if (bitBuses.containsKey(name)) // If the bits have already been defined, check that the number of bits matches
{final Bits b = bitBuses.get(name);
if (b.bits != Bits)
stop("BitBus", name, "has already been defined with", b.bits,
"versus", Bits, "requested");
}
bitBuses.put(name, this); // This will be overwritten by the sub class if we are being called from a sub class - but that is not a problem as we still have this information present
}
public void sameSize(Bits b) // Check two buses are the same size and stop if they are not
{final int A = bits(), B = b.bits();
if (A != B) stop("Input", name(), "has width", A, "but input", b.name(),
"has width", B);
}
public String toString() // Convert the bits represented by an output bus to a string
{final StringBuilder s = new StringBuilder();
final int N = bits();
for (int i = 1; i <= N; i++)
{final Bit b = b(i);
final Gate g = getGate(b);
if (g != null) s.append(g.value == null ? '.' : g.value ? '1' : '0');
else stop("No such gate as:", b.name);
}
return s.reverse().toString(); // Prints string with lowest bit rightmost
}
public Integer Int() // Convert the bits represented by an output bus to an integer
{int v = 0, p = 1;
final int B = bits();
for (int i = 1; i <= B; i++) // Each bit on bus
{final Bit b = b(i);
final Gate g = gates.get(b.name); // Gate with the name of the bit
if (g == null)
{err("No such gate as:", name(), i); // Generally occurs during testing where we might want to continue to see what other errors occur
return null;
}
if (g.value == null) return null; // Bit state not known
if (g.value) v += p;
p *= 2;
}
return v; // Value of bit bus
}
public void set(Integer Int) // Set the value of a bit bus - perhaps we are loading from simulated memory
{final int B = bits();
final boolean[]b = bitStack(B, Int); // Number as a stack of bits padded to specified width
for (int i = 1; i <= B; i++) // Each bit on bus
{final Gate g = gates.get(b(i).name); // Gate with the name of the bit
g.value = b[i-1]; // Set bit
}
}
public void ok(Integer e) // Confirm the expected values of the bit bus. Write a message describing any unexpected values
{final Integer g = Int(); // The values we actually got
final StringBuilder b = new StringBuilder();
int fails = 0, passes = 0;
if (false) {}
else if (e == null && g == null) {}
else if (e != null && g == null) {b.append(String.format("Expected %d, but got null\n", e )); ++fails;}
else if (e == null && g != null) {b.append(String.format("Expected null, but got %d\n", g )); ++fails;}
else if (!e.toString().equals(g.toString()))
{b.append(String.format("Expected %d, but got %d\n", e, g)); ++fails;}
else ++passes;
if (fails > 0) err(b);
testsPassed += passes; testsFailed += fails; // Passes and fails
}
}
Bits bits(CharSequence name, int bits) // Forward declare a bit bus: get a predefined bit bus or define a new one if there is no predefined bit bus of the same name. This allows us to create bit buses needed in advance of knowing the gates that will be attached to them - in effect - forward declaring the bit bus.
{return new Bits(name.toString(), bits); // Resulting bit bus
}
Bits bits(String name, int bits, long...values) // Create a bit bus set to a specified value.
{final int N = bits * values.length;
final Bits B = bits(name, N);
final boolean[]b = bitStack(bits, values); // Number as a stack of bits padded to specified width
for(int i = 1; i <= N; ++i) if (b[i-1]) One(B.b(i)); else Zero(B.b(i)); // Generate constant
return B;
}
Bits bits(String name, String value) // Create a bit bus set to a value specified by a string in which a zero represents a zero and anything else represents a one.
{final int N = value.length();
final Bits B = bits(name, N);
for(int i = 1; i <= N; ++i)
{if (value.charAt(i-1) == '0') Zero(B.b(i)); else One(B.b(i)); // Generate constant
}
return B;