---
# System prepended metadata

title: NMC Software Implementation

---

# NMC Software Implementation

## Host-side

![](https://i.imgur.com/3RN62I4.png)

### Vendor Specified I/O Command

![](https://i.imgur.com/WmVQNcF.png)

```plain!
       NMC   X  X Data Model
         \   |  |  |   /  Wr Rd
 bit: 7 | 6  5  4  3  2 | 1  0 |  hex  | description
      1 | 1  0  0  0  0 | 0  1 |  C1h  | create mapping table for new file
      1 | 1  0  0  0  0 | 1  0 |  C2h  | close mapping table
      1 | 1  0  0  0  0 | 1  1 |  C3h  | inference the specified file
      1 | 1  0  0  1  0 | 0  1 |  C9h  | write tiff chunk
      1 | 1  0  0  1  1 | 0  1 |  CDh  | write model
```

#### Command Flows

![](https://hackmd.io/_uploads/Hy1IUfIE2.png)


#### Write-tiff (C1+C2+C9)

#### Write-model (C1+C2+CD)

#### Inference (C3)

流程概述：

1. Host 發出 Inference Request
2. 


:::spoiler Vendor Command Tests

> 指定檔案作為 input data
> 
> ![](https://i.imgur.com/2tFWrV8.png)

自定義的 command 除了 opcode 與額外資訊之外，其他行為應與 write 相同，因此用 read 檢查是否有正確寫入資料：

> 原始檔案
> 
> ![](https://i.imgur.com/e7SO9gE.png)
> 
> 寫入的內容
> 
> ![](https://i.imgur.com/hcJeR8l.png)

測試更大的檔案：

> 4K
> 
> ![](https://i.imgur.com/3CJXPRd.png)
>
> 
> 16K x 8
> 
> ![](https://i.imgur.com/cTJAvuo.png)
:::

### Test on OpenSSD

- [x] 整合 nvme-cli 與 preprocessing
    1. 透過 `nvme write-tiff` 啟動程式
    2. 在 `nvme write-tiff` 過程中會先對圖片進行前處理
    3. 接著再透過 `io-passthru` 將資料片段寫入 SSD
- [x] 在 OpenSSD 上實測
- [x] 檢查 OpenSSD 讀取出的資料與存入的資料是否相同

:::danger
- OpenSSD 的 sector size (4096) 與 QEMU (512) 不同
    - [ ] 設定 QEMU 的 sector size
- 可靠性測試
    - [ ] 測試更大的圖片
    - [ ] big-tiff
:::

## Data Placement

:::info
- 原圖片 = 87424 x 205952 x 3 bytes 
    - 50.3058 GiB
    - 31.4411 秒 (沒有重複讀取資料)
- 共有 30171 個 patch
    - 88.3916 GiB
    - 55.24475 秒 (patch 間有重疊，會有資料被重複讀取)
:::

### Mapping Table

> 需要用 Mapping Table 讓 Unet 知道檔案存在哪些 block

Assume:

- 分配單位為 block (4M)
- 每個 Channel 分開存

Table Size:

- (87424 * 205952 * 3) / (4M) / 8 = 1610 blocks used per channel
- 16384 blocks per channel → $2^{14}$ → 14 bits or 16 bits
- 14 bits:
    - 1610 * 14 bits = 2817.5 bytes per channel
- 16 bits:
    - 1610 * 16 bits = 3220 bytes per channel

:::warning
- Page-level (256 MiB)
    - #Entries = 64 x 2048 x 256 (pages)
    - Entry Size = 32b (PPN) + 32b (LPN) = 8 Bytes
- Block-level (1 MiB)
    - #Entries = 64 x 2048 (blocks)
    - Entry Size = 32b (PPN) + 32b (LPN) = 8 Bytes
- Demand-based page-level
    - Page-level (256M) + metadata (32K)
:::

### Result Data Buffer

width: 200000 / 256 = 781.25  (先不考慮 patch 間有重疊)
height: 80000 / 256 = 312.5   (先不考慮 patch 間有重疊)

312 * 781 * (2 * 256 * 256) = 29.7451171875 GiB (RAM 放不下)

:::warning
定期回傳給 host -> DRAM 需要保留多少空間給 FPGA 存放 inference 結果?
:::



<!-- --- -->

## Device-side

### Flush Data Buffer



```bash!
# user issue write command
$ sudo nvme write /dev/nvme0n1 -s 0 -z 4096
```

:::success
寫入操作都會先被暫存在 buffer 中、不會被寫入 SSD，所以在 user 發出寫入請求後，data buffer 中會有 dirty entry，而在 user 發出 flush 請求後，這些 dirty entry 都應要被寫入 SSD 並變成 clean entry。
:::

```plain!
# before flush op
INFO Dump dirty data buffer entries
INFO buffer entry [1023]
INFO    .logicalSliceAddr   = 0
INFO    .dirty              = 1
INFO    .prevEntry          = 65535
INFO    .nextEntry          = 1021
INFO    .hashPrevEntry      = 65535
INFO    .hashNextEntry      = 65535
INFO    .blockingReqTail    = 65535

IO Flush Command

# after flush op
INFO Dump dirty data buffer entries
INFO LSA[0] -> VSA[0]
INFO VSA[0] = Ch 0 Way 0 Blk 0 Page 0
```

:::warning
因為 Flush Buffer 不會把原本的 buffer entry 踢掉，所以 user 直接讀原本的 LBA 也只會讀到暫存在 buffer 中的值，無法確認是否有真的寫入 SSD。
:::

```bash!
# user issue read command
$ sudo nvme read /dev/nvme0n1 -s 0 -z 4096
flush test
```

:::info
若要檢查資料是否有被寫入 SSD，有幾種可行方案：

1. 把一個 free logical page $LPA_{Y}$ 指向應有資料的 physical page $PPA_{X}$，再讀取 $LPA_{Y}$
2. 把 $LPA_{X}$ 的 Buffer Entry 清掉（drop cache）後再讀 $LPA_{X}$
:::

#### Method 1

:::success
找一個不在 data buffer 中的 $LPA_{Y}$，然後將 $LPA_{Y}$ 指到應有資料的 $PPA_{X}$
:::

```plain!
# LPA 33 not in data buffer (not used)
INFO Dump the data buffer entry of LSA 33

# map LPA 33 to PPA 0
INFO Update L2V Mapping : LSA[33] -> VSA[0]
```

:::success
接著由 user 嘗試讀取，讓 fw 去 SSD 中讀取 $PPA_{X}$
:::

```bash!
# user issue read command
$ sudo nvme read /dev/nvme0n1 -s 33 -z 4096
```

:::danger
讀取 $LPA_{Y}$ 時，fw 卡在 scheduler 中出不來。
:::


:::spoiler DFTL

- Translation Pages
    - Flash Page (16 KiB)
        - 一個 Page 可以存 4096 個 PPN (Physical Page Number)
        - 一個 Block 可以存 256 x 4096 個 PPN
        - SSD 共有 64 x 2048 x 256 個 Page
        - 需要 32 個 Block (128 MiB)
    - 紀錄**連續 Logical Pages** 對應的 Physical Pages
    - 以 **Entry 為單位暫存**在 DRAM 中
- Global Translation Directory (GTD)
    - SRAM
    - 紀錄 Translation Page 對應的 Physical Page
        - 共有 8192 個 Translation Pages
        - 需要 2 個 flash pages 儲存 (32K)
    - 需要頻繁更新

![](https://i.imgur.com/lb2PwrW.png)

:::