编辑“︁
RISC-V
”︁(章节)
跳转到导航
跳转到搜索
警告:
您没有登录。如果您进行任何编辑,您的IP地址会公开展示。如果您
登录
或
创建账号
,您的编辑会以您的用户名署名,此外还有其他益处。
反垃圾检查。
不要
加入这个!
== 设计 == === 指令子集 === RISC-V 指令使用[[模組化設計|模塊化設計]],包括幾個可以互相替換的基本指令集,以及額外可以選擇的擴充指令集。所有基本跟擴充的指令集都是由科技產業、研究機構跟學術界合作開發的。基本指令集規範了指令跟他們的編碼、控制流程、暫存器數目(以及它們的長度)、記憶體跟定址方式、邏輯(整數)運算以及其他。只要有軟體以及一個通用的編譯器的支援,只用基本指令集就可以製作一個簡單的通用型的電腦。 標準的擴充指令集可以搭配所有的基本指令集以及其他擴充指令集,而不會發生衝突。 很多 RISC-V 電腦可能使用精簡擴充指令集來降低電力消耗、程式的大小以及記憶體的使用。未來也有計畫支援[[hypervisor]]和[[虛擬化]]。<ref name="priv-isa" /> 只要再加上一個監督指令集 (S) 的擴充,以及以下 RVGC 指令集,就有足夠的指令可以支援一個 [[Unix]]-style [[作業系統]]。 {| class="wikitable" |- ! 指令集名稱 ! 描述 ! 版本 ! 狀態{{efn|name=frozen|標記為凍結狀態的模塊代表其最終的功能已完備,而且在提交批准之前預計不會發生重大變化。}} |- ! colspan=4 | 基本指令集 |- | RVWMO || RISC-V 弱內存模型 || 2.0 || {{Yes|已批准}} |- | RV32I || 基本整數指令集, 32位元 || 2.1 || {{Yes|已批准}} |- | RV32E || 基本整數指令集(嵌入式系統), 32位元, 16 個暫存器 || 2.0 || {{Yes|已批准}} |- | RV64I || 基本整數指令集, 64位元 || 2.1 || {{Yes|已批准}} |- | RV64E || 基本整數指令集(嵌入式系統), 64位元, 16 個暫存器 || 2.0 || {{Yes|已批准}} |- | RV128I || 基本整數指令集, 128位元 || 1.7 || {{No|開放}} |- ! colspan=4 | 標準擴充指令集 |- | M || 整數乘除法標準擴充 || 2.0 || {{Yes|已批准}} |- | A || 不可中斷指令(Atomic)標準擴充 || 2.1 || {{Yes|已批准}} |- | F || 單精度浮點標準擴充 || 2.2 || {{Yes|已批准}} |- | D || 雙精度浮點標準擴充 || 2.2 || {{Yes|已批准}} |- | Zicsr || 控制與狀態暫存器 || 2.0 || {{Yes|已批准}} |- | Zifencei || 指令抓取[[内存屏障|屏障]] || 2.0 || {{Yes|已批准}} |- | G || 所有以上的擴充指令集以及基本指令集的總和的簡稱 || {{n/a}} || {{n/a}} |- | Q || 四精度浮點標準擴充 || 2.2 || {{Yes|已批准}} |- | L || 十進位浮點標準擴充 || 0.0 || {{No|開放}} |- | C || 壓縮指令標準擴充 || 2.0 || {{Yes|已批准}} |- | B || 位元運算標準擴充 || 1.0 || {{Yes|已批准}} |- | J || 動態指令翻譯標準擴充 || 0.0 || {{No|開放}} |- | T || 順序記憶體存取標準擴充 || 0.0 || {{No|開放}} |- | P || 單指令多資料流(SIMD)運算標準擴充 || 0.9.10 || {{No|開放}} |- | V || 向量運算標準擴充 || 1.0 || {{No|开放}} |- | Zk || 標量加密標準擴充 || 1.0.1 || {{Yes|已批准}} |- | H || Hypervisor 標準擴充 || 1.0 || {{Yes|已批准}} |- | S || Supervisor 標準擴充 || 1.12 || {{Yes|已批准}} |- | Zam || 非對齊不可中斷指令標準擴充 || 0.1 || {{No|開放}} |- | Zihintpause || 暫停提示 || 2.0 || {{Yes|已批准}} |- | Zihintntl || 非時間局部性提示 || 0.3 || {{Yes|已批准}} |- | Zfa || 額外浮點運算指令標準擴充 || 1.0 || {{Yes|已批准}} |- | Zfh || 半精度浮點標準擴充 || 1.0 || {{Yes|已批准}} |- | Zfhmin || 半精度浮點最小集標準擴充 || 1.0 || {{Yes|已批准}} |- | Zfinx || 整數寄存器單精度浮點標準擴充 || 1.0 || {{Yes|已批准}} |- | Zdinx || 整數寄存器雙精度浮點標準擴充 || 1.0 || {{Yes|已批准}} |- | Zhinx || 整數寄存器半精度浮點標準擴充 || 1.0 || {{Yes|已批准}} |- | Zhinxmin || 整數寄存器半精度浮點最小集標準擴充 || 1.0 || {{Yes|已批准}} |- | Zmmul || 整數純乘法標準擴充 || 1.0 || {{Yes|已批准}} |- | Ztso || 全存儲排序標準擴充 || 1.0 || {{No|冻结}} |} {{noteslist}} 為了分辨各種不同的指令組合,非特權指令集標準中訂定了一些專有名詞。首先先指明基本指令集的種類,包括表示 RISC-V 的代號 RV,然後是暫存器的寬度跟其他變化,例如 RV64I 或 RV32E。然後用上表的字母(以及表列的順序)表示用了哪種擴充指令,例如 RV64IMAFD。 基本指令集、擴充整數或浮點運算、多CPU系統使用的同步指令擴充,標準擴充指令MAFD被認為是大部分的一般運算都需要的,所以有一個字母的簡稱 G 用來表示 IMAFDZicsr_Zifencei。 使用嵌入式系統的一個小的32位元電腦可能用 RV32EC,而大型的64位元電腦可以用 RV64GC,即 RV64IMAFDCZicsr_Zifencei 的簡稱。 隨著擴充指令集數量的增加,指令集標準提供了另外一種命名方式,用 Z 字首緊接著字母名稱表示標準擴充,例如 Zifencei 表示指令抓取屏障擴充。 === 寄存器集 === RISC-V 有 32 個整數[[暫存器]](在嵌入式版本則是 16 個)。當浮點延伸集被實作的時候,還有 32 個浮點暫存器。除了「記憶體存取指令」之外,一般指令「只能」定址暫存器而無法存取記憶體。 如同有些 RISC 指令集(MIPS, SPARC, DEC Alpha),其中一個暫存器為「[[零暫存器]]」(zero register),剩下的暫存器為通用暫存器。在 RISC-V 當中,第一個整數暫存器是零暫存器,儲存數值到零暫存器是沒有作用的,而讀取零暫存器的數值將始終得到 0。使用零暫存器可以讓指令集設計更簡單。比方說,把「暫存器 X 複製到暫存器 Y」 ({{code|MOV Y, X}}),可以使用「將暫存器 X 與 0 相加後,複製到暫存器 Y」 ({{code|ADD Y, X, r0}}) 實作。 RISC-V 有提供「控制暫存器」及「狀態暫存器」,但是 user-mode 程式只能存取用來「量測效能」及「浮點管理」的部分。 RISC-V 並沒有指令可以儲存和回復(save and restore。註:通常用於 context-switch,中斷處理,或是函數呼叫)多個暫存器。這些設計在 RISC-V 當中,被認為是不必要的,過於複雜的,可能過慢的設計。 === 記憶體存取 === 就像許多的 RISC 一樣,RISC-V 屬於[[載入-儲存架構]],只有 load 與 store 指令可以存取記憶體。 Load 和 Store 指令可以直接使用程式碼中的常數、在堆疊中的本地變數、或是資料結構中的內容。定址的方式是使用基底暫存器與 12-bit 的 signed 相對地址 (± 2KB)。如果基底暫存器是 0,則資料或是常數可以在低位址,或是高位址(負的相對地址,導致繞回到高的記憶體地址。比方說 ROM 的記憶體地址)。 記憶體的定址單位是 8-bit 的 byte,以 little-endian 存放在記憶體。Load 與 Store 支援的資料長度從 8-bit 到電腦的 word 大小。記憶體存取並不需要對齊到 word 的大小,不過如果有對齊的話,可以增加效能。這項功能可以減小程式碼大小,而且透過軟體的模擬,還可以簡化硬體的設計(會觸發一個「對齊失敗」的中斷)。 和其他類似成功的電腦一樣,RISC-V 也是 little-endian。這稍稍降低了複雜度與成本,因為所有大小的 word 的讀取都遵循一樣的順序。舉例來說,RISC-V 的指令集都是從最低位址的 byte 開始解碼。RISC-V 的規格書保留了實作 big-endian 的可能性。 如同許多的 RISC 指令集一樣,RISC-V 並沒有可以「寫入多個暫存器」的定址模式。比方說:不支援 auto-incrementing(像是 {{code|*ptr++}} 就無法使用一個指令完成,而必須拆解成「一個 load 指令」及「一個 {{code|ADDI}} 指令」。) RISC-V 管理 CPU 與 [[线程|thread]] 之間的共用記憶體的方式是確保在單一的 thread 當中,記憶體存取指令的執行順序永遠是遵照原本的編譯順序。不過在不同的 thread 以及在 I/O 裝置之間,RISC-V 不保證存取的順序──除非有像 {{code|FENCE}} 這樣的指令出現。 {{code|FENCE}} 保證在其之前的執行結果,一定會被其後的 thread 或 I/O 裝置看到。{{code|FENCE}} 有 8-bit 可以分別指定 memory read/write 與 I/O read/write 的各種組合順序。透過這些組合,{{code|FENCE}} 可以保證記憶體與 memory-mapped I/O 之間的執行順序。比方說:其中一個組合是可以在不影響 I/O 運算的情況下,只保證記憶體的讀取和寫入順序。也就是說,如果 I/O 運算可以和記憶體同時執行的話,{{code|FENCE}} 不會強迫他們之間要互相等待。單一 CPU 上執行單一 thread 的情況下,可以把 {{code|FENCE}} 視作 {{code|NOP}} 指令。 有些 RISC CPUs(例如:MIPS、PowerPC、DLX、Berkeley's RISC-I)在 Load/Save 指令當中使用 16-bit 位移。使用 load upper word 指令來設定最高的 16-bit。這讓最高的 16-bit 資料可以很容易被設定,而不需要位移指令。然而,大部分使用 load upper word 的時機都是為了要載入一個常數(比如:地址)。RISC-V 則是使用類似 SPARC 12-bit 與 20-bit 的設計,而 RISC-V 所採用的 12-bit 設計可以讓指令更小。也就是說,這使得 32-bit 的 load/store 指令,就算需要在 32 個暫存器(需要 5-bit 定址)當中選兩個來用(一共 10-bit),還是有足夠的 bit 數目來支援 RISC-V 的可變長度指令編碼 (variable-length instruction coding)。 注:請參考 32-bit 的 I-type 指令格式,就會發現這個 12-bit 位移比 16-bit 來得優異的地方。這使得 32-bit 的空間當中,扣掉 12-bit 的常數值,以及最低的兩個 bit 為 11(表示這是一個 32-bit 長的指令),再扣掉兩個 5-bit 的暫存器位址,還有將近 8-bit 的空間可以留給 opcode 及 func3 。如果是使用 16-bit immediate 的話,會使得空間不夠放下兩個 5-bit 的暫存器位址。舉例來說,{{code|SLLI Rd, Rs, immediate}} (Shift Left Logical Immediate) 將會被迫拆解成兩個指令來完成:{{code|LDR Ri, immediate; SLLI Rd, Rs, Ri}}。 === 立即數 === RISC-V 讀取 32-bit 常數與位址是透過設定 upper 20-bit 的指令達到的。{{code|LUI}} 指令(Load Upper Immediate)把(指令中的)20-bit 讀取到暫存器的 31~12 bits 當中。 另一個 {{code|AUIPC}} 指令,也是一樣讀取 upper 20-bit,同時又加上 PC(Program Counter)之後,存放到某個基底暫存器。這個指令讓[[地址无关代码]]能夠支援「相對於程式碼位置的 32-bit 地址」。 這個基底暫存器可以再搭配 12-bit 位移,使用在 Load 與 Store 指令當中。如果需要的話,也可以使用 {{code|ADDI}} 指令,將 lower 12-bit 的常數加到一個暫存器中(注:這樣就完成一個完整的 32-bit 常數讀取)。在 64-bit 架構下,{{code|LUI}} 與 {{code|AUIPC}} 執行的結果會被[[符号扩充|位元擴充]]至 64-bit。 有些高速的 CPU 會把一些指令「融合」成一個指令。比如說:上述的 {{code|LUI}} 與 {{code|AUIPC}} 就很適合和 Load/Save 指令一起融合。 === 函數呼叫、跳躍和分支 === RISC-V 的函數呼叫 {{code|JAL}}(Jump and Link)把回傳地址放入一個暫存器。由於相較於其他把回傳地址存入堆疊的設計,它省下了一次對堆疊記憶體的存取,所以在許多的處理器設計中是比較快速的。 {{code|JAL}} 有一個 20-bit signed 位移。這個位移會被乘上 2 之後,加到 PC 當中,以產生指向 32 位元指令的相對位址。如果該位址沒有對齊到 32-bit 位址(即不可被 4 整除),CPU 會觸發一個[[异常处理|例外]]。 RISC-V 的 {{code|JALR}}(Jump and Link Register)指令與 {{code|JAL}} 相似,但是 {{code|JALR}} 是把一個 12-bit 的相對位移和某一個暫存器相加,而 {{code|JAL}} 是用 20-bit 的相對位移與 PC 相加。 {{code|JALR}} 的指令格式與使用暫存器的 load/store 指令相似。搭配另一個設定高位 20-bit 的基底暫存器,可以組成一個 32-bit 的地址(可以是絕對位址,例如 {{code|LUI}};或是相對於 PC 的位址,例如 {{code|AUIPC}})。(使用零暫存器當基底暫存器,則可以跳到 0 ± 2KB 的絕對位址) 透過使用零暫存器,兩種無條件跳躍:「20-bit PC 相對位址」以及「暫存器為底的 12-bit」,分別使用 {{code|JAL}} 與 {{code|JALR}} 兩個指令來實作。在這個情況下,因為目的地暫存器是零存器,所以回傳位址會被丟棄。 如同許多的 RISC 系統,在一個函數呼叫當中,RISC-V 編譯器必須使用多個指令將暫存器一個一個地存到堆疊當中,然後在函數結束的時候,一個一個地將暫存器自堆疊中還原。RISC-V 沒有「儲存多個」或是「還原多個」暫存器的指令,因為這些指令被認為會讓 CPU 變得過於複雜,而且可能更慢。<ref name="riscvc"/>然而 RISC-V 的這種設計會增加程式大小,而設計者原本的規劃是透過呼叫子程序來減少程式大小。<ref name="isacompressed"/> RISC-V 沒有{{link-en|條件碼暫存器|Status register}}。設計者相信條件碼暫存器會讓高速 CPU 的設計更加複雜,因為它強迫了不同執行階段的指令之間進行交互。這樣的設計會使得高精度計算變得更複雜,有些數值計算需要更多的能量。 相反地,RISC-V 透過比較兩個暫存器來實現分支,指令包括:相等、不相等、小於、無號數小於、大於、無號數大於。十種「比較分支」運算,可以透過反轉運算元順序的方式,只用上述六種指令實作出來。舉例來說:「如果大於時跳躍」可以用運算元順序相反的「如果小於或等於時跳躍」來實作。 這六種比較分支指令具有 12-bit 的有號位移,可以跳到 PC±4KB 的範圍內。 RISC-V 要求 CPU 實作「預設分支預測」(default branch prediction)。如果是往回跳躍 (例如:{{code|do {...} while (expr)}}中的 {{code|expr}} 判斷式),CPU 要預測跳躍會發生,也就是預測 {{code|expr}} 「會」成立。如果是向前跳躍(例如:{{code|if (expr) {...} else {...}}} 中的 {{code|else}} 部分),CPU 預測這個跳躍會發生,也就是預測 {{code|expr}} 「不會」成立。CPU 判斷往回或向前的方法,是看指令中相對位址的最高位元,也就是有號數(signed bit)的部分:如果是 1,表示是負數,要往回跳躍;如果是 0,表示是正數,要向前跳躍。當然,複雜的 CPU 實作也可以加入更多的分支預測。 RISC-V 手冊也建議軟體(如:編譯器)利用預設分支預測的特性,來避免分支造成 [[Pipelines|pipeline]] 被停滯。方法就是利用上一段提到的 signed bit 來「暗示」 CPU 這個分支會不會發生。所以,就是算是簡單又便宜的 CPU ,也可以透過編譯器來優化效能。如果有需要,編譯器也可以透過統計等方式來優化效能。 所以,為了避免不必要的分支預測電路(以及不必要的 pipeline 停滯),無條件跳躍不要用「比較分支」來實作。 RISC-V 並不支援「條件執行」指令(conditional execution,註:當某個條件成立的時候,才執行該指令)。設計者宣稱沒有這種設計的 CPU 比較容易設計,而且編譯器在進行優化的時候,也比較不容易假設錯誤。設計者宣稱高速又不照順序執行的 CPU 反正都會同時執行正反兩種結果,之後再丟棄其中一個。他們也宣稱,即使在簡單的 CPU 當中,條件執行其實是比較沒有價值的,不如跳躍預測來的有用。不使用條件執行的程式碼會比較大,但是他們宣稱壓縮指令集在大部分的情況下,可以解決這樣的問題。 許多的 RISC 設計都有「[[延迟间隙|分支延遲槽]]」(branch delay slot),用來充份使用跳躍指令的下一個記憶體位址,這可以略略增加整體的 CPU 效能。RISC-V 並不支援這個功能,因為他會讓多時序、超純量,以及 long pipeline 變得很複雜。而動態分支預測其實已經做得很好,可以不需要這個功能了。 === 算术和逻辑集 === RISC-V 把數學運算指令歸類到一個很小的 I 子集當中,包括:加法、減法、位移、位元運算,及比較分支。這些可以使用軟體的方式去模擬其他大部分的 RISC-V 指令(atomic 運算是值得一提的例外)。RISC-V 目前沒有「數開頭有幾個零」以及一些用來加速軟體浮點運算的位元運算。 整數乘法子集(M 子集)包括:有號數與無號數的乘法與除法。 [[浮點數|浮點]]子集(F 子集)包括單精度運算,以及類似於整數的「比較分支」。它需要額外的 32 個浮點暫存器,這些暫存器是與整數暫存器分開的。雙精度浮點子集(D 子集)一般假設浮點暫存器是 64 位元,而且會與 F 子集一起協作。RISC-V 亦有定義四精度 128-bit 浮點子集(Q 子集)。沒有支援硬體浮點指令的 RISC-V CPU,依舊可以使用軟體的浮點程式庫。 RISC-V 在遇到運算錯誤的時候,並不會抛出[[异常处理|异常]],包括:[[算術溢出|overflow]]、[[算术下溢|underflow]]、subnormal 及 [[除以零#计算机科学|divide by zero]]。相反的,整數運算和浮點運算都會產生合理的預設數值,而且浮點運算指令還會設定狀態位元。Divide-by-zero 可以透過在除法運算之後放置分支指令來發現。這些狀態位元可以也可以被作業系統或是定期的中斷檢查到。 === 原子内存操作 === RISC-V 支援計算機在多個 CPU 與[[线程]]之間共享記憶體。RISC-V 的標準記憶體同步模式是「{{link-en|釋放一致|Release consistency}}」原則。也就是說,讀取和寫入順序可以被重排,但是有些讀取可以被設定成「獲取」(acquire)運算,必須在其後的存取之前被執行;有些寫入可以被當作「釋放」(release)運算,必須在其之前的存取的後面執行。 基本指令集包含了以[[内存屏障|{{code|FENCE}}指令]]提供的最小支援,來保證記憶體存取順序。儘管這已經足夠了({{code|FENCE R, RW}} 提供「獲取」,{{code|FENCE RW, W}} 提供「釋放」),使用組合操作指令可以更有效率。 原子操作子集(A 子集)支援兩種類型的原子內存操作,以實現{{link-en|釋放一致性|Release consistency}}。首先,它提供了通用的 [[Load-link/store-conditional|load-reserved {{code|lr}} 及 store-conditional {{code|sc}} 指令]]。{{code|lr}}執行加載,並嘗試為其執行緒保留該地址。僅當該保留未被來自另一個來源的干預性寫入破壞時,才會執行對保留地址的 store-conditional {{code|sc}}。如果寫入成功,則將零放入目標暫存器中;如果失敗,則以非零值表示軟體需要重試操作。在任何一種情況下,保留都會被釋放。 第二組原子指令 AMO(Atomic Memory Operation)執行 [[Read-modify-write]] 操作:讀取(可選為讀取-獲取)到目標暫存器,然後執行讀出值和來源暫存器值之間的操作,然後寫入(可選為寫入-釋放)結果。將記憶體屏障設計為可選的,允許了多個操作的組合。每個 AMO 的操作碼中都有「獲取」及「釋放」位元,用於啟用可選的記憶體屏障。
摘要:
请注意,所有对Local Chinese Wikipedia的贡献均可能会被其他贡献者编辑、修改或删除。如果您不希望您的文字作品被随意编辑,请不要在此提交。
您同时也向我们承诺,您提交的内容为您自己所创作,或是复制自公共领域或类似自由来源(详情请见
Project:著作权
)。
未经许可,请勿提交受著作权保护的作品!
取消
编辑帮助
(在新窗口中打开)
导航菜单
个人工具
未登录
讨论
贡献
创建账号
登录
命名空间
页面
讨论
大陆简体
不转换
简体
繁體
大陆简体
香港繁體
澳門繁體
大马简体
新加坡简体
臺灣正體
查看
阅读
编辑
查看历史
更多
搜索
导航
首页
最近更改
随机页面
MediaWiki帮助
工具
链入页面
相关更改
特殊页面
页面信息