在2023年初,達坦科技發起成立硬件設計學習社區,邀請所有有志於從事數字芯片設計的同學加入我們的學習互助自學小組,以理解數字芯片設計的精髓,強化理論知識的同時提升實操技能,繼而整體提升設計能力。現在,完成第一期學習的同學整理了MIT6.175和MIT6.375的關鍵內容以及Lab實踐的學習筆記。
6.175和6.375的課程和Lab學習都有一定的難度,要求採用Bluespec語言實現RISC-V處理器,並支持多級流水、分支預測、緩存、異常處理、緩存一致性等功能。此外,Lab環節還涉及軟硬件聯合開發,要求基於所實現的RISC-V處理器運行真實的RISC-V程序,並給出性能評估。希望第一期學員(GitHub:kazutoiris )的學習筆記對想從事數字芯片設計的工程師有所幫助。
MIT 6.175
環境搭建虛擬機場景建議使用 Ubuntu 20.04 + bsc + connectal 進行環境初始化。
⚠️ Alma Linux/Rocky Linux/CentOS 對於 connectal 支持有限,可能會影響到後期仿真。
💡 使用 bsvbuild.sh 可以很方便的進行仿真,而且還支持了一鍵波形導出等功能。
Docker 場景建議使用 kazutoiris/connectal 鏡像。在 Docker Hub 上的部分鏡像使用的 bsc 是舊版,而且並沒有安裝 connectal。我的鏡像由 Ubuntu 22.04 + bsc 2023.1 + connectal + bsvbuild.sh 四部分組成,足夠滿足 Lab 要求。
version: "3.9"
services:
connectal:
image: kazutoiris/connectal:latest
volumes:
- .:/root
network_mode: none
四種 FIFO (Lab 4)
Lab4 的要求是設計各種 Depth-N FIFO。可併發 FIFO 允許流水線級與級之間相互連接而不引入額外的調度約束。一個可併發 FIFO 需要實現可併發的 enqueue 和 dequeue 方法。要實現較大深度的FIFO,需要使用循環緩衝。
💡 握手常用的 FIFO 實例有三種,分別是:Depth-1 Pipeline FIFO、Depth-1 Bypass FIFO、Depth-2 Conflict-Free FIFO。這三種 FIFO 面積最小(可以省略 notEmpty、notFull 寄存器),且能夠滿足大多數流水線設計的需求。
- Conflict FIFO不可同時 deq 和 enq。
-
Conflict-Free FIFO
{notFull, enq, notEmpty, first, deq} < clear非空時可同時 deq 和 enq。
-
Pipeline FIFO
{notEmpty, first, deq} < {notFull, enq} < clear
滿可同時 deq 和 enq。級聯會產生較長的 Ready 組合信號鏈,從後級一直指向前級。
-
Bypass FIFO
{notFull, enq} < {notEmpty, first, deq} < clear
空可同時 deq 和 enq。級聯會產生較長的 Valid、Payload 組合信號鏈,從前級一直指向後級。
Ready 和 Valid 握手
- Valid 打一拍,Ready 不打拍
因為 Ready 快於 Valid,所以當 Ready 由有效變為無效時,需要緩存前一個週期的一拍數據。
(因為這一拍數據剛好就在實現延遲一拍的寄存器上,所以可以不用多加寄存器就可以直接實現)
因為第一拍數據送入之前為空,而之後都是處於滿的狀態,所以可以當成 Depth-1 Pipeline FIFO ,在滿時可同時進行 deq 和 enq 操作。
case class Delay1() extends Component {
val io = new Bundle {
val m = slave Stream UInt(8 bits)
val s = master Stream UInt(8 bits)
}
io.m.ready := ~io.s.valid
io.s.payload := RegNextWhen(io.m.payload, io.m.fire) init 0
io.s.valid := RegNextWhen(Mux(io.s.fire, False, True), io.m.fire || io.s.fire) init False
}
- Ready 打一拍,Valid 不打拍
因為 Valid 快於 Ready,所以當 Ready 由有效變為無效時,線路上多發了一拍數據。這一拍數據就要新增一個 buffer 去進行緩存。
因為第一拍數據送入之前為滿,而之後都是處於空的狀態,所以可以當成 Depth-1 Bypass FIFO ,在空時可同時進行 deq 和 enq 操作。
(這裏可以考慮一下傳給 master 的 Ready 信號。如果 buffer 為空,那麼必然是 Ready 的;如果 buffer 為滿,那麼必然不是 Ready 的。這樣就可以省掉一個寄存器去為 slave 的 Ready 打一拍。)
case class Delay2() extends Component {
val io = new Bundle {
val m = slave Stream UInt(8 bits)
val s = master Stream UInt(8 bits)
}
private val buffer = Reg(UInt(8 bits)) init 0
private val bufferValid = RegInit(False)
io.m.ready := !bufferValid // buffer 為空
io.s.payload := Mux(!bufferValid, io.m.payload, buffer) // buffer 為空則直通
io.s.valid := io.m.valid || bufferValid // buffer 為空則直通
when(io.m.fire && !io.s.ready) { // buffer 為空且 m 有效且 s 不 ready
buffer := io.m.payload
bufferValid := True
}
when(io.s.fire) { // buffer 輸出後失效
bufferValid := False
}
}
- Ready 和 Valid 各打一拍,要求所有輸出都為寄存器輸出。
Ready 從有效變為無效的時候,Ready 到達晚一拍,Valid 有一拍有效數據已經送出去了。等 Ready 到達 master,已經多送出了兩拍有效數據。所以這兩拍數據必須被緩存起來。
在數據傳輸剛開始的時候,第一拍數據是從 FIFO 中直出的。第二拍的時候,slave 的 Ready 送至 master。在之後的數據傳輸過程中,其中停滯的只有一個數據。
顯而易見,可以使用 Depth-2 Conflict-Free FIFO,在非滿非空的時候可以同時 deq 和 enq。master 的 Ready 信號就是 notFull,slave 的 Valid 信號就是 notEmpty。可見,所有輸出均為寄存器輸出。
case class Delay3() extends Component {
val io = new Bundle {
val m = slave Stream UInt(8 bits) // 要求 Ready 為寄存器輸出
val s = master Stream UInt(8 bits) // 要求 Valid、Payload 為寄存器輸出
}
private val delay1 = Delay1()
private val delay2 = Delay2()
io.m <> delay2.io.m // Ready 為寄存器輸出
delay2.io.s <> delay1.io.m
io.s <> delay1.io.s // Valid、Payload 為寄存器輸出
}
EHR 寄存器 (Lab4)EHR 寄存器是一種特殊的寄存器,它可以進行併發操作。這意味着可以同時讀取和寫入 EHR 寄存器,而不需要任何同步或鎖定,這使得 EHR 寄存器非常適合於流水線設計。
⚠️ EHR 往往會導致關鍵路徑過長,而且難以跟蹤具體路徑,所以要儘可能避免在大型項目中使用。
圖上可以看出,r[0] 為寄存器輸出,r[1] 為組合邏輯輸出。
w[1] 具有最高優先級,當 w[1] 寫入時,會忽略 w[0] 的寫入。
RISC-V (Lab5)
elf2hex 可以通過 GitHub - riscvarchive/riscv-fesvr: RISC-V Frontend Server 編譯得到。儘管倉庫目前是棄用狀態,但是編譯出來的工具仍然可用。現行倉庫 GitHub - riscv-software-src/riscv-isa-sim: Spike, a RISC-V ISA Simulator 也有這個工具,但是附帶了很多不必要的工具。
🔥 trans_vmh.py 需要用 Python 3 運行,需要手動在 Makefile 裏修改 python 為 python3。當然,你也可以直接修改文件頭,直接作為可執行文件運行。
⚠️ CSR 寄存器在 gcc 中有改動,現行的命名、序號都與測試程序中不同。需要你在編譯測試程序時進行修改。
- 雙週期(馮諾依曼架構)
取指 -> 執行
在第一個週期中,處理器從內存中讀取當前指令並對其進行解碼。在第二個週期中,處理器讀取寄存器文件,執行指令,進行ALU操作、內存操作,並將結果寫入寄存器文件。
- 四周期(支持內存延遲)
取指 -> 解指 -> 執行 -> 寫回
在取指階段,向內存發送 PC 地址請求,但是不要求立刻響應。在解指階段,讀取內存請求響應。在執行階段,向內存發送數據讀取或寫入請求,但是不要求立刻響應。在寫回階段,讀取內存請求響應。
- 兩級流水線
取指 -> 執行
與雙週期不同的是,在執行的同時在進行取指。當處理器執行分支指令時,下一條指令的地址可能是未知的,這就是所謂的“控制流不確定性”。
這種不確定性會導致處理器在執行分支指令時停頓,直到下一條指令的地址被確定。這種停頓會降低處理器的性能。為了解決這個問題,可以使用PC+4預測器來預測下一條指令的地址。如果預測正確,處理器可以繼續執行下一條指令。如果預測錯誤,則需要回退並重新執行分支指令後面的指令。這種回退會導致處理器停頓,從而降低性能。
- 六級流水線 (Lab6)
取指 -> 解指 -> 讀取寄存器 -> 執行 -> 內存 -> 寫回
取指:向 iMem 請求指令。解指:從 iMem 接收響應並解碼指令。讀取寄存器:從寄存器文件中讀取。執行:執行指令並在必要時重定向。內存:向 dMem 發送請求寫回:從 dMem 接收響應,並寫入寄存器。
💡 一點調試小技巧可以指定一個 cnt 計數器,在達到一定時鐘週期後強制終止。在調試的時候,測試程序往往會進入死循環,導致日誌文件相當龐大,而且進程也無法被終止。
Branch Prediction (Lab6)
- BHT 記錄分支指令最近一次或幾次的執行情況(成功或不成功),預測是否採用 BTB 的目標地址跳轉(即:跳不跳?taken or not taken)。
- BTB 主要記錄分支指令跳轉目標地址(即:往哪裏跳?branch target)
- RAS 僅用於函數返回地址場景的預測。當程序執行到分支跳轉指令時,RAS 需要判斷指令是否屬於“函數調用”類型的分支跳轉指令。如果遇到 rd = x1 的 JAL/JALR 指令時,RAS 入棧返回地址;如果遇到 rd = x0 且 rs1 = x1 的 JALR 指令出棧 RAS,作為函數返回地址。
- BONUS 是一種基於 BHT 和 BTB 的分支預測算法,它將兩者的結果進行加權平均,以得到更為準確的預測結果。
分支預測的記錄部分是放在執行環節,預測部分放在取指環節。
💡 在分支預測中,建議使用 cononicalize 規則去單獨處理 PC 指針的重定向。如果你在取指、解碼、執行階段直接進行重定向,往往會導致各種各樣的時序環。
ℹ️ 分支預測錯誤需要洗刷流水線,可以通過設置一位 canary 並判斷值是否一致。相較於直接 clear 全部前級,會更加方便。
❗ 只需要洗刷前級,禁止洗刷後級。
All the images in this section are cited from [jdelgado].
DDR3 Memory (Lab7)
DDR3 提供了大容量、高帶寬、高延遲的內存。讀取方式和之前讀取 Mem 基本一致,不過響應位寬是 512 位。返回的指令可以存入 Cache,通過指定 index 來依次訪問。也正是由於這個原因,DDR3 在訪問時需要對地址做對齊。
⚠️ 務必修改 trans_vmh.py。導入的位寬是按 vmh 文件的位寬來算的,不是按在 RegFile 中指定的位寬來算的。
Cache Coherence (Project)
在項目中,需要實現多個核心之間的緩存一致性。
MessageFIFO 優先出棧 resp 請求。MessageRoute 通過遍歷所有核心,如果存在對應核心的迴應,則會將回應送入對應核心的 MessageFIFO。c2m 是來自 L1 的 DCache(實際上是來自 MessageRoute)的 MessageFIFO 的接口,向上請求和向下響應可以從此接口讀取。m2c 是 MessageFIFO 到 L1 的 DCache(實際上是到 MessageRoute)的接口,應將向下請求和向上響應發送到此接口。
ℹ️ 多核運行時會重複打印,可以指定只有 0 號核才可以打印。
阻塞式緩存
阻塞式緩存在接收到未命中的請求後,會等待內存響應後才能繼續處理其他請求。這種方式會導致處理器停頓,從而降低性能。
整體流程:Ready → StrtMiss → SndFillReq → WaitFillResp → Resp → Ready
Ready:處理器可以繼續執行下一條指令。StrtMiss:處理器發起未命中請求。SndFillReq:處理器發送請求到內存。WaitFillResp:處理器等待內存響應。Resp:處理器接收到內存響應。
非阻塞式緩存
非阻塞式緩存能夠在接收到未命中的請求後,繼續處理其他請求,而不是等待內存響應。
非阻塞式緩存的實現方式是 MSHR [1]。MSHR 是一個隊列,用於存儲未命中的請求。
非阻塞式緩存的 miss 有三種可能:primary miss:某個塊的第一次 miss。secondary miss:對某個已經在 fetch 的 block 又一次 miss。structural-stall miss:由於 MSHR 不夠導致的 miss,會發生阻塞。
MSI 狀態機轉換模型
- MSI
MSI 是一種緩存一致性協議的狀態類型,它代表了緩存行的三種狀態:Modified(已修改)、Shared(共享)和Invalid(無效)。其中,Modified 表示該緩存行已被修改且未被寫回主存,Shared 表示該緩存行被多個緩存共享,而 Invalid 表示該緩存行無效,不包含有效數據。
- MESI
MESI 協議將“Exclusive”狀態添加到 MSI 狀態機中,可以減少只在單個緩存中存在的緩存行的回寫次數。 - MOSI
MOSI 協議將“Owner”狀態添加到 MSI,可以減少由從其他處理器讀取而觸發的寫回操作。 - MOESI
MOESI 中的 O 是 Owner 的意思,表示該緩存行已被修改且已被寫回主存,E 是 Exclusive 的意思,表示該緩存行只被一個緩存獨享。這兩個狀態可以被用到,也可以被忽略。All the images in this section are cited from [kshitizdange].
Snooping 協議
所有的一致性控制器按照相同的順序觀察(嗅探)一致性請求,共同維護一致性。依靠當前塊的所有請求,分佈式一致性控制器能夠正確地更新表示塊狀態的狀態機。Snooping 協議向所有一致性控制器廣播請求,包括髮起請求的控制器。一致性請求通常在有序廣播網絡(如總線)上傳輸,這可以確保每個一致性控制器都以相同的順序觀察到所有請求,從而保證所有一致性控制器都可以正確地更新緩存塊的狀態。
Directory 協議
Directory 協議是一種用於解決多處理器中緩存一致性的硬件策略,適用於在分佈式共享存儲多處理器系統中實現。它使用目錄表來維護緩存塊的狀態,以便在多個處理器之間共享數據時保持一致性。當一個處理器想要讀取或寫入一個緩存塊時,它會向目錄表發送請求,以確定該塊是否已被其他處理器緩存。如果是,則確定哪些處理器緩存了該塊。然後,該處理器可以與其他處理器通信以獲取或更新該塊的副本。
ℹ️ Snooping & Directory
Snooping 協議採用廣播的形式。如果緩存控制器需要發起一個請求,是通過廣播請求消息到所有其它的一致性控制器。
Directory 協議則是緩存控制器單播請求到那個塊所在的控制器。每個控制器都維護一個目錄,其中包含了每個塊的狀態,例如當前所有者和當前共享者的信息等。當一個請求到達根目錄時,控制器遞歸會查找這個塊的目錄狀態,並進行定向轉發
Snooping 協議會廣播所有請求,需要較少的硬件資源,但是在大型系統中可能會導致總線流量過大。
Directory 協議需要更多的硬件資源,但可以提供更好的可擴展性和更少的總線流量。
❗ 關於 Memory ConsistencyCache Coherence → 緩存一致性Memory Consistency → 內存連貫性
為什麼不同時使用 coherence 呢?為什麼要區分一致和連貫呢?
原因:緩存一致性是指多個處理器或核心共享同一緩存時,確保它們都可以訪問相同的數據並且具有相同的值。當一個處理器或核心修改了緩存中的數據時,其他處理器或核心應該能夠看到這個修改並且更新它們自己的緩存。例如,假設處理器有兩個核心,它們都有自己的緩存,並且它們都在讀取和寫入同一塊內存。如果一個核心寫入了一個值,那麼另一個核心應該能夠看到這個值的變化並且更新自己的緩存,確保它也可以讀取到最新的值。
內存連貫性是多線程共享同一內存並使用緩存層次結構的關鍵要素之一。例如,如果一個線程上寫入了一個變量,那麼在另一個線程讀取該變量時,能夠讀取到該變量的最新值。
緩存一致性的主要對象是核心,而內存連貫性的主要對象是線程。所以,緩存一致性實現依靠硬件,而內存一致性依靠軟件。在具有緩存的系統中,緩存一致性協議確保了緩存數據的 Cache coherence;而在不具有緩存的系統中,由於不存在緩存一致性問題,因此只需要考慮 Memory consistency。
Part of the content presented in this section has been cited from [AP].
MIT 6.375
Bluespec 中的一些特性
調度屬性
- no_implicit_conditions 屬性用於斷言:一個規則中所有的方法不含隱式條件。
- fire_when_enabled 屬性用於斷言:當某個規則的顯式和隱式條件為真時,該規則必須被觸發。也就是説,fire_when_enabled 檢查一個規則是否因為與其他規則有衝突,且緊急程度較低,而導致在本來該激活(顯式和隱式條件都滿足)時被抑制了激活。
- descending_urgency 在衝突發生時,指定多個規則的緊急程度,緊急的規則抑制不緊急的規則的激活。mutually_exclusive 在多個規則互斥(不會同時激活)的情況下,如果編譯器分析不出來互斥關係,以為衝突會發生,用 mutually_exclusive 告訴編譯器它們是互斥的。
- conflict_free 在多個規則可能同時激活,但它們中的引起衝突的語句並不會同時執行時,如果編譯器分析不出來互斥關係,以為衝突會發生,用 conflict_free 告訴編譯器衝突並不會發生。
- preempts 給兩個規則強制加上衝突(即使他們之間不衝突),同時指定緊急程度。
❗儘管 Bluespec 中可以單獨為 rule 指定不同的優先級,但是這往往會引起各種各樣的時序衝突。所以一般都用於斷言。
泛型約束💡 和 Java/Kotlin 中的泛型約束類似,規定了泛型的上界。
CORDIC 算法
CORDIC 算法的運算流程如下:
- 選擇一個初始向量和目標角度。
- 將目標角度分解為多個旋轉角度,每個旋轉角度都是一個固定的值,可以通過查表得到。
- 對於每個旋轉角度,計算出旋轉後的向量,並將其作為下一次迭代的初始向量。
- 重複步驟 3 直到達到目標精度或迭代次數。
具體可以參考 第三章 CORDIC · FPGA並行編程。
一些常用的概念
Setup Time 、Hold Time 和 Clock-to-Q Time
概念
- Setup time 指的是數據在時鐘沿(上升沿或下降沿)到來之前必須被穩定保持的最短時間。如果數據沒有在 Setup time 內穩定下來,那麼接收端可能無法正確識別數據,併產生錯誤結果。
- Hold time 指的是數據在時鐘沿(上升沿或下降沿)到來之後需要保持的最短時間。如果數據沒有在 Hold time 內得到保持,那麼接收端可能無法正確地接收到數據,併產生錯誤結果。
- Clock-to-Q time 是指從時鐘(Clock)信號的上升沿到輸出數據(Q)穩定的時間。
時序分析
下面以“D型數據鎖存器邊沿觸發器”為示例。
🔥 網上的一些“D觸發器”圖片並不是邊沿觸發的,而是電平觸發的。寫着觸發器,實際上是鎖存器。
- Setup time假設此時 CLK 從 0 跳變到 1。
Setup time 指的是數據在時鐘沿(上升沿或下降沿)到來之前必須被穩定保持的最短時間。
如果不穩定會怎麼樣?如果 D 信號發生變化,會直接影響到 S 端的信號變化。由於前級處於直通模式,前級的變化會直接反映給後級。如果此時進行狀態切換,則會導致前級記錄錯誤數據。這段時間用於洗刷前級錯誤數據。
所以,此時的 Setup time 包含了前級的四個與非門和一個非門電路。即 D 信號通過路徑上的所有門電路。
- Hold time假設此時 CLK 從 0 跳變到 1。
Hold time 指的是數據在時鐘沿(上升沿或下降沿)到來之後需要保持的最短時間。
其保持原因和 Setup time 幾乎一致。由於信號存在延遲,並沒有立刻完成狀態切換。所以,此時 CLK 的信號跳變所經過的所有門電路也應該一併考慮。
所以,此時的 Hold time 包含了前級的四個與非門和兩個非門電路。即 D 信號、CLK 信號通過路徑上的所有門電路。
- Clock-to-Q time
Clock-to-Q time 是指從時鐘(Clock)信號的上升沿到輸出數據(Q)穩定的時間。在圖中就是後級的門電路,包括了四個與非門。 - 下降沿時序分析
上面的都是在分析上升沿,這是因為兩個很明顯的原因: - 這張圖中的 D 觸發器是時鐘上升沿觸發的。
- 很明顯,CLK 信號直通前級,但是經過一個反相器到達後級。所以前級狀態切換的速度肯定比後級快,所以就沒有必要去考慮 Setup time 和 Hold time。
ℹ️ 但是,如果是下降沿觸發的 D 觸發器呢?那麼這個反相器會加在前級,前級的延時會比後級高。這種情況為什麼在分析上升沿時候也可以不考慮 Setup time 和 Hold time 呢?
分析:先明確一點,Setup time 和 Hold time 指的是整個電路中最開始的輸入信號,對應着圖上的 D 信號。
後級從直通進入保持狀態。在 CLK 發生跳變前,前級處於保持狀態,所以前級進入後級的信號在上升沿到下降沿的半個時鐘週期內都不會發生變化,這早已滿足後級 Setup time 要求,所以不需要考慮 Setup time。
當 CLK 發生跳變後,前級處於直通模式。此時,任何 D 信號的變化都會對前級產生影響。但是,D 信號發生的任何變化都需要經過前級的所有門電路。前級門電路的數量大於後級數量,所以前級帶來的延遲也比後級大。即使變化傳遞至後級時,後級也早已進入穩定。所以不需要考慮 Hold time。
SRAM單端口
SRAM(1R/W)
SRAM 是通過 BL 和 ~BL 控制讀寫,WL 控制具體的行。
- SRAM 寫 0BL = 0, ~BL = 1, WL = 1。此時 Y = 0。
- SRAM 寫 1BL = 1, ~BL = 0, WL = 1。此時 Y = 1。
- SRAM 讀BL = 1, ~BL = 1, WL = 1。通過檢查 BL 和 ~BL 的電壓降低情況可以知道 Y 的值。如果 BL 電壓降低,那麼 Y = 0;如果 ~BL 電壓降低,那麼 Y = 1。
偽雙端口 SRAM (1R1W)
偽雙端口具有獨立的讀寫字線(RWL, WWL)和讀寫位線(RBL, WBL 和 WBLB)。
真雙端口 SRAM (2R2W)
具有兩套完整的讀寫字線和讀寫位線。
All the images in this section are cited from [宇芯電子].
DRAM
- DRAM 寫
- 雙側的 BL 預充電到 0.5V;
- 接通 WL 後,BL 設置成相應的電壓;
- 數據寫入。
- DRAM 讀
- 在 WL 不被激活的時候,兩側的 BL 的電壓保持為 0.5V;
- 接通 WL 後,電容與右側 BL 接通,部分電荷從電容流出或流入,使其電壓小幅增加或減小(足夠被檢測到);
- 在放大器的作用下,電壓較高的一側越來越高,低的越來越低,最後輸出可識別信號;
- 在電壓的幫助下,電荷重新流入電容,使其充滿電。
ℹ️ SRAM 要 6 個 transistor,而 DRAM 只需要 1 個 transistor。為什麼 CPU 裏面一般放的是 SRAM,不是 DRAM 呢?
原因:在 CMOS 中,正反饋是指輸出信號被放大並反饋到輸入端,從而增強輸入信號的幅度。這種放大器被稱為正反饋放大器。在 CMOS 中,正反饋放大器是由 PMOS 和 NMOS 兩個互補的 MOSFET 電晶體管組成的。
SRAM 用了正反饋的鎖存器/寄存器,速度顯然比類似於模擬電路(就是一個模擬的開關對電容充電)的 DRAM 要快很多。
References
[jdelgado] José G. Delgado-Frias, https://eecs.wsu.edu/~jdelgado/CompArch/branch_predictionF12....[kshitizdange] https://kshitizdange.github.io/418CacheSim/final-report[AP] Nagarajan, V., Sorin, D. J., Hill, M. D., & Wood, D. A. (2020). A Primer on Memory Consistency and Cache Coherence, Second Edition. Synthesis Lectures on Computer Architecture, 15(1), 1–294.[宇芯電子] https://www.cnblogs.com/wridy/p/13273377.html
- MSHR是指“Miss Status Holding Register”,是一種用於緩存中的數據結構,用於保存未命中的緩存請求的狀態信息。
達坦科技硬件設計學習社區持續開放,點擊原文(GitHub - kazutoiris/MIT6.175)瞭解社區學習詳情。若想詢問加入細節,請添加小助手微信號:Apathy_no
達坦科技(DatenLord)專注下一代雲計算——“天空計算”的基礎設施技術,致力於拓寬雲計算的邊界。達坦科技打造的新一代開源跨雲存儲平台DatenLord,通過軟硬件深度融合的方式打通云云壁壘,實現無限制跨雲存儲、跨雲聯通,建立海量異地、異構數據的統一存儲訪問機制,為雲上應用提供高性能安全存儲支持。以滿足不同行業客户對海量數據跨雲、跨數據中心高性能訪問的需求。
公眾號:達坦科技DatenLordDatenLord
官網:www.datenlord.io
知乎賬號:https://www.zhihu.com/org/da-tan-ke-ji
B站:https://space.bilibili.com/2017027518