iT邦幫忙

2021 iThome 鐵人賽

DAY 2
0
Software Development

猴子都寫得出來的 RISC-V CPU Emulator系列 第 20

RISC-V: Memory Fence 指令

  • 分享至 

  • xImage
  •  

CPU 運作過程中很容易遇到需要等待的情況,
例如 Cache Miss、Data Dependency等等。

為了隱藏等待造成的延遲,
現代 CPU 可能會亂序執行,Memory 也有 Read/Write Buffer 等結構,
導致 CPU 或 I/O Device 之間觀測到的執行順序可能跟預期的不一樣。
如果在多處理器執行的時候,
會因為觀測到的指令順序不同,導致行為和預期的不符。

FENCE 就是用來確保其它 Hart (Hardware Thread) 和 I/O Device 看到的指令執行順序,
任何一個 FENCE 後的特定類型指令(successor),
都不會比在 FENCE 前的特定類型指令(predecessor)早發生。

I-type

指令格式如下:

|31     20|19   15|14    12|11   7|6       0|
+-------------------------------------------+
|   imm   |  rs1  | funct3 |  rd  | opcode  |
+-------------------------------------------+

FENCE

rs1 和 rd 都留給未來擴充更精確的 FENCE 指令用,
如果是標準的執行環境,rs1 和 rd 都應該要是 0

|31  28|27|26|25|24|23|22|21|20|19   15|14    12|11   7|6       0|
+----------------------------------------------------------------+
| 0000 |PI|PO|PR|PW|SI|SO|SR|SW|   0   |  000   |  0   | 0001111 |
+----------------------------------------------------------------+

FENCE.TSO

針對 LOAD 和 STORE 各自做出限制的 FENCE 指令,
但是 LOAD 和 STORE 之間並沒有限制,
相較於標準的 FENCE 放寬了 R-W 之間的順序。

|31  28|27|26|25|24|23|22|21|20|19   15|14    12|11   7|6       0|
+----------------------------------------------------------------+
| 1000 |0 |0 |1 |1 |0 |0 |1 |1 |   0   |  000   |  0   | 0001111 |
+----------------------------------------------------------------+

實際程式

github 頁面 Tag: ITDay20

今天是快樂的一天,
這次實作的模擬器很單純,不會有亂序的問題。
就做一個空殼出來,然後修修 Bug 吧!

Binary Operation 的部分真的很容易犯錯,
今天又修了一個 Range Overlaping 的問題,
除了避免自己手寫,並盡量整理成可以重複使用的函式、
以及多寫測試確認之外,還要再想想有沒有更好的工作流程。

這次也為 FENCE 新增一個 get_imm_fence_fm

//instructionDecoder.cpp
int32_t INSTRUCTION_DECODER::get_imm_b()
{
	auto value = sc_dt::sc_int<32>();
	value(12, 12) = instruction_value(31, 31);
	value(11, 11) = instruction_value(7, 7);
	value(10, 5) = instruction_value(30, 25);
	value(4, 1) = instruction_value(11, 8);
	value <<= 19;
	value >>= 19;
	return value;
}

uint32_t INSTRUCTION_DECODER::get_imm_fence_fm()
{
	return instruction_value.range(31, 28);
}

在寫 BRANCH 測試的 Binary Code 才發現有個大問題:
有兩個 switch statement 的 break 被漏掉了。

為了減少維護程式過程中出錯的可能性,
習慣只要碰到同樣的問題 3 次,就要想辦法解決它。

這兩次出錯的原因都是 Dispatch 的流程太不好閱讀了,
決定把牠們拆出來到小的 Dispatcher。

//executor.cpp
void EXECUTOR::execute()
{
	new_pc = register_file->get_pc() + 4;
	cmmand_dispatch();
	register_file->set_pc(new_pc);
}

void EXECUTOR::cmmand_dispatch()
{
	switch (instruction_decoder->get_opcode()) {
		case INSTRUCTION_DECODER_INTERFACE::IMM_OP:
			imm_dispatch();
			break;
		case INSTRUCTION_DECODER_INTERFACE::LUI_OP:
			LUI_E();
			break;
		case INSTRUCTION_DECODER_INTERFACE::AUIPC_OP:
			AUIPC_E();
			break;
		case INSTRUCTION_DECODER_INTERFACE::LOAD_OP:
			load_dispatch();
			break;
		case INSTRUCTION_DECODER_INTERFACE::STORE_OP:
			store_dispatch();
			break;
		case INSTRUCTION_DECODER_INTERFACE::JAL_OP:
			JAL_E();
			break;
		case INSTRUCTION_DECODER_INTERFACE::JALR_OP:
			jalr_dispatch();
			break;
		case INSTRUCTION_DECODER_INTERFACE::BRANCH_OP:
			branch_dispatch();
			break;
		case INSTRUCTION_DECODER_INTERFACE::MISC_MEM_OP:
			fence_dispatch();
			break;
		default:
			std::cout << "INVALID: Opcode :" << instruction_decoder->get_opcode() << std::endl;
			break;
	}
}
...

找到一個躲很久沒被發現的 hello_thread 小朋友,
幫他取一個符合身分地位的好名子。

//cpu.h
...
    void cpu_thread(void);
...
//cpu.cpp
...
void CPU::cpu_thread(void)
...

20 天的小小心得

快要變成狐猴了,
最近累到下班回家不睡一兩個小時沒辦法寫 Code,
半夜寫完 Code 又要趕快睡覺準備上班,
打從心底尊敬所有鐵人賽完賽的人。


上一篇
RISC-V: Branch 指令
下一篇
RISC-V: ECALL/EBREAK 指令
系列文
猴子都寫得出來的 RISC-V CPU Emulator31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言