【PicoRV32】學習過程紀錄 & 文章素材 目錄
!!! 個人零碎筆記,未來整個架構搞懂了才會重新整理
設計架構
1. 怎麼理解整個設計架構
------- 模組介紹 ---------
| 模組名稱 | 描述 |
|---|---|
| picorv32_wrapper.sv | 整合picorv32和axi4_memory_bhv |
| picorv32 | 使用 native memory interface 的主 CPU |
| picorv32_axi | 提供 AXI4-Lite master 介面的 CPU |
| picorv32_axi_adapter | native memory ↔ AXI4 bridge |
| picorv32_wb | Wishbone master 介面 |
| picorv32_pcpi_* | 外接乘法 / 除法協同處理器 |
| axi4_memory_bhv.sv | 在模擬環境中充當 AXI4-Lite 介面Slave端的記憶體模型 |
先大致畫出整個module 階層的block digram

接著從程式碼中可以得知編譯好的firmware.hex檔,在picorv32_wrapper.sv中讀入,到axi4_memory_bhv.sv中的memory陣列(模擬ram)

下一步就是去axi4_memory_bhv.sv查看他的行為
介面通道省去了LNE、ID、BURST、RESP等完整AXI訊號
picorv32.sv
picorv32.sv 主要分為三個部分
-
Memory Interface
look-ahead 訊號 : 下一拍會讀或寫哪個位址、資料與 byte enable -
Instruction Decoder
-
Main State Machine
- mem_state
mem_state 是 PicoRV32 記憶體存取狀態機的核心暫存器,用來:
• 區分 CPU 當前是否在 取指、讀資料、寫資料,或閒置。
• 控制 mem_valid / mem_instr / mem_wstrb 等訊號的組合。
• 等待外部記憶體或匯流排的 mem_ready 回應。
• 配合 RVC(壓縮指令集) 處理跨 32-bit 邊界的抓取。
| mem_state | 實際語意(依你的程式) | 關鍵行為摘要 |
|---|---|---|
| 0 | Idle(閒置) | 若 `mem_do_prefetch |
| 1 | Read/Fetch 進行中 | 這一態處理「指令抓取或資料讀取」的實際握手。mem_xfer 成功後:— 若有 RVC 需要第二半字,可能設 mem_la_secondword 繼續讀;— 否則結束讀取:若這次是「rinst/rdata」→ 回 0;若這次只是 prefetch → 轉 3。 |
| 2 | Write 進行中 | 實際資料寫入(mem_wstrb != 0)。mem_xfer 成功後清 mem_valid,回 0。 |
| 3 | Prefetch-hold(只預取、暫停等真正取指) | 僅在「做過 prefetch 但尚未真的 rinst」時短暫停留;一旦 mem_do_rinst 成立即回 0。 |
RISCV 壓縮指令集
- 什麼是 RISC-V 壓縮指令集(RVC)
• RVC 是 RISC-V 的 C 擴展(Compressed Extension),將常用的 32 位元指令(4 bytes) 壓縮成 16 位元指令(2 bytes)。
• 主要目標:- 減少程式碼體積(code size)。
- 提升指令快取(I-Cache)命中率。
- 降低指令記憶體頻寬需求(對嵌入式、低功耗系統特別有用)。
⸻
-
RVC 判斷與基本規則
• 判斷指令是否為壓縮格式:RISC-V 定義:
若指令低 2 bits (instr[1:0]) ≠ 11(二進制),則為壓縮指令(16-bit)
若 instr[1:0] == 11(二進制),則為標準 32-bit 指令instr[1:0] 指令類型 00, 01, 10 16-bit RVC 11 32-bit RVC -
取指流程與 PicoRV32 處理邏輯
因為 16-bit 壓縮指令與 32-bit 指令混用,取指單元需要特別處理:- 取第一個 32-bit word(可能只用前半 16-bit)。
- 判斷是否為壓縮指令:
• 是 → 直接解碼執行,剩下的 16-bit 暫存在 mem_16bit_buffer。
• 否 → 需要讀取完整的 32-bit 再解碼。 - 連續壓縮指令處理:
• 如果前一個 cycle 已有剩下的高 16-bit,下一條指令直接使用它,不需要再讀記憶體。
另一個要注意的是壓縮指令使否跨過不同的word(4-byte),先舉個簡單的例子:
假設記憶體內容(每格是 1 byte,地址右邊是位址的 hex):
Word0: [Byte0] [Byte1] [Byte2] [Byte3]
0x00 0x01 0x02 0x03
Word1: [Byte4] [Byte5] [Byte6] [Byte7]
0x04 0x05 0x06 0x07
- 情況: RVC 跨 word
PC = 0x03
不可能是合法的 32-bit 指令對齊,但對 RVC 來說是合法的(因為 RVC 只要對齊 2 bytes)。
指令第一半在 Word0 的 Byte3,第二半在 Word1 的 Byte4
必須多抓 下一個 word(Word1),才能湊滿 16-bit
這時 mem_la_firstword_xfer = 1,mem_la_addr = {next_pc[31:2] + 1, 2’b00} → 去抓 Word1
在picorv32.sv中,349行就是在確認上述的情況,
可以清楚的看到next_pc[1]這個條件,就是在檢測"跨 word",
因為在沒有"跨 word"的情況下,指令是對齊4byte,也就是地址最低兩位一定是 2'b00

Assert用法
在這段程式碼中,assert(…) 是用來在模擬(simulation)期間檢查條件是否成立的斷言(assertion),如果條件不成立,就會在模擬時報錯,方便開發者及早發現邏輯錯誤。
MEM_STATE_FETCH_OR_READ: begin
assert(mem_wstrb == 0);
assert(mem_do_prefetch || mem_do_rinst || mem_do_rdata);
assert(mem_valid == !mem_la_use_prefetched_high_word);
assert(mem_instr == (mem_do_prefetch || mem_do_rinst));
if (mem_xfer) begin
if (COMPRESSED_ISA && mem_la_read) begin
mem_valid <= 1;
mem_la_secondword <= 1;
if (!mem_la_use_prefetched_high_word)
mem_16bit_buffer <= mem_rdata[31:16];
end else begin
mem_valid <= 0;
mem_la_secondword <= 0;
if (COMPRESSED_ISA && !mem_do_rdata) begin
if (~&mem_rdata[1:0] || mem_la_secondword) begin
mem_16bit_buffer <= mem_rdata[31:16];
prefetched_high_word <= 1;
end else begin
prefetched_high_word <= 0;
end
end
mem_state <= mem_do_rinst || mem_do_rdata ? MEM_STATE_IDEL : MEM_STATE_PREFETCH;
end
end
end
┌──────────────────────────────────────────────────────────────┐
│ State: MEM_STATE_FETCH_OR_READ │
│ ※ 進入此狀態時,以下斷言必須成立: │
│ A1) mem_wstrb == 0 │
│ A2) mem_do_prefetch || mem_do_rinst || mem_do_rdata │
│ A3) mem_valid == !mem_la_use_prefetched_high_word │
│ A4) mem_instr == (mem_do_prefetch || mem_do_rinst) │
└──────────────┬───────────────────────────────────────────────┘
│
│ 等待資料交換完成
▼
┌───────────┐ mem_xfer = 1 ?
│ mem_xfer? │────────────────────────┐
└─────┬─────┘ 否(No) │ 是(Yes)
│ ▼
│ ┌───────────────┐
│ │ COMPRESSED? │ (COMPRESSED_ISA && mem_la_read)
│ └──────┬────────┘
│ │
│ 是(Yes)│ 否(No)
│ ▼
│ ┌───────────────────────────────┐
│ │ for C-ISA 取「第二個 half」 │
│ │ mem_valid <= 1 │
│ │ mem_la_secondword <= 1 │
│ │ if (!mem_la_use_prefetched_ │
│ │ high_word) │
│ │ mem_16bit_buffer <= │
│ │ mem_rdata[31:16]; │
│ └───────────┬───────────────────┘
│ │
│ └─────→ (留在本狀態等待下一拍)
│
▼
(No path change,本拍什麼都不做)
┌──────────────────────────────────────────────┐
否(No) │ 一般情形(或讀資料 load) │
│ mem_valid <= 0 │
│ mem_la_secondword <= 0 │
│ if (COMPRESSED_ISA && !mem_do_rdata) { │
│ if (~&mem_rdata[1:0] || │
│ mem_la_secondword) { │
│ mem_16bit_buffer <= mem_rdata[31:16]; │
│ prefetched_high_word <= 1; │
│ } else { │
│ prefetched_high_word <= 0; │
│ } │
│ } │
│ mem_state <= (mem_do_rinst || │
│ mem_do_rdata) ? │
│ MEM_STATE_IDLE : │
│ MEM_STATE_PREFETCH; │
└──────────────────────────────────────────────┘
訊號意義
◾ mem_valid
mem_valid 在 PicoRV32 裡的作用,就是 CPU 對外宣告「我有一筆記憶體存取請求要送出」,直到外部用 mem_ready=1 回應為止。
它是 CPU 端主動拉高 的信號,外部只讀它、不會去驅動它。
◾ mem_ready
mem_ready 在 PicoRV32 裡的作用,就是 外部記憶體或匯流排介面回應「資料已經準備好」或「寫入已完成」的握手訊號。
它是 由外部模組(記憶體模型、AXI adapter 等)驅動,CPU 只是讀它來判斷是否可以結束這次 bus transaction。
◾ mem_instr
在 PicoRV32 裡mem_instr 是 CPU 與外部記憶體介面之間的一條標記訊號,作用是:
CPU跟MEM(或arbiter)說,這次 mem_valid 發出的存取是「取指令」而不是資料讀寫。
1392

被折叠的 条评论
为什么被折叠?



