|
|
I've correctly implemented the chips mentioned in Chapter 5. I'm still unsure if I understand the CPU chip correctly, so I am hoping to get some confirmation.
Suppose we wanted to set the A register of our Hack computer to the value of D. Based on the CPU specification and a C-instruction `A=D`:
1. Since at the beginning of the instruction execution we do not have D from the ALU, our CPU would first try to set our A register to some superflous value (not from `instruction`, because the relevant bit is set to instead obtain the value from the ALU output).
2. After the second Mux, the ALU outputs the D value to the first Mux.
3. We've reached the end of our `.hdl` file, so now we are in `tock`. Our ALU output is stabilized, so we again use the relevant control bit to set the a register to the ALU output.
Am I understanding this operation correctly? Is there anything I'm missing?
|
Administrator
|
The CPU isn't going to try to write some superfluous value into the A-register. It will only attempt to change the contents of the A-register after the output of the ALU has had time to make it back to the A-register data input.
At the 'tick' event, the results from the machine state (registers, RAM, PC) are updated according to the PREVIOUS instruction. So unless the prior instruction wrote to the A-register, nothing will happen to it on the tick event. At the 'tock' event, all chip outputs are evaluated based on the machine state that was captured on the 'tick'. This is when the D input will be routed to the A-register input AND when the load signal on it will be asserted. Nothing further will happen until the following 'tick' event, at which time the A-register contents will be changed.
|
|
Thanks for your response. I'll try to summarize it here:
1. In our tick, the registers update their values based on the previous instruction.
2. In our tock, we calculate the new value of the ALU, and this value is sent to our registers. We determine which registers should be set to the new value based on the ddd bits, and we set them.
3. The new register values are emitted in the following tick.
Am I understanding this correctly?
|
Administrator
|
Sounds correct.
One thing to keep in mind is that "tick" and "tock" are merely artifacts of how the hardware simulator is implemented. The fine details of how an actual CPU behaves depends on the exact details of how the DFF is implemented. The author's description implies that it is negative-edge triggered, or possibly uses a master-slave arrangement in which the input is active-HI sensitive by the output is captured on the falling edge.
Both of these are at odds with the symbols they use for their parts, which indicates a positive-edge triggered device. This is possibly due to the authors being CS and not EE folks. It's also possible that this is simply a degree of detail that they treat pretty loosely because it's not germane to the course.
In practice, when using the commonly-used positive-edge triggered registers, at the rising edge the values at the input of each DFF is locked into the register and they then propagate through the combinatorial logic over the course of the rest of the clock cycle. The falling edge does nothing (at least, nothing external to the DFF implementation).
Looking at it in more detail, what is really happening is that the value at the input of each DFF must be stable for some amount of time before the rising clock edge (known as the setup time) and must remain stable for some amount of time after the rising clock edge (known as the hold time). This will be the case as long as the DFF holds the old value at it's output long enough after the rising edge to satisfy the hold time requirement for the DFFs that follow it. This is a function of the propagation delay from input to output of the DFF.
A consequence is that if the DFFs that are used are too fast -- the new value gets to the DFF output too quickly -- you can completely mess up the logic. I actually killed a chip design by switching to a faster DFF without verifying the timing (due to schedule and budget constraints imposed on us by the customer).
|
|
This post was updated on .
What happens in the case where there is no previous instruction? In terms of an A-instruction: would we (in this order)
1. First send the A-instruction value to the a register through the first multiplexer, and set the load bit to 1
2. Calculate a value in the ALU, and send it back to the multiplexer
3. Again send the A-instruction value to the A register
4. Emit the value of the a register, and perform some calculation based on the next instruction?
|
Administrator
|
There's always a prior instruction. The program counter ALWAYS has a value. That value is ALWAYS applied to the address lines of the ROM. The ROM ALWAYS presents what is in memory at that address to the instruction input of the CPU.
When you execute a A-type instruction, at the 'tick' that starts the clock cycle, ALL of the registers (A, D, PC, and RAM[A]) are loaded with the values at their inputs due to the prior instruction according to the state of their load signals from the prior instruction.
At the 'tock' part of the clock cycle, all of the combinatorial logic has been evaluated based on the current instruction and the new contents of the registers, but no registers change yet. These signals then just sit there for the remainder of the current clock cycle and the registers are updated with the 'tick' that starts the next clock cycle.
|
|
This post was updated on .
Okay. I think there was a misunderstanding in regards to the meaning of "prior instructions".
When you used that term, I had assumed that by "prior instruction", you simply meant, "the program instruction in the ROM that has the previous address from the one we are evaluating now". If that were the case, there would be no "prior instruction" if we were operating on the value at address 0 (assuming there were no jumps to that point). Reading your latest comment, I think you were referring more generally to any instruction that relates to the CPU.
So, suppose our first program instruction was to set the A-register to a constant value. The FIRST instruction would be to get the program counter value and send it to the ROM. The SECOND instruction would be to get the value at that supplied address from the ROM and output it to the CPU. Our THIRD instruction, which is based on the previous two instructions, is to use the Mux16 to send this constant value to the a-register, and the FOURTH instruction is to load it, based on the leftmost bit in our program instruction (which would be from the same program instruction as our third step).
If we were to use a C-instruction on the A-register, our FIRST and SECOND instructions would be the same. Our THIRD instruction would get the ALU output value that was inputted to the Mux16 and route it to the a register, and the FOURTH instruction would be to load that value into the a register, based on the destination bits in our program instruction.
|
Administrator
|
By "prior instruction", I meant the instruction that was EXECUTED before the current one, regardless of where it is in memory.
When the program first starts up, the situation is ambiguous. In the simulator, registers and RAM and other things can be initialized in a few different ways. I'm not sure how this simulator does it (or whether the various N2T simulators do it consistently). For well-written programs, it doesn't matter.
In the real world, most systems power up in a random state. The registers have random values and the RAM has random values. But there is almost always some king of power-on reset that forces certain things to happen so that well-written code can start up reliably.
In the Hack, the RESET input forces the PC to be zero and will hold it there regardless of anything else that happens until the RESET is released. So the instruction at ROM[0] will be executed over and over again during this period, but no jump can happen (because the PC is forced to be zero). Once RESET is released, the first instruction is executed again and now the PC can change under the control of the program.
I don't know what you are saying as for as first, and second and third instruction when you want to set the A-register to a constant value. This only involves a single instruction, such as
@42
I don't have time right now, but when I get a chance I'll put together a little timing diagram that I think is better than the one the author uses and will post that.
|
|
This post was updated on .
Thanks for your comments. I took a break to read and work a bit on the Chapter 6 project and then I came back to this. I think I have a good understanding on what you're stating; would you be able to run through my reasoning to make sure?
Suppose the first program instruction is @17. The program counter's value is 0, which is sent to the ROM. The ROM outputs the aforementioned program instruction to the CPU.
In the CPU, the Mux16 outputs the program instruction to the a register, and loads it based on the leftmost instruction bit. This value is sent to the ROM, which outputs the m value at the corresponding a-value address and sends it to inM in the CPU. The PC is incremented and the CPU gets the next instruction.
Suppose our next instruction is to set D=A. Since the ALU hasn't calculated a value yet for this instruction, the leftmost Mux16 doesn't have anything to output to the a-register; thus, we begin by outputting the value of that register. Our A register outputs its value set in the PREVIOUS instruction, and the relevant bit in our instruction outputs the A register value to the ALU. The ALU outputs A, which is loaded into D. Our new D value is outputted on the next tick. The PC is incremented and the CPU gets the next instruction.
Suppose, then, our next instruction is A=D. Again, we start by outputting the a register. When we get to the ALU, it outputs the D register, which is sent to the leftmost Mux16. The Mux16 outputs this value to the A-register, and the value is set on the following tick.
|
Administrator
|
eatmorepies wrote
Thanks for your comments. I took a break to read and work a bit on the Chapter 6 project and then I came back to this. I think I have a good understanding on what you're stating; would you be able to run through my reasoning to make sure?
Suppose the first program instruction is @17. The program counter's value is 0, which is sent to the ROM. The ROM outputs the aforementioned program instruction to the CPU.
So far so good.
In the CPU, the Mux16 outputs the program instruction to the a register, and loads it based on the leftmost instruction bit.
Assuming by "the Mux16" you mean the Mux that feeds the A-register, they yes. Although the A-register doesn't change until the next clock happens.
This value is sent to the ROM, which outputs the m value at the corresponding a-value address and sends it to inM in the CPU.
This is off the rails. No value is sent to the ROM (remember, the ROM is where the program instructions are stored). The output of the A-register (which haven't changed yet, in this case) ALWAYS go to the address lines of the RAM and the value at that RAM location ALWAYS come in to one side of the Mux16 that feeds the Y input of the ALU. But, nothing happens with that value unless the control bits in the instruction cause something to be written somewhere. But since this is an A-type instruction, the ONLY write that happens (on the next clock edge) is the writing of the instruction to the A-register.
The PC is incremented and the CPU gets the next instruction.
Yes.
Suppose our next instruction is to set D=A. Since the ALU hasn't calculated a value yet for this instruction, the leftmost Mux16 doesn't have anything to output to the a-register; thus, we begin by outputting the value of that register. Our A register outputs its value set in the PREVIOUS instruction, and the relevant bit in our instruction outputs the A register value to the ALU. The ALU outputs A, which is loaded into D. Our new D value is outputted on the next tick. The PC is incremented and the CPU gets the next instruction.
Essentially correct, but the part about something happen "since the ALU hasn't calculated a value yet" is off base. At the beginning of this clock cycle, the value 17 from the prior clock cycle is actually written to the A-register. At this same time, the control bits in the new instruction configure the ALU and the Muxes so that they route the value from the A-register through the ALU. The output of the ALU is ALWAYS applied to the input of the D-register, but it will only get written (at the beginning of the next instruction) if the current instruction's control bits tell it to.
Suppose, then, our next instruction is A=D. Again, we start by outputting the a register. When we get to the ALU, it outputs the D register, which is sent to the leftmost Mux16. The Mux16 outputs this value to the A-register, and the value is set on the following tick.
The "we start by outputting the a register" part is misleading. Again, the A-register's output value is ALWAYS applied to the top input of the Mux that feeds the Y input of the ALU, it ALWAYS goes out to the address bus of the RAM, and it ALWAYS goes to the input of the PC. In the case of A=D, none of this matters because the control bits don't result in any of these having any effect on anything.
|
|
This post was updated on .
Shoot, I had meant to write RAM instead of ROM in regards to where the a-register value is sent.
Instructions persist in ROM, program data is stored in RAM. inM gets a value from RAM based on the address that was supplied to the RAM in the previous instruction. Values are supplied to RAM given the CPU's outM and addressM values, and are written depending on the current value of writeM.
I understand what you mean concerning the different chips the A register value is outputted to; the CPU wouldn't be able to save and load data from the RAM if that value wasn't outputted to addressM, nor would jumps work if we did not supply the PC with an address to jump to. That is what I had in mind, but I should have been more explicit in my previous comment.
Thanks for your help.
|
|