# 嵌入式安全HW1 > 核心想法: > Data存入mem前,做漢明碼的計算,並保存在map中。 > Data取出時,利用Addr來對應,並取得當初設定的漢明碼,做ECC修正 > 程式碼:https://github.com/likeyou600/NCU_GEM5_ECC_HW1 ## Part 1 (Setting Up Gem5) >設定 Gem5:安裝 Gem5 並執行基本模擬以確保設定正確。 模擬可以是簡單的記憶體存取場景。 ### 1. 安裝依賴 ```bash! sudo apt install build-essential git m4 scons zlib1g zlib1g-dev \ libprotobuf-dev protobuf-compiler libprotoc-dev libgoogle-perftools-dev \ python3-dev libboost-all-dev pkg-config python3-tk ``` ### 2. build gem5 ```bash! scons build/X86/gem5.opt -j 6 ``` ### 3. run ```bash! build/X86/gem5.opt configs/ncu_lab1_config/ecc_memobj.py //with debug flag build/X86/gem5.opt --debug-flags=ECCMemobj configs/ncu_lab1_config/ecc_memobj.py ``` ## Part 2 >設計 ECC 模組:設計一個能夠偵測並修正單位錯誤的簡單 ECC 模組。 這個模組可以是獨立的 C++ 或 Python 類,用於實現基本的糾錯程式碼(如漢明碼)。 ### 1. 產生漢明碼 - 漢明碼為2的冪次方位置,其他位置把原始資料(10101101)擺上去,我們將bit數為1的地方,轉成二進制。 - 將這些轉成二進制的地方( 紅色字 ),拿出來做XOR運算,可得1010 ![image](https://hackmd.io/_uploads/HkcOmFjf0.png) - 然後我們將漢明碼反者填回去表格(0101填回去) ![image](https://hackmd.io/_uploads/HklqNFiz0.png) - 實作部分 - 實際上我們不需要把漢明碼填回表格,只需記住data對應的漢明碼是多少就好 ```cpp!= int ECCMemobj::generateHammingCode(std::vector<int>& input, int n_bits_data, int m_bits_parity) { int parity = 0, idx = 0, cnt = 0; //idx為input的index //i為input+parity位數 for (int i = 1; i <= n_bits_data + m_bits_parity; ++i) { if (i == pow(2, cnt)) { ++cnt; continue; } else if (input[idx]) { parity ^= i; ++idx; } else ++idx; } return parity; } ``` --- ### 2. ECC修正 - 將隨便一筆Data(包括漢明碼)填入表格(1101110101),並且將有1的地方都轉成2進制 ![image](https://hackmd.io/_uploads/BJFxDKoGC.png) - 將這些轉成二進制的地方( 紅色字 ),拿出來做XOR運算,可得0110,代表bit6有錯,將第6bit反轉 ![image](https://hackmd.io/_uploads/H1g0UDYsGR.png) - 實作部分 - 與實際邏輯相同 ```cpp!= void ECCMemobj::ECC_fix(std::vector<int>& input, int parity, int n_bits_data, int m_bits_parity) { int combine[n_bits_data + m_bits_parity] = { 0 }, cnt = 0, idx = 0, input_parity = 0; for (int i = 1; i <= n_bits_data + m_bits_parity; ++i) { if (i == pow(2, cnt)) { ++cnt; continue; } else if (input[idx]) { input_parity ^= i; } combine[i] = idx; //conbine只是記input 的 index ++idx; } input_parity ^= parity; if (input_parity != 0) { DPRINTF(ECCMemobj, "find Error Bit index : %d \n", combine[input_parity] + 1); //從1開始看 input[combine[input_parity]] ^= 1; //利用combine vector 找回原本input index } } ``` ## Part 3 >將 ECC 整合到 Gem5 中:修改 Gem5 的記憶體系統以整合 ECC 模組。 這涉及確定 Gem5 程式碼庫中的適當位置以添加 ECC 檢查、修改記憶體讀取/寫入操作以包括 ECC 編碼和解碼,以及確保正確儲存和檢索 ECC 元資料(例如奇偶校驗位)。 想法有2個 ### 1.直接修改mem_ctrl部分 - 修改recvTimingReq() - 在pkt->isWrite() 、 addToWriteQueue之前 做漢明碼生成 - 在pkt->isRead() 做ECC修正 - 改到太底層程式碼,有更好的做法,用方法2. --- ### 2.創立simple memory object ![image](https://hackmd.io/_uploads/HJ4JmqsGA.png) --- #### 2-1.檔案作用解釋 ![image](https://hackmd.io/_uploads/H12jQ5jz0.png) - configs/ecc_memobj.py - System介面設計 - 需要System執行什麼Process - 系統效能分析 - src/ecc_memobj.cc - 主要simple mem object程式碼 - handleRequest、handleResponse - src/ecc_memobj.hh - ecc_memobj.cc 標頭檔 - 定義global變數、function - src/ECCMemobj.py - 定義這個模擬對象的類型名稱 - 指定標頭文件 - 定義物件接口(port) - src/SConscript.py - 定義 SimObject --- #### 2-2.ecc_memobj.cc 重點在於ecc_memobj.cc 修改部分如下 ##### 1. handleRequest (cpu->mem) 簡單步驟如下 ```cpp!= ECCMemobj::handleRequest(PacketPtr pkt) || vv if (pkt->isWrite()) || vv //輸出pkt基本資訊 DPRINTF(ECCMemobj, "cpu->mem Got request for addr %#x\n", pkt->getAddr()); DPRINTF(ECCMemobj, "size %#x\n", pkt->getSize()); uint8_t* data = pkt->getPtr<uint8_t>(); || vv //輸出binary_data std::vector<int> data_vector(8 * size); for (size_t i = 0; i < size; i++) { std::bitset<8> binary(data[i]); for (int j = 7; j >= 0; j--) { // 從高位到低位逐位複製 data_vector[index++] = static_cast<int>(binary[j]); // 將二進制位轉換為字符 } } DPRINTF(ECCMemobj, "data : %s \n", vector_int_to_string(data_vector).c_str()); || vv //產生hamming code,並存入map,供response使用 int parity = generateHammingCode(data_vector, n_bits_data, m_bits_parity); hammingCodeMap[pkt->getAddr()].first = parity; hammingCodeMap[pkt->getAddr()].second = size; DPRINTF(ECCMemobj, "hamming code : %d \n", parity); ``` ##### 2. handleResponse (mem->cpu) 簡單步驟如下 ```cpp!= ECCMemobj::handleResponse(PacketPtr pkt) || vv if (pkt->isRead() && hammingCodeMap.find(pkt->getAddr()) != hammingCodeMap.end() && hammingCodeMap[pkt->getAddr()].second == pkt->getSize()) || vv //輸出pkt基本資訊 DPRINTF(ECCMemobj, "cpu->mem Got request for addr %#x\n", pkt->getAddr()); DPRINTF(ECCMemobj, "size %#x\n", pkt->getSize()); uint8_t* data = pkt->getPtr<uint8_t>(); || vv //輸出binary_data std::vector<int> data_vector(8 * size); for (size_t i = 0; i < size; i++) { std::bitset<8> binary(data[i]); for (int j = 7; j >= 0; j--) { // 從高位到低位逐位複製 data_vector[index++] = static_cast<int>(binary[j]); // 將二進制位轉換為字符 } } DPRINTF(ECCMemobj, "data : %s \n", vector_int_to_string(data_vector).c_str()); || vv //map 找對應的hamming code int out_parity = hammingCodeMap[pkt->getAddr()].first; DPRINTF(ECCMemobj, "hamming code out: %d \n", out_parity); || vv //尋找錯誤位置並修正data_vector ECC_fix(data_vector, out_parity, n_bits_data, m_bits_parity); || vv //把修正後的再次設定進Pkt int s = pkt->getSize(); int idx = 0; int output = 0; unsigned char c; for (int i = 0; i < s; ++i) { for (int j = 7; j >= 0; --j) { if (data_vector[idx]) { output += 1 << j; } ++idx; } c = (unsigned char)output; output = 0; data[i] = c; } || vv //輸出fix_binary_data data_vector.resize(8 * size, 0); for (size_t i = 0; i < size; i++) { std::bitset<8> binary(data[i]); for (int j = 7; j >= 0; j--) { // 從高位到低位逐位複製 data_vector[index++] = static_cast<int>(binary[j]); // 將二進制位轉換為字符 } } DPRINTF(ECCMemobj, "fix data : %s \n", vector_int_to_string(data_vector).c_str()); ``` ## Part 4、Part 5 >模擬:使用修改後的 Gem5 運行模擬,以評估 ECC 對記憶體可靠性和系統效能的影響。 學生應該在記憶體存取中引入錯誤,並演示 ECC 模組如何檢測和糾正這些錯誤。 >報告和演示:您應該包括 ECC 模組的設計決策和實現細節、整合過程中面臨的挑戰以及如何克服這些挑戰,以及模擬結果,重點關注 ECC 的影響關於系統性能和可靠性。 - (情況:對單個pkt,做全長的漢明碼生成)嘗試每10次write data,就壞掉1個 ->成功 時間0.63~0.68s ![image](https://hackmd.io/_uploads/Bk6BUqiGR.png) - (情況:對單個pkt,做全長的漢明碼生成)嘗試每次write data都翻轉1bit ->失敗 ![image](https://hackmd.io/_uploads/HJLDLYsfA.png) >[!Warning] >問題 為何嘗試每次write data都翻轉1bit會發生失敗的情形 >若考慮最小單位為8bit來做漢明碼呢 - (情況:對單個pkt,分別做8bit最小單位的漢明碼生成) - 暫時失敗 ![image](https://hackmd.io/_uploads/SkPoWyhz0.png)