Intel’s Silvermont Microarchitecture

May 08, 2013

I want to talk a bit more about Verilog and how it is different from simply writing something using software.

I occasionally hear people use the phrase “crawl, walk, run” when describing early steps in a project. The idea is you can’t expect a baby to just start running. Even the greatest Olympic runner starts with – literally – baby steps, and builds on those.

Recently, I’ve been talking about a very simple computer that was built using cardboard by Bell Labs back in the 1960s. That computer, CARDIAC, is simple enough that I thought it might be useful for teaching students (even lifelong ones) about simple logic design.

Last time, I talked about CARDIAC’s internal structure and presented a partial implementation in Verilog. This time, I want to talk a bit more about Verilog and how it is different from simply writing something using software.

Sticking to “crawl, walk, run”, let’s consider a simple half adder circuit. You can build these out of relays even, so they aren’t very complicated. The idea is to take two binary digits and produce a sum and a carry bit.

There are many ways you could develop a half adder circuit. Traditionally, you’d draw a schematic:

For a simple circuit like that, this isn’t especially onerous. However, for a big circuit like a CPU, or even a modestly complex circuit like a LED display driver, it gets old fast.

Most modern FPGAs implement logic like this using a look-up table. So even if you draw the schematic, you wind up with a table inside the FPGA that looks like this (courtesy of the FPGA tools that convert your input into a bit stream to configure the FPGA):

Again, this is easy to figure out for this simple case, but not so easy when you have a very complex circuit. That’s why most designers use an HDL (hardware definition language) like Verilog or VHDL. A complex circuit is easier to describe than it is to draw. Of course, there are many ways you can describe something.

Here’s one (pretty silly) implementation of a half adder in Verilog:

module halfadd(input a, input b, output reg sum, output reg cy);
always @(a,b)
case ({a,b})
 2'b00: 
	begin
	sum<=1'b0;
	cy<=1'b0;
	end
2'b01: 
	begin
	sum<=1'b1;
	cy<=1'b0;
	end
2'b10: 
	begin
	sum<=1'b1;
	cy<=1'b0;
	end
2'b11: 
	begin
	sum<=1'b0;
	cy<=1'b1;
	end
endcase
endmodule

That’s nothing more than writing the table in a different format. A few notes on syntax, though. The {a,b} construct forms a word that is a and b joined together. In this case, since a and b are one bit each, the result is two bits.

The syntax of 2’b00 and 1’b0 tells the Verilog translator that you are writing a binary word with two bits (in the first case) and one bit in the last case. The “b” is for binary. You could write a hex constant like 16’hff, for example, which would be 255 in a 16-bit word.

You may notice in some Verilog that you see a mix of “=” and “<=” for assignment. There is a subtle but important difference. Inside a block, the <= operator only takes effect at the end of the block. Imagine you had the code:

x<=4'hf;
y<=x;
z=4'h9;
zz=z;

If x already contained, say, zero, then at the end of the block x would equal 4, but y would equal 0 because the z were equal to zero at the start, zz would equal 9 at the end of the block because the = operator takes effect immediately.

You may think you don’t care about this, but you do. Remember that Verilog is just a description of what you want and, usually, what you want is going to wind up in a piece of hardware where everything occurs together. In English, the statements above say something like this: “At the exact same time, set y to x and x to 4. Also set z to 9 and then set zz to z.” The and then is what gives you trouble. In software, not only is that natural, it is very difficult to do things at the exact same time. In hardware, it is difficult to define that sequence, and the translator has to add extra circuitry to make sure zz gets the correct value. Of course, even in hardware things don’t happen exactly at the same time, but that’s a topic for a bit later.

How else could you describe the half adder? You could describe it by the gates it uses, but that’s not much better (and maybe even harder) than drawing the schematic:

and and1(a,b,carry);
xor summer(a,b,sum);

Or you could write:

assign sum=a^b;
assign carry=a&b;