前面差不多理清了计算机组成的一些重要概念,由MCU是包含了CPU,当然还有存储器以及一些外设,为了对MCU有更好的理解,先拿其中的CPU说起吧,话不多说,先来一张概图:
有了这个结构图,别慌,一步步的分部分分析就会明白,也很清晰了。
首先来看ALU(ALU拆开为Arithmetic Logic Unit)中文名为算术逻辑单元,我们前面讲的概念CPU中的CPU中的算术运算(加减),逻辑运算(左移右移),在这里就成了它,操作数给到ALU,ALU运算后会产生结果,但光有结果往往还不够的,要知道有时候比如不够减了,或者位溢出了,那结果里得有这个状态阿。于是我们设计了程序状态寄存器(Program Status Register)用来记录这些结果状态标志,提到这里,不妨说一下,ALU要是能不断运算,那得有数给它才行吧,看到图中的R0,……R3方块了吧,那就是CPU的通用寄存器(可以用来存取操作数),ALU可以从这里取,而且算完了还可以放到这里面。但提到这里就会有疑问,这些寄存器是不是足够多到把我们的操作数放下呢,答案是否定的,不得不说,这些寄存器资源非常宝贵,所以我们操作数往往有时单单从它取还不够的,还得从外面存储器里取才行(内存或硬盘),对于结果也是,不能总放在寄存器里把(寄存器大多存放中间结果)。当我们运算完后,往往需要把结果送到存储器里,这就理清了ALU的操作数来去了,但不知道发现没有,到底是谁告诉我们ALU什么时候该取数呢,从哪里取,放在哪。想到我们概念CPU里就会发现我们指令里有些控制做加减,有些还控制数据流向的指令。这下好了,这正是图上另一个大方框里表示的,不过在这里给起了一个名字叫控制单元CLU(Control Unit),它对应着我们概念CPU中的指令功能一样(指令解析+数据流向),它与其他的连接关系,这里就大可说一番,那一根连到外围存储器的线,那就是告诉外面存储器该干嘛干嘛,别干等阿,一根线连到结果Result,Result得告诉我们控制器阿,不然结果都已经不对了,还执行也没意义了。另外三根线都与ALU有直接关系,一根控制外围存储器与ALU取操作数的,另外一根是ALU与与寄存器取数的,还有一根就是告诉ALU该干嘛干嘛的,分析到这里,肯定又会有疑问,那这些指令又是哪来的,放在哪呢?
一般我们指令是程序员编好了直接放在存储器(这里我将内存什么的都统称为这个,而且程序我也默认是已编译为二进制的)里的,我们知道存储器就好像一个个抽屉一样,每个抽屉都有编号,就像药店后面的小柜子样的(这个比喻是否让你想到了指针呢哈哈哈),只不过这些编号是事先约定的叫地址,我们只要把这些指令一条条的放到不同的地址就好了,等到执行的时候再一条条的执行,这样就会想到那怎么样就使得程序一条条的执行了呢。人们发明了程序计数器(或者称之为指针控制寄存器把),它能够自动对地址累加,里面存储的永远是下一条执行的指令对应的地址,在指令执行的时候,不总是按顺序往下的,有时候中断或者碰到程序跳转指令的时候,指令会跳转到另一个地方,完全执行完了再回到原来的位置进行执行,那怎么回来呢,首先会想到的就是我再增加一个寄存器用来保存返回地址的,别说,早期CPU还真是这么设计的,当然此话的言下之意便是现今的CPU不是这样的,因为后来发现随着程序的变大和子程序调用的增多(比如中断嵌套加深等等情况),导致设计的寄存器越来越多,而且寄存器的个数就确定了程序嵌套的个数,这就显然不符合人们的习惯阿,明显限制程序员的发挥了。于是人们想了想产生一个好的idea,那就是栈的概念(Stack Register),分为PUSH(压栈)和POP(出栈)两个操作,完美解决了这个问题,但需要遵循一个条件(First in Last out)
图中还有一个寄存器漏掉了,那就是变址寄存器(Index Register),可称之为地址寄存器,这个寄存器可用来存储一条指令或是一个存储单元的地址,方便寻址的。对于MCU可能就是一个简单的存放存储单元地址的,但对于计算机里的CPU来说其范围就大了,其可包含基址,变址和相对地址,其寻址方式也更多样化,拿一个标准的MCU来说,大致具有以下寄存器:
累加寄存器(数据寄存器) Accumulation Register
变址寄存器(地址寄存器)Index Register
指针寄存器 Stack Pointer Register
程序计数器 Program Counter,简称PC
程序状态字寄存器 Program Status Word,简称PSW
(这个有别名,条件代码寄存器 Condition Codes Register)
话题最后,说一下堆栈,需要注意的是堆和栈是两个不同的概念,堆是内存里一段连续空间,可以用来存数据,而栈可以存取局部变量数据或返回地址,栈指针寄存器就是用来存取这个返回地址的,但指针寄存器一般保存的就是下一步的返回地址,你程序调用多了,那肯定是保存你上一步调用的返回地址,所以可联想到这个栈肯定是后进先出,另外栈和堆分别占用不同存取空间,但堆可能自上往下放,栈则可能自下往上放,所以这就可能发生堆栈溢出问题(即一个存储单元可能发生堆和栈抢占关系),若这样,则就可能不是返回地址,这时程序就有可能跑飞了(程序执行的地方完全无法预料,多提一下,在实际产品中一般程序需要加个看门狗喂狗Watch Dog Timer,一旦跑飞不喂狗了,程序就复位了,这样至少不至于死机),一般栈的使用还有注意的是放了东西就记得要拿走(这个点与C语言里的malloc申请空间有点相似),比如PUSHA->PUSHB->POPB->POPA,压栈后记得后面一定得出栈,,而且顺序不能弄错了,否则就还原不了现场了。