This is the first programming post since I came to Japan. If you just want to know what I’m up to and are not interested in the technology, you can stop reading after the first paragraph, although you’re welcome to read on and enjoy the pictures. π The last few weeks I’ve been reading a lot about processor design, and now I’m starting to learn Verilog. Verilog is a hardware description language (HDL) specified in IEEE 1364. Simply put, a HDL is a programming language that can be used to describe the structure or behavior of logical circuits. Right now I’m learning how to design a simple processor, I already have a set of registers and an ALU (arithmetic logic unit) ready and have started implementing the instruction decode logic.

The diagram on the right shows the structure of a 1-bit adder. It accepts a possible carry from a lower bit addition and output a carry bit as necessary. For more about adders please refer to Wikipedia or other sources, I’m just going to describe this in Verilog to show the principle.

A Verilog module is similar to a class in object oriented languages: It defines a list of inputs, outputs, internal variables and behavior or structure of a component. Instances of modules are created to actually use the module. I’ll be using the structural approach in this example, which means that I will define a module by describing its components and their connections, rather than using equations to calculate the output values based on the inputs. Modules for basic logic gates are built-in, with gate level logic, simple wires are sufficient to connect the components.

Because I’m used to splitting code up as much as possible, I first define a half-adder module for the AND/XOR pairs:

module halfadd(a, b, sum, carry); input a; input b; output sum; output carry; wire a; wire b; wire sum; wire carry; xor xsum(sum, a, b); and csum(carry, a, b); endmodule // halfadd

Verilog’s built-in gates take the output as first argument and the inputs after that. The half-adder is defined in a file called halfadd.v, which I include into the full-adder file. I create two half-adder instances and an XOR gate to build the full-adder:

`include "halfadd.v" module fulladd(a, b, cin, sum, cout); input a; input b; input cin; output sum; output cout; // inputs wire a; wire b; wire cin; // internal wiring wire s1; wire c1; wire c2; // outputs wire sum; wire cout; halfadd h1(a, b, s1, c1); halfadd h2(s1, cin, sum, c2); xor carry(cout, c1, c2); endmodule // halfadd

The only thing left to do is test the `fulladd`

module. A test-bench module can be used to simulate inputs. In this example you can see “behavioral” Verilog, which will seem familiar to most programmers. π Not everything that can be written in this style could be turned into hardware, however, for a simulation these restrictions can be completely disregarded.

`include "fulladd.v" module fulladd_test(); reg [2:0] in; wire sum; wire carry_out; initial begin $dumpfile ("test.vcd"); $dumpvars (0, fulladd_test); $monitor ("a=%b, b=%b, carry_in=%b, sum=%b, carry_out=%b", in[0], in[1], in[2], sum, carry_out); in = 0; #20 $finish; end always #2 in = in + 1; fulladd a1(in[0], in[1], in[2], sum, carry_out); endmodule // fa_test

The most interesting thing here is probably the `in`

variable. It is defined as a 3 bit register with indices from 2 to 0, so it’s a little endian value. Each bit is connected to one of the full-adder’s inputs, and I will increment this value during the simulation to simulate all possible input values. For bigger systems it would require an impossible amount of time to simulate *all* possible inputs, but here I can do it. Writing good tests is as important (and difficult) as writing a good design, and while working on my processor I’ve already spent a while searching for an error in my design to find that the test was wrong instead (that time).

`initial`

marks a block that will be executed a simulation start, when the module is initalized. The `$dumpfile`

and `$dumpvars`

commands make the simulation write a trace of the variables to the file `test.vcd`

, while `$monitor`

prints the specified variables to the terminal every time one of them changes. Statements with `#NUMBER`

in front of them are delayed that many units of simulated time. I use this both to stop the simulation after 20 units of simulated time and to increase the input value every two time units.

To actually run the simulation, I used the Icarus Verilog compiler and GTKWave to analyze the results (packages `iverilog`

and `gtkwave`

in Ubuntu):

`$ iverilog -o fulladd_test fulladd_test.v`

$ vvp fulladd_test

VCD info: dumpfile test.vcd opened for output.

a=0, b=0, carry_in=0, sum=0, carry_out=0

a=1, b=0, carry_in=0, sum=1, carry_out=0

a=0, b=1, carry_in=0, sum=1, carry_out=0

a=1, b=1, carry_in=0, sum=0, carry_out=1

a=0, b=0, carry_in=1, sum=1, carry_out=0

a=1, b=0, carry_in=1, sum=0, carry_out=1

a=0, b=1, carry_in=1, sum=0, carry_out=1

a=1, b=1, carry_in=1, sum=1, carry_out=1

a=0, b=0, carry_in=0, sum=0, carry_out=0

a=1, b=0, carry_in=0, sum=1, carry_out=0

a=0, b=1, carry_in=0, sum=1, carry_out=0

$ gtkwave test.vcd

Two useful links:

- A Mini-Howto that helped me getting something running quickly
- Verilog tutorial

If you have access to it (many universities have), I also recommend looking at the actual standard document.