0%

110:一生一芯F4(3)

F4 计算机系统的状态机模型

处理器的组成和工作原理

F4.1.1 继续执行上述指令

尝试继续执行指令, 记录寄存器的状态变化过程. 你发现执行到最后时, 处理器处于什么样的状态? 上述数列的求和结果在哪个寄存器中?

1
2
3
4
5
6
7
8
0: li r0, 10   # 这里是十进制的10
1: li r1, 0
2: li r2, 0
3: li r3, 1
4: add r1, r1, r3
5: add r2, r2, r1
6: bner0 r1, 4
7: bner0 r3, 7

状态机视角的寄存器状态变化:

1
2
3
4
5
6
7
8
9
10
11
12
PC r0 r1 r2 r3
(0, 0, 0, 0, 0) # 初始状态
(1, 10, 0, 0, 0) # 执行PC为0的指令后, r0更新为10, PC更新为下一条指令的位置
(2, 10, 0, 0, 0) # 执行PC为1的指令后, r1更新为0, PC更新为下一条指令的位置
(3, 10, 0, 0, 0) # 执行PC为2的指令后, r2更新为0, PC更新为下一条指令的位置
(4, 10, 0, 0, 1) # 执行PC为3的指令后, r3更新为1, PC更新为下一条指令的位置
(5, 10, 1, 0, 1) # 执行PC为4的指令后, r1更新为r1+r3, PC更新为下一条指令的位置
(6, 10, 1, 1, 1) # 执行PC为5的指令后, r2更新为r2+r1, PC更新为下一条指令的位置
(4, 10, 1, 1, 1) # 执行PC为6的指令后, 因r1不等于r0, 故PC更新为4
(5, 10, 2, 1, 1) # 执行PC为4的指令后, r1更新为r1+r3, PC更新为下一条指令的位置
....
(7, 10, 10, 55, 1) # 结果存储在r2

F4.1.2 计算10以内的奇数之和

尝试用上述指令编写一个程序, 求出10以内的奇数之和, 即计算1+3+5+7+9. 编写后, 尝试列出处理器状态的变化过程, 以此来检查你编写的程序是否正确.

编写程序如下:

1
2
3
4
5
6
7
8
0: li r0, 11
1: li r1, 1
2: li r2, 0
3: li r3, 2
4: add r2, r2, r1
5: add r1, r1, r3
6: bner0 r1, 4
7: bner0 r3, 7

处理器状态如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
PC r0 r1 r2 r3
(0, 0, 0, 0, 0)
(1, 11, 0, 0, 0)
(2, 11, 1, 0, 0)
(3, 11, 1, 0, 0)
(4, 11, 1, 0, 2)
(5, 11, 1, 1, 2)
(6, 11, 3, 1, 2)
(4, 11, 3, 1, 2)
(5, 11, 3, 4, 2)
(6, 11, 5, 4, 2)
(4, 11, 5, 4, 2)
(5, 11, 5, 9, 2)
(6, 11, 7, 9, 2)
(4, 11, 7, 9, 2)
(5, 11, 7, 16, 2)
(6, 11, 9, 16, 2)
(4, 11, 9, 16, 2)
(5, 11, 9, 25, 2)
(6, 11, 11, 25, 2)
(7, 11, 9, 16, 2) # End

指令集架构的状态机模型

见课件

C程序入门

F4.3.1 在线运行C程序

你可以在Compiler Explorer这个在线网站中运行上述C程序. 具体地, 在代码编辑器中输入上述C代码后, 点击编辑器上方工具栏的Add new..., 选择Execution Only, 即可弹出执行结果的终端窗口. 如果你的操作正确, 你将看到终端窗口输出z = 3.

你可以尝试按照你的理解修改C代码, 并观察程序的输出.

image.png

F4.3.2 继续执行上述程序

尝试继续执行上述代码, 记录状态的变化过程. 程序执行结束时, 程序处于什么样的状态?

1
2
3
4
5
6
7
8
9
10
11
12
13
// 待执行的程序
#include <stdio.h>

/* 1 */ int main() {
/* 2 */ int sum = 0;
/* 3 */ int i = 1;
/* 4 */ do {
/* 5 */ sum = sum + i;
/* 6 */ i = i + 1;
/* 7 */ } while (i <= 10);
/* 8 */ printf("sum = %d\n", sum);
/* 9 */ return 0;
/* 10*/ }

状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
PC sum i
(2, ?, ?) # 初始状态
(3, 0, ?) # 执行PC为2的语句后, sum更新为0, PC更新为下一条语句的位置
(5, 0, 1) # 执行PC为3的语句后, i更新为1, PC更新为下一条语句的位置(第4行无有效操作, 跳过)
(6, 1, 1) # 执行PC为5的语句后, sum更新为sum + i, PC更新为下一条语句的位置
(7, 1, 2) # 执行PC为6的语句后, i更新为i + 1, PC更新为下一条语句的位置
(5, 1, 2) # 执行PC为7的语句后, 由于循环条件i <= 10成立, 因此重新进入循环体
(6, 3, 2)
(7, 3, 3)
(5, 3, 3)
(6, 7, 3)
...
(10, 55, 11) # End

数字电路的状态机模型

F4.4.1 从状态机视角理解数列求和电路的工作过程

在上一小节中, 你已经通过寄存器和加法器搭建出一个简单数列求和电路, 用于计算1+2+...+10. 尝试列出电路状态的变化过程.

这个是上一节中搭建的用于数列求和的电路

image.png

我们将左边的寄存器视作状态A 右边的寄存器视作状态B,那么有状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
 A  B
(0, 0)
(1, 0)
(2, 1)
(3, 3)
(4, 6)
(5, 10)
(6, 15)
(7, 21)
(8, 28)
(9, 36)
(10, 45)
(11, 55)

在计算机上执行C程序

F4.5.1 结合数列求和的例子理解编译器的工作

我们已经给出了数列求和的C程序和相应的指令序列, 尝试从状态机的视角理解它们之间的联系.

对比先前的C程序和指令序列的状态迁移过程,我们可以发现他们的在逻辑上等价的:

  • 他们有着相同的初始状态
  • 指令序列的寄存器状态 = C程序的变量状态
  • 指令序列的PC值 = C程序的执行的行号
  • 指令序列的转移规则 = C程序的语句

结合老师上课讲的内容,他们可以被视作是等价的。

F4.5.2 结合数列求和的例子理解编译器的工作

具体地, 点击编辑器上方工具栏的Add new..., 选择Compiler, 即可弹出相应汇编代码的窗口. 汇编代码默认采用x86指令集, 你不一定能理解每一条指令的具体含义, 但通过背景颜色的高亮和鼠标移动, 你可以看到C程序片段和汇编代码片段之间的对应关系.

image.png