# 嵌入式安全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

- 然後我們將漢明碼反者填回去表格(0101填回去)

- 實作部分
- 實際上我們不需要把漢明碼填回表格,只需記住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進制

- 將這些轉成二進制的地方( 紅色字 ),拿出來做XOR運算,可得0110,代表bit6有錯,將第6bit反轉

- 實作部分
- 與實際邏輯相同
```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

---
#### 2-1.檔案作用解釋

- 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

- (情況:對單個pkt,做全長的漢明碼生成)嘗試每次write data都翻轉1bit ->失敗

>[!Warning]
>問題 為何嘗試每次write data都翻轉1bit會發生失敗的情形
>若考慮最小單位為8bit來做漢明碼呢
- (情況:對單個pkt,分別做8bit最小單位的漢明碼生成)
- 暫時失敗
