-
Notifications
You must be signed in to change notification settings - Fork 8
/
CHDL
841 lines (635 loc) · 26.7 KB
/
CHDL
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
C++ Hardware Design Library
===========================
CHDL (call it "the" CHDL at your peril) is a domain-specific language based on
C++. Specifically, it attmpts to fill the role of a "structural" hardware
description language for implementing designs.
A simple design, a full adder, implemented in CHDL, might look like the
following:
#include <chdl/gates.h>
#include <chdl/gateops.h>
node FullAdder(node cout, node cin, node a, node b) {
node h = Xor(a, b), s = Xor(h, cin);
cout = (a && b) || (h && cin);
return s;
}
Adders are already provided in the CHDL library, so a device of this nature
would not be completely necessary.
Contents
--------
- Concepts
- Gate-Level Design
- Node
- The node class
- nodeType enumeration
- Creating New Node Types
- The nodeimpl hierarchy
- Register
- Vector
- API
- CLOG2() and LOG2() Utility Functions
- Nodes
- Literals
- Fundamental Gates
- Registers
- Special Nodes
- Tri-state Nodes
- Module Inputs
- Gates
- Basic Operator Overloads (&&, ||, !)
- Vectors
- Vector Operator Overloads ([], &, |, ^, <<, >>, +, -, *, /, -, ~, ==, !=)
- Indexing
- Logic
- Arithmetic
- Equality
- Shift
- Vector Concatenation (Cat(vec, vec) Cat(vec, node) Cat(vec).Cat(vec)...)
- Encoders and Decoders
- Higher-order Arrays
- Flatten
- chdl::sz<T> Template
- Multiplexers
- Conditional Assignment (Cassign)
- Buses
- Inputs and Ouputs
- Arithmetic
- Memories
- Optimization
- Clock Domains
- Simulator
- Tickables, pre_tick, tick, tock, post_tock
- C++ Interoperability (Ingress/Egress)
- Assertions
- Console Input and Output
- Analysis
- Cycle Detection
- Critical Path Length
- Critical Path Report
- HDL Writer
- Simulation Tools
- File Formats
- NAND File Format
- NETL File Format
- TLIB File Format
- HDL Writer
- Technology Mapper
- Frequently-Asked Questions
Concepts
--------
Gate-Level Design
-----------------
Designs expressed using CHDL are ultimately expressed at the gate level. While
many layers of abstraction may separate the design of a complex hardware device
from the level of gates, a program using CHDL is ultimately a C++ program
describing a set of fundamental logic units, or gates, the connections between
them, and the organization of their inputs and outputs.
A very simple gate-level design is the following 1-bit adder. It adds two 1-bit
numbers together, producing a 2-bit result. This device is often referred to as
a "half adder":
sum[1] = And(a, b);
sum[0] = Inv(Nand(Nand(Inv(a), Inv(b)), Nand(a, b)));
Note that it is represented with only two kinds of gates, nand gates and
inverters. For the sake of simplicity, all gate-level designs in CHDL are
represented internally as a combination of only two kinds of basic gates. (In
addition to special nodes for sequential logic and things like memory and
tri-state objects that are not easy to represent as combinations of gates.)
Node
----
A "node" in a gate-level design can be thought of as a net in a circuit. Each
node in a design is an independent entity that may take on a logic value during
the simulation of the design. Because each node represents the output of a gate
or an input to a circuit, the nodes themselves are considered synonymous with
the objects of which they are outputs.
Register
--------
In the context of CHDL design, registers can be thought of as simple delay
elements. There is an implicit global clock signal making periodic transitions
from low to high, and registers all take output generated during one clock cycle
and delay it until the next low-to-high transition of the clock signal.
Vector
------
Signals in CHDL are often grouped together into bundles. The most basic way to
bundle signals is as vectors of nodes. These fixed-length addressible structures
provide a simple method for accessing individual elements from C++ code,
including loops. In the following example, a 4-element vector is created and
then set up as a simple shift register:
node input, output;
bvec<4> shreg;
for (unsigned i = 0; i < 4; ++i)
shreg[i] = Reg(i == 0 ? input : shreg[i - 1]);
output = shreg[3];
API
---
Capitalization and Style Conventions
------------------------------------
Compile-time constants are rendered in ALL_CAPS with underscores representing
word separation. This is true for both variables and constexpr functions like
chdl::CLOG2. Within CHDL, class names and variables are all rendered in lower
case with underscore_separators or camelCase. Functions representing ordinary
computation are in lowerCaseCamelCase() and functions representing hardware
modules of any type are rendered in CapitalCamelCase().
Type of Identifier | Capitalization Example
---------------------+------------------------
Constant value | ALL_CAPS
Constexpr function | ALL_CAPS()
Variable | lower_case, lowerCase
Function | log2(my_int)
Function (hardware)| Log2(my_bvec)
With respect to the dimensions of code, the Linux kernel style guide
recommendation of eighty columns and no more than a couple of hundred lines per
function apply. Long lines are a curse to be dealt with by creative indentation.
Indentation should be done, of course, only with ' ' characters, never with
'\t'. Unlike Linux, the preference is for two-space indentations. This conceit
is mainly in place to compensate for the compact line size in header files.
For the sake of uniformity, projects implemented primarily with CHDL should
attempt to follow these style guidelines as well.
CLOG2() and LOG2() Utility Functions
------------------------------------
Frequently in digital logic it is necessary to find the number of bits needed to
hold a certain number of states. This is the ceiling of the logarithm base two
of the number of states. We have provided a function, CLOG2, that can be used
anywhere a constant value can be used (it is evaluated at compile time) which
provides the ceiling of the logarithm base two. This is especially useful in the
function prototypes of generic components like encoders and decoders. For
instance:
template<unsigned N> bvec<LOG2(N) + 1> PopCount(bvec<N> x);
Specifies a population count function. Its output will be in the range [0, N]
and so must have at least floor(log2(N)) + 1 bits of storage.
Nodes
-----
A CHDL node can be thought of as a node (also known as a net) in a digital
circuit. The chdl::node object itself is a referential type, representing an
index of a node implementation in CHDL's internal state. Using this approach,
it is ensured that assignment to a chdl::node will not only affect its sub-
sequent uses, but all prior uses as well:
node next_x, x(Reg(next_x)),
next_y, y(Reg(next_y)),
next_z, z(Reg(next_z));
next_x = Inv(x);
next_y = Xor(x, y);
next_z = Xor(And(x, y), z);
The downside to this is that copies performed by container objects, formerly
benign, now have the capacity to unwittingly overwrite nodes.
Literals
--------
A literal in CHDL is a node with a time-constant value:
node x = Inv(Lit(0)),
y = And(x, Lit(1));
Literals are typically used as arguments to functions:
node val = Mux(a, Mux(b, Lit(0), Lit(1)), Lit(1));
The CHDL optimization system ensures that these are efficiently compacted, often
disappearing altogether. For example, Or(And(Lit(1), x), y).
Fundamental Gates
-----------------
In CHDL, the fundamental gate types are nand gates and inverters. These can be
created using the functions "Nand" and "Inv". From these, any other Boolean
logic function can be constructed.
Registers
---------
In addition to combinational logic, there must also be a fundamental unit for
building sequential logic. In CHDL, this is the register, generated using the
Reg() function. Its output is initially 0 and subsequently represents the input
time-shifted by a single cycle.
Special Nodes
-------------
The node implementation base object (nodeimpl) can be overloaded to create a
variety of "special" nodes. Without full support from optimization, code
generation, and technology mapping utilities, however, these can lead to designs
that can be simulated but not technology mapped or output as Verilog.
The most prevalent "special" node types are those used to implement static RAMs.
These are fairly well-hidden from the end user and not exposed in any header
files.
Tri-state Nodes
---------------
While most circuits involving tri-state elements could be reduced to circuits
using multiplexers and optimized and simulated without any knowledge of these
tri-state pieces, both the optimization of circuits for technology mapping and
the interfacing requirements of circuits demand that a tri-state node type be
included. These behave similarly to ordinary node objects, but include a
connect() member function to be used to attach inputs (along with enable
signals). Tri-state nodes that are made outputs become input/output signals.
The following example implements a transceiver on a bi-directional serial bus:
tristatenode io;
OUTPUT(io);
node tx, tx_d, rx, rx_d;
io.connect(tx, tx_d);
rx_d = io && rx; // The and gate is likely not necessary.
Module Inputs
-------------
Input nodes cannot be simulated, but do participate in synthesis and will
become, in netlist outputs, ports of the design. In a typical design, input
nodes will be part of the top level design used for synthesis, and these will
be replaced by other logic in a different top level design used for simulation.
The following code generates a simple synthesizeable design with one input port
called "in" and one output port called "out":
node in = Input("in"), out = Inv(in);
OUTPUT(out);
Gates
-----
Internally, CHDL represents designs as a set of fundamental logic gates and
registers. The CHDL API hides the details (e.g. which gates these are) from the
user and provides the usual complement of basic logic gates:
node x_and_y = And(x, y),
x_nand_y = Nand(x, y),
x_or_y = Or(x, y),
x_nor_y = Nor(x, y),
x_xor_y = Xor(x, y),
x_2mux_y = Mux(z, x, y);
Basic Operator Overloads
------------------------
C++ logical operators have been overloaded to provide an infix notation for
common logical operations:
node x_and_y = x && y,
x_nand_y = !(x && y),
x_or_y = x || y,
x_nor_y = !(x || y),
x_xor_y = x != y,
x_xnor_y = x == y;
Conspicuously absent is the use of the ternary (? :) operator to provide a
basic multiplexor function. This cannot be provided due to a fundamental
limitation of C++.
Vectors
-------
CHDL provides its own fixed-length vector class, vec<N, T>, and an alias
template for vec<N, node>, bvec<N>. Multi-dimensional vectors are simple vecs of
vecs, e.g. vec<M, vec<N, node> >, vec<M, vec<N, bvec<8> > >, etc.
Vector Operator Overloads
-------------------------
The following sections describe the operator overloads provided for use with
CHDL vectors.
Indexing
--------
The most basic operation provided by vec is the indexing operator []. The input
can be either an integer or a range<A, B> object. Examples of valid uses of the
indexing operator:
bvec<8> x, y;
for (unsigned i = 0; i < 8; ++i)
x[i] = y[7 - i];
bvec<32> a, b;
bvec<16> c, d;
c = a[range<0, 15>()];
d = a[range<8, 23>()];
Logic
-----
Bitwise logic operations, equivalent to their C/C++ counterparts, are provided
for convenience, including &, |, ^, and ~.
Arithmetic
----------
A full set of arithmetic operators are provided as well, including +, *, /, and
unary and binary -, but these must be used with caution. While adders are cheap
and multipliers only somewhat expensive, frequent use of dividers can easily
lead to intractable designs, especially for large bit widths. A good rule of
thumb is to assume that the delay of an adder is proportional to the logarithm
of the bit width (Log(N)), the delay of a multiplier is proportional to Log(N)
times a somewhat larger constant, and the delat of a divider is proportional to
N*Log(N) with approximately the same constant factor as adders.
Comparison
----------
Comparison operators (!=, ==, <, >, <=, and >=) have been provided for vectors
of nodes (bvecs), all treating the inputs as unsigned binary integers.
Shift
-----
Logical shift operations are also provided using the >> and << operators. The
second argument is expected to be ceil(log2(N)) bits long.
Vector Concatenation
--------------------
In addition to the operator overloads, several functions are provided for
manipulating vectors. The most basic of these are the Cat() functions, providing
various methods of vector concatenation:
bvec<N> a, b;
node x, y;
// The resulting vector:
bvec<N+1> xa(Cat(x, a)), // [x, a[N-1], ..., a[0]]
by(Cat(b, y)); // [b[N-1], ..., b[0], y]
bvec<2*N> ab(Cat(a, b)); // [a[N-1], ..., a[0], b[N-1], ..., b[0]]
In addition to the basic 2-argument Cat() functions, there is a multi-argument
Cat() function with the following syntax:
node x;
bvec<N> a, b;
bvec<M> c, d;
bvec<1 + 2*M + 2*N> xadbc(Cat(x).Cat(a).Cat(d).Cat(b).Cat(c));
It may be tempting to use concatenators to construct aggregate types (bundles of
signals) and indeed this was an early use, but the currently preferred technique
is to use the aggregate types provided by the CHDL STL.
Encoders and Decoders
---------------------
A set of common digital logic circuits provided by CHDL are encoders (including
priority encoders) and decoders.
node enable;
bvec<N> x, x_out;
bvec<CLOG2(N)> x_msb, x_lsb;
bvec<(1<<N)> x_1hot;
x_1hot = Decoder(x, enable);
x_out = Enc(x_1hot); // Output undefined if input not 1-hot
// Two common types of priority encoder.
x_msb = Log2(x);
x_lsb = Lsb(x);
Higher-order Arrays
-------------------
The CHDL vec<> type does not simply decribe a bit vector; a vector of nodes. It
is in fact a general fixed-length vector type; like a fixed-length complement to
the C++ standard library's vector<>, with some features designed specifically
for CHDL-related data types.
Nested vector types are particularly useful, providing a form of multi-
dimensional vector. In the following example, this multidimensional vector
provides a low-level implementation of a register file or SRAM:
node wr;
bvec<16> q, d;
vec<8, bvec<16> > r;
bvec<3> sel;
bvec<8> w(Decoder(sel, wr));
for (unsigned i = 0; i < 8; ++i)
r[i] = Wreg(w[i], d);
q = Mux(sel, r);
TAP(r); // Shows up in .vcd file as r_0, r_1, ..., r_7
Flatten
-------
The flatten() function takes a multidimensional vector input and reduces it to a
single-dimensonal vector containing all of the nodes of the input in depth-first
order. Continuing the register file example from the previous section:
bvec<8 * 16> all_regs(Flatten(r));
chdl::sz<T> Template
-----------------
A convenient tempalate interface is provided to obtain the (compile-time
static) number of nodes contained in any object, including arrays of any
number of dimensions. For instance, when flattening a multi-dimensional array,
the size of the containing bvec can be determined using sz<T>:
typedef vec<N, vec<M, bvec<8> > > v_t;
v_t v;
. . .
bvec<sz<v_t>::value> flat = Flatten(v);
This is, in fact, how the size of the Flatten template's return type is defined.
Multiplexers
------------
The Mux() functions provide multiplexers in CHDL. There are two basic kinds of
multiplexers. The first type maps an input of type vec<N, T> to a single output
of a type T with a select signal of type bvec<CLOG2(N)>. The other kind has two
signal inputs and a select input. The first signal input corresponds to a select
signal value of 0. This is the reverse from the ternary operator it replaces.
Unfortunately, in C++, the ?: operator cannot be overloaded.
The first example is a simple if-then scenario. We have a counter we would like
to count from 0 to 100 and then repeat:
bvec<7> ctr;
ctr = Reg(Mux(ctr == Lit<7>(100),
ctr + Lit<7>(1),
Lit<7>(0)));
In the second example, we build a simple ROM with a bit-serial output:
bvec<3> sel;
vec<8, bvec<8> > rom;
rom[0] = Lit<8>(0x43);
rom[1] = Lit<8>(0x48);
rom[2] = Lit<8>(0x44);
rom[3] = Lit<8>(0x4c);
rom[4] = Lit<8>(0x63);
rom[5] = Lit<8>(0x68);
rom[6] = Lit<8>(0x64);
rom[7] = Lit<8>(0x6c);
bvec<3> ctr; ctr = Reg(ctr + Lit<3>(1));
bvec<8> q_parallel = Mux(sel, rom);
node q_serial = Mux(ctr, q_parallel);
It is assumed that sel remains stable throughout the eight cycles necessary to
transmit a result.
Conditional Assignment
----------------------
The hierarchies of multiplexers needed to perform even the most basic tasks can
create opaque, difficult-to-understand code. Consider the task of implementing
an up/down counter with reset. The next value, in pseudocode, would be:
if (reset)
ctr = 0;
else if (count_up && !count_down)
ctr = ctr + 1;
else if (count_down && !count_up)
ctr = ctr - 1;
else
ctr = ctr;
In the portion of CHDL covered so far, this could be implemented with multi-
plexers:
bvec<N> ctr_next, ctr(Reg(ctr_next));
ctr_next = Mux(reset,
Mux(count_up && !count_down,
Mux(count_down && !count_up, ctr, ctr - Lit<N>(1)),
ctr + Lit<N>(1)),
Lit<N>(0));
Even with the best organization, this statement is rather hard to grok. For
these kinds of conditionals, a muliplexer with its inputs reversed would lead to
slightly easier-to-understand code:
ctr_next = If(reset,
Lit<N>(0),
If(count_up && !count_down,
ctr + Lit<N>(1),
If(count_down && !count_up,
ctr - Lit<N>(1),
ctr)));
but this is only about as readable as the typical conditonal statement in LISP.
This reversed-input multiplexer is not a part of CHDL. Instead, CHDL provides
Cassign(), a conditional assignment framework. Using Cassign, this statement be-
comes:
Cassign(ctr_next).
IF(reset, Lit<N>(0)).
IF(count_up && !count_down, ctr + Lit<N>(1)).
IF(count_down && !count_up, ctr - Lit<N>(1)).
ELSE(ctr);
One of the more powerful aspects of Cassign() is its support for nested con-
ditionals. If, say, you wanted to, in the event of a reset with one of the count
lines held high, reset to a value of +1 or -1 accordingly, this could be done
with a nested conditional:
Cassign(ctr_next).
IF(reset).
IF(count_up && !count_down, Lit<N>(1)).
IF(count_down && !count_up, Lit<N>(-1)).
ELSE(Lit<N>(0)).
END().
IF(count_up && !count_down, ctr + Lit<N>(1)).
IF(count_down && !count_up, ctr - Lit<N>(1)).
ELSE(ctr);
Buses
-----
The tri-state complement to the bvec is called a bus. Unlike the bvec, which is
simply an alias template for vec<N, node>, the bus is a class that inherits from
vec<N, tristatenode>, providing a connect() member function just like the node
level connect function.
Inputs and Outputs
------------------
CHDL designs are frequently used as parts of larger designs. The way for signals
to get into a CHDL design are through the use of the Input() function. There are
two related ways for signals to leave a CHDL design. "Taps" are signals that
show up in simulation and are named in Verilog output but are not exposed as
ports in generated Verilog code and may optionally not be provided as outputs
from generated netlists. "Outputs" do show up as ports for generated Verilog
code as well as netlists. The following example uses several of the provided
interfaces for creating taps, inputs, and outputs:
bvec<3> sel;
vec<8, bvec<N> > x;
TAP(x);
q = Mux(sel, x);
OUTPUT(q);
tap("x5_out", x[5], true); // The final parameter makes this an output.
x[0] = Input("x_zero");
Outputs that are also tri-state become in/out signals both in CHDL netlists and
generated Verilog code.
A concept called a "ghost tap" is provided to assist the optimizer. The purpose
of the ghost tap is to prevent a given node from being optimized away as a
"dead" (unused) signal. The gtap() family of functions provide support for this.
It is mostly used to signify nodes that will be read by other pieces of
software.
Arithmetic
----------
Memories
--------
Optimization
------------
CHDL provides a simple logic optimization system designed to enable design
flexibility without sacrificing performance. For example, a multiplexer can be
given a literal as an input, leading to the multiplexer and all of its
non-active inputs being optimized away. If the result from an multiplier is
truncated, the logic for those bits disappears. For the typical CHDL user, most
of what needs to be known about optimization is that it is a necessary step for
fast simulation and compact output files, and that the way to invoke it is by
calling the optimize() function.
Clock Domains
-------------
In many real-world designs, multiple clock signals are present. CHDL provides
limited support for this, in <chdl/cdomain.h>. By setting the tick interval for
a given clock domain in units of a fundamental unitless clock tick rate, the
relative rate of progression of different clock domains in simulation can be
selected. By calling the internal simulation functions and specifying clock
domains, clock domains can be manually advanced relative to each other to
simulate a full range of clocking scenarios.
Simulator
---------
C++ Interoperability (Ingress/Egress)
-------------------------------------
The headers <chdl/ingress.h> and <chdl/egress.h> contain functions for
interfacing designs running in the CHDL simulator and external C++ code. These
interfaces provide simple ways to get integer and Boolean values into and out
of the CHDL simulator. They are very useful for performing various kinds of
mixed-level simulation and automated verification/validation.
A simple sample design using Egress and EgressInt:
bvec<8> count; count = Reg(count + Lit<8>(1));
unsigned char count_val;
EgressInt(count_val, count);
bool even;
Egress(even, !count_val[0]);
optimize();
for (unsigned i = 0; i < 100; ++i) {
advance();
cout << "count = " << count_val << (even?"(even)":"(odd)") << endl;
}
The advance function advances the simulation by a single cycle. After it has
been called at least once, the variables correspond to the value of their
assigned variable at the beginning of the cycle. (So in this example, count_val
will be 0 during the first iteration, 1 during the second iteration, etc.)
A simple sample design using Ingress and IngressInt:
bool cenable_var;
unsigned d_var;
bvec<32> d = IngressInt<32>(d_var);
node count_enable = Ingress(cenable_var),
load_counter = Ingress(cload_var);
bvec<32> counter, next_counter;
counter = Wreg(next_counter);
Cassign(next_counter).
IF(load_counter, d).
IF(count_enable, counter + Lit<32>(1)).
ELSE(counter);
TAP(count_enable);
TAP(load_counter);
TAP(counter);
optimize();
for (unsigned i = 0; i < 100; ++i) {
cenable_var = rand()%2;
load_counter = (rand()%16 == 0);
advance();
}
This is most of the way to a test, lacking only an Egress and the corresponding
result check. Note that unlike IngressInt, EgressInt requires a bit width as a
template parameter. This is due to the fact that, in C++, template arguments
cannot be inferred from return type.
Assertions
----------
CHDL provides a system for error detection using assertions. The ASSERT() macro
provides an easy interface for creating simulation-time assertions. If the node
passed to the ASSERT() macro is ever false during simulation, the simulation is
stopped and an error message is printed. In the following example, if the
simulation runs for more than 128 cycles, an error message is printed:
bvec<8> counter;
counter = Reg(counter + Lit<8>(1));
ASSERT(!counter[7]);
Console Input and Output
------------------------
For tests with more advanced input and output requirements, the ConsoleIn() and
ConsoleOut() functions provide a parallel character-level interface to C++
streams.
Analysis
--------
Cycle Detection
---------------
Critical Path Length
--------------------
Critical Path Report
--------------------
File Formats
------------
NAND File Format
----------------
A very basic netlist format has been developed for storing CHDL designs for use
by other utilities. This file is called a "nand" file because all logic is
represented (currently) as a network of nand gates and inverters. Each node is
NETL File Format
----------------
The NETL file format produced by the technology mapper follows the same basic
netlist structure as the NAND file format but allows a richer set of primitives
instead of the basic NAND-inverter graph of the NAND format.
TLIB File Format
----------------
The TLIB format is used to define a technology library as a set of gates to be
matched. Each line contains the name of a gate, followed by whitespace,
followed by a polish expression for the NAND-inverter tree that matches that
gate. This structure must be a tree in form, but leaf nodes (inputs) can
represent the same input, and arbitrary internal or output nodes can be assigned
as hidden inputs.
As an example, let's write a 2-input XOR gate in TLIB format:
xor2 inn12ni1i2
"xor2" is obviously the name of the gate. The string on the right is the
structure of the gate; a tree output in depth-first form:
i
|
n
/ \
n n
/ \ / \
1 2 i i
| |
1 2
An example using hidden inputs is a D flip-flop with a write enable, where input
1 is a write enable:
=ArnnAi1n21
Capital letters are used to indicate aliases; here '=A' declares that the output
of the register is aliased to hidden input A. Its reappearance creates a cycle
that could not otherwise be represented:
r----+
| |
n |
/ \ |
n n<-+
/ \ |
2 1 i
|
1
HDL Writer
----------
Technology Mapper
-----------------
Frequently-Asked Questions
--------------------------
Q: I want to create a simple sequential circuit, like a counter, but my attempt:
bvec<4> ctr1(Reg(ctr1 + Lit<4>(1))); //, or
bvec<4> ctr2 = Reg(ctr2 + Lit<4>(1));
does not seem to work. In fact, it behaves very strangely. What am I doing
wrong?
A: Use two statements instead:
bvec<4> ctr; ctr = Reg(ctr + Lit<4>(1));
Why is this unfortunate verbosity necessary? Currently, CHDL relies on the
bvec object "ctr" being constructed before it can be used. The reference to
"ctr" in "ctr + Lit<4>(1)" leads to undefined behavior in C++ because ctr
has not been constructed yet. So, while it is syntactically valid C++, it is
semantically ambiguous.