iT邦幫忙

2021 iThome 鐵人賽

DAY 2
1

之前寫了一個很簡單的 Program Loader,
現在就來真正的實作它,讓它能夠把編譯好的程式放到指定位置吧。
主要參考對象是我們的老朋友:RISC-V-TLM

大部分的 SOC 都會有一些開機的流程,
例如先把程式燒進去指定裝置的指定位置上,
然後就是多個階段的 Boot Loader,
第一階段被 Hardware 的機制 Load 起來,
第二階段被第一階段 Loader Load 起來,
每一階段都做一點準備 (設定 Externam Device Register, 啟動 MMU 等),
最後把主程式 Load 進記憶體開始執行,
大概會有個 2~3 階段。

目前這個模擬器規劃比較簡單,
因為模擬環境目前沒有需要提前設定的設計,
也沒有 Binary Code 大小的限制,
一開機就可以直接跳進主程式執行,
所以只要有辦法把主程式放到指定位置,
再讓 Cpu 從那邊開始執行就可以了!

實際程式

github 頁面 Tag: ITDay27

開場先處理 Load File 失敗的問題,
讓 Simulator 在讀到非法指令的時後就結束。

//executor.cpp
		default:
			cpu->raise_exception(CPU_INTERFACE::ILLEGAL_INSTRUCTION_EXCEPTION_CAUSE);
			break;

為簡化開發流程,
loadBinaeyFromHex 目前只支援特定檔名與檔案格式,
之後擴充的時候再支援指定路徑的部分。

//memory.h
...
	void loadBinaeyFromHex(std::string filePath = "./binary.hex");
...

Loader 大致上跟 RISC-V-TLM 的做法一樣,
這邊將 hexFile.is_open()line[0] != ':' 改成判斷完先處理,
而不是一層一層包起來,稍微提升可讀性。

嘗試用 std::map + Switch Statement 簡化每次比較字串的流程,
不需要每次都寫 else if(std::stoul(line.substr(7, 2)) == "02")
但看起來可讀性沒有比較好,反而是執行效率有機會變好一點。

修掉了奇怪的 * 16
RISC-V-TLM 在 "03" 的 case 有一段程式碼:
code_segment = stol(line.substr(9, 4), nullptr, 16) * 16; /* ? */
最後那段 * 16; /* ? */ 看起來是不小心把某個 Memory 的位置放錯了,
導致需要用特別行為的處理,
但在這邊沒有這個問題,改回正常運算流程。

//memory.cpp
...
void MEMORY::loadBinaeyFromHex(std::string filePath)
{
	std::ifstream hexFile(filePath);
	std::string line = "";

	if(!hexFile.is_open()) {
		std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl;
		std::cout << "Binary File Open Failed!!" << std::endl;
		std::cout << "Cause: " << std::strerror(errno) << std::endl;
		std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl;
		return;
	}

	uint32_t extended_address = 0;
	uint32_t memory_offset = 0;
	uint32_t program_counter = 0;
	dataMemory.assign(0x100000, 0);
	while(std::getline(hexFile, line)) {
		if(line[0] != ':') {
			continue; //skip this line
		}

		switch (std::stoul(line.substr(7, 2))) {
			case 0: { //Data
				auto byteCount = std::stol(line.substr(1, 2), nullptr, 16);
				auto address = extended_address + std::stoul(line.substr(3, 4), nullptr, 16);
				for(int i=0; i < byteCount; i++) {
					auto value = std::stoul(line.substr(9 + (i*2), 2), nullptr, 16);
					dataMemory[address + i] = value;
					std::cout << "00" << " address: 0x" << address + i << " value: 0x" <<  std::hex << value << std::endl;
				}
			}
			break;
			case 2: { //Extended segment address
				extended_address = std::stoul(line.substr(9, 4), nullptr, 16);
			}
			break;
			case 3: { //Start segment address
				uint32_t code_segment = stoul(line.substr(9, 4), nullptr, 16);
				program_counter = code_segment + stoul(line.substr(13, 4), nullptr, 16);
				std::cout << "03 " << "program counter should be: 0x" << std::hex << program_counter << std::endl;
			}
			break;
			case 4: { //Start srgmant address
				memory_offset = stoul(line.substr(9, 4), nullptr, 16) << 16;
				extended_address = 0;
			}
			break;
			case 5: { //Get start program counter
				program_counter = stol(line.substr(9, 8), nullptr, 16);
				std::cout << "05 " << "program counter should be: 0x" << std::hex << program_counter << std::endl;
			}
			break;
			default:
				break;
		}

	}
}
...

執行結果

這次測試直接把 Program Counter 寫死到指定位置,
預計把 Program Loader 從 Memory 獨立出去之後,再讓 Cpu 可以從正確位置開始執行。

程式碼來源則是之前先做好的 Cross Compiler 編譯結果,
之後會再開一篇說明,
有興趣的可以先照 RISC-V-TLM 的流程建立編譯環境。
另外注意如果是虛擬環境(VMware等等),要記得把容量和記憶體調高,
不然會載不下 gcc + 編譯 gcc 的過程會因為記憶體不足碰到奇怪的問題。

現在才發現 RISC-V-TLM 有點奇怪,
我們從從第一道指令執行結果就不一樣了!
導致我的第二道指令存取到不合法的位置。

看來要找一下 RISC-V-TLM 一開始到底做了些什麼,
不然沒道理 ADDI 一開始 x2 - 32 會變成 0x3ffffdf
x2 到底存了什麼神奇數字?又是從哪邊來的呢?
我們明天好好研究一下吧!

//RISCV-SIM by hsufit
current_pc: 0x1054 target_pc: 0x1058 ADDI 2 0 2 rs1Value: 0xffffffe0 rs2Value: 0x0 rdValue: 0xffffffe0 immValue: 0xffffffe0
error at address: 0xfffffffc!!
current_pc: 0x1058 target_pc: 0x105c SW 2 8 28 rs1Value: 0xffffffe0 rs2Value: 0x0 rdValue: 0x0 immValue: 0x1c
//RISC-V-TLM by mariusmm
time 0 s: PC: 0x10054. time 0 s: ADDI: x2 + -32 -> x2(0x3ffffdf)
time 10 ns: PC: 0x10058. time 10 ns: SW: x8(0x0) -> x2 + 0x1c (@0x3fffffb)

上一篇
Logger: Code Stream Logger
下一篇
Run HEX File 之 Debug 總集篇
系列文
猴子都寫得出來的 RISC-V CPU Emulator31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言