RISC-V体系结构编程与实践

978-7-115-60360-9
作者: 奔跑吧Linux社区
译者:
编辑: 谢晓芳

图书目录:

目  录

第 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

详情

本书旨在介绍RISC-V体系结构的设计和实现。本书首先介绍RISC-V体系结构的基础知识、实验环境搭建、常用指令、函数调用规范与栈,然后讲述GNU汇编器、链接器、链接脚本和GCC内嵌汇编代码,接着讨论RISC-V体系结构中的异常处理、中断、内存管理、高速缓存、缓存一致性、TLB管理、原子操作、内存屏障指令,最后阐述RSIC-V体系结构中的压缩指令扩展、虚拟化扩展等。 本书不仅适合软件开发人员阅读,还可以作为计算机相关专业和相关培训机构的教材。

图书摘要

相关图书

电子硬件工程师入职图解手册  硬件知识篇
电子硬件工程师入职图解手册 硬件知识篇
Altium Designer 22电路设计与仿真实战从入门到精通
Altium Designer 22电路设计与仿真实战从入门到精通
龙芯嵌入式系统原理与应用开发
龙芯嵌入式系统原理与应用开发
龙芯嵌入式系统软硬件平台设计
龙芯嵌入式系统软硬件平台设计
GPU编程实战(基于Python和CUDA)
GPU编程实战(基于Python和CUDA)
电梯结构与原理
电梯结构与原理

相关文章

相关课程