RISC-V: Memory Fence 指令

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 又要赶快睡觉准备上班,
打从心底尊敬所有铁人赛完赛的人。


<<:  Chapter5 - 当一个勤劳的园丁,来修剪我们美丽的树(II)Canvas素材 修图、压缩、效能优化

>>:  【领域展开 19 式】初次使用 Elementor 编辑页面

[Python 爬虫这样学,一定是大拇指拉!] DAY26 - 实战演练:多执行绪 - 抓取多个个股日成交资讯

多执行绪(multithreading) 所以我们的多执行绪在程序是怎麽运作呢? 一般情况: 假设 ...

21. 闲聊 x 网页浏览快捷键(Windows/Linux)

ㄟ,拍谢,这篇又变成 windows 了。 我平常还是用 windows 系统,因为 windows...

MDU3603 QFN8 MOS

MDU3603 QFN8 MOS 采用先进的 MOSFET 技术,提供低导通电阻。 这种场效应管适用...

Endpoint

我们用到的 API endpoint 只有一个,就是用来取得港铁机场快綫、东涌綫、屯马綫及将军澳綫最...

Day 15 | 魔术方块AR游戏开发Part4 - 面的旋转(下)+游戏机制

在上一篇我们完成面的旋转,却发现旋转途中若点击放开,面会停留在旋转途中,今天我们就要来解决这个问题。...