☮ Chad D. Kersey ♡

A Weblog

Tag: HDL

Running the CHDL CPU Example

CHDL example 6, a simulated MIPS-esque CPU with a 5-stage pipeline running a Sieve of Eratosthenes program finally works. This example can be loaded and simulated and will surely provide an important test case for future modifications to CHDL. For those of you interested in seeing CHDL run for yourself, I will provide a (very) brief tutorial on building and running the examples.

Step 1: Getting CHDL

CHDL is hosted on github, which (among other things) means that it must be accessed using a git client, like so:

  $ git clone git://github.com/cdkersey/chdl.git

Step 2: Building CHDL

Once you have downloaded CHDL, building it should just be a matter of running make in the root source directory. There are a lot of compilation units, so if you’re on a multicore machine don’t forget to use the -j option to speed up the build.

  $ cd chdl
  $ make -j 8

Step 3: Building and Running the Examples

Once you have built the core CHDL library, you can build the examples in the examples/ directory.

  $ cd examples
  $ make -j 8

Once this finishes, you will have a set of files called example[i].vcd, for i in 1 through 7. These are waveform files, containing the state of every node (or bit vector) tapped with the “TAP()” macro, and viewable in waveform viewers like the free gtkwave. If you do not have a waveform viewer installed, go ahead and obtain gtkwave.

  $ sudo aptitude install gtkwave

If you’re not on Debian, and/or do not have aptitude installed, you’re beyond help.

Step 4: Viewing the Waveforms

The waveform files can then be viewed with gtkwave:

  $ gtkwave example6.vcd

The TAP()s from the source code are listed along the left side of the window and can be dragged to the viewing area.

chdl_waveform

In the above figure, the fetch program counter is being viewed in “analog” mode, showing the path taken through the program memory over time. Note that after the program is finished executing, the processor enters a tight loop. The output of the program is the series of prime numbers in register 12 (a good test case because it’s a simple program whose results are hard to produce accidentally). Happy hacking!

Announcing CHDL

Strange things happen as a consequence of the lack of freedom in the hardware world. Consider the case of the man who made a CPU out of discrete transistors because he was uncomfortable with FPGA vendor lock-in. (http://www.6502.org/users/dieter/mt15/mt15.htm) I do not pretend to understand the EDA community enough to make any claims about the tools they do or do not have, open source or otherwise. I have experienced a lack, but it may just be the rarified air of the field when compared to software-oriented disciplines. Other tools exist. I have not found the ones I have encountered particularly well-suited to my personal needs, so I have built another.

CHDL (call it “the” CHDL at your peril) is two things: a C++-based hardware description language and a C++ hardware design library. The former fills a perceived need for radical generality and simplicity in gate-level design specification and the latter fills a need for a free software toolchain for realizing these designs.

CHDL (the language) can be used to specify abstract digital logic designs with uncommonly terse syntax. Designs specified in this way can then be subject to optimizations and simulated directly or written out to netlists. It is the processing of these netlists that is the domain of CHDL (the library) and the related utilities. The netlist files may be translated to other HDLs (like ones supported by FPGA vendor toolchains), translated to C and simulated more quickly, or technology mapped and physically implemented.

What does CHDL code look like? Here is a a nontrivial design from the standard library: a Kogge-Stone adder:

 template  bvec Adder(bvec a, bvec b, node cin = Lit(0)) {
    vector<bvec<N+1>> g(log2(N)+3), p(log2(N)+3), i(log2(N)+3);
    bvec s;

    g[0][0] = cin;
    p[0][0] = Lit(0);

    for (unsigned j = 0; j < N; ++j) p[0][j+1] = Xor(a[j], b[j]);
    for (unsigned j = 0; j < N; ++j) g[0][j+1] = a[j] && b[j];

    unsigned k;
    for (k = 0; (1l<<k) < 2*N; ++k) {
      for (unsigned j = 0; j < N+1; ++j) {
        if (j < (1l<<l)) {
          g[k+1][j] = g[k][j];
          p[k+1][j] = p[k][j];
        } else {
          i[k+1][j] = p[k][j] && g[k][j - (1l<<k)];
          g[k+1][j] = i[k+1][j] || g[k][j];
          p[k+1][j] = p[k][j] && p[k][j - (1l<<k)];
        }
      }
    }

    for (unsigned j = 0; j < N; ++j) s[j] = Xor(p[0][j+1], g[k][j]);
    return s;
  }

This template function, when called, instantiates an adder of the given size. It will instantiate one of these adders each time it is called. Note that all of the loops are run at design instantiation time. The function’s goal is to create some gates. Once it has returned, those gates occupy some global state, where they can then be simulated, optimized, or written out to a file.

I have placed the very-much-in-development CHDL on GitHub (https://github.com/cdkersey/chdl) along with the hope that I am not alone in my ambition, and that likeminded individuals will find value in these manic machinations.