hello everyone and welcome back to the series where we've been exploring some basics of how computers work our efforts thus far have culminated in this little setup here containing two 4-bit registers each able to remember a single number and so for example we could store the number 15 in the first one and let's say 10 in the second one then using our alou we're able to take those two values and do any calculation we want so long as it's addition or subtraction so I'll set it up to do 15 minus 10 and then if we pulse the clock we can see the answer now stored over here that's obviously incredibly exciting but there's still some ways to go before we could reasonably call this a general purpose computer and problematically we're already rather running out of room in this little simulation program so I've been gradually tinkering away on a new version that's at last ready to be put to the test today as you can see it looks pretty similar but there are some important differences for example the input pins are now standalone elements rather than being tethered to the side of the screen which means that we can now zoom in and out and roam around to our heart's content plenty of space to build a computer the input pins by the way now also come in 4-bit and even 8- bit varieties so that we don't need to manually manage all those individual wires which I hope will make our lives a bit less tedious as we try to build more elaborate things all right the first elaborate thing I'd like to try tackling today is the main memory of our computer because while these individual registers are great for remembering single values that are being worked with right now we're going to want something a lot larger where we can store the results of our various calculations and access them later as the need arises so let's crack open one of these registers just to remind ourselves of what's inside and to no great surprise it simply contains four one-bit registers we could certainly build our main memory out of these but they contain a bunch of logic mainly for handling synchronization with the clock signal that we don't really want to be duplicating over and over for every bit of memory it would just be kind of wasteful and slow I think but we've now arrived at our fundamental building block of memory that we constructed from a pair of norates where each output feeds back into the input of the other to keep a single bit of data locked firmly in place so I think our plan should be essentially to just create a giant grid of these little latches that we can activate individually based on a given memory address let's give that a try and we should probably start somewhat small with just a 4x4 grid perhaps and that means that we'd need a 4bit address to cover all 16 latches we could think of the bottom two address bits as specifying the row and the other two bits as specifying the column so this bottom left latch for example we could say should only be active if both the row and column address bits are zero and the latch next to it should only be active if the row is zero and the column is one and so on and so forth okay let's quickly create a new chip to help us out with this address detection so this will have a two- bit input let's imagine those are the column bits of the address and we wanted to simply decode that into four individual output bits that indicate whether column 0 is active or column 1 column 2 or column 3 i think that should be easy enough we can just plop in four and gates over here and wire these up to each of the outputs then to detect an input of zero we simply need to test for both input bits being zero so let's quickly invert both of the inputs then wire them up to the first and gate and we can of course see output zero now lighting up even easier than this is detecting an input of three since we just need to know if both inputs are on and so we can wire them up to this and gate directly all right let's then handle the detection of an input of two which just means that the first bit must be off and the second bit must be on finally to detect an input of one it's obviously the other way around the first bit is on and the second bit is off okay let's save this little decoder chip and I'll specify in the name that it's decoding a two bit input in particular just for fun here's a real life version of the chip we just built so currently zero is selected then one two and three all right now going back to our grid of latches let me move these along to give us some space here quickly and then we can bring in two of our new decoder chips one for the columns and one for the rows and of course we'll want to feed the memory address input into these two decoders then as we were talking about earlier we can have an AND gate over here that just says that if row zero is enabled and column Z is enabled then this bottom left latch will be open for business it's going to be a bit annoying to add in and gates for every latch though so let's actually make another little chip to help us out here i've set this up with the same data and store inputs as the latch but then also these new row and column inputs to make use of these let's add in a latch and an endgate actually I suppose we'll need two of them the first and gate tests if the row and column are both active meaning that this latch has been selected and only if that's the case and the store signal is on will we want to write the input data into the latch we also only want to output the data from the latch if it is selected otherwise we just want it to be essentially disconnected and in this simulation we can do that using the three-state buffer that we've talked about previously so let me just grab that from the chip library quickly and wire it up all right so we can see that when the latch is not selected the output signal here is just black indicating that it's outputting neither a high logic level nor a low logic level but rather it's just completely disconnected and only if both the row and column inputs are on will it actually output the state of the latch so currently that happens to be high the initial state is just random but we can try storing a zero in there instead and now we can see that the output is low okay let's give this little sort of one bit memory cell a name maybe MEM1 for short i quickly want to mention by the way that real world memory cells are extremely optimized of course using as few as six transistors and many clever ideas to make the whole thing as small and reliable as possible we would need a much more advanced simulation to delve into all of that but I hope to get there someday also this latchbased approach is actually only used for fairly small memory chips like a handful of megabytes at most because while it's very fast it takes up quite a lot of physical space so memory in the realm of gigabytes is typically constructed out of capacitors instead though somewhat smaller ones than I'm holding here capacitors store electrical charge so if we plug this in currently it's empty which we can represent as a zero but if we send some power its way it will quickly charge up and we can call that a one then when the input goes away the charge remains so it's remembering a single bit of information am I charged or not charged what's a bit tricky though is that if we want to read the data then the capacitor will be discharged meaning that the memory is lost and the charge also annoyingly just leaks away over time and we're talking fractions of a second at tiny scales so this kind of memory requires some extra circuitry to constantly read and rewrite its entire contents to keep it nice and fresh that sounds a little ridiculous but the fact that one bit of memory can be stored with just a single capacitor and one transistor to enable or disable the output makes it very compact this by the way is known as dynamic memory due to the constant refreshing whereas our latchbased approach is called static memory also while we're on this brief terminology tangent I want to add that both of these require a constant source of power of course otherwise the data is lost and so they're examples of volatile memory to remember stuff even when powered down the data has to be transferred to some slower kind of persistent memory so that could be for example one of these old spinning discs with this little arm that swings out and is able to store data longterm by changing the magnetic polarity of tiny regions on the disc anyway I should stop rambling and let's return instead to the simplicity of our simulation where we have our column and row decoders set up already but now we're going to create a grid of those memory cells we made and let's wire these all up so we've got to get all the row and column bits connected to each chip while doing our best to keep things at least reasonably tidy okay that's the rows finished and now for the columns with that done let's give it a quick test so just focusing on the first two address bits that seems to be selecting the rows correctly and then let's toggle the last two bits and that does seem to be selecting the columns correctly as well so next up we of course need a data input and that also needs to be wired up to every cell then we'll need a store input but we should think ahead a little bit here because to expand on this we'll want this little grid of 16 cells to itself become a cell in a larger grid so this actually needs its own row and column inputs and let's be diligent about coloring these consistently and then we can bring in a pair of ant gates and set them up just like in the first cell we made and with that done we can hook up the store signal okay that's all the inputs handled but we still need to collect the outputs from all of the cells and we've made things easy for ourselves by ensuring that only one cell can be actively outputting its data at any time so we can simply run a little wire through here to directly join together all of the outputs and finally like before we'll again want the output to only be enabled if this cell is selected all right let's do a quick test to see if this thing is actually working so first of all we can maybe try storing a zero at the current address and then turn off the store signal we can see that it is indeed outputting zero now and we could turn the data signal back on but it doesn't do anything of course unless we tell it to actually store that data again all right let's also flick through a bunch of different addresses and we can notice the output changing since the startup state of each latch is random as we've seen before and finally let's try disabling one or both of the row and column inputs here and if either of them are off then the whole cell becomes inactive it's not outputting anything and our store signal doesn't do anything either oh it looks like we finished just in time for a surprise inspection uh okay if you have a look over here you'll see the address bits never mind apparently my water is far more interesting okay I don't think you should be drinking on the job so time for the inspection at last some headbutts some sniffing all standard testing procedure of course and a little bribery to end with just in case there are some design flaws that require overlooking all right he says it's perfect so let's give this thing a name and since it's made out of 16 memory cells I guess we can just call it ME16 all right the time has now come to take this new cell and construct another 4x4 grid we'll again need a 4bit address to be able to select each of these 16 cells plus another four address bits on top of that of course for the address input of each cell so let's bring in an 8 bit input for our address here and split that into two 4-bit values one going to the address input of the cells and the other being split up and decoded into the column and row bits as before then let's hook up those column lines quickly and the row lines as well we'll then also want a data and store signal but I'm not planning to make another grid out of this new cell so we can leave out the row and column inputs this time and instead just go ahead and wire the two of these up so that's data done and now we can do the store signal finally we can just join all the outputs together on a single bit line like before since again only one of those outputs will be active at any time okay that is done so the top four address bits select one of the 16 bit memory cells in our grid so we can see that this one here is selected at the moment and then the bottom four address bits determine which of the one bit cells will be selected inside of there so in total we're able to address 16 * 16 which is to say 256 individual bits of data let's call this then me 256 256 bits is not super helpful though because obviously we typically want to store numbers not single bits of data so let's take this latest memory cell and just duplicate it eight times to give us 256 bytes of data instead and we can add in an 8bit output pin and just connect the outputs of all the cells up to that it would also be nice to have a decimal display of the output value so let's quickly grab one from the library here and then wire that up as well as we can see the current value happens to be 197 this display is something we built in a previous video you might recall if you've seen that episode with all this double double stuff and binary to decimal decoding it was a surprisingly long and intricate journey just to display some numbers but because it's a bit of a pain I have also by the way added in a somewhat lazier option of just automatically displaying the decimal value under the pins so you don't have to suffer through all that if you're building this yourself anyway let's get a move on with the inputs we'll have our store signal as always and an 8bit address and data input by the way I've decided to color this incoming data in purple this time rather than red like we've done before just to help differentiate it from the output data here all right let's wire up the memory address next and finally the store signal with that we at last have our 256 bytes of addressable memory so let's flick through some addresses just to see the different values that happen to be stored in there at the moment and of course we can overwrite them with whatever data we want so at address 255 let's store a value of 150 for example and then to test this a little let's go to some other address and store a different value in there now we can try returning to the previous address and sure enough our old value is still safe and sound just for fun again here is a real life version of our chip more or less anyway so I'll plug this in here and then let's just mess about with it for a moment so here's address 1 3 7 and 15 and let's store a zero in there all right that seems to work so let's head over to some other address here's address 14 and we can store maybe a value of 12 in there then let's go back to address 15 where we can see our zero still waiting patiently for us and let's just try overwriting that with some other value all right it's working nicely what's great about this type of memory is that at least if you're not fiddling with the switches by hand it takes the same amount of time to jump from one address to any other which is in contrast of course to something like the hard drive disc we briefly considered earlier which has to physically spin and swing its little arm around meaning it can take shorter or longer depending on where it needs to go but because the order of access doesn't matter for our memory here it's an example of random access memory or of course RAM for short having said that some programmers nevertheless do spend a lot of time worrying about the order in which they're requesting data from it and that's because computers typically have a large amount of relatively slow dynamic RAM which is that memory made from capacitors we talked about before but then also a much smaller amount of this very fast static RAM called the cache snuggled right up close to the CPU to minimize the commute when some data is requested the computer first goes to the cache to see if it's already in there by any chance and if not it has to go all the way to the slower main RAM to fetch it on the journey back it optimistically lugs along a load of surrounding data which it plops into the cache the hope being of course that the next memory request will be at a nearby address so it can grab the data from the cache then to save itself another long road trip we're not going to worry about any of this in our little simulation but I thought it was worth just briefly mentioning anyway all right we're almost done for today but there is one thing bothering me about our RAM chip here which is that it's not synchronized with a clock this means that we have to be very careful that the store signal is never on while we're busy changing the address value because otherwise we'll be overwriting data willy-nilly that is easy enough to avoid while we're doing everything by hand but it can introduce annoying race conditions once these inputs are instead being controlled by other parts of the computer so I'm going to try and just hack something together quickly to address the issue okay firstly I've made now this 8- bit register just out of eight one-bit registers since we've built that nice clock synchronization into these just as a reminder the idea is that new data can only be stored into the register in the brief moment where the clock signal changes from low to high altering the inputs when the clock signal is already high does nothing and also when it's low nothing can happen of course it's again only on the so-called rising edge of the clock that changes to the inputs take effect letting us keep everything nicely synchronized my plan now is to take our asynchronous RAM and just interact with it through these two synchronous registers for the address and data so let's wire up the outputs of those as the inputs to the RAM chip and then we should connect up the store signal here as well next let's set up the inputs for the registers and the data register should only be updated if the store signal is on but the address can be updated either way so we can just set its store input to always be on finally let's add in an output pin and a decimal display all right let's give that a try so if we set some data over here and alter the address even though the store signal is already on the RAM is getting its input from the registers and so it's not affected until we trigger the clock that's great although it's worth noting that this would be risky in the real world since signals take time to settle into a stable state so for instance in the brief moment where the output of the address register has not quite stabilized yet if the store signal is already on we could be in trouble a solution to that might be to write the store signal itself into a register along with perhaps some additional logic that delays it by one clock cycle to ensure that the address has had time to settle down thankfully the simulation is a lot more forgiving in terms of timing though and we can safely assume here that our signals will all arrive ready at the RAM chip at the same instant one slight annoyance that remains however is that if we're simply retrieving data from RAM let's say from address 51 for example it's a bit bothersome to have to wait for the next clock pulse before RAM will actually see that new address so let's try instead just hooking the address input up to RAM directly and then since we don't need to worry about precise timing considerations we can simply add in some logic that says that it's now only able to store data if both the store signals on of course and the input address matches the address in the register that way the RAM's output is always based on the most upto-date address value but it will only store data in the clock synchronized address okay we should try this out but this 8-bit equality chip over here I've set up the inputs and outputs already but we still need to implement the actual logic first though let's quickly make one little helper chip that simply tests if two one bit values are the same and we actually already have something very close the exclusive or which tests if two bits are different so we can just invert the result of that to make an exclusive norate where the output is only high if both inputs are the same we can then easily scale it up to 8 bit inputs by just using eight copies of the XNO gate along with a bunch of AND gates to check that the result is unanimous then let's just make sure that this is doing what we want so if we change one input we can see that the output has turned off and then if we change the other input to match it the output turns back on again all right with that done our synchronous RAM should hopefully be working let's give it a quick test i'll start by just storing a zero into address zero then let's flip through some different addresses and we can see that the output is updating right away but even though the store signal is still on it's not overwriting the data in those addresses thanks to our little address test here okay let's try setting some more data maybe we want to store the value 42 into address 85 so we can pulse the clock to write that in then let's go back to address zero and make sure our value of zero is still stored in there which it is and maybe we can try changing the data input turning off the store signal and then running the clock or changing the store signal without running the clock and in both cases the memory is unaffected as it should be all right let's finally return to address 85 and we can see that our value of 42 is still safe and sound over there so at long last we have some nicely behaved random access memory that we can use in our little simulated computer okay to end with let's take a brief trip down memory lane to see how far we've come so we can take a look inside of the asynchronous RAM with its eight cells each storing 256 bits and we can peek inside one of those to see our grid of 16 cells each storing 16 bits of data this cell over here is selected for the current address so let's continue the journey down into there where we can see yet another grid of 16 cells this time storing just one bit each again this one is selected so let's look in there and inside this cell we at last find our humble latch which we can open up to see the single bit of data that it's holding on to over here so it's been quite a journey to get to this point but still a long ways ahead of us of course that is all for today though so thanks for watching and for all the support from patrons of the channel until next time cheers
2025-04-14 13:57