RISC-V体系结构编程与实践
目 录
第 1章 RISC-V体系结构基础知识 1
1.1 RISC-V介绍 1
1.1.1 RISC-V指令集优点 1
1.1.2 RISC-V指令集扩展 2
1.1.3 RISC-V商业化发展 2
1.2 RISC-V体系结构介绍 3
1.2.1 RISC-V体系结构 3
1.2.2 采用RISC-V体系结构的常见处理器 3
1.2.3 RISC-V体系结构中的基本概念 4
查看完整
第 1章 RISC-V体系结构基础知识 1
1.1 RISC-V介绍 1
1.1.1 RISC-V指令集优点 1
1.1.2 RISC-V指令集扩展 2
1.1.3 RISC-V商业化发展 2
1.2 RISC-V体系结构介绍 3
1.2.1 RISC-V体系结构 3
1.2.2 采用RISC-V体系结构的常见处理器 3
1.2.3 RISC-V体系结构中的基本概念 4
查看完整
作者简介 奔跑吧Linux社区 由一群热爱开源的工程师组成,致力于开源硬件和开源软件的推广。 审校者简介 香山处理器团队 面向世界的体系结构创新开源平台,目前已形成由多家企业组成的香山联合开发团队。其中,香山处理器是由中国科学院计算技术研究所发起的开源高性能RISC-V处理器核项目。 龙蜥社区RISC-V SIG 坚持开放、开源,致力于龙蜥社区以及 RISCV 软硬件生态的共建和推广。 进迭时空 专注研发新一代高性能RISC-V处理器和计算系统,让开发者基于RISC-V芯片更自由地开发更有创意的新应用。
本书旨在介绍RISC-V体系结构的设计和实现。本书首先介绍RISC-V体系结构的基础知识、实验环境搭建、常用指令、函数调用规范与栈,然后讲述GNU汇编器、链接器、链接脚本和GCC内嵌汇编代码,接着讨论RISC-V体系结构中的异常处理、中断、内存管理、高速缓存、缓存一致性、TLB管理、原子操作、内存屏障指令,后阐述RSIC-V体系结构中的压缩指令扩展、虚拟化扩展等。
本书不仅适合软件开发人员阅读,还可以作为计算机相关专业和相关培训机构的教材。
本书不仅适合软件开发人员阅读,还可以作为计算机相关专业和相关培训机构的教材。
目 录
第 1章 RISC-V体系结构基础知识 1
1.1 RISC-V介绍 1
1.1.1 RISC-V指令集优点 1
1.1.2 RISC-V指令集扩展 2
1.1.3 RISC-V商业化发展 2
1.2 RISC-V体系结构介绍 3
1.2.1 RISC-V体系结构 3
1.2.2 采用RISC-V体系结构的常见处理器 3
1.2.3 RISC-V体系结构中的基本概念 4
1.2.4 SBI服务 5
1.3 RISC-V寄存器 6
1.3.1 通用寄存器 6
1.3.2 系统寄存器 7
1.3.3 U模式下的系统寄存器 8
1.3.4 S模式下的系统寄存器 9
1.3.5 M模式下的系统寄存器 11
1.4 香山处理器介绍 15
1.4.1 香山处理器体系结构 15
1.4.2 香山处理器的前端子系统 16
1.4.3 香山处理器的后端子系统 18
1.4.4 香山处理器的访存子系统 20
1.4.5 香山处理器的L2/L3高速缓存 25
第 2章 搭建RISC-V实验环境 29
2.1 实验平台 29
2.1.1 QEMU 29
2.1.2 NEMU 30
2.2 搭建实验环境 31
2.2.1 实验2-1:输出“Welcome RISC-V!” 31
2.2.2 实验2-2:单步调试BenOS和MySBI 32
2.3 BenOS和MySBI基础实验代码解析 34
2.3.1 MySBI基础代码分析 34
2.3.2 BenOS基础代码分析 37
2.3.3 合并BenOS和MySBI 41
2.4 QEMU RISC-V Linux实验平台 41
第3章 基础指令集 44
3.1 RISC-V指令集介绍 44
3.2 RISC-V指令编码格式 45
3.3 加载与存储指令 46
3.4 PC相对寻址 49
3.5 移位操作 53
3.6 位操作指令 55
3.7 算术指令 56
3.8 比较指令 57
3.9 无条件跳转指令 58
3.10 条件跳转指令 59
3.11 CSR指令 61
3.12 寻址范围 62
3.13 陷阱:为什么ret之后就进入死循环 62
3.14 实验 64
3.14.1 实验3-1:熟悉加载指令 64
3.14.2 实验3-2:PC相对地址寻址 64
3.14.3 实验3-3:memcpy()函数的实现 65
3.14.4 实验3-4:memset()函数的实现 65
3.14.5 实验3-5:条件跳转指令1 65
3.14.6 实验3-6:条件跳转指令2 66
3.14.7 实验3-7:子函数跳转 66
3.14.8 实验3-8:在汇编中实现串口输出功能 66
第4章 函数调用规范与栈 67
4.1 函数调用规范 67
4.2 入栈与出栈 70
4.3 RISC-V栈的布局 72
4.3.1 不使用FP的栈布局 72
4.3.2 使用FP的栈布局 74
4.3.3 栈回溯 76
4.4 实验 78
4.4.1 实验4-1:观察栈布局 78
4.4.2 实验4-2:观察栈回溯 78
第5章 GNU汇编器 79
5.1 编译流程与ELF文件 79
5.2 一个简单的汇编程序 82
5.3 汇编语法 84
5.3.1 注释 84
5.3.2 符号 84
5.4 常用的伪指令 85
5.4.1 对齐伪指令 85
5.4.2 数据定义伪指令 86
5.4.3 与函数相关的伪指令 87
5.4.4 与段相关的伪指令 87
5.4.5 与宏相关的伪指令 89
5.4.6 与文件相关的伪指令 91
5.5 RISC-V依赖特性 91
5.5.1 RISC-V特有的命令行选项 91
5.5.2 RISC-V特有的伪指令 92
5.6 实验 92
5.6.1 实验5-1:汇编语言练习—查找数 92
5.6.2 实验5-2:汇编语言练习—通过C语言调用汇编函数 92
5.6.3 实验5-3:汇编语言练习—通过汇编语言调用C函数 92
5.6.4 实验5-4:使用汇编伪操作实现一张表 92
5.6.5 实验5-5:汇编宏的使用 93
第6章 链接器与链接脚本 94
6.1 链接器 94
6.2 链接脚本 95
6.2.1 一个简单的链接程序 95
6.2.2 设置入口点 96
6.2.3 基本概念 97
6.2.4 符号赋值与引用 97
6.2.5 当前位置计数器 98
6.2.6 SECTIONS命令 99
6.2.7 常用的内置函数 101
6.3 加载重定位 103
6.3.1 BenOS重定位 103
6.3.2 OpenSBI和Linux内核重定位 105
6.4 链接重定位与链接器松弛优化 108
6.4.1 链接重定位 108
6.4.2 函数跳转优化 112
6.4.3 符号地址访问优化 114
6.5 实验 116
6.5.1 实验6-1:分析链接脚本 116
6.5.2 实验6-2:输出每个段的内存布局 116
6.5.3 实验6-3:加载地址不等于运行地址 117
6.5.4 实验6-4:设置链接地址 117
6.5.5 实验6-5:链接器松弛优化1 117
6.5.6 实验6-6:链接器松弛优化2 117
6.5.7 实验6-7:分析Linux 5.15内核的链接脚本 117
第7章 内嵌汇编代码 118
7.1 内嵌汇编代码基本用法 118
7.1.1 基础内嵌汇编代码 118
7.1.2 扩展内嵌汇编代码 118
7.1.3 内嵌汇编代码修饰符 120
7.1.4 使用汇编符号名字 121
7.1.5 内嵌汇编代码与宏结合 122
7.1.6 使用goto修饰词 122
7.1.7 小结 123
7.2 案例分析 124
7.3 注意事项 128
7.4 实验 128
7.4.1 实验7-1:实现简单的memcpy()函数 128
7.4.2 实验7-2:使用汇编符号名写内嵌汇编代码 128
7.4.3 实验7-3:使用内嵌汇编代码完善memset()函数 129
7.4.4 实验7-4:使用内嵌汇编代码与宏的结合 129
7.4.5 实验7-5:实现读和写系统寄存器的宏 129
7.4.6 实验7-6:goto模板的内嵌汇编代码 129
第8章 异常处理 130
8.1 异常处理基本概念 130
8.1.1 异常类型 130
8.1.2 同步异常和异步异常 131
8.1.3 异常入口和返回 131
8.1.4 异常返回地址 132
8.1.5 异常返回的处理器模式 133
8.1.6 栈的选择 133
8.2 与M模式相关的异常寄存器 133
8.2.1 mstatus寄存器 134
8.2.2 mtvec寄存器 134
8.2.3 mcause寄存器 135
8.2.4 mie寄存器 135
8.2.5 mtval寄存器 136
8.2.6 mip寄存器 136
8.2.7 mideleg和medeleg寄存器 136
8.2.8 中断配置 137
8.3 与S模式相关的异常寄存器 137
8.3.1 sstatus寄存器 137
8.3.2 sie寄存器 137
8.3.3 sip寄存器 138
8.3.4 scause寄存器 138
8.3.5 stvec寄存器 138
8.3.6 stval寄存器 139
8.4 异常上下文 139
8.4.1 保存异常上下文 141
8.4.2 恢复异常上下文 141
8.5 案例分析8-1:实现SBI系统调用 142
8.5.1 调用ECALL指令 142
8.5.2 实现SBI系统调用 143
8.6 案例分析8-2:BenOS的异常处理 148
8.6.1 设置异常向量表 148
8.6.2 保存和恢复异常上下文 149
8.6.3 异常处理 151
8.6.4 委托中断和异常 153
8.6.5 触发异常 153
8.7 实验 154
8.7.1 实验8-1:在SBI中实现串口输入功能 154
8.7.2 实验8-2:在BenOS中触发非法指令异常 155
8.7.3 实验8-3:输出触发异常时函数栈的调用过程 155
8.7.4 实验8-4:在MySBI中模拟实现RDTIME伪指令 155
第9章 中断处理与中断控制器 156
9.1 中断处理基本概念 156
9.1.1 中断类型 156
9.1.2 中断处理过程 157
9.1.3 中断委派和注入 158
9.1.4 中断优先级 158
9.2 CLINT 159
9.3 案例分析9-1:定时器中断 160
9.3.1 访问mtimer 160
9.3.2 在MySBI中实现定时器服务 160
9.3.3 定时器中断处理 161
9.3.4 打开中断总开关 163
9.3.5 小结 164
9.4 PLIC 164
9.4.1 中断号 165
9.4.2 中断优先级 166
9.4.3 中断使能寄存器 166
9.4.4 中断待定寄存器 166
9.4.5 中断优先级阈值寄存器 167
9.4.6 中断请求/完成寄存器 167
9.5 案例分析9-2:串口中断 167
9.5.1 初始化PLIC 168
9.5.2 使能串口0的接收中断 169
9.5.3 处理中断 169
9.6 实验 171
9.6.1 实验9-1:定时器中断 171
9.6.2 实验9-2:使用汇编函数保存和恢复中断现场 171
9.6.3 实验9-3:实现并调试串口0中断 171
第 10章 内存管理 172
10.1 内存管理基础知识 172
10.1.1 内存管理的“远古时代” 172
10.1.2 地址空间的抽象 174
10.1.3 分段机制 175
10.1.4 分页机制 175
10.2 RISC-V内存管理 178
10.2.1 页表分类 179
10.2.2 Sv39页表映射 180
10.2.3 Sv48页表映射 182
10.2.4 页表项描述符 183
10.2.5 页表属性 185
10.2.6 与地址转换相关的寄存器 186
10.3 物理内存属性与物理内存保护 187
10.3.1 物理内存属性 187
10.3.2 物理内存保护 188
10.4 案例分析10-1:在BenOS里实现恒等映射 190
10.4.1 页表定义 191
10.4.2 页表数据结构 193
10.4.3 创建页表 193
10.4.4 打开MMU 198
10.4.5 测试MMU 198
10.4.6 图解页表创建的过程 200
10.5 内存管理实验 204
10.5.1 实验10-1:建立恒等映射 204
10.5.2 实验10-2:为什么MMU无法运行 205
10.5.3 实验10-3:实现一个MMU页表的转储功能 205
10.5.4 实验10-4:修改页面属性 205
10.5.5 实验10-5:使用汇编语言来建立恒等映射 206
10.5.6 实验10-6:在MySBI中实现和验证PMP机制 206
第 11章 高速缓存 207
11.1 为什么需要高速缓存 207
11.2 高速缓存的访问延时 208
11.3 高速缓存的工作原理 210
11.4 高速缓存的映射方式 212
11.4.1 直接映射 212
11.4.2 全相联映射 213
11.4.3 组相联映射 213
11.4.4 组相联的高速缓存的例子 214
11.5 虚拟高速缓存与物理高速缓存 215
11.5.1 物理高速缓存 215
11.5.2 虚拟高速缓存 215
11.5.3 VIPT和PIPT 215
11.6 重名和同名问题 216
11.6.1 重名问题 217
11.6.2 同名问题 217
11.6.3 VIPT产生的重名问题 218
11.7 高速缓存策略 220
11.8 高速缓存的维护指令 221
11.8.1 高速缓存管理指令 221
11.8.2 高速缓存预取指令 222
第 12章 缓存一致性 224
12.1 为什么需要缓存一致性 224
12.2 缓存一致性的分类 225
12.2.1 缓存一致性协议发展历程 225
12.2.2 缓存一致性分类 226
12.2.3 系统缓存一致性问题 227
12.3 缓存一致性的解决方案 227
12.3.1 关闭高速缓存 228
12.3.2 使用软件维护缓存一致性 228
12.3.3 使用硬件维护缓存一致性 228
12.4 MESI协议 228
12.4.1 MESI协议简介 229
12.4.2 本地读写与总线操作 230
12.4.3 MESI状态转换 230
12.4.4 初始状态为I 231
12.4.5 初始状态为M 233
12.4.6 初始状态为S 234
12.4.7 初始状态为E 234
12.4.8 小结与案例分析 235
12.4.9 MOESI协议 237
12.5 高速缓存伪共享 237
12.6 两种缓存一致性控制器 239
12.6.1 CCI缓存一致性控制器 239
12.6.2 CCN缓存一致性控制器 240
12.7 案例分析12-1:伪共享的避免 241
12.8 案例分析12-2:DMA和高速缓存的一致性 242
12.8.1 从内存到设备的FIFO缓冲区 243
12.8.2 从设备的FIFO缓冲区到内存 243
12.9 案例分析12-3:自修改代码的一致性 244
12.10 实验 245
12.10.1 实验12-1:高速缓存伪共享 245
12.10.2 实验12-2:使用Perf C2C发现高速缓存伪共享 245
第 13章 TLB管理 246
13.1 TLB基础知识 247
13.2 TLB重名与同名问题 249
13.2.1 重名问题 249
13.2.2 同名问题 250
13.3 ASID 251
13.4 TLB管理指令 253
13.4.1 TLB维护指令介绍 253
13.4.2 TLB广播 254
13.4.3 SFENCE.VMA指令使用场景 256
13.5 TLB案例分析 256
13.5.1 TLB在Linux内核中的应用 256
13.5.2 ASID在Linux内核中的应用 257
13.5.3 Linux内核中的TLB维护操作 257
13.5.4 BBM机制 259
第 14章 原子操作 261
14.1 原子操作介绍 261
14.2 保留加载与条件存储指令 262
14.3 独占内存访问工作原理 263
14.3.1 独占监视器 264
14.3.2 独占监视器与缓存一致性 265
14.4 原子内存访问操作指令 266
14.4.1 原子内存访问指令工作原理 266
14.4.2 原子内存访问指令与LR/SC指令的效率对比 267
14.4.3 RISC-V中的原子内存访问指令 268
14.5 比较并交换操作 270
第 15章 内存屏障指令 275
15.1 内存屏障指令产生的原因 275
15.1.1 顺序一致性内存模型 276
15.1.2 处理器一致性内存模型 277
15.1.3 弱一致性内存模型 277
15.1.4 释放一致性内存模型 278
15.1.5 MCA模型 279
15.2 RISC-V约束条件 280
15.2.1 全局内存次序与保留程序次序 280
15.2.2 RVWMO的约束规则 281
15.3 RISC-V中的内存屏障指令 284
15.3.1 使用内存屏障的场景 284
15.3.2 FENCE指令 285
15.3.3 内置获取和释放屏障原语的指令 285
15.3.4 FENCE.I指令 286
15.3.5 SFENCE.VMA指令 286
15.4 RISC-V内存屏障指令移植指南 286
15.4.1 从RISC-V到x86体系结构 286
15.4.2 从RISC-V到ARM体系结构 287
15.4.3 Linux内核常用的内存屏障API函数 287
15.5 案例分析 288
15.5.1 消息传递问题 288
15.5.2 单方向内存屏障与自旋锁 289
15.5.3 邮箱传递消息 290
15.5.4 关于DMA的案例 291
15.5.5 在Linux内核中使指令高速缓存失效 291
15.6 模拟和测试内存屏障故障 291
15.6.1 使用Litmus测试工具集 292
15.6.2 编写C程序来模拟 295
15.7 实验 297
15.7.1 实验15-1:编写Litmus脚本并测试内存一致性1 297
15.7.2 实验15-2:编写Litmus脚本并测试内存一致性2 298
第 16章 合理使用内存屏障指令 299
16.1 存储缓冲区与写内存屏障指令 300
16.2 无效队列与读内存屏障指令 305
16.3 内存屏障指令总结 307
16.4 案例分析:Linux内核中的内存屏障指令 308
16.4.1 第 一次使用内存屏障指令 309
16.4.2 第二次使用内存屏障指令 310
16.4.3 第三次使用内存屏障指令 313
16.4.4 第四次使用内存屏障指令 314
16.4.5 小结:内存屏障指令的使用 315
16.5 实验 315
16.5.1 实验16-1:验证和测试内存一致性1 315
16.5.2 实验16-2:验证和测试内存一致性2 315
16.5.3 实验16-3:验证和测试内存一致性3 315
第 17章 与操作系统相关的内容 316
17.1 C语言常见陷阱 317
17.1.1 数据模型 317
17.1.2 数据类型转换与整型提升 318
17.1.3 移位操作 320
17.2 创建进程 320
17.2.1 进程控制块 320
17.2.2 0号进程 321
17.2.3 do_fork()函数的实现 323
17.2.4 进程上下文切换 324
17.2.5 新进程的第 一次执行 326
17.3 简易进程调度器 327
17.3.1 扩展进程控制块 327
17.3.2 就绪队列 327
17.3.3 调度类 328
17.3.4 简易调度器的实现 329
17.3.5 自愿调度 330
17.3.6 抢占调度 331
17.3.7 测试用例 333
17.3.8 关于调度的思考 333
17.4 让进程运行在用户模式 335
17.5 系统调用 338
17.5.1 系统调用介绍 338
17.5.2 在用户模式下调用SVC指令 338
17.5.3 在内核模式下对系统调用的处理 339
17.5.4 系统调用表 340
17.6 实现clone系统调用 341
17.7 实验 343
17.7.1 实验17-1:进程创建 343
17.7.2 实验17-2:进程调度 343
17.7.3 实验17-3:让进程运行在用户模式 343
17.7.4 实验17-4:新增一个malloc()系统调用 343
17.7.5 实验17-5:新增一个clone()系统调用 344
第 18章 可伸缩矢量计算与优化 345
18.1 矢量计算基本概念 345
18.1.1 SISD与SIMD 345
18.1.2 定长计算与可变长矢量计算 347
18.1.3 通道 347
18.1.4 矢量与标量 347
18.2 RVV寄存器 348
18.2.1 矢量寄存器 348
18.2.2 mstatus寄存器中的矢量上下文状态 348
18.2.3 vtype寄存器 348
18.2.4 vl寄存器 350
18.2.5 vlenb寄存器 351
18.2.6 vstart寄存器 351
18.3 配置编译和运行环境 351
18.3.1 搭建编译环境 351
18.3.2 运行第 一个“hello RVV!”程序 352
18.3.3 单步调试汇编程序 353
18.3.4 单步调试C语言与汇编混合程序 355
18.4 RVV指令格式 357
18.5 配置指令 358
18.6 加载和存储指令 360
18.6.1 单位步长模式 360
18.6.2 任意步长模式 363
18.6.3 聚合加载/离散存储 364
18.6.4 打包数据的加载与存储 365
18.6.5 首次异常加载指令 367
18.6.6 加载和存储全部矢量数据 368
18.7 矢量掩码指令 369
18.7.1 逻辑操作指令 369
18.7.2 VCPOP.M指令 369
18.7.3 VFIRST.M指令 370
18.7.4 VMSBF.M指令 370
18.7.5 VMSIF.M指令 371
18.7.6 VMSOF.M指令 372
18.8 矢量整型算术指令 372
18.8.1 加宽和变窄算术指令 372
18.8.2 加法和减法指令 373
18.8.3 加宽模式的加法和减法指令 374
18.8.4 位操作指令 376
18.8.5 移位操作指令 376
18.8.6 比较指令 376
18.8.7 数据搬移指令 377
18.9 案例分析18-1:使用RVV指令优化strcmp()函数 377
18.9.1 使用纯汇编方式 378
18.9.2 测试 379
18.10 案例分析18-2:RGB24转BGR24 380
18.10.1 使用C语言实现RGB24转BGR24 380
18.10.2 使用RVV指令优化 380
18.10.3 测试 381
18.11 案例分析18-3:4 × 4矩阵乘法运算 382
18.11.1 使用C语言实现4 × 4矩阵乘法运算 382
18.11.2 使用RVV指令优化 383
18.11.3 测试 387
18.12 案例分析18-4:使用RVV内置函数 388
18.13 案例分析18-5:自动矢量优化 388
18.14 术语 390
18.15 实验 391
18.15.1 实验18-1:RGB24转BGR32 391
18.15.2 实验18-2:8 × 8矩阵乘法运算 391
18.15.3 实验18-3:使用RVV指令优化strcpy()函数 391
18.15.4 实验18-4:使用RVV内置函数优化 391
18.15.5 实验18-5:使用RVV优化转置矩阵的求法 392
第 19章 压缩指令扩展 393
19.1 RISC-V指令集的特点 393
19.2 RVC支持的指令格式与指令编码 394
第 20章 虚拟化扩展 396
20.1 虚拟化技术介绍 396
20.1.1 虚拟化技术的发展历史 396
20.1.2 虚拟机管理程序的分类 398
20.1.3 内存虚拟化 398
20.1.4 I/O虚拟化 399
20.2 RISC-V虚拟化扩展 399
20.2.1 CPU虚拟化扩展 399
20.2.2 M模式下系统寄存器的扩展 400
20.2.3 HS模式下的系统寄存器 402
20.2.4 VS模式下的系统寄存器 404
20.3 RISC-V内存虚拟化 404
20.4 RISC-V虚拟化扩展中的新增指令 406
20.4.1 加载与存储虚拟机内存指令 406
20.4.2 虚拟化内存屏障指令 406
20.5 进入和退出虚拟机 407
20.5.1 异常陷入 408
20.5.2 异常返回 408
20.5.3 新增的中断与异常类型 409
20.6 中断虚拟化 410
20.6.1 虚拟中断注入 410
20.6.2 陷入与模拟 411
20.7 案例分析20-1:进入和退出虚拟机 412
20.7.1 进入虚拟机 413
20.7.2 退出虚拟机 414
20.8 案例分析20-2:建立虚拟化两阶段地址映射 415
20.8.1 建立第二阶段的地址映射 416
20.8.2 建立第 一阶段的地址映射 418
20.8.3 测试 419
20.9 案例分析20-3:在虚拟机中实现虚拟定时器 420
20.10 案例分析20-4:在VMM中加载和存储虚拟机内存地址 422
20.11 案例分析20-5:在VMM中模拟串口设备 424
20.12 实验 429
20.12.1 实验20-1:加载虚拟机1 429
20.12.2 实验20-2:加载虚拟机2 430
20.12.3 实验20-3:虚拟化地址映射 430
20.12.4 实验20-4:解析虚拟机陷入的指令 430
20.12.5 实验20-5:在VMM中模拟实现vPLIC 430
20.12.6 实验20-6:在虚拟机中加载并运行Linux内核 430
附录A 关于RISC-V体系结构自测题的参考答案与提示 431
附录B RV64I指令速查表 433
附录C RV64M指令速查表 437
附录D RV64常用伪指令速查表 439
^ 收 起
第 1章 RISC-V体系结构基础知识 1
1.1 RISC-V介绍 1
1.1.1 RISC-V指令集优点 1
1.1.2 RISC-V指令集扩展 2
1.1.3 RISC-V商业化发展 2
1.2 RISC-V体系结构介绍 3
1.2.1 RISC-V体系结构 3
1.2.2 采用RISC-V体系结构的常见处理器 3
1.2.3 RISC-V体系结构中的基本概念 4
1.2.4 SBI服务 5
1.3 RISC-V寄存器 6
1.3.1 通用寄存器 6
1.3.2 系统寄存器 7
1.3.3 U模式下的系统寄存器 8
1.3.4 S模式下的系统寄存器 9
1.3.5 M模式下的系统寄存器 11
1.4 香山处理器介绍 15
1.4.1 香山处理器体系结构 15
1.4.2 香山处理器的前端子系统 16
1.4.3 香山处理器的后端子系统 18
1.4.4 香山处理器的访存子系统 20
1.4.5 香山处理器的L2/L3高速缓存 25
第 2章 搭建RISC-V实验环境 29
2.1 实验平台 29
2.1.1 QEMU 29
2.1.2 NEMU 30
2.2 搭建实验环境 31
2.2.1 实验2-1:输出“Welcome RISC-V!” 31
2.2.2 实验2-2:单步调试BenOS和MySBI 32
2.3 BenOS和MySBI基础实验代码解析 34
2.3.1 MySBI基础代码分析 34
2.3.2 BenOS基础代码分析 37
2.3.3 合并BenOS和MySBI 41
2.4 QEMU RISC-V Linux实验平台 41
第3章 基础指令集 44
3.1 RISC-V指令集介绍 44
3.2 RISC-V指令编码格式 45
3.3 加载与存储指令 46
3.4 PC相对寻址 49
3.5 移位操作 53
3.6 位操作指令 55
3.7 算术指令 56
3.8 比较指令 57
3.9 无条件跳转指令 58
3.10 条件跳转指令 59
3.11 CSR指令 61
3.12 寻址范围 62
3.13 陷阱:为什么ret之后就进入死循环 62
3.14 实验 64
3.14.1 实验3-1:熟悉加载指令 64
3.14.2 实验3-2:PC相对地址寻址 64
3.14.3 实验3-3:memcpy()函数的实现 65
3.14.4 实验3-4:memset()函数的实现 65
3.14.5 实验3-5:条件跳转指令1 65
3.14.6 实验3-6:条件跳转指令2 66
3.14.7 实验3-7:子函数跳转 66
3.14.8 实验3-8:在汇编中实现串口输出功能 66
第4章 函数调用规范与栈 67
4.1 函数调用规范 67
4.2 入栈与出栈 70
4.3 RISC-V栈的布局 72
4.3.1 不使用FP的栈布局 72
4.3.2 使用FP的栈布局 74
4.3.3 栈回溯 76
4.4 实验 78
4.4.1 实验4-1:观察栈布局 78
4.4.2 实验4-2:观察栈回溯 78
第5章 GNU汇编器 79
5.1 编译流程与ELF文件 79
5.2 一个简单的汇编程序 82
5.3 汇编语法 84
5.3.1 注释 84
5.3.2 符号 84
5.4 常用的伪指令 85
5.4.1 对齐伪指令 85
5.4.2 数据定义伪指令 86
5.4.3 与函数相关的伪指令 87
5.4.4 与段相关的伪指令 87
5.4.5 与宏相关的伪指令 89
5.4.6 与文件相关的伪指令 91
5.5 RISC-V依赖特性 91
5.5.1 RISC-V特有的命令行选项 91
5.5.2 RISC-V特有的伪指令 92
5.6 实验 92
5.6.1 实验5-1:汇编语言练习—查找数 92
5.6.2 实验5-2:汇编语言练习—通过C语言调用汇编函数 92
5.6.3 实验5-3:汇编语言练习—通过汇编语言调用C函数 92
5.6.4 实验5-4:使用汇编伪操作实现一张表 92
5.6.5 实验5-5:汇编宏的使用 93
第6章 链接器与链接脚本 94
6.1 链接器 94
6.2 链接脚本 95
6.2.1 一个简单的链接程序 95
6.2.2 设置入口点 96
6.2.3 基本概念 97
6.2.4 符号赋值与引用 97
6.2.5 当前位置计数器 98
6.2.6 SECTIONS命令 99
6.2.7 常用的内置函数 101
6.3 加载重定位 103
6.3.1 BenOS重定位 103
6.3.2 OpenSBI和Linux内核重定位 105
6.4 链接重定位与链接器松弛优化 108
6.4.1 链接重定位 108
6.4.2 函数跳转优化 112
6.4.3 符号地址访问优化 114
6.5 实验 116
6.5.1 实验6-1:分析链接脚本 116
6.5.2 实验6-2:输出每个段的内存布局 116
6.5.3 实验6-3:加载地址不等于运行地址 117
6.5.4 实验6-4:设置链接地址 117
6.5.5 实验6-5:链接器松弛优化1 117
6.5.6 实验6-6:链接器松弛优化2 117
6.5.7 实验6-7:分析Linux 5.15内核的链接脚本 117
第7章 内嵌汇编代码 118
7.1 内嵌汇编代码基本用法 118
7.1.1 基础内嵌汇编代码 118
7.1.2 扩展内嵌汇编代码 118
7.1.3 内嵌汇编代码修饰符 120
7.1.4 使用汇编符号名字 121
7.1.5 内嵌汇编代码与宏结合 122
7.1.6 使用goto修饰词 122
7.1.7 小结 123
7.2 案例分析 124
7.3 注意事项 128
7.4 实验 128
7.4.1 实验7-1:实现简单的memcpy()函数 128
7.4.2 实验7-2:使用汇编符号名写内嵌汇编代码 128
7.4.3 实验7-3:使用内嵌汇编代码完善memset()函数 129
7.4.4 实验7-4:使用内嵌汇编代码与宏的结合 129
7.4.5 实验7-5:实现读和写系统寄存器的宏 129
7.4.6 实验7-6:goto模板的内嵌汇编代码 129
第8章 异常处理 130
8.1 异常处理基本概念 130
8.1.1 异常类型 130
8.1.2 同步异常和异步异常 131
8.1.3 异常入口和返回 131
8.1.4 异常返回地址 132
8.1.5 异常返回的处理器模式 133
8.1.6 栈的选择 133
8.2 与M模式相关的异常寄存器 133
8.2.1 mstatus寄存器 134
8.2.2 mtvec寄存器 134
8.2.3 mcause寄存器 135
8.2.4 mie寄存器 135
8.2.5 mtval寄存器 136
8.2.6 mip寄存器 136
8.2.7 mideleg和medeleg寄存器 136
8.2.8 中断配置 137
8.3 与S模式相关的异常寄存器 137
8.3.1 sstatus寄存器 137
8.3.2 sie寄存器 137
8.3.3 sip寄存器 138
8.3.4 scause寄存器 138
8.3.5 stvec寄存器 138
8.3.6 stval寄存器 139
8.4 异常上下文 139
8.4.1 保存异常上下文 141
8.4.2 恢复异常上下文 141
8.5 案例分析8-1:实现SBI系统调用 142
8.5.1 调用ECALL指令 142
8.5.2 实现SBI系统调用 143
8.6 案例分析8-2:BenOS的异常处理 148
8.6.1 设置异常向量表 148
8.6.2 保存和恢复异常上下文 149
8.6.3 异常处理 151
8.6.4 委托中断和异常 153
8.6.5 触发异常 153
8.7 实验 154
8.7.1 实验8-1:在SBI中实现串口输入功能 154
8.7.2 实验8-2:在BenOS中触发非法指令异常 155
8.7.3 实验8-3:输出触发异常时函数栈的调用过程 155
8.7.4 实验8-4:在MySBI中模拟实现RDTIME伪指令 155
第9章 中断处理与中断控制器 156
9.1 中断处理基本概念 156
9.1.1 中断类型 156
9.1.2 中断处理过程 157
9.1.3 中断委派和注入 158
9.1.4 中断优先级 158
9.2 CLINT 159
9.3 案例分析9-1:定时器中断 160
9.3.1 访问mtimer 160
9.3.2 在MySBI中实现定时器服务 160
9.3.3 定时器中断处理 161
9.3.4 打开中断总开关 163
9.3.5 小结 164
9.4 PLIC 164
9.4.1 中断号 165
9.4.2 中断优先级 166
9.4.3 中断使能寄存器 166
9.4.4 中断待定寄存器 166
9.4.5 中断优先级阈值寄存器 167
9.4.6 中断请求/完成寄存器 167
9.5 案例分析9-2:串口中断 167
9.5.1 初始化PLIC 168
9.5.2 使能串口0的接收中断 169
9.5.3 处理中断 169
9.6 实验 171
9.6.1 实验9-1:定时器中断 171
9.6.2 实验9-2:使用汇编函数保存和恢复中断现场 171
9.6.3 实验9-3:实现并调试串口0中断 171
第 10章 内存管理 172
10.1 内存管理基础知识 172
10.1.1 内存管理的“远古时代” 172
10.1.2 地址空间的抽象 174
10.1.3 分段机制 175
10.1.4 分页机制 175
10.2 RISC-V内存管理 178
10.2.1 页表分类 179
10.2.2 Sv39页表映射 180
10.2.3 Sv48页表映射 182
10.2.4 页表项描述符 183
10.2.5 页表属性 185
10.2.6 与地址转换相关的寄存器 186
10.3 物理内存属性与物理内存保护 187
10.3.1 物理内存属性 187
10.3.2 物理内存保护 188
10.4 案例分析10-1:在BenOS里实现恒等映射 190
10.4.1 页表定义 191
10.4.2 页表数据结构 193
10.4.3 创建页表 193
10.4.4 打开MMU 198
10.4.5 测试MMU 198
10.4.6 图解页表创建的过程 200
10.5 内存管理实验 204
10.5.1 实验10-1:建立恒等映射 204
10.5.2 实验10-2:为什么MMU无法运行 205
10.5.3 实验10-3:实现一个MMU页表的转储功能 205
10.5.4 实验10-4:修改页面属性 205
10.5.5 实验10-5:使用汇编语言来建立恒等映射 206
10.5.6 实验10-6:在MySBI中实现和验证PMP机制 206
第 11章 高速缓存 207
11.1 为什么需要高速缓存 207
11.2 高速缓存的访问延时 208
11.3 高速缓存的工作原理 210
11.4 高速缓存的映射方式 212
11.4.1 直接映射 212
11.4.2 全相联映射 213
11.4.3 组相联映射 213
11.4.4 组相联的高速缓存的例子 214
11.5 虚拟高速缓存与物理高速缓存 215
11.5.1 物理高速缓存 215
11.5.2 虚拟高速缓存 215
11.5.3 VIPT和PIPT 215
11.6 重名和同名问题 216
11.6.1 重名问题 217
11.6.2 同名问题 217
11.6.3 VIPT产生的重名问题 218
11.7 高速缓存策略 220
11.8 高速缓存的维护指令 221
11.8.1 高速缓存管理指令 221
11.8.2 高速缓存预取指令 222
第 12章 缓存一致性 224
12.1 为什么需要缓存一致性 224
12.2 缓存一致性的分类 225
12.2.1 缓存一致性协议发展历程 225
12.2.2 缓存一致性分类 226
12.2.3 系统缓存一致性问题 227
12.3 缓存一致性的解决方案 227
12.3.1 关闭高速缓存 228
12.3.2 使用软件维护缓存一致性 228
12.3.3 使用硬件维护缓存一致性 228
12.4 MESI协议 228
12.4.1 MESI协议简介 229
12.4.2 本地读写与总线操作 230
12.4.3 MESI状态转换 230
12.4.4 初始状态为I 231
12.4.5 初始状态为M 233
12.4.6 初始状态为S 234
12.4.7 初始状态为E 234
12.4.8 小结与案例分析 235
12.4.9 MOESI协议 237
12.5 高速缓存伪共享 237
12.6 两种缓存一致性控制器 239
12.6.1 CCI缓存一致性控制器 239
12.6.2 CCN缓存一致性控制器 240
12.7 案例分析12-1:伪共享的避免 241
12.8 案例分析12-2:DMA和高速缓存的一致性 242
12.8.1 从内存到设备的FIFO缓冲区 243
12.8.2 从设备的FIFO缓冲区到内存 243
12.9 案例分析12-3:自修改代码的一致性 244
12.10 实验 245
12.10.1 实验12-1:高速缓存伪共享 245
12.10.2 实验12-2:使用Perf C2C发现高速缓存伪共享 245
第 13章 TLB管理 246
13.1 TLB基础知识 247
13.2 TLB重名与同名问题 249
13.2.1 重名问题 249
13.2.2 同名问题 250
13.3 ASID 251
13.4 TLB管理指令 253
13.4.1 TLB维护指令介绍 253
13.4.2 TLB广播 254
13.4.3 SFENCE.VMA指令使用场景 256
13.5 TLB案例分析 256
13.5.1 TLB在Linux内核中的应用 256
13.5.2 ASID在Linux内核中的应用 257
13.5.3 Linux内核中的TLB维护操作 257
13.5.4 BBM机制 259
第 14章 原子操作 261
14.1 原子操作介绍 261
14.2 保留加载与条件存储指令 262
14.3 独占内存访问工作原理 263
14.3.1 独占监视器 264
14.3.2 独占监视器与缓存一致性 265
14.4 原子内存访问操作指令 266
14.4.1 原子内存访问指令工作原理 266
14.4.2 原子内存访问指令与LR/SC指令的效率对比 267
14.4.3 RISC-V中的原子内存访问指令 268
14.5 比较并交换操作 270
第 15章 内存屏障指令 275
15.1 内存屏障指令产生的原因 275
15.1.1 顺序一致性内存模型 276
15.1.2 处理器一致性内存模型 277
15.1.3 弱一致性内存模型 277
15.1.4 释放一致性内存模型 278
15.1.5 MCA模型 279
15.2 RISC-V约束条件 280
15.2.1 全局内存次序与保留程序次序 280
15.2.2 RVWMO的约束规则 281
15.3 RISC-V中的内存屏障指令 284
15.3.1 使用内存屏障的场景 284
15.3.2 FENCE指令 285
15.3.3 内置获取和释放屏障原语的指令 285
15.3.4 FENCE.I指令 286
15.3.5 SFENCE.VMA指令 286
15.4 RISC-V内存屏障指令移植指南 286
15.4.1 从RISC-V到x86体系结构 286
15.4.2 从RISC-V到ARM体系结构 287
15.4.3 Linux内核常用的内存屏障API函数 287
15.5 案例分析 288
15.5.1 消息传递问题 288
15.5.2 单方向内存屏障与自旋锁 289
15.5.3 邮箱传递消息 290
15.5.4 关于DMA的案例 291
15.5.5 在Linux内核中使指令高速缓存失效 291
15.6 模拟和测试内存屏障故障 291
15.6.1 使用Litmus测试工具集 292
15.6.2 编写C程序来模拟 295
15.7 实验 297
15.7.1 实验15-1:编写Litmus脚本并测试内存一致性1 297
15.7.2 实验15-2:编写Litmus脚本并测试内存一致性2 298
第 16章 合理使用内存屏障指令 299
16.1 存储缓冲区与写内存屏障指令 300
16.2 无效队列与读内存屏障指令 305
16.3 内存屏障指令总结 307
16.4 案例分析:Linux内核中的内存屏障指令 308
16.4.1 第 一次使用内存屏障指令 309
16.4.2 第二次使用内存屏障指令 310
16.4.3 第三次使用内存屏障指令 313
16.4.4 第四次使用内存屏障指令 314
16.4.5 小结:内存屏障指令的使用 315
16.5 实验 315
16.5.1 实验16-1:验证和测试内存一致性1 315
16.5.2 实验16-2:验证和测试内存一致性2 315
16.5.3 实验16-3:验证和测试内存一致性3 315
第 17章 与操作系统相关的内容 316
17.1 C语言常见陷阱 317
17.1.1 数据模型 317
17.1.2 数据类型转换与整型提升 318
17.1.3 移位操作 320
17.2 创建进程 320
17.2.1 进程控制块 320
17.2.2 0号进程 321
17.2.3 do_fork()函数的实现 323
17.2.4 进程上下文切换 324
17.2.5 新进程的第 一次执行 326
17.3 简易进程调度器 327
17.3.1 扩展进程控制块 327
17.3.2 就绪队列 327
17.3.3 调度类 328
17.3.4 简易调度器的实现 329
17.3.5 自愿调度 330
17.3.6 抢占调度 331
17.3.7 测试用例 333
17.3.8 关于调度的思考 333
17.4 让进程运行在用户模式 335
17.5 系统调用 338
17.5.1 系统调用介绍 338
17.5.2 在用户模式下调用SVC指令 338
17.5.3 在内核模式下对系统调用的处理 339
17.5.4 系统调用表 340
17.6 实现clone系统调用 341
17.7 实验 343
17.7.1 实验17-1:进程创建 343
17.7.2 实验17-2:进程调度 343
17.7.3 实验17-3:让进程运行在用户模式 343
17.7.4 实验17-4:新增一个malloc()系统调用 343
17.7.5 实验17-5:新增一个clone()系统调用 344
第 18章 可伸缩矢量计算与优化 345
18.1 矢量计算基本概念 345
18.1.1 SISD与SIMD 345
18.1.2 定长计算与可变长矢量计算 347
18.1.3 通道 347
18.1.4 矢量与标量 347
18.2 RVV寄存器 348
18.2.1 矢量寄存器 348
18.2.2 mstatus寄存器中的矢量上下文状态 348
18.2.3 vtype寄存器 348
18.2.4 vl寄存器 350
18.2.5 vlenb寄存器 351
18.2.6 vstart寄存器 351
18.3 配置编译和运行环境 351
18.3.1 搭建编译环境 351
18.3.2 运行第 一个“hello RVV!”程序 352
18.3.3 单步调试汇编程序 353
18.3.4 单步调试C语言与汇编混合程序 355
18.4 RVV指令格式 357
18.5 配置指令 358
18.6 加载和存储指令 360
18.6.1 单位步长模式 360
18.6.2 任意步长模式 363
18.6.3 聚合加载/离散存储 364
18.6.4 打包数据的加载与存储 365
18.6.5 首次异常加载指令 367
18.6.6 加载和存储全部矢量数据 368
18.7 矢量掩码指令 369
18.7.1 逻辑操作指令 369
18.7.2 VCPOP.M指令 369
18.7.3 VFIRST.M指令 370
18.7.4 VMSBF.M指令 370
18.7.5 VMSIF.M指令 371
18.7.6 VMSOF.M指令 372
18.8 矢量整型算术指令 372
18.8.1 加宽和变窄算术指令 372
18.8.2 加法和减法指令 373
18.8.3 加宽模式的加法和减法指令 374
18.8.4 位操作指令 376
18.8.5 移位操作指令 376
18.8.6 比较指令 376
18.8.7 数据搬移指令 377
18.9 案例分析18-1:使用RVV指令优化strcmp()函数 377
18.9.1 使用纯汇编方式 378
18.9.2 测试 379
18.10 案例分析18-2:RGB24转BGR24 380
18.10.1 使用C语言实现RGB24转BGR24 380
18.10.2 使用RVV指令优化 380
18.10.3 测试 381
18.11 案例分析18-3:4 × 4矩阵乘法运算 382
18.11.1 使用C语言实现4 × 4矩阵乘法运算 382
18.11.2 使用RVV指令优化 383
18.11.3 测试 387
18.12 案例分析18-4:使用RVV内置函数 388
18.13 案例分析18-5:自动矢量优化 388
18.14 术语 390
18.15 实验 391
18.15.1 实验18-1:RGB24转BGR32 391
18.15.2 实验18-2:8 × 8矩阵乘法运算 391
18.15.3 实验18-3:使用RVV指令优化strcpy()函数 391
18.15.4 实验18-4:使用RVV内置函数优化 391
18.15.5 实验18-5:使用RVV优化转置矩阵的求法 392
第 19章 压缩指令扩展 393
19.1 RISC-V指令集的特点 393
19.2 RVC支持的指令格式与指令编码 394
第 20章 虚拟化扩展 396
20.1 虚拟化技术介绍 396
20.1.1 虚拟化技术的发展历史 396
20.1.2 虚拟机管理程序的分类 398
20.1.3 内存虚拟化 398
20.1.4 I/O虚拟化 399
20.2 RISC-V虚拟化扩展 399
20.2.1 CPU虚拟化扩展 399
20.2.2 M模式下系统寄存器的扩展 400
20.2.3 HS模式下的系统寄存器 402
20.2.4 VS模式下的系统寄存器 404
20.3 RISC-V内存虚拟化 404
20.4 RISC-V虚拟化扩展中的新增指令 406
20.4.1 加载与存储虚拟机内存指令 406
20.4.2 虚拟化内存屏障指令 406
20.5 进入和退出虚拟机 407
20.5.1 异常陷入 408
20.5.2 异常返回 408
20.5.3 新增的中断与异常类型 409
20.6 中断虚拟化 410
20.6.1 虚拟中断注入 410
20.6.2 陷入与模拟 411
20.7 案例分析20-1:进入和退出虚拟机 412
20.7.1 进入虚拟机 413
20.7.2 退出虚拟机 414
20.8 案例分析20-2:建立虚拟化两阶段地址映射 415
20.8.1 建立第二阶段的地址映射 416
20.8.2 建立第 一阶段的地址映射 418
20.8.3 测试 419
20.9 案例分析20-3:在虚拟机中实现虚拟定时器 420
20.10 案例分析20-4:在VMM中加载和存储虚拟机内存地址 422
20.11 案例分析20-5:在VMM中模拟串口设备 424
20.12 实验 429
20.12.1 实验20-1:加载虚拟机1 429
20.12.2 实验20-2:加载虚拟机2 430
20.12.3 实验20-3:虚拟化地址映射 430
20.12.4 实验20-4:解析虚拟机陷入的指令 430
20.12.5 实验20-5:在VMM中模拟实现vPLIC 430
20.12.6 实验20-6:在虚拟机中加载并运行Linux内核 430
附录A 关于RISC-V体系结构自测题的参考答案与提示 431
附录B RV64I指令速查表 433
附录C RV64M指令速查表 437
附录D RV64常用伪指令速查表 439
^ 收 起
作者简介 奔跑吧Linux社区 由一群热爱开源的工程师组成,致力于开源硬件和开源软件的推广。 审校者简介 香山处理器团队 面向世界的体系结构创新开源平台,目前已形成由多家企业组成的香山联合开发团队。其中,香山处理器是由中国科学院计算技术研究所发起的开源高性能RISC-V处理器核项目。 龙蜥社区RISC-V SIG 坚持开放、开源,致力于龙蜥社区以及 RISCV 软硬件生态的共建和推广。 进迭时空 专注研发新一代高性能RISC-V处理器和计算系统,让开发者基于RISC-V芯片更自由地开发更有创意的新应用。
本书旨在介绍RISC-V体系结构的设计和实现。本书首先介绍RISC-V体系结构的基础知识、实验环境搭建、常用指令、函数调用规范与栈,然后讲述GNU汇编器、链接器、链接脚本和GCC内嵌汇编代码,接着讨论RISC-V体系结构中的异常处理、中断、内存管理、高速缓存、缓存一致性、TLB管理、原子操作、内存屏障指令,后阐述RSIC-V体系结构中的压缩指令扩展、虚拟化扩展等。
本书不仅适合软件开发人员阅读,还可以作为计算机相关专业和相关培训机构的教材。
本书不仅适合软件开发人员阅读,还可以作为计算机相关专业和相关培训机构的教材。
比价列表