The material for week 2 and 3 of NAND2Tetris has been almost disappointingly easy so far. My design for the first two RAM chips worked on the first try yesterday! Today I’ll work on implementing the other chips while I’m hanging out at the Southern California Linux Expo (SCaLE). (This first day of the conference is sort of a slow day, so I have some time to kill in between events.)
Even more bigger RAM chips!
I already built an 8-register RAM and a 64-register RAM. Next up, a 512-register RAM!
The structure is the same, but now I’m using a 9-bit address input. I need to do a bit of quick math: how many 64-register RAMs does it take to build a 512-register RAM? OK, yup, 8 of them. So the design should be exactly the same but now with yet another step to break up the address input – now into three parts! But this should still be pretty easy. It’ll just take a bit of time to type up all the HDL and make sure I don’t make any typos.
OK, I did make a couple typos but my intended design for the 512-register RAM did work on the first try. Yay!
Now I rinse and repeat for the 4096-register RAM… Success!
And once again, I rinse and repeat for the 16384-register RAM… This tedious exercise really makes me appreciate exponential growth, haha.
Uh oh, an error?! Oh, OK, I got lazy and assumed I would use the same design but then I realized that the 16K RAM is only 4 times larger than the 4K RAM, not 8 times larger like all the other designs. Oh, arithmetic! Arithmetic matters, haha. OK, easy fix. I need to change all my internal parts from 8-way chips to 4-way chips, but that’s easy. Oh, and I had to reconfigure the way I split up the address input, but that’s also easy.
And let’s test it out… Success! Wow, 16K of RAM is a lot of numbers! Whew! I’d love to see a visualization (or a Minecraft build) to appreciate just how big this thing is!
Designing my first program counter
I never completely understood what a program counter actually is! Oops. When I read about it in chapter 3 of the course book the other day, I settled on this initial definition:
Program counters contain the addresses of the instruction to be run by the computer, fetching the next instructions after each clock cycle. Or something like that…
Now I’m actually going to look at the chip specification for the 16-bit program counter that I’m supposed to build. It has one 16-bit output and four inputs: the 16-bit data input and three single-bit inputs: the load bit, “inc” bit, and reset bit.
What my program counter needs to do:
- If the load bit is set to 1, it should save the input data in its memory and spit it back out as its next output.
- If the “inc” bit (short for “increment”) is set to 1, then it should add 1 to whatever number is saved in its memory and spit out that new number as its next output.
- If the rest bit is set to 1, it should save 0 in its memory and spit that out as its next output.
And that – nothing more, nothing less – is what a program counter does! OK, so I understand that now. But why does it do that? My understanding is that the program counter is what fetches a series of instructions, one after another, for the computer to actually run its programs! That makes it an awfully important piece of the puzzle!
Some time later: I was a bit bored during one of the conference talks today, so I sketched an initial design for this program counter. Now let’s run it! Fingers crossed…
Aw, so close! But my design isn’t quite right. I mistakenly assumed that it should only increment the number stored in the register if the load bit is set to 1, but that isn’t what the specifications specified! Oops! OK, back to the drawing board.
A few minutes later: My design was all backwards! I reversed the order of my chips and now it looks right. Let’s try it out! Aw, it’s still broken. Hmm, what did I do wrong… Oh, there should be a delay between when the increment bit is set to 1 and when the output actually reflects the change. Mine is updating on the same clock cycle, which doesn’t jive with the specifications.
OK, so how do I fix this? How do I add a delay? Hmm. Should my register chip be first or last in this design? My gut tells me it should be last, like my original design, because the register is what creates the delay and stores the data. If I want to change some data, store it, and retrieve it later, it makes sense that the storage chip should be the last part of my little assembly line.
The tricky part is that the register will only save the changed data if the load bit is set to 1, but the program counter specifies that the data should be saved regardless of the load bit! I’m tempted to think that I’m supposed to build a new memory chip out of flip-flops, but the course emphasizes repeatedly that I’m generally supposed to build the larger chips out of the chips I built before. So I’m inclined to believe that there must be a way to implement this with a register. There must be!
I need to go to my next event soon, so I’ll just let my brain chew on that while I practice socializing.
Learning summary (#TIL)
- I successfully designed my first RAM chip and then chained a bunch of them together to build a 16-bit 64-register RAM!
Next steps
- Finish the third project by designing even larger RAM chips and a 16-bit program counter.
Time breakdown
- Study time: 1 hour 11 min
- Building third project: 1 hour 11 min