卢骏 · 2020年09月01日

奔跑吧,SOC(一) 软件是怎么控制硬件的

很多人肯定很疑惑,在嵌入式开发中,为什么写c代码,就能够控制硬件。这一切是怎么发生的了,下面我就给大家解剖一下,软件是怎么控制硬件的。

我们从控制8个led为例来说明:

从最简单的开始,如果使用硬件,控制8个led,最简单的方式是什么:直接接上开关。就是以下的图。通过开关控制led管脚输出不同的电平,就能控制led了。但是这个方法很不灵活,我们要手动的去拨动开关,才能改变led状态。

1.png

我们将上面电路改一下,把开关的地方换成寄存器,并加一些额外的电路,这样就可以通过使能信号和信号值改变led值输出不同电平,间接的控制led了。而使能信号和信号值我们是可以通过外部给的,就比开关要灵活一些了。

2.jpg

既然,上面有寄存器,我们可以给这个寄存器定义一个地址,当然这个地址目前是可以随意定义的。假设为0x77777777。你可能会有疑问,为什么要给寄存器定义地址了?而且定义的地址为什么是32位了,这个先不着急,听我慢慢道来。

我们继续在前面加电路:

3.jpg

这里,多了一个前级电路,前级电路提供两个信号,一个地址,一个数据。在地址信号通路上,有一个电路模块,判断地址是不是0x77777777,是的话,使能信号为1,这样数据不就直接传递给寄存器了。不是的话,使能信号为0。寄存器的输出不变。这样,通过地址和数据就能改变led了。

这里0x77777777是不是很熟悉,这不就是之前定义的寄存器A的地址吗?原来,地址就是用来产生判断是否产生使能信号的,使能才能使数据能够输入到寄存器中。

到了这里,是不是对软件控制硬件有些眉目了。别着急,我们继续往下走。

既然前级电路只需要提供地址和数据,并且地址是0x77777777,就可以控制寄存器A的值了,而控制了寄存器A,就控制led。那么我们将前级电路换成一个32位CPU。

我们知道,CPU是可以产生3种总线信号的,一种地址总线,一种数据总线,一种控制总线。控制总线我们这里用不到。将地址总线接到地址线上,将数据总线接到数据线上。就是以下的电路:

4.jpg

如果我们让CPU产生地址为0x77777777,然后发出想要写入寄存器A的数据B,那么数据B不就被写入到寄存器A中,这样,不就控制led了。这里知道为什么地址要32位的把,因为CPU是32位的,地址就是32位宽的。之所以定义32位的地址,目的是为了和CPU的地址总线32位宽兼容。

到这里,是不是有豁然开朗的感觉了。我们再继续。

我们知道CPU是要取机器码然后执行的。如果刚好某条机器码,能让地址总线上产生0x77777777,数据总线上产生数据B。那么结果,数据B不就被写入到寄存器A中了。假设CPU是32位risc的CPU,机器码就是32位。那么该机器码应该是如下:

1001 0000 1110 0000 1011 1010 1111 0010(假设)

既然这是一条机器码,那么就应该有一个汇编指令与之对应,假设是

str r1, [r0]

我们预先将地址0x77777777写入到r0中,数据B写入到r1中,那么上面一条语句执行后,不就将数据B写入到寄存器A中了,不就控制led了。这样不就实现了软件控制了硬件了。既然汇编代码可以控制硬件了,那么高级语言同样也是控制硬件的,只要编译后的汇编代码是以上代码就行了。

整体控制硬件的代码就是

ldr r0, = 0x77777777

ldr r1, =B

str r1, [r0]

对应的C代码就是

((volatile unsigned long )0x77777777) = B;

使用指针操作,往0x77777777地址的寄存器写入数据B,加入volatile关键字,是防止编译器对操作进行优化。

通过上面的过程,是不是也可以理解,为什么在嵌入式底层驱动开发中,基本都是用C语言,而不用其他高级语言,比如JAVA等。因为这些高级语言没有指针,你就不能控制寄存器,不能控制寄存器,你当然就控制不了硬件了。C++也很少用,因为底层驱动开发需要高效率代码,不能太复杂,而C++在这方面,比不过C语言。

以上是写入的过程了,如果想要知道led的状态呢?通过读取寄存器A的值,不就知道led的状态了。原理是一样的。只不过数据线要变成两根,一个是负责写,一根负责读。电路图如下:

5.jpg

因为只考虑了一个寄存器,当不选中寄存器A时,读取的数据为全0。

通过,上面的讲述,对软件控制硬件有没有了解一些了。软件控制硬件,本质上,就是通过代码去修改或读取硬件对应的寄存器的值。这样,就相当于间接的控制了硬件。而硬件的寄存器对于一个处理器来说,都是固定的,都预先定义好了地址。所以在看ARM的数据手册中,可以看到很多寄存器的地址。这些地址的作用,也就是能够让你在写程序的时候,能够正确的往这些寄存器里面写入或读取正确的值,从而控制硬件。

CPU对外看到的都是寄存器,所以硬件设计的时候,就要对硬件的功能设置几个寄存器,然后对这几个寄存器分别定义几个地址,这样CPU才可以去控制这几个寄存器,也就能控制硬件了。定义的寄存器地址位宽是和CPU的地址线位宽是有关系的,如果是一个8位的CPU,也就是经典的C51,地址的宽度就是8位,所以你可以在头文件reg51.c中看到使用sfr定义的地址位宽是8位。在STM32中,CPU是32位的,所以地址的宽度就是32位的,所以你看到STM32数据手册中,寄存器的地址都是32位的,而且是4字节对齐的。

以上,CPU只是控制了一个硬件,led,但是我们知道,CPU是可以控制很多硬件的,那这又是怎么实现的了?这个就得谈谈片上互联总线了。

更多相关阅读

奔跑吧,SOC(四) 外设地址集合
Cadence工具使用
VCS使用以及命令行调试

原文首发于骏的世界博客
作者:卢骏
更多IC设计相关的文章请关注IC设计极术专栏,每日更新。

推荐阅读
关注数
20175
内容数
1307
主要交流IC以及SoC设计流程相关的技术和知识
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息