# Lecture 1: Number Systems, Flip Flops, and Counters

Last Update: 2023-11-02## > Number Systems

In general, humans count in a number system called base 10 (often referred to as “decimal”). In this system, we start at zero and every time that we see another sheep jump over the fence, we simply “add one” to the current number. In decimal, this means that we count like this: 0, 1, 2, and so on until we get to 9. Once we see another sheep, the next number that we count is 10. After that, 11, 12, and so on. The point here is that in base 10, there are ten different symbols which are used per place value in the number. Once we’ve used up all the symbols, the count “carries over” to the next column to the left and we continue. (Remember that a number can have any number of leading zeros before the decimal point. We usually just don’t write them out in everyday usage. For example, 01 = 1) What happens if we change the number base? What if instead of 10, we tried something like 2? We can follow the same logic as we did with decimal. In this new number system, binary, each column of the number can take a total of two values before we need to carry over to the next. Back to our sheep counting example, we would count sheep in binary as: 0, 1, 10, 11, 100, etc. Note that even though these numbers look like they are getting very large very quickly, since each column has a smaller representable range, this sequence is equivalent to the decimal sequence 0, 1, 2, 3, 4. (It may be helpful to see what’s happening by writing the leading 0’s in the binary numbers: 000, 001, 010, 011, 100) An important observation at this point is that since each column allows the number base’s worth of possible values, this implies that each column going to the left is the next power of that base. For decimal:- 1 = 10**0
- 10 = 10**1
- 100 = 10**2
- 1000 = 10**3

- 1 = 2**0
- 2 = 2**1
- 4 = 2**2
- 8 = 2**3

- ~A & ~B = ~(A | B)
- ~A | ~B = ~(A & B)

## > Adders [TODO]

An adder is a device that, unsurprisingly, adds two numbers. Let us first take a look at a basic example, 5 + 8. Using the Boolean operations defined above, we can create something called a half adder. This takes in two single bit binary numbers and produces the sum of those numbers.## > Flip Flops [TODO]

Flip flops allow a circuit to hold a state. [gate-level construction, setup/hold time explanation]## > Counters [TODO]

We now have all the building blocks to start constructing sequential circuits. Perhaps the most intuitive next step is to combine the adder and some flip flops to create a counter.## > Verilog

Verilog is a hardware description language (HDL) which was originally designed for simulation of digital circuits. Its syntax borrows heavily from C since its original parser was based on that of the C compiler. As such, if you are familiar with a c-like language, its syntax might look vaguely familiar. Despite its similarity, you must always remember that the Verilog is describing a circuit and not (in general) a sequential program.### Verilog: AND gate

Let’s start with a very simple example: an “and” gate.module and_gate ( input a, b, output y ); assign y = a & b; endmodule

### Verilog: Flip Flop

Now, let’s try something more complicated: a flip flop.module dff ( input d, clk, output reg q, output qn ); assign qn = ~q; always @(posedge clk) begin q <= d; end endmodule

### Aside: Simulator Scheduling

To understand nonblocking assignments, we first need to understand the simulation event scheduler. Like any numerical simulation tool, Verilog simulators break time up into individual time slices. Within each slice, there are a few key operations which are performed. While there are a number of event regions per time slice, we will concern ourselves with only the “active events” and the “NBA updates.” Most of the action occurs in the active Active event region. Here, flow logic (such as “if/else”) are determined, continuous assignments are computed, blocking assignments are computed, and the RHS of nonblocking assignments are computed. Continuous assignments operate by computing the value of the RHS and immediately assigning that value to the LHS. Blocking assignments operate similarly to continuous assignments, except that the order they are executed is determined by the order they take within a begin/end block. For instance, take the code in Listing 3. Within the always block, there are two “blocking assignments.” First, “b = a” followed by “c = b.” Since these are nonblocking assignments, “b = a” is evaluated first and b’s value is updated. Then the simulator moves to the next statement, “c = b.” Since b has already been updated, c will now take the value of a. In effect, this module makes for a bit of a convoluted buffer from a to c.module blocking_assign ( input a, output c ); wire b; // Can also use: // always @(*) always @(a) begin b = a; c = b; end endmodule

module nonblocking_assign ( input a, clk, output c ); wire b; always @(posedge clk) begin b <= a; c <= b; end endmodule

### Verilog: Counter

Finally, we combine all the above information into a single module: the binary counter.module counter ( input clk, output [3:0] A ); reg [3:0] count; // Combinational logic (assuming A is a ‘reg’) // Option 1: // always @(*) begin // A = count; // end // // Option 2: // always @(count) begin // A = count; // end // // Option 3: assign A = count; // Sequential logic always @(posedge clk) begin count <= count + 1; end endmodule