本文作为上一篇文章《 【冬瓜哥手绘】上/下页、快/慢页、MSB/LSB都些什么鬼? 》的补充。由于上一篇文章冬瓜哥在写的时候比较急,有两个地方忘了补充,冬瓜哥早晨起来就总感觉缺了点什么,遂写了此文。冬瓜哥在本文中的知识体系得到了Ron@Memblaze同学的支持,以及赵登涛、Ray@Micron、Zac@Ramaxel的帮助。表示感谢!
1. MSB/LSB的全称是什么。冬瓜哥一般从不在文章中留任何坑或者让人看了迷茫的字眼,上篇文章中竟然忘了解释。MSB就是Most Significant Bit,LSB则是Last Significant Bit。这里的Significant并不是“最重要的,最关键的“意思,而是”有意义的,有效的“。某些场景下,比如某个寄存器为8位寄存器,但是程序没有这么多数据要放,用不了8位,只能用到6位,此时MSB就是5,LSB就是0了。对于MLC Cell来讲,2bit当然是用满的。
2. 对Upper Page和Lower Page写入数据的时候,必须按照顺序。这里面的各种顺序比较复杂,需要将每个场景进行一步步的推演。我们从最原始状态开始,也就是该Page所在的Block被擦除之后,该Page的该Cell处于E态,也就是上1下1的状态(关于E、D1、D2、D3态的定义请参考上篇文章)。
场景A:应用要对该cell承载的upper page中的该cell位(也就是MSB)写1。主控将该命令发送给Flash芯片之后,Flash芯片对该Cell不做任何动作,因为upper page本来就是1,所以该cell仍处于E态。但是主控端在元数据中会标明该page已经被写过,后续不能再被覆盖写入,除非擦掉。
场景B:应用要对该cell承载的upper page中的该cell位(也就是MSB)写0。主控从host端收到该IO之后,按照上图所示,应该是将该cell直接充电到D3态,上0下1。但是我们说过,如果直接到这个状态,就不能再回退了,也就是该cell所承载的lower page的该cell就不能再写成0了(不能退回到D2态,不能单bit放电)。所以,上一篇文章中也说过,此时主控必须改 [物理-逻辑] 映射表,让lower page先顶上,也就是说,主控其实会把该page该cell的LSB进行写0操作,也就是将cell充电到D1态。
场景C:该cell处于原始态,然后 用要对该cell承载的lower page中的该cell位(也就是LSB)写0。主控从host端收到该IO之后,按照上图所示,应该是将该cell直接充电到D1态,上1下0。
场景D:基于场景C的结果,也就是D1态,此后应用如果想将该cell的MSB写为1,则Flash无需动作,因为本来就是1。这里一定要成分理解一点: Flash颗粒内部的控制逻辑是根本不知道某个Cell当前处于什么状态的,主控让它写1,Flash并不知道是从哪个状态到哪个状态,但是写1是很特殊的场景,写1无需动作,默认就是1,不管当前是E态还是D1态 。 有人可能有疑问,如果当前是D2或者D3态,然后想把MSB写成1怎么办?答案是不可能出现这种情况。因为D2/D3态表明upper page之前已经被写过一遍了,主控不可能或者说必须不能在该page所在block没有被擦除之前再写一遍该page 。 不管是写了1还是写了0,每个page在再次被擦除之前只能被写一次,写1无需动作,写0就得充电。好,可以看到场景D的结果就是该cell在被下次擦除之前,将永远处于D1态,因为MSB和LSB都被写了,只不过LSB被写了0,MSB被写了1。
场景E: 基于场景C的结果,也就是D1态, 此后应用如果想将该cell的MSB写为0,那么Flash在收到主控发过来的命令之后,理论上应该是直接充电到D2态就对了。但是可以看到,D3态的MSB也是0,那么是否可以充电到D3态?绝对不能,因为LSB之前被写了0,如果充到D3态,则LSB之前所保存的数据就会丢失。是这么个道理,但是如果假设,之前该cell的LSB是被写了1的,当时Flash没有任何动作,默认就是1,仍然为E态,然后再将MSB写0,那么此时Flash应该对该cell充电到什么状态呢?那就必须充电到D3态了。也就是说, FLash芯片必须明确的知道“该cell的LSB在上一次被写入时是写的0还是1”这件事情,从而才能在后续的MSB写0过程中决定充电到D1还是D3态。 但是上文中提到过,Flash芯片并不知道该cell当前处于什么状态,也不知道该cell中的LSB之前是不是已经被写过了,如果被写过,写成了1还是写成了0,都不知道。 那怎么办呢?两个办法,要么就是主控需要告诉Flash芯片这个信息,要么就需要Flash根据当前cell所处的状态来判断该cell的LSB之前到底是被写了1还是0,也就是说,Flash先将该Cell的电压值读出(按照前一篇文章中的二分法),判断,然后再充电到该有的状态。这里我们就总结一下这个过程的判断逻辑:
可以看到,对MSB写0时,Flash产生了写惩罚,需要先读出当前的状态。而对LSB写0则不会出现这种情况。有人会问:D1和D2态的LSB也都是0,如果对LSB写0,Flash如何判断充电到D1还是D2?答案是只能充电到D1,因为大家事先已经约定好了,擦除之后,主控必须先对LSB写0。擦除之后,如果先对MSB写了1,仍然处于E态,再对LSB写0则过渡到D1态;擦除后,对LSB写1,则主控不可能或者必须不能再对其写0,因为每个page只能写一遍;擦除后,对LSB写0,也是过渡到D1态,所以上述三种组合的结果都是D1态,那么就无需判断了,只要收到让LSB写0的指令,直接到D1,并没有写惩罚。
那么能否让主控在发送IO指令时直接告诉flash这些信息,从而避免多读一次?不现实,如果为每个cell增加状态描述,会极大扩充所需传输的数据量。另外,根据Ron@Memblaze同学的观点,多读这一次不算什么,真正慢是慢在对cell充电的过程并不是一步到位的,而是分多次充电,一次充一点,小步进。这样做的原因是因为一旦过充就无法回头,为了降低过充的几率,才采取这种小电压步进充电。
综上所述,MSB读快,写慢;LSB读慢写快。所以,块页和慢页还得具体分读写场景来看。