內容簡介
本書旨在介紹RISC-V體系結構的設計和實現。本書首先介紹RISC-V體系結構的基礎知識、實驗環境搭建、常用指令、函式調用規範與棧,然後講述GNU彙編器、連結器、連結腳本和GCC內嵌彙編代碼,接著討論RISC-V體系結構中的異常處理、中斷、記憶體管理、高速快取、快取一致性、TLB管理、原子操作、記憶體屏障指令,最後闡述RSIC-V體系結構中的壓縮指令擴展、虛擬化擴展等。
本書不僅適合軟體開發人員閱讀,還可以作為計算機相關專業和相關培訓機構的教材。
作者簡介
作者簡介 奔跑吧Linux社區 由一群熱愛開源的工程師組成,致力於開源硬體和開源軟體的推廣。 審校者簡介 香山處理器團隊 面向世界的體系結構創新開源平台,目前已形成由多家企業組成的香山聯合開發團隊。其中,香山處理器是由中國科學院計算技術研究所發起的開源高性能RISC-V處理器核項目。 龍蜥社區RISC-V SIG 堅持開放、開源,致力於龍蜥社區以及 RISCV 軟硬體生態的共建和推廣。 進迭時空 專注研發新一代高性能RISC-V處理器和計算系統,讓開發者基於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.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