Outline#

The goals of this weeks lab are to:

  1. Understand the basic building blocks of sequential circuits
  2. Understand the clock input
  3. Understand the difference between level triggered and edge triggered circuits
  4. Design a register file which can be used to store/retrieve binary values

Preparation#

Before you attend this week’s lab, make sure:

  • You have Digital installed on your computer (if doing the lab remotely)
  • You can use Git to commit and push your designs to GitLab
  • You are able to design and implement simple combinational logic circuits from a specification

Introduction#

So far, all circuits you built were combinational. The output is determined only by the current values of the inputs. However, real-world digital circuits remember and save the state of a system. Such circuits have memory and are called sequential circuits. Sequential circuits may explicitly remember certain previous inputs, or they may summarize the prior inputs in a compressed form called the system’s state. For example, a four-bit binary pattern can store one of the sixteen states for a system. In sequential logic, the output depends on the system’s state in addition to the current inputs.

Consider the two following circuits:

Two circuits. First circuit is a single NOT gate connected to itself in a loop. Second circuit shows two NOT gates connected in a loop, with the output of the first gate labelled 'A' and the output of the second gate labelled 'B'.

The first circuit is a NOT gate where the gate’s output is fed back into its input. This creates an unstable circuit. Can you analyze why? To analyze the circuit, assume the output of the inverter is HIGH at some point in time. This implies the input to the inverter is HIGH. Therefore, the output is contrary to our assumption (LOW). Indeed, the circuit is unstable and oscillates between two states. A ring oscillator contains an odd number of inverters connected in a chain. The output of a ring oscillator oscillates between HIGH and LOW, and the frequency of oscillation is dictated by the propagation delays of the NOT gates. What happens if we connect an even number of NOT gates in a chain?

The second circuit is similar to the first one but contains two NOT gates connected in a chain. To analyze this circuit, use a process similar to the one we used above. Assume the output on the wire (A) is HIGH. The input to the second NOT gate is, therefore, HIGH. The output of the second NOT gate (B) is LOW. If we follow through with the circuit, we get the same output for A (HIGH) that we assumed. Repeat the thought experiment, but this time consider the output (A) is LOW. And then follow through the circuit. You will find that the output you start with is the same as you finally get. The circuit has memory! It retains the state it is in at any point. This circuit has two stable states: (1) A is HIGH, B is LOW, (2) A is LOW, B is HIGH.

Both stable states of 2 NOT gates in a loop. In the first A is low and B is high. In the second A is high and B is low.

These are two examples of sequential circuits with feedback loops. Although the circuit with two inverters connected in a feedback loop can store one bit of state, unfortunately, we cannot control what the specific state (HIGH or LOW) is via an explicit input terminal. We also cannot control when the state changes. The following circuits will fix these shortcomings.

Exercise 1: SR Latch Example#

We want a circuit that can store a single bit of information. The second circuit above has two stable states (HIGH or LOW) and thus stores a single bit. We can either read A or B (they are complements of each other). The feature we are looking for is the capability to switch between states. The SR latch offers this feature because it has two inputs: one which sets (S) the state of the circuit to output a HIGH value and one which resets (R) the state to output a LOW value. A possible implementation for the SR latch has been provided in sr_latch.dig

You need to recall the truth table for the NOR gate to demystify the behaviour of the SR latch.

Open the example sr_latch.dig design in Digital and experiment with it. Can you analyze the circuit? Note that when the two inputs are LOW the circuit behaves just like the two connected NOT gates above. If you set S or R to HIGH, what is the new state of the SR latch? Analyse the circuit and convince yourself that this works. This is a good place to check in with your tutor if you are unsure or want to make sure that you understand everything.

The truth table for the SR latch is as follows

SRQ00Qprev01010111X

When S is HIGH, the output of the SR latch is HIGH. This means setting the latch HIGH. When S is LOW, the output of the SR latch is LOW. This means resetting the latch LOW. When both S and R are LOW, then the value of Q is Qprev meaning Q maintains its current state (where the current state depends on whether it was set or reset most recently). When both S and R are HIGH, the output is X. This means the output of the latch is undefined, and we do not care about the behaviour. Try out this case if you haven’t already, and you will find that depending on the order you switched the inputs, the latch either outputs HIGH or LOW. Consider what it means to have both inputs HIGH simultaneously; we want the latch to both set and reset. It doesn’t make sense! We get around this by just assuming that this will never happen. In other words, it’s the designer’s responsibility who is using the circuit to make sure that these inputs are never HIGH at the same time.

You can switch to drawing diagonal lines by pressing d. Drawing diagonal lines is often considered poor design practice and should be used sparingly. One of the few times it is used is to show the symmetric feedback in this sequential circuit.

For sequential circuits, the output depends on the previous state. For instance, we can set (or reset) the SR latch and then make the S and R inputs LOW. The output of the latch depends on whether S or R was HIGH previously. In such cases, simply testing the truth table for functional correctness is insufficient. We need the ability to track the circuit’s behaviour over time. To see a timing diagram in Digital, click on the button in the top left of the testing window (see the picture below). This diagram shows how the values of the various inputs and outputs of the circuit change over time. This can better represent the fact that the output depends on the history of the system.

Test results window open with timing diagram button at top circled

There are many different designs for the SR latch. Can you come up with a different one with similar behaviour?

Exercise 2: D Latch#

The SR latch behaves strangely when both S and R are HIGH. Furthermore, the S and R inputs decide the (future) state of the SR latch and when that state changes (i.e., instantly). Designing sequential circuits becomes more manageable when we separate the concerns of what and when.

The D latch has the following inputs:

  • D: The data input to the latch.
  • en: This input is called Enable. When it is HIGH, the D latch copies the value on the D input to the output. When it is LOW, the D latch blocks the new data from flowing to the output.

We say the latch is ‘transparent’ when en is HIGH because the circuit acts like a buffer or a wire, copying the input D to the output. When en goes LOW the value on D is ‘latched’ and stored by the circuit. The stored value is fixed (memory!) and cannot change no matter how D changes until en goes HIGH again.

Design a circuit that meets the above specifications. Designing sequential circuits from scratch can be difficult. Can you create a combinational circuit in front of the SR latch that transforms en and D (inputs of D latch) to the inputs of the SR latch? This means you can then reuse the circuit from the previous exercise (don’t copy paste the circuit in though as you don’t need to change the SR latch internals; just import the SR latch as a custom component). Verify your design with the tests provided.

Complete and test d_latch.dig

Exercise 3: Clocks#

Now that we have seen circuits that can store the state of a system, we want the ability to periodically update the state. So far, we have used an Input component connected to the enable wire of our D latch and toggled it manually. Digital has a second type of input called a Clock Input. This component acts as any other standard input. You can manually click it to toggle its state and create an input when using your circuit in a larger design. However, the difference is that it produces a periodic signal that regularly switches between HIGH and LOW. In real-time, the clock component produces a periodic square wave. Typically, there is only ever one of these components in a circuit.

Typically, a clock in digital electronics does not refer to something that provides an absolute value of time. Instead, it is a circuit component that produces a stable (and regular) periodic signal over time. It is used to synchronize sequential elements in digital circuits. Designing circuits with combinational and sequential elements is much easier if we can synchronize all the elements to a single clock input.

The clock period (or cycle time) is the time between the rising edges of a clock signal. The reciprocal of cycle time is clock frequency. For now, remember that increasing clock frequency increases the work a digital system can accomplish. Note that we will not go into the low-level details of how the clock signal is produced in this course.

Clock diagram. Plots voltage (y-axis) against time (x-axis). Low to high transition is rising edge. High to low transistion is falling edge. Time between two rising edges is clock period. Time between rising and falling edge is clock width.

Open incrementer.dig. This example circuit uses four D latches you designed in the previous exercise to store a 4-bit number. The input to the latches is fed by an adder, and the output of the latches goes back to the input of the adder. The other adder inputs are selected to just add one to its input a. What do you expect this circuit to do?

Run the incrementer.dig simulation and click the clk line to toggle it. You can also press C, which is more convenient.

If you expected the circuit to break when we tried to advance the clock, well done! If not, can you see why the circuit breaks when the clock line is HIGH?

Hint: think about the behaviour of a D latch when the enable line is HIGH. Clearly, what we have is not quite useful enough. We want a circuit that changes its state only at specific instants in time.

We say that the D latch is level-sensitive or level-triggered because whether it latches the input or not depends on the level (HIGH or LOW) of the enable (en) signal. A flip-flop, on the other hand, is edge triggered. A flip-flop latches the input only when the clk signal transitions from LOW to HIGH. The flip-flop is sometimes called edge-triggered.

Open d_flipflop.dig. To implement the D flip-flop, we can use a pair of D latches in series with the output of one connecting to the input of the next. The important part is how the clk input is fed to the two flip-flops. The clk input to one of the flip-flops is negated. Thus, only one of the latches is enabled (transparent) at any time. The first latch in the series acts as a master and the second one acts as a slave. When the master latch is transparent (input flows to the output), the slave is locked (blocks the input from flowing to the output). When the rising edge of clk arrives, the master is locked, and the slave is transparent. In other words, when the clock switches from LOW to HIGH, the first latch is locked and outputs whatever its input was to the second latch, which is now transparent.

Experiment with this design to make sure you understand what’s going on. Do check in with your tutor and make sure you understand everything. What would happen if you attached the NOT gate to the clk input of the other latch?

Once you have fully analyzed the D flip-flop design and convinced yourself that it would work, go back to incrementer.dig and replace all the latches with flip-flops. Does the circuit now behave as intended?

The inputs should be in the same order, the clk input of the flip-flop replacing the en input of the latch. If this is not the case you can reorder the inputs of a circuit. Go to Edit -> Order Inputs and make sure the order is as follows: clk, D. You can select an input and then use the arrows to change the ordering.

Add an enable line to your D flip-flop. When the enable line is HIGH, the flip-flop should behave as an ordinary flip-flop. When the enable line is LOW, the flip-flop should not update its output at the clock edge. Save your design in d_flipflop_with_en.dig. Note that it is possible to do this by just adding some additional logic to your D flip-flop and not changing any of the flip flop’s internals, so begin by importing the D flip-flop as a custom component. Verify your design with the tests provided.

So far we have been storing 1 bit values. To store more bits we can just add more D flip-flops and connect the control lines (clk and en) together. In particular, we want a component that can store a 16-bit value for future labs. This component is called a register. A register can store a group of bits and binary numbers and patterns. Designing this component is tedious as it doesn’t contain any additional logic, just wire splitters to separate each individual wire from the 16-bit input. We have provided this design in register.dig. It uses 16 copies of your D flip-flop (with enable line) that you designed in the previous exercise.

Check that the inputs for the individual D flip-flops are in the correct order. From top to bottom, they should be clk, D, en. If you used the template file provided and didn’t add or delete any inputs, you should be fine.

This register component is almost identical with the register component in the Digital component library. The only difference is that the Digital registers are always initialised to zero, whereas your register starts with a random value (it just depends on the initial state of all the SR latches at start-up time).

Counter#

Open the file counter.dig. Design a circuit that increments a register each clock cycle. If the ld (short for load) signal is HIGH then load the value from the input D instead. Verify your design with the tests provided. Ensure that you have pushed your work when finished.

Exercise 4: Testing Sequential Circuits#

Since sequential circuits depend on the current input and past inputs, we can’t just enumerate all possible inputs as we did for combinational circuits. Tests for sequential circuits look more like a computer program, where we can specify the inputs, toggle the clock line, and then check if the new outputs match what we expect.

If a c is encountered in a test, all the other values are set as the test describes, and then the signal associated with c is toggled (switched from LOW to HIGH, and then back from HIGH to LOW). The test is then checked against for correctness. This means that we can write a test where we can set some control signals, toggle the clock line, and then check the expected result. For example, the provided tests for the previous exercise check that the counter starts at zero then increases by one each clock cycle.

clk ld D Q

0 0 0 0
c 0 0 0x1
c 0 0 0x2

This test assumes that you have used the built-in register component rather than the one given to you as it tests that the counter starts at zero, not some random value.

Write additional tests in for your circuit in counter.dig to check that the ld input works as intended.

Exercise 5: Register File#

Now we have a register that can store one 16 bit value. We can replicate the register components to store more than one single binary number (value). We also want a way to interact with them as a group. For example, we may want a way to choose which register will output its value to a shared output line or which register will read a shared input and update its value. This group of registers and the circuitry to control them as a group is called a register file.

In digital circuits such as register files, ports communicate values in and out of the circuit. For instance, we need a read port in the register file to read the contents of one of the many register. Specifically, we need an address port to specify the register number we want to read. The contents of the specified register are output on the (read) data port. The context typically clarifies if we are talking about the address or data port or the read port in general.

  • A read port consists of one (address) input to specify which register we want to read from and one (data) output to provide the contents of that register.

  • A write port consists of three inputs; a select input to specify which register we want to write to, a data input to provide the data we want to write and a write enable input to signal whether we write anything at all or leave the contents the same.

Register files can have as many read and write ports as necessary. The register file you will design will have two read ports and one write port.

Open reg_file.dig. The register file you will design will have five 16-bit registers.

You should use the register component built into Digital (Memory -> Register) instead of the register component provided in the previous exercise. It has better integration with the testing features built into Digital and predictably initialises to zero when starting the simulation.

The inputs and outputs of the circuit are explained below.

  • RS1 (3-bits): Read Select 1, Chooses which register to send to the first output OUT1.
  • RS2 (3-bits): Read Select 2, Chooses which register to send to the second output OUT2.
  • WS (3-bits): Write Select, Chooses which register to write to on the next positive clock edge.
  • WE (1-bit): Write Enable, If HIGH, the register specified by WS will be written to on the next positive clock edge.
  • IN (16-bits) the data to be stored.
  • CLK (1-bit) clock line.
  • OUT1 (16-bits) the value of the first register selected.
  • OUT2 (16-bits) the value of the second register selected.

The select inputs use three bits because that is the minimum number of bits needed to represent five different values (to differentiate between our registers). In fact, with three bits we can have up to eight unique values. We only use the first five in this exercise, the other three we leave undefined at this point (in other words, you can make the circuit do whatever you want when the select lines are outside of the 0-4 range). Next week, we will do something with these extra slots.

Hints:

  • Look through the inputs and outputs and work out which are used to implement each port of the register file.
  • The logic related to reading from and writing to the registers is unrelated and can be designed separately. If you are struggling start with the logic to read from a register, consider the circuits we have looked at in previous labs, they might be useful.
  • Have a look at the Demultiplexer and Decoder components (under Plexers) as these may help. Remember you can right-click a component to bring up the component settings and click help to get an explanation of what a circuit does and what the inputs and outputs are.

Start working on reg_file.dig.

The test currently present in reg_file.dig will fail if RS1 or RS2 are 000. This is because register 0 is a special case register that you will implement in the next exercise.

At this point you can verify your register file works manually or continue onto the next exercise before running the provided tests.

Special Case Registers#

You will use this register file in future labs to design a complete processor. In preparation for that, add the following feature to your register file: when reading register 000 the 16-bit constant 0 should always be returned. Any writes to register 000 should be ignored.

The final register file that you have designed should conform to the following specification. This will be important next week when we start bringing everything together.

Code Mnemonic Meaning Behaviour
000 RZ Zero Register Always read zero, writes have no effect.
001 R1 Register 1 General purpose register.
010 R2 Register 2 General purpose register.
011 R3 Register 3 General purpose register.
100 R4 Register 4 General purpose register.
101 - undefined Read and write is undefined.
110 - undefined Read and write is undefined.
111 - undefined Read and write is undefined.

Don’t worry about the mnemonics right now. They are just more human-friendly ways of referring to the registers.

Modify your register file to add this special case zero register in slot 000. At this point the test present in reg_file.dig will pass if you have implemented your register file correctly.

Ensure that you have pushed your work when finished. This circuit in particular will be useful in your upcoming assessments.

While it may seem silly to lose the use of one of our registers and just return a constant value this has some implications for the design of the processor. Think about why it may be useful to keep a constant value in a dedicated register. What other dedicated uses could registers have?

The register file you have designed has two read ports and one write port, allowing two registers to be read at the same time but only one register to be written to each clock cycle. Add a second write port to allow two separate registers to be written to separately on each clock cycle. This is a tricky extension so if you don’t get it right a way that’s fine, you can come back to it later.

Exercise 6: Memory#

The register file you have designed could store a maximum of 8 16-bit numbers (7 if you have completed Exercise 5). What if you want to store more than that? We could make the register file bigger by adding more registers but this is not the approach modern computers take. Instead computers separate out registers and the memory system. The memory system is many orders of magnitude larger than the register file. In this course, we will use memory with 16-bit selection lines giving us 216 memory locations. You can imagine this memory system as a huge register file with the reading and writing logic similar to what you have designed but at a much larger scale. In reality, because computer memory can store so much more than the register file, the physical hardware behind it is quite different. In fact, modern processors have a whole hierarchy of memories with different speeds and capacities to maximise their speed. We will study the memory hierarchy in one of the lectures.

Digital has some components that contain all the logic necessary for a memory system so that you don’t need to design it or worry about these complexities. Have a look at the components under Memory -> RAM to see what is available to you. You will use some of these components next week.

If we have a huge memory that we can treat as a large register file why do we need registers at all?

bars search times