The bulk of the program for the computer will be stored in ROM, or Read-Only Memory. We chose to use ROM because it tends to be more compact than RAM, and the speed of memory access will be one of the bottlenecks of our computer (ROM will be massive, like 1000s of tiles).
We chose to use grid-based ROM. This means that data is stored in a grid, and the value of a particular row is accessed, sending down one bit per column of data. In order to do this, we needed two types of data cells:
- A “false” bit will be a gate which passes the read signal through and the data signal through without affecting either of them.
- A “true” bit will pass the read and data signals through, but will also output a 1 for the data signal if it receives a read signal.
It turns out a false bit is simply a wire crossing. The true bit requires more work. I had a sudden realization, that the “true” gate never has to deal with both signals coming in at once, since we are either reading the current row or some other row, but never both at the same time (which would mix the data together). So, a “true” gate is allowed to malfunction for that input combination. This is what I came up with:
The idea here is to line a bunch of these up, so that a single “read” signal reads out the entire word. By lining up the data wires, we allow for the data from different addresses to be passed down the grid.
Okay, so we have the data grid figured out. Next is addressing. Addressing is also done with a grid, but a different kind of grid. We have an address signal (each column receives 1 bit of the address) and a read signal (sent across each row to test that combination of bits for matching). A “false” gate allows the address bit through, but only allows the read signal through if the address bit is 0. A “true” gate allows the address bit through, but only allows the read signal through when the address bit is 1. I created two 1×2 multi-tiles to accomplish this.
The basic concept is that we line a lot of these addressing gates side-to-side (sharing the read line), and then the read signal can only get through all of them if the address matches at each bit. Then, that read signal goes on to access the data from that row from the data grid. The addressing gates are stacked vertically so the desired address is tested against each row of gates.
Parallel to Serial
So, we have data that is coming off of the data grid in parallel, how to we convert it to serial for use by the computer? It is actually very easy. The data is coming off in parallel, but isn’t synchronized horizontally, since it is produced as the read signal moves right-to-left. It turns out that to convert this to serial, all we have to do is OR the signals left-to-right (in the opposite direction) and the spacing between signals is automatically twice the width of the columns, which is exactly the separation we need.
Serial to Parallel
Much more difficult is the serial to parallel conversion we need for addressing. Performing a “true” conversion would be rather difficult, since we have to have timing circuits that are capable of waiting and selecting each bit to move down each wire. It turns out we can cheat. We can send the entire signal down each line, and the read signal for each row will take a “cross-section” of this signal which corresponds to the “correct” value of each bit. There’s a little more to this, though. Since the address signal starts going up each column in right-to-left order, and the read signal also travels in this direction, it would mean that our cross-section is skewed, and it will interact with the same bit (the last bit) of the address all the way across. To correct for this, we slow down the address with a 33:11 delay between each column, so that it enters the next column 33 ticks later instead of 11 ticks. This means that the read signal interacts with the last, then second-to-last, etc., then first bit of the address, which gives us the cross-section we want.
Putting Everything Together
Here is a 4-address, 3-bit-word ROM I constructed:
The table that represents this ROM is as follows, with data (appears big-endian, but is read off in reverse) on the left and address on the right.
1 0 1 | 1 1 (5|3) 0 1 0 | 0 1 (2|2) 0 0 1 | 1 0 (1|1) 1 1 1 | 0 0 (7|0)
The “read” signal that is send alongside the address should be 11 ticks behind (right-to-left) of the last bit in the address. It is possible to send the signal earlier but have delay wire that delays from entering the ROM it until after the address.
ROM in Practice
The ROM as I have it above is different than the exact configuration that we will eventually use. For example, the real version will probably have 16 address bits and who-knows-how-many bits of data. The main difference might be in the timing. The version above is faster to read from smaller addresses than larger ones, 22 ticks per address faster. This is both good and bad, because our computer wouldn’t be slowed unnecessarily when the wanted data is nearby, but it will have to deal with data coming back at a variable time. There are two solutions:
- Return the read signal with the data to indicate when the data has been read. Then, other circuitry needs to be developed that can take data in at variable times and perform the operation once every piece of data is received, whenever that may be.
- Reverse the order of the data grid, so that the data comes out the opposite side from where the data comes in (as opposed to the same side like above). Then, the data wire will have to come from the far end back to the computer. This effectively slows every read to the same speed.