概述:
重点是Memory (存储/内存 两种含义)

  • 存1位(Gated Latch 锁存器)
  • 存8位(Register 寄存器)
  • 16x16的矩阵存256位
    • 数据选择器/多路复用器(Multiplexer)解码8位地址,定位到单个锁存器
  • 4位代表行,4位代表列
  • 组合256位内存 + 多路复用器
  • 可寻址的256字节 内存
    • 一条1980年代的内存,1M大小
  • 8个模块,每个模块有32个小方块
    • 每个小方块有4个小块,每个小块是128位x64位

上篇教程,我们用逻辑门做了个简单的ALU,它能执行算术(Arithmetic)和逻辑(Logic)运算,ALU中的A和L因此得名。   当然算出来的结果,如果扔掉就么有什么意义了,得找个方法存起来,可能还要进行多个连续操作,这就用到计算机内存了。如果你在主机上打过一场长时间的对局,或玩困难模式的“扫雷”,然后狗跑过来了,被电源绊倒了,把插头拔了出来,你知道失去进度的痛苦。你损失数据的原因是电脑用的是“随机存取存储器”,简称“RAM”(Random Access Memory),它只能在有点的情况下存储东西,比如游戏状态。另一种存储(memory)叫持久存储,电源关闭时数据也不会丢失,它用来存其他东西。我们之后会讨论存储(memory)的持久性问题。
我们先从简单的开始,做只能存储1位的电路,之后扩大,做出我们的内存模块,下次和ALU结合起来,做出CPU。
我们到现在说过的电路都是单向的,总是向前流动,比如上篇的8位“脉动进位加法器”,但也可做回向电路,把输出连回输入,我们拿一个OR门试试,把输出连回输入,看看会发生什么。首先两个输入都设置为0,“0 or 0”是0,所以电路输出0,如果将A变成1,所以“1 or 0”是1,所以输出1.输出的1再输回B,or门看到两个输入都是1,“1 or 1”仍然为1,所以输出不变。如果将A变成0,or门依然输出1,现在我们有个电路能记录“1”,然而有个小问题:这个是永久的。无论怎么试,都无法从1变回0。
我们换and门看看会怎么样,开始时,A和B都设1,“1 and 1”结果永远输出1,如果之后,A设为0,由于and门,输出会变成0,这个电路能记录0,就像之前,无论A设置什么值,电路始终输出0,现在我们有了能存0和1的电路,为了做出有用的存储(memory),我们把两个电路结合起来,叫做“and-or锁存器”,它有两个输入,“设置”(set)输入1,把输出变1,“复位”输入1,把输出变成0(连接or门的是set输入,连接and门和not门的是rest输入)。如果“设置”和“复位”都是0,电路会输出最后放入的内容,也就是它存住了1位的信息。这叫做“锁存”,因为它“锁定”了一个值,放入数据的动作叫做“写入”,拿出数据的动作叫做“读取”,现在我们终于有办法存一个位了。麻烦的是,用两条线“设置”和“复位”来输入,有点难理解,为了更容易,我们希望只有一条输入线,将它设为0或1来存储,还需要一根线来“启用”(enable)内存,启用时允许写入,没有启用就“锁定”,这条线叫做“允许写入”(enable line),加一些额外逻辑门,可以做出这个电路。这叫做“门锁”(Gated Latch),因为门可以打开和关上。现在有些复杂了,我们不想关心单独的逻辑门,所以我们提升一层抽象,把“门锁”放到盒子里,这个盒子能存1个bit,我们来测试一下新的组件。
组件包括:数据输入线(data in),允许写入线(write enable),门锁(gated latch),数据输出线(data out)。
一切都从0开始,数据输入从0变成1,从1变成0,什么也不会发生,输出依然是0,这是因为“允许写入线”是关闭的,所以内存(memory)不会有任何改变。所以需要给“允许写入线”输入1,“打开”门,现在往“数据线”输入1,1就可以存起来了。现在又打开“允许写入线”,即“允许写入线”设为1,“数据线”设为0,门锁中的值会成为0,这样就完成了,“允许写入线”关闭,输出0,成功了。
当然,只能存1bit没什么大用,肯定玩儿不了游戏,或做其他事情。但我们没限制只能用一个锁存器,如果我们并排放8个锁存器,可以存8位信息,比如一个8bit数字。一组这样的锁存器叫“寄存器”(register),寄存器能存一个数字,这个数字有多少位,叫做“位宽”(width)。早期电脑使用8位寄存器,然后是16位,32位,如今许多计算机都有64位宽的寄存器。写入寄存器前,要先启用里面所有的锁存器,我们可以使用一根线连接所有锁存器的“允许输入线”,把它设为1,然后用8条数据线发数据,然后将“允许写入线”设回0,现在8位的值就存起来了。如果只有很少的位,把锁存器并排放置,也勉强够用了。64位寄存器需要64根数据线,64根线连到输出端,幸运的是,我们只要1根线启用所有锁存器,但加来也有64x2+1=129条线了。如果存256位,需要513条线。
解决这个问题的方法是矩阵。在矩阵中,我们不并排放锁存器,而是做成网格,存256位,我们用16x16网格的锁存器,有16行16列。要启用某个锁存器,就打开相应的行线和列线。放大看看怎么做的,我们只想打开交叉处锁存器的“允许写入线”,所有其他锁存器,保持关闭。我们可以使用and门,只有行线和列线均为1,and门才输出1,所以我们可以选择单个锁存器,这种行列排列法,用一根“允许写入线”连所有锁存器,为了让锁存器变成“允许写入”,行线和列线和“允许写入线”都必须是1,每次只有1个锁存器会这样。代表我们可以只用一根“数据线”连所有锁存器来传数据,因为只有一个锁存器会启用,只有那个会存数据。其他锁存器会忽略数据线上的值,因为没有“允许写入”。我们可以用类似的技巧,做“允许读取线”来读数据,从一个指定的锁存器,读取数据,所以对于256位的存储,值需要35条线,1条“数据线”,1条“允许写入线”,1条“允许读取线”,还有16行16列的线用于选择锁存器,这省了好多线。
但是我们需要某种方法来唯一指定交叉路口,我们可以想成城市,你可能想和某人在第12大道和第8街的交界碰面。这是一个交叉点的地址,我们刚刚存了一位的地址是“12行8列”,由于最多16行,用4位就够了,12用二进制表示是1100,列地址也可以这样:8用二进制表示为1000,刚才说的“12行8列”可以写成11001000,为了将地址转成行和列,我们需要“多路复用器”,多路复用器有不同大小,因为有 16行,我们需要1到16多路复用器,工作方式是,输入一个4位数字,它会把那根线,连到相应的输出线,如果输入0000,它会选择第一列,如果输入0001,它会选择下一列,以此类推。
一个多路复用器处理行,一个多路复用器处理列。
那么把256位内存当成一个整体好了,又提升了一层抽象。
256位内存:它输入一个8位地址:4位代表列,4位代表行,我们还需要“允许写入线”和“允许读取线”,最后我们需要一条数据线(data wire),用于读或写数据。
不幸的是,256位的内存也没法做什么事,所以还需扩大规模,把它们并排放置,就像寄存器,一行8个,可以存一个8位数字,8位也叫一个字节(byte),

https://www.bilibili.com/video/BV1EW411u7th?p=6