背后其实反映出两项重要的能力
状态机模型帮助大家将程序, 指令集, 处理器关联起来
程序 | 抽象计算机 | CPU | |
---|---|---|---|
状态 | \(\{<V, PC>\}\) | \(\{<R, M>\}\) | \(\{时序逻辑电路\}\) |
状态转移规则 | C语言语句的语义 | 指令的语义 | 组合逻辑电路 |
FM | C语言标准手册 | 指令集手册 | 架构设计文档 |
32位 | 64位 | |
---|---|---|
c90 | TT | FT |
c99 | FT | FT |
YEMU = 指令集模拟器 = 用C语言实现指令集手册定义的状态机
0
开始执行addi
和ebreak
ebreak
指令:
a0=0
时, 输出寄存器a1
低8位的字符 -
抽象成putch()
a0=1
时, 结束运行 - 抽象成halt()
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
uint32_t R[32], PC;
uint8_t M[64] = {
0x13, 0x05, 0x00, 0x00, 0x93, 0x05, 0x10, 0x04, 0x73, 0x00, 0x10, 0x00,
0x13, 0x05, 0x10, 0x00, 0x93, 0x05, 0x00, 0x00, 0x73, 0x00, 0x10, 0x00,
0x6f, 0x00, 0x00, 0x00,
};
bool halt = false;
void inst_cycle() {
uint32_t inst = *(uint32_t *)&M[PC];
if (((inst & 0x7f) == 0x13) && ((inst >> 12) & 0x7) == 0) { // addi
if (((inst >> 7) & 0x1f) != 0) {
R[(inst >> 7) & 0x1f] = R[(inst >> 15) & 0x1f] +
(((inst >> 20) & 0x7ff) - ((inst & 0x80000000) ? 4096 : 0));
}
} else if (inst == 0x00100073) { // ebreak
if (R[10] == 0) { putchar(R[11] & 0xff); }
else if (R[10] == 1) { halt = true; }
else { printf("Unsupported ebreak command\n"); }
} else { printf("Unsupported instuction\n"); }
PC += 4;
}
int main() {
PC = 0; R[0] = 0; // can be omitted since uninitialized global variables are initialized with 0
while (!halt) { inst_cycle(); }
return 0;
}
0
和1
0
和1
的简单计算
示例 - 手写递归版本汉诺塔的汇编代码
AM = 按照计算机发展史将计算机功能抽象地模块化的裸机运行时环境
异常 = 一种特殊的跳转
SoC的设备栈/存储栈:
MROM | UART | SRAM | flash | |
---|---|---|---|---|
程序功能 | 代码/只读数据 | printf() |
可写数据/堆/栈 | 代码/只读数据 |
函数 | - | putch() |
- | flash_read() (非XIP) |
C代码 | 指针解引用 | 指针解引用 | 指针解引用 | 指针解引用 |
RISC-V指令 | 取指/load | store | 取指/load/store | 取指/load[/store(SPI)] |
CPU单元 | IFU/LSU | LSU | IFU/LSU | IFU/LSU |
SoC总线桥 | - | AXI-APB-wb | - | AXI-APB-wb-SPI |
总线接口 | AXI-Lite | wishbone(wb) | AXI-Lite | SPI(含XIP) |
地址译码 | 选择存储单元 | 选择寄存器 | 选择存储单元 | 选择存储单元 |
1 bit的访问 | - | 移位寄存器 | 字线选通晶体管 | 字线控制栅极加压(读) |
1 bit的表示 | 电源/地 | 线缆信号 | 6管存储单元 | 浮栅晶体管的充/放电 |
遇到问题时知道去哪里找到正确的答案
使用工具 = 编程
学习工具首先需要端正心态 - 我真的想提高效率
短时间的投入是负收益, 但这些技能会让你终身受益
读代码的最终目的是理解程序
先从容易理解的方式下手(trace), 再结合程序的动态行为理解静态代码
make -n
-> Makefile
使用正确的工具提升工作效率
make -n
的信息方便理解正确的代码 != 好代码
好代码的两条重要准则
使用正确的编程模式写出好代码
assert
检查非预期行为需求 -> 设计 -> 代码 -> Fault -> Error -> Failure
-Wall
,
-Werror
Flag | 进度 | 说明 |
---|---|---|
完善课件的已知问题 | ✔️ | 重新讲之前都会看看之前记录的问题 |
将课件内容同步到讲义 | 🔺 | 硬件部分基本上重写了, 必做题不多的内容还没想好怎么同步到讲义 |
添加更多的示例代码 | ✔️ | 已添加CEMU, 程序的机器级表示demo, 异常处理demo |
添加开源EDA | ✔️ | 已添加yosys + iNO + iSTA |
添加性能优化目标 | 🔺 | B阶段已添加面积目标, A阶段暂未添加性能目标 |
出个基于开源EDA的后端学习讲义 | ✖️ | EDA小组正在编写, 将要发布初版 |
改进直播和录制效果 | ✔️ | 基本上没什么噪声了 |
NVBoard + ysyxSoC + NPC + RISC-V + AM + RT-Thread + FCEUX
和业界现有的流程非常类似, 只不过使用开源组件
自我启发: 在课件/视频中说100次, 还不如在讲义中加一道必做题
PS: 以后去掉 “期”的概念, 讲义会按时间版本重新梳理
预学习阶段还分F阶段和E阶段
技术迭代真的很快
北京一零一中学的初中生尝试学习 “一生一芯”