Lecture 19: Registers, Muxes, and all that jazz (by Trek Palmer) ================================================ In the last lecture we saw various forms of flip-flops, but a flip-flop is essentially a 1-bit memory. We like to deal with multibit values, so we need something more. Generally speaking, any hardware component that stores multibit values so that they can be read and updated in a single cycles is known as a register. A simple register ----------------- A simple register is simply an array of flip-flops: | | | Dn +---+----+ Qn| +----+----+Qn-1| +-----+----+ Q0 | | | FF +---+ | | FF +----+ . . . | | FF +----+ | +-+----+ | +-+----+ | +-+----+ | | | | | | clk-|-+------------|--+---------------------|---+ | Dn-1 | D0 | The only issue here is that the same clock has to be propagated to all the flipflops, so as the bit-width of the register grows, the more clock propagation becomes an issue. But this is a low-level hardware detail, and as such we're not supposed to care. A shift register ---------------- Shifting, it turns out is a simple extension to a vanilla register. Behold: Q1 |Out Dn +---+----+ Qn+-+----+----+Qn-1+-- ---+-----+----+ | | | FF +---+ | FF +----+ . . . | FF +----+ | +-+----+ +-+----+ +-+----+ | | | | clk-|-+---------------+-------------------------+ | Dn-1 D0 This will shift the input by n places. An extension of this idea is to place selection logic on each flip-flop's input so that you can select either to store the previous flip-flop's value or some incoming value. Decoding -------- At times it is necessary to be able to select on inputs depending on their effective place value. So, we have n inputs and 2^n outputs, if the input represents the value k, then the kth line needs to go high. You can see how this would be useful if, say, you have an instruction that encodes the operation to be performed as a numerical value (for those that don't remember, the ARM does this). So, let's attack this problem in a very belt and suspenders way. Let's take a two bit example: X | Y | Output ----+---+------- 0 | 0 | 0 0 | 1 | 1 1 | 0 | 2 1 | 1 | 3 X-------------+-----+-----+-----\ | | | AND |------ 3 +---------|-----|-----+-----/ | +--+ | | \ / +-----+-----\ | \/ | AND |------ 2 | o +-----+-----/ | | | | | | | +-----|-----+-----\ | | | | AND |------ 1 +---------|-----|-----+-----/ | | | | +-----|-----+-----\ | |\ | | AND |------- 0 Y---+--| \o---------+-----+-----/ | / |/ This circuit is known as a decoder, and with some hairy extensions, it is the 32-bit equivalent of this circuit that is the heart of the decode part of the fetch-decode-execute cycle (hence the name). MUXs ----- Decoding is how to go from an n bit number to one of the 2^n outputs. A multiplexer is a way of taking 2^n inputs and using n bits to select which of those inputs to pass on to circuits downstream. In this respect, multiplexers can be considered selectors. So, given a MUX with four inputs (x1 - x4), and two select bits (w1, w2), the "truth table" would be: W1 | W2 | output -----+----+-------- 0 | 0 | X1 0 | 1 | X2 1 | 0 | X3 1 | 1 | X4 X1-----------------------+------\ +----------------+ AND |----------+ | +--------+------/ | | | | X2------|-------|--------+------\ | +-------|--------+ AND |-----+ | ______ | +--|--------+------/ | +----------\ \ | | | +----------------| OR \______output X3------|----|--|--------+-------\ +----------------| / +---|----|--|--------+ AND |----+ +----------/_____/ | | | +--------+-------/ | | | | | | X4--|---|----|--|--------+-------\ | +---|----|--|--------+ AND |---------+ | | +--|--------+-------/ | | | | | o | o | / \ | / \ | /___\ |/___\ | | | | +---+ +--+ | | W1 W2 MUX's are very useful, you basically use them when you have multiple values you need to select off of. This often arises if you're computing several values in parallel and don't know which one you're going to need until after the values are generated. This sort of situation occurs fairly often in hardware. Sequential Circuits -------------------- So far what we've been working with is known as combinational logic, meaning you just lump the circuits together to process some input. If we want a system that uses flip-flop based memory, then we're going to have to worry about the amount of time it takes to stabilize the outputs and inputs to the memory device. There are two approaches, you can deeply analyze the circuitry and determine that the signals are all valid when they're processed. This is hard (and is known as asynchronous circuitry). An easier solution is to extend the clock signal necessary for real flip flops to the entire logic of the chip. If everybody's operating on the same clock, they can't get out of sync with the memory! This solution is easier and is known as synchronous circuitry. The only down side is that as clock speeds increase, the amount of area that can be covered by a clock within a single tick shrinks. Any circuitry that relies on memory, that is, any circuit whose succeding state depends upon inputs as well as the current state is known as a sequential circuit (because it progresses through a sequence of states, presumably).