在云栈社区的计算机基础板块,软硬件界限常常被提起——而今天这个模糊地带的主角,就是固件。于是,从业者也被分成了三类:
- 软件工程师:一般指做图形界面的程序员,写C++、Java、Web等。
- 硬件工程师:玩电路板的,画原理图、PCB。
- 固件工程师:也叫单片机工程师,既要写代码(主要是 C语言 、汇编),又要画电路图。
玩单片机的人也许会问:为什么我写的C语言能直接操控底层硬件?其实在计算机组成原理中已经有很详细的介绍,这里粗略地串一下核心思路。
你可以搜索一下“从零开始造电脑”,大神Steve会告诉你,用晶体管就可以搭出CPU(单片机本身也是CPU)。我们当然不会真拿晶体管去焊一台电脑,但把规模放大一点,用74系列逻辑IC、单片机拼一套自己的计算机,是完全可行的。人们把大量晶体管集成成一片74系列的IC;集成度再高一些,就是我们手机或台式机里的多核CPU。

好,回到正题。CPU的核心任务是计算,加法的实现直接依赖数字电路里的门电路。上图的加法器就是用与门、异或门搭出来的。若用74系列IC,把输入Ai、Ci接到GND,Bi接VCC,立刻就能完成一次常数加法。但CPU要处理的,显然不只是硬连线定死的常数值——它需要能灵活地切换运算类型。

图中的蓝色箭头接VCC,红色箭头接GND,就是手动设置高低电平。在CPU内部,ROM里存储的数据一样能输出高低电平,相当于用“烧录”代替了硬连线。加数和被加数就可以存在ROM里,运算结果则暂存到寄存器。
但加完之后还想算乘法(信号处理中的卷积核心就是乘加器),谁来指挥这个切换?答案就是ALU(算术逻辑单元)。

控制单元从ROM取出数据,通过选择器决定调用加法器还是乘法器,再把结果存入寄存器。问题是,如果ROM里只存数据,控制单元怎么知道该做加法还是乘法?解决方案就是在ROM里划出一块区域专门存指令码。指令码和普通数据一样,就是一串0、1的二进制数,但因为用途不同,被赋予了新的含义。在单片机里,指令码对应汇编里的操作码(比如MOV),操作数就是数据(如01H)。
指令码的设计理念主要有CISC、RISC、VLIW、TTA四种,这里不展开。重点在于,PC(程序计数器)掌控着ROM的地址走向——PC不准,整个单片机就跑飞。
假设在一个8位CPU的ROM里,首地址存0x03表示加法,接着两个地址存加数和被加数;第四个地址存0x05表示乘法,后面两个地址存乘数和被乘数。控制单元按照既定规则去解析这些指令码(最简单的规则可以用与门输出使能信号),就能让加法器或乘法器“知道”该它上场了。当然,真实CPU不会这么粗糙,它会用状态机、流水线等机制来调度这些基本单元。

说穿了,改变ROM的内容,就是在操控ALU,进而操控CPU的各个硬件单元。下面这张相对完整的ALU内部结构图,可以帮你把数据缓存、指令缓存、PC、指令译码器这些东西串起来。

ROM的内容本质上是电荷状态(电容有无电荷代表1和0),也就是固件、软件工程师写的代码;而硬件是晶体管搭成的模拟和数字电路,比如单片机里的比较器、ADC。硬件是物理的,不易更改;ROM里的内容用烧录器就能轻松改写,成本低、灵活度高。正因为这种结构,你很难一刀切地说这些电荷是“软件”还是“硬件”,但行业里还是习惯把与硬件紧密耦合、完工后很少改动的代码叫做固件——它卡在软硬之间,低调却不可或缺。
|