NVMe-eMMC Bridge 芯片固件开发(三):Firmware 主流程与 NVMe 协议栈

1. Firmware 整体架构

1.1 软件分层架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
┌─────────────────────────────────────────────────────────────────────┐
│ Host (NVMe Driver) │
├─────────────────────────────────────────────────────────────────────┤
│ PCIe Interface │
╔═════════════════════════════════════════════════════════════════════╗
║ BayBridge Firmware ║
║ ┌───────────────────────────────────────────────────────────────┐ ║
║ │ NVMe Protocol Layer (NVMe 1.4) │ ║
║ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ ║
║ │ │ Admin Cmds │ │ IO Cmds │ │ Completion Engine │ │ ║
║ │ │ Identify │ │ Read/Write │ │ MSI/MSI-X/INTx │ │ ║
║ │ │ Get/Set │ │ Flush │ │ Interrupt Coal. │ │ ║
║ │ │ FW Update │ │ TRIM │ │ │ │ ║
║ │ └─────────────┘ └─────────────┘ └─────────────────────┘ │ ║
║ └───────────────────────────────────────────────────────────────┘ ║
║ ┌───────────────────────────────────────────────────────────────┐ ║
║ │ Task Scheduling Layer │ ║
║ │ ┌──────────────────────┐ ┌────────────────────────────┐ │ ║
║ │ │ Task Queue Manager │ │ Power Management (APST) │ │ ║
║ │ │ PRE_CANDIDATE → │ │ L0/L1.2 State Machine │ │ ║
║ │ │ CANDIDATE → READY → │ │ LTR Configuration │ │ ║
║ │ │ EXECUTING → DONE │ │ │ │ ║
║ │ └──────────────────────┘ └────────────────────────────┘ │ ║
║ └───────────────────────────────────────────────────────────────┘ ║
║ ┌───────────────────────────────────────────────────────────────┐ ║
║ │ eMMC Protocol Layer (eMMC 5.1) │ ║
║ │ ┌─────────────────────┐ ┌───────────────────────────────┐ │ ║
║ │ │ Command Queuing │ │ Non-CQ Mode (Legacy) │ │ ║
║ │ │ CMD44/45: Add Task │ │ CMD17/18: Single/Multi Rd │ │ ║
║ │ │ CMD46/47: Execute │ │ CMD24/25: Single/Multi Wr │ │ ║
║ │ │ CMD13: Query QSR │ │ │ │ ║
║ │ └─────────────────────┘ └───────────────────────────────┘ │ ║
║ └───────────────────────────────────────────────────────────────┘ ║
╚═════════════════════════════════════════════════════════════════════╝
├─────────────────────────────────────────────────────────────────────┤
│ eMMC Interface (HS400) │
├──────────────────────────────┬──────────────────────────────────────┤
│ eMMC #0 │ eMMC #1 │
└──────────────────────────────┴──────────────────────────────────────┘

1.2 Firmware 核心职责

模块 功能 协议依据
NVMe 协议处理 Admin/IO 命令解析与执行 NVMe 1.4 Spec
eMMC 控制 Command Queuing 读写操作 eMMC 5.1 Spec
数据流调度 Host ↔ Controller ↔ eMMC 流水线 -
电源管理 PCIe L1.2、APST 状态机 NVMe 1.4 Chapter 8
中断处理 MSI/MSI-X 中断派发 NVMe 1.4 Chapter 7

2. Main 函数主循环

2.1 Firmware 初始化流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
    ┌─────────────────────┐
│ Bootloader Exit │
│ Jump to main() │
└──────────┬──────────┘

┌─────────────────────┐
│ CAP Timeout 设置 │ NVMe CAP.TO = 60s
└──────────┬──────────┘

┌─────────────────────┐
│ FW Checksum 校验 │ 验证固件完整性
└──────────┬──────────┘

┌─────────────────────┐
│ 中断系统配置 │ 安装 ISR, 屏蔽中断
└──────────┬──────────┘

┌─────────────────────┐
│ 电源管理初始化 │ L1.2, LTR 配置
└──────────┬──────────┘

┌─────────────────────┐
│ NVMe 中断模式初始化 │ MSI/MSI-X/INTx
└──────────┬──────────┘

┌─────────────────────┐
│ eMMC 卡初始化 │ HS400, CQ Enable
└──────────┬──────────┘

┌─────────────────────┐
│ Namespace 初始化 │ 容量映射
└──────────┬──────────┘

┌─────────────────────┐
│ SMART 信息初始化 │ 读取 eMMC 保留区
└──────────┬──────────┘

┌─────────────────────────────┐
│ 进入 while(1) 主循环 │
└─────────────────────────────┘

2.2 主循环处理流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
┌─────────────────────────────────────────────────────────────────────┐
│ Main Loop (while 1) │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ ① Timer │───►│ ② Interrupt │───►│ ③ NS Change Poll │ │
│ │ Tasks │ │ Process │ │ │ │
│ └─────────────┘ └─────────────┘ └──────────┬──────────┘ │
│ │ │ │ │
│ │ ┌─────────────┴─────────────┐ │ │
│ │ │ CMD Buffer 就绪? │ │ │
│ │ │ → command_parse() │ │ │
│ │ └───────────────────────────┘ │ │
│ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ ④ eMMC Read/Write Processing │ │
│ │ CQ Mode: add_task_to_cq() → execute_ready_task() │ │
│ │ Non-CQ: emmc_read_write() │ │
│ └──────────────────────────┬──────────────────────────────┘ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ ⑤ send_completion_task() │ │
│ │ 遍历 COMPLETED 任务,发送 CQE + MSI │ │
│ └──────────────────────────┬──────────────────────────────┘ │
│ ▼ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ ⑥ Dataset │───►│ ⑦ Flush │───►│ ⑧ SMART Update │ │
│ │ Mgmt │ │ Commands │ │ │ │
│ └─────────────┘ └─────────────┘ └──────────┬──────────┘ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ ⑨ Power Management │ │
│ │ FW Status → APST Timer → Shutdown 检测 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────┐ │
│ │ Loop 继续 │ │
│ └────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘

2.3 关键代码片段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
while (1) {
// ① 定时器任务:Enter OS Timer 超时后清除 KeepL0
if (T0_Counter[OS_READY].end_flag && async_event_received) {
pm.set_internalstate(&pm_dscp, ID_KEEP_L0, 0); // 允许进入 L1
}

// ② 中断事件处理(CMD Buffer 就绪时触发 command_parse)
interrupt_process();

// ④ eMMC 读写处理
if (global_admin_command_req_dma_channel_flag == 0) {
emmc_read_write_cq_mode(); // 或 non_cq_mode
}

// ⑤ Completion 发送
send_completion_task();

// ⑨ Shutdown 处理
if (shutdown_status == 1) {
smart_info_writeback(&smart_rw, &smart_data);
emmc_power_off_notification(3);
shutdown_status_set(SHUTDOWN_PROCESSING_COMPLETE);
}
}

3. NVMe 命令处理流程

3.1 命令获取流程(符合 NVMe 1.4 Spec Figure 90)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
┌────────────────────────────────────────────────────────────────────────┐
│ NVMe Command Processing Flow │
├────────────────────────────────────────────────────────────────────────┤
│ │
│ Host Driver Controller Firmware │
│ ─────────── ─────────────────── │
│ │ │ │
│ │ ① Write SQE to SQ Memory │ │
│ │─────────────────────────────► │ │
│ │ │ │
│ │ ② Write SQ Tail Doorbell │ │
│ │─────────────────────────────► │ │
│ │ ▼ │
│ │ ┌─────────────────────┐ │
│ │ │ HW Fetch SQE to │ │
│ │ │ CMD Buffer 0/1 │ │
│ │ └──────────┬──────────┘ │
│ │ │ │
│ │ ▼ │
│ │ ┌─────────────────────┐ │
│ │ │ CMD Buffer 就绪中断 │ │
│ │ │ → interrupt_process │ │
│ │ └──────────┬──────────┘ │
│ │ │ │
│ │ ▼ │
│ │ ┌─────────────────────┐ │
│ │ │ ③ command_parse() │ │
│ │ │ 解析 Opcode/NSID │ │
│ │ └──────────┬──────────┘ │
│ │ │ │
│ │ ┌────────────────┴────────────────┐ │
│ │ ▼ ▼ │
│ │ ┌─────────────────┐ ┌─────────────────┐ │
│ │ │ Admin Queue │ │ IO Queue │ │
│ │ │ (QID = 0) │ │ (QID > 0) │ │
│ │ │ │ │ │ │
│ │ │ Identify │ │ Read/Write │ │
│ │ │ Get/Set Feature│ │ Flush │ │
│ │ │ FW Download │ │ Dataset Mgmt │ │
│ │ └────────┬────────┘ └────────┬────────┘ │
│ │ │ │ │
│ │ ▼ ▼ │
│ │ ┌─────────────────────────────────────────────────┐ │
│ │ │ ④ 执行命令 / 插入任务队列 │ │
│ │ └─────────────────────┬───────────────────────────┘ │
│ │ │ │
│ │ ▼ │
│ │ ┌─────────────────────────┐ │
│ │ │ ⑤ send_complete() │ │
│ │ │ 写 CQE + 发 MSI 中断 │ │
│ │ ◄───────────┤ │ │
│ │ └─────────────────────────┘ │
│ │ │
└───────┴────────────────────────────────────────────────────────────────┘

3.2 Admin 命令列表(NVMe 1.4 Spec Figure 139)

Opcode 命令名称 功能说明
0x00 Delete I/O SQ 删除 IO 提交队列
0x01 Create I/O SQ 创建 IO 提交队列
0x02 Get Log Page 获取日志页(Error, SMART, FW Slot)
0x04 Delete I/O CQ 删除 IO 完成队列
0x05 Create I/O CQ 创建 IO 完成队列
0x06 Identify 获取 Controller/Namespace 信息
0x09 Set Features 设置功能参数(Power Mgmt, LBA Range)
0x0A Get Features 获取功能参数
0x0C Async Event Req 异步事件请求(标记进入 OS 阶段)
0x10 FW Commit 固件激活
0x11 FW Download 固件下载
0x80 Format NVM 格式化 Namespace

3.3 IO 命令列表(NVMe 1.4 Spec Figure 186)

Opcode 命令名称 功能说明
0x00 Flush 刷新易失性写缓存到非易失介质
0x01 Write 写数据到 LBA 地址
0x02 Read 从 LBA 地址读数据
0x09 Dataset Mgmt TRIM/Deallocate 命令

3.4 命令解析代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
int command_parse(void)
{
command_t command = {0};
byte queue_identifier = start_command_process_action();

// 从 Ping-Pong Buffer 获取命令
fetch_command(global_buffer_identifier, &command);

if (queue_identifier == 0) {
admin_command_parse(&command); // Admin 队列
} else {
nvme_command_parse(queue_identifier, &command); // IO 队列
}
return 0;
}

static void admin_command_parse(command_p command)
{
switch (command->opcode) {
case 0x06: identify_process(command); break;
case 0x09: set_features_process(command); break;
case 0x11: firmware_image_download_process(command); break;
// ... 其他 Admin 命令
default:
send_complete(command->command_identifier, 0, 0,
SC_INVALID_COMMAND_OPCODE);
}
}

static int nvme_command_parse(byte queue_id, command_p command)
{
switch (command->opcode) {
case 0x01: return read_write_process(queue_id, command, 1); // Write
case 0x02: return read_write_process(queue_id, command, 0); // Read
case 0x00: flush_process(queue_id, command); break; // Flush
default:
send_complete(command->command_identifier, queue_id, 0,
SC_INVALID_COMMAND_OPCODE);
}
return 0;
}

4. Read/Write 命令处理

4.1 NVMe 到 eMMC 的地址映射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
┌─────────────────────────────────────────────────────────────────────────┐
│ Namespace 到 eMMC 映射模式 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Case 1: DOUBLE_EMMC_TOTAL_NS (双 eMMC 组成单一 Namespace) │
│ ═══════════════════════════════════════════════════════════ │
│ │
│ NVMe Namespace 1: │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ LBA 0 │ LBA boundary │ LBA MAX │ │
│ └──────────────┴──────────────┴──────────────────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────┐ ┌─────────────────┐ ┌──────────────┐ │
│ │ eMMC #0 │ │ Cross Boundary │ │ eMMC #1 │ │
│ │ LBA 0~MAX0 │ │ Fused Command │ │ LBA 0~MAX1 │ │
│ └──────────────┘ └─────────────────┘ └──────────────┘ │
│ │
│ Case 2: DOUBLE_EMMC_DOUBLE_NS (双 eMMC 双 Namespace) │
│ ═══════════════════════════════════════════════════════ │
│ │
│ NVMe NS 1: NVMe NS 2: │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ LBA 0~MAX │ │ LBA 0~MAX │ │
│ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ eMMC #0 │ │ eMMC #1 │ │
│ └──────────────┘ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘

4.2 Read/Write 处理流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
┌─────────────────────────────────────────────────────────────────────────┐
│ read_write_process() 流程 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ NVMe Command (SQE) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Opcode: 0x01(Write) / 0x02(Read) │ │
│ │ NSID: Namespace Identifier │ │
│ │ PRP1: Physical Region Page Entry 1 (64-bit) │ │
│ │ PRP2: PRP Entry 2 or PRP List Pointer │ │
│ │ SLBA: Starting LBA (DW10-11) │ │
│ │ NLB: Number of Logical Blocks - 1 (DW12) │ │
│ └──────────────────────────┬──────────────────────────────┘ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ 1. 地址范围检查 │ │
│ │ SLBA + NLB ≤ MAX? │ │
│ └──────────┬──────────┘ │
│ │ │
│ ┌────────────────┼────────────────┐ │
│ ▼ ▼ ▼ │
│ ┌────────────┐ ┌────────────────┐ ┌────────────┐ │
│ │ 在 eMMC#0 │ │ 跨边界任务 │ │ 在 eMMC#1 │ │
│ │ 范围内 │ │ (Fused Cmd) │ │ 范围内 │ │
│ └─────┬──────┘ └───────┬────────┘ └─────┬──────┘ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 2. 插入到对应 eMMC Task Queue │ │
│ │ - PRP1/PRP2: DMA 地址 │ │
│ │ - Address: eMMC LBA │ │
│ │ - Block_len: 块数量 │ │
│ │ - Direction: Read(0) / Write(1) │ │
│ └──────────────────────────┬──────────────────────────────┘ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ 3. 状态 = PRE_CAND │ │
│ │ 等待下发到 eMMC CQ │ │
│ └─────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘

4.3 任务信息结构

1
2
3
4
5
6
7
8
9
10
11
12
typedef struct {
u64 prp1; // PRP Entry 1 (Host Memory Address)
u64 prp2; // PRP Entry 2 / PRP List
u64 address; // eMMC LBA Address
u16 block_len; // Number of 512B Blocks
u16 nvme_sq_id; // NVMe Submission Queue ID
u16 nvme_cmd_id; // NVMe Command Identifier
u8 direction; // 0=Read, 1=Write
u8 fused_flag; // FUSED_EMPTY / FUSED_NEW / FUSED_READY
u8 next_pointer; // 链接到另一个 eMMC 的任务 (Fused)
u8 start_card; // 起始 eMMC 卡号
} task_info_struct;

5. eMMC Command Queuing 模式

5.1 CQ 模式流程(eMMC 5.1 Spec Section 6.6.40-44)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
┌─────────────────────────────────────────────────────────────────────────┐
│ eMMC Command Queuing Protocol Flow │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Controller Firmware eMMC Device │
│ ─────────────────── ─────────── │
│ │ │ │
│ │ ┌────────────────────────────────┐ │ │
│ │ │ Phase 1: 任务下发 (Queuing) │ │ │
│ │ └────────────────────────────────┘ │ │
│ │ │ │
│ │ CMD44: Set Task Parameters │ │
│ │ Arg = [Block Count, Task ID, Dir] │ │
│ │─────────────────────────────────────────► │ │
│ │ R1 ◄───│ │
│ │ │ │
│ │ CMD45: Set Task Address │ │
│ │ Arg = [LBA Address] │ │
│ │─────────────────────────────────────────► │ │
│ │ R1 ◄───│ │
│ │ │ │
│ │ ... 重复 CMD44+CMD45 添加更多任务 │ │
│ │ │ │
│ │ ┌────────────────────────────────┐ │ │
│ │ │ Phase 2: 状态查询 (Query) │ │ │
│ │ └────────────────────────────────┘ │ │
│ │ │ │
│ │ CMD13: Query Queue Status Register │ │
│ │ Arg = 0x00008000 │ │
│ │─────────────────────────────────────────► │ │
│ │ QSR ◄───│ │
│ │ QSR = 32-bit 位图,每位表示任务就绪 │ │
│ │ │ │
│ │ ┌────────────────────────────────┐ │ │
│ │ │ Phase 3: 任务执行 (Execute) │ │ │
│ │ └────────────────────────────────┘ │ │
│ │ │ │
│ │ CMD46: Execute Read Task │ │
│ │ Arg = [Task ID << 16] │ │
│ │─────────────────────────────────────────► │ │
│ │ │ │
│ │◄─────────────────── Data ─────────────────│ ← 数据传输 │
│ │ │ │
│ │ CMD47: Execute Write Task │ │
│ │ Arg = [Task ID << 16] │ │
│ │─────────────────────────────────────────► │ │
│ │ │ │
│ │─────────────────── Data ─────────────────►│ → 数据传输 │
│ │ │ │
└────────┴───────────────────────────────────────────┴────────────────────┘

5.2 任务状态机

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
       ┌─────────────────────────────────────────────────────────┐
│ Task State Machine │
└─────────────────────────────────────────────────────────┘

┌──────────┐ NVMe Cmd ┌───────────────┐ CMD44+45 ┌───────────┐
│ EMPTY │ ────────────────► │ PRE_CANDIDATE │ ─────────────► │ CANDIDATE │
│ (0x00) │ Received │ (0x01) │ Success │ (0x02) │
└──────────┘ └───────────────┘ └─────┬─────┘
▲ │
│ │
│ │ CMD13
│ │ QSR 查询
│ │
│ ┌───────────────┐ CMD46/47 ┌───────────┐ ▼
│ │ COMPLETED │ ◄───────────── │ EXECUTING │ ◄── ┌───────┐
│ │ (0x04) │ Success │ (0x03) │ │ READY │
│ └───────┬───────┘ └───────────┘ │(0x05) │
│ │ │ └───────┘
│ │ send_complete() │
│ │ │ Error
│ ▼ ▼
│ ┌───────────────┐ ┌───────────────┐
└─────────│ 释放槽位 │ │ ERR_STATUS │
└───────────────┘ │ (0x7F) │
└───────────────┘

5.3 CQ 模式核心代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
int emmc_read_write_cq_mode()
{
// Phase 1: 将 PRE_CANDIDATE 任务下发到 eMMC CQ
for (i = 0; i < task_status_map_index[card_slot]; i++) {
task_id = task_status_map[card_slot][i];

// CMD44 + CMD45: 添加任务
add_task_to_cq(card_slot, task_id);

// 更新状态: PRE_CANDIDATE → CANDIDATE
task_status_flag[card_slot][task_id] = CANDIDATE;
}

// Phase 2: 查询任务就绪状态 (CMD13)
emmc_task_sequence_select_for_execute(card_slot);

// Phase 3: 执行就绪任务 (CMD46/47)
if (emmc_task_completion) {
execute_ready_task();
}
}

int add_task_to_cq(u8 card_slot, u8 task_id)
{
// CMD44: 设置任务参数
cmd44_cq_set_task_params(card_slot,
task_queue[task_id].block_len,
task_id,
0, // Priority
!task_queue[task_id].direction);

// CMD45: 设置任务地址
cmd45_cq_set_task_address(card_slot,
task_queue[task_id].address);
}

6. Completion 发送机制

6.1 NVMe Completion 流程(NVMe 1.4 Spec Figure 91)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
┌─────────────────────────────────────────────────────────────────────────┐
│ Completion Entry Generation │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 命令执行完成 │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Completion Queue Entry (CQE) │ │
│ ├─────────────────────────────────────────────────────────────────┤ │
│ │ DW0: Command Specific │ │
│ │ DW1: Reserved │ │
│ │ DW2: SQ Head Pointer (15:0) | SQ Identifier (31:16) │ │
│ │ DW3: Status Field (31:17) | Phase Tag (16) | CID (15:0) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ 写 CQE 到 │ DMA Write to Host Memory │
│ │ CQ Tail 位置 │ │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ 更新 Phase Tag │ 队列回绕时翻转 │
│ │ CQ Tail++ │ │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ 发送 MSI 中断 │ ← Interrupt Coalescing 可选 │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ Host Driver 处理 CQE,写 CQ Head Doorbell │
│ │
└─────────────────────────────────────────────────────────────────────────┘

6.2 中断聚合策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void send_complete(u16 cmd_id, byte queue_id, u32 spec, u16 status)
{
// 填充 CQE
cpl.dw0 = spec;
cpl.sq_head_pointer = get_sq_head(queue_id);
cpl.command_identifier = cmd_id;
cpl.phase_tag = complete_queue_phase[cq_id];
cpl.status_field = status;

// 发送 CQE 到 Host 内存
fn_memcpy_4bytes((void *)CPL_BUFFER_BASE, &cpl, sizeof(cpl_t));
CPL_SEND_CTRL = cq_id | START_SENDING_BIT;

// 中断发送策略
if (status == 0 && queue_id != 0) {
// IO 命令成功:考虑 Interrupt Coalescing
if (aggregation_threshold == 0) {
INTERRUPT_REQUEST_SET = 1 << interrupt_vector[cq_id]; // 立即
} else {
msi_count[...]++; // 累积后批量发送
}
} else {
// Admin 命令或错误:立即发送中断
INTERRUPT_REQUEST_SET = 1 << interrupt_vector[cq_id];
}
}

7. 数据传输流程

7.1 Host ↔ Controller ↔ eMMC 数据流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
┌─────────────────────────────────────────────────────────────────────────┐
│ Data Transfer Pipeline │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ │
│ │ Write │ │
│ │ Command │ │
│ └──────┬──────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Host Memory (PRP) │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ Page 0 │ │ Page 1 │ │ Page 2 │ │ Page N │ │ │
│ │ │ (PRP1) │ │ │ │ │ │ │ │ │
│ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │
│ └───────│────────────│────────────│────────────│───────────────────┘ │
│ │ │ │ │ │
│ │ PCIe DMA │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Controller Data Buffer (Streaming) │ │
│ │ ┌───────────────────────────────────────────────────────────┐ │ │
│ │ │ DMA Engine: 自动处理 PRP → FIFO → eMMC │ │ │
│ │ └───────────────────────────────────────────────────────────┘ │ │
│ └────────────────────────────────┬────────────────────────────────┘ │
│ │ │
│ │ eMMC HS400 (200MB/s) │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ eMMC Device │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ │ Internal Write Buffer → NAND Flash │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ═══════════════════════════════════════════════════════════════════ │
│ │
│ ┌─────────────┐ │
│ │ Read │ │
│ │ Command │ (数据流反向) │
│ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘

7.2 PRP 处理(NVMe 1.4 Spec Section 4.4)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
┌─────────────────────────────────────────────────────────────────────────┐
│ PRP Entry Interpretation │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Case 1: Data Size ≤ 4KB (单页) │
│ ─────────────────────────────── │
│ PRP1 → [Data Page] │
│ PRP2 → (unused) │
│ │
│ Case 2: 4KB < Data Size ≤ 8KB (双页) │
│ ──────────────────────────────────── │
│ PRP1 → [Data Page 0] │
│ PRP2 → [Data Page 1] │
│ │
│ Case 3: Data Size > 8KB (PRP List) │
│ ────────────────────────────────── │
│ PRP1 → [Data Page 0] │
│ PRP2 → [PRP List Page] │
│ │ │
│ ├──► PRP Entry 0 → [Data Page 1] │
│ ├──► PRP Entry 1 → [Data Page 2] │
│ ├──► ... │
│ └──► PRP Entry N → [Data Page N] │
│ │
└─────────────────────────────────────────────────────────────────────────┘

8. 总结

8.1 设计要点

特性 实现方式 优势
事件驱动 主循环轮询 + 中断配合 响应速度与功耗平衡
三级流水 NVMe Fetch → Task Queue → eMMC Execute 并行处理提升吞吐
双 Buffer CMD Buffer 0/1 Ping-Pong 避免 Fetch 阻塞
CQ 模式 eMMC Command Queuing 随机性能提升
错误恢复 CMD/Data 错误自动重试 系统可靠性

8.2 关键性能参数

参数 典型值 说明
Task Table 深度 32 每个 eMMC 通道
CQ 深度 32 硬件 Command Queue
CMD Buffer 2 x 1KB 双 Buffer 交替
Data Buffer 4KB Admin 命令数据
FW Status 切换 ~10ms 约 263200 Loop

参考资料

  • NVM Express Base Specification 1.4
  • JEDEC Standard JESD84-B51 (eMMC 5.1)
  • SPARC V8 Architecture Manual