My implementation of CPU.hdl (which follows the book's advice) passes the test, but I'd like to understand better how it works. In particular, I'm confused about how the contents of the A-register changes when executing a program. For example, suppose that the computer is executing the following boring program:
What happens to the A-register as this program is executed? The load bit is asserted throughout, and:
at time 0: input=5
at time 1: input=6, contents=5.
at time 2: input=7, contents=6.
What is in? Just before time 1, the input is still 5. At time 1, the new instruction (@6) is coded in the instruction input and routed around the chip. This takes a (very short but) non-zero time. So just after time 1, the input is 6. At time 2, what is stored in the A-register? 6, not 5.
My question is: How do we guarantee this behaviour, physically? We know that, given load[t-1], out[t+1] = in[t]. How do we ensure that in=6 so that out=6 (latching on to the new input), rather than in=5 so that out=5 (latching on to the old input)? It seems like the behaviour would be extremely sensitive to how fast instructions are routed around the chip.
Perhaps the answer to this requires understanding the physical details of flip-flops. But is there anything that can be said without going into that?
Instructions "execute" (store their result in RAM/register) at the end of their clock period. During the clock period, the instruction is read from ROM, decoded, and the control signals generated. As you observed, it takes time for all that combinational logic to function. In the real world the control signals can be rather glitchy while that happens, but eventually they stabilize. The length of time this takes limits the maximum clock rate.
One of the hardware engineer's tasks is to determine what the worst case propagation delay is for instruction read and decode, and specify the maximum allowable clock rate.
Here's a break down of what needs to happen in what order for @6 to be decoded and executed:
1.0 A-register, D-register and PC are clocked. (@5 executed)
( A-reg 'in' should now be considered unstable.)
1.1 new PC value sent to ROM.
1.2 ROM sends 6 to CPU 'instruction'.
1.3 CPU sets control signals for A-register input mux 'sel' and A-register 'load'.
1.4 A-reg input mux sends 'instruction' (6) to A-reg 'in'.
(A-reg 'in' is now stable.)
2.0 A-register, D-register and PC are clocked. (6 written to A-reg)
Thank you! That was helpful. So, as I understand it, the source of my confusion was in thinking, wrongly, that what gets stored in a register at time t+1 is whatever its input was at time t; when in fact what gets stored in a register at time t+1 is whatever its input was at time t+0.9 (or thereabouts).
When we idealize away physical details like the time it takes for a signal to route around a chip, we can say that out[t+1] = in[t]. But the previous paragraph is a more accurate description of what goes on physically. Is that right?