RC-EP 和 Switch-EP 的复位链路建立过程区别

RC-EP 和 Switch-EP 的复位链路建立过程确实有重要区别。让我详细解释这个关键差异:


1. 根本区别:复位信号的来源

1.1 RC-EP 场景(传统方式)

复制代码
RC (Root Complex) 控制复位:
┌─────────────┐     PERST#     ┌─────────────┐
│    RC       ├───────────────►│     EP      │
│             │                │             │
│  主动发起   │                │  被动响应   │
└─────────────┘                └─────────────┘
      │                              │
      ▼                              ▼
同时释放 PERST#                检测到 PERST# 释放
      │                              │
      ▼                              ▼
开始发送 EIOS                 开始发送 EIOS
      │                              │
      ▼                              ▼
双方几乎同时开始链路训练

关键点

  • RC 主动控制 PERST# 信号
  • RC 和 EP 同时看到 PERST# 释放
  • 双方几乎同时开始链路训练

1.2 Switch-EP 场景(你的方案)

复制代码
EP 控制复位(通过 GPIO):
┌─────────────┐                ┌─────────────┐
│   Switch    │                │     EP      │
│             │                │             │
│  被动检测   │◄───────────────┤  主动控制   │
└─────────────┘                └─────────────┘
      │                              │
      ▼                              ▼
持续监测线路                    GPIO 控制复位释放
      │                              │
      ▼                              ▼
检测到 EIOS 才开始              主动发送 EIOS
      │                              │
      ▼                              ▼
响应式开始链路训练               主动开始链路训练

关键点

  • EP 主动控制复位(通过 GPIO)
  • Switch 不知道复位何时发生
  • Switch 被动等待 EP 的信号

2. 时序差异对比

2.1 RC-EP 同步时序

c 复制代码
// RC 和 EP 的同步复位时序
void rc_ep_synchronous_reset(void)
{
    // RC 侧
    rc_perform_reset() {
        // 1. RC 拉低 PERST# (双方同时看到)
        set_perst_low();
        
        // 2. 等待 Tperst-min (100ms)
        delay_ms(100);
        
        // 3. RC 释放 PERST# (双方同时看到)
        set_perst_high();
        
        // 4. RC 立即开始发送 EIOS
        send_eios_immediately();  // ← 关键!
    }
    
    // EP 侧
    ep_handle_reset() {
        // 1. 检测到 PERST# 变低
        wait_for_perst_low();
        
        // 2. 进入复位状态
        enter_reset_state();
        
        // 3. 检测到 PERST# 释放
        wait_for_perst_high();
        
        // 4. EP 立即开始发送 EIOS
        send_eios_immediately();  // ← 关键!
    }
    
    // 结果:双方几乎同时开始训练
    // 时间差主要来自信号传播延迟(纳秒级)
}

2.2 Switch-EP 异步时序

c 复制代码
// Switch 和 EP 的异步复位时序
void switch_ep_asynchronous_reset(void)
{
    // EP 侧(主动)
    ep_gpio_controlled_reset() {
        // 1. EP 通过 GPIO 拉低下游设备复位
        gpio_set_value(RST_GPIO, 0);
        
        // 2. EP 自身可能处于不同状态:
        //    a) 如果 EP 也被复位:需要先完成自身初始化
        //    b) 如果 EP 保持活动:可以直接控制
        
        // 3. EP 释放 GPIO 复位
        delay_ms(100);  // 保持复位时间
        gpio_set_value(RST_GPIO, 1);
        
        // 4. EP 等待下游设备初始化
        delay_ms(100);  // Tperst-relax
        
        // 5. EP 开始发送 EIOS
        send_eios();  // ← EP 主动发起
    }
    
    // Switch 侧(被动)
    switch_passive_detection() {
        // 1. Switch 完全不知道复位发生
        //    持续监测 RX 线路
        
        // 2. 可能的状态:
        //    a) 链路原本是 DOWN:在 Detect.Quiet
        //    b) 链路原本是 UP:在 L0,需要检测到中断
        
        // 3. 当检测到 EIOS 时:
        if (detect_eios()) {
            // 进入链路训练状态机
            enter_ltssm_detect_active();
        }
        
        // 4. Switch 响应式开始训练
        //    比 EP 晚一个 EIOS 检测时间
    }
    
    // 结果:EP 先开始,Switch 后响应
    // 时间差:EIO检测时间 + 状态机转换时间
}

3. 状态机初始状态差异

3.1 RC-EP:双方从相同状态开始

上电/全局复位
拉低PERST#
释放PERST#
立即发送EIOS
上电/全局复位
检测到PERST#低
检测到PERST#高
立即发送EIOS
进入Detect.Active
进入Detect.Active
RC_Reset
RC_PerstLow
RC_PerstHigh
RC_SendEIOS
EP_Reset
EP_PerstLow
EP_PerstHigh
EP_SendEIOS
Both_DetectActive

3.2 Switch-EP:不同初始状态

Switch保持活动
检测到链路中断
超时或主动进入
EP控制复位
GPIO拉低复位
GPIO释放复位
等待Tperst-relax
发送EIOS
检测到EIOS
开始训练
开始训练
Switch_L0
Switch_Recovery
Switch_DetectQuiet
EP_Control
EP_GpioLow
EP_GpioHigh
EP_Wait
EP_SendEIOS
Switch_DetectActive
Both_Training


4. 实际实现的关键考虑

4.1 需要处理的状态不匹配

c 复制代码
// Switch 可能处于的不同状态
enum switch_initial_state {
    SWITCH_IN_L0,          // 链路原本是通的
    SWITCH_IN_RECOVERY,    // 正在尝试恢复
    SWITCH_IN_DETECT_QUIET, // 检测静默状态
    SWITCH_IN_DISABLED,    // 端口被禁用
    SWITCH_IN_HOT_RESET,   // 热复位状态
};

// EP 需要根据 Switch 状态调整策略
void ep_adjust_for_switch_state(struct pcie_ep *ep)
{
    enum switch_initial_state sw_state = detect_switch_state(ep);
    
    switch (sw_state) {
    case SWITCH_IN_L0:
        // Switch 认为链路是通的
        // EP 需要先中断链路,再重新训练
        ep_force_link_down(ep);
        delay_ms(1);  // 确保Switch检测到中断
        break;
        
    case SWITCH_IN_RECOVERY:
        // Switch 已经在尝试恢复
        // EP 可以立即发送EIOS加入恢复过程
        break;
        
    case SWITCH_IN_DETECT_QUIET:
        // 理想状态:Switch在等待
        // EP 可以直接开始训练
        break;
        
    case SWITCH_IN_DISABLED:
        // Switch端口被禁用
        // EP 需要等待或触发端口使能
        ep_request_port_enable(ep);
        break;
        
    case SWITCH_IN_HOT_RESET:
        // Switch正在热复位
        // EP 需要等待复位完成
        wait_for_hot_reset_complete(ep);
        break;
    }
    
    // 然后开始复位下游设备
    reset_downstream_via_gpio(ep);
}

4.2 确保 Switch 在正确状态

c 复制代码
// 主动将 Switch 置于 Detect.Quiet 状态
void prepare_switch_for_reset(struct pcie_ep *ep)
{
    // 方法1: 通过配置空间禁用/启用端口
    pci_write_config_dword(ep->pdev, 
                          PCI_EXP_LNKCTL,
                          PCI_EXP_LNKCTL_LD);
    delay_ms(10);
    pci_write_config_dword(ep->pdev,
                          PCI_EXP_LNKCTL,
                          0);
    
    // 方法2: 发送 Electrical Idle 序列
    // 强制链路进入电气空闲
    send_electrical_idle_sequence(ep, 100); // 100us
    
    // 方法3: 触发热复位
    // 让 Switch 重新开始链路训练
    trigger_hot_reset(ep);
    
    // 等待 Switch 进入 Detect.Quiet
    wait_for_switch_detect_quiet(ep, 100); // 100ms超时
}

5. 推荐的完整流程

5.1 优化的异步复位流程

c 复制代码
// 推荐的 EP 控制复位完整流程
int ep_controlled_reset_sequence(struct pcie_ep *ep)
{
    int ret;
    
    // 阶段1: 准备阶段
    dev_info(ep->dev, "Phase 1: Preparing for reset\n");
    
    // 1.1 检查当前链路状态
    u16 link_status = pcie_get_link_status(ep);
    
    // 1.2 如果链路是通的,先优雅断开
    if (link_status & PCI_EXP_LNKSTA_DLLLA) {
        dev_dbg(ep->dev, "Link is up, gracefully bringing down\n");
        
        // 发送 Electrical Idle
        pcie_send_electrical_idle(ep);
        delay_ms(2);  // 确保Switch检测到
        
        // 禁用链路
        pcie_disable_link(ep);
        delay_ms(10);
    }
    
    // 1.3 确保 Switch 在 Detect.Quiet
    ret = ensure_switch_in_detect_quiet(ep);
    if (ret) {
        dev_warn(ep->dev, "Switch not in Detect.Quiet, forcing state\n");
        force_switch_to_detect_quiet(ep);
    }
    
    // 阶段2: 复位下游设备
    dev_info(ep->dev, "Phase 2: Resetting downstream device\n");
    
    // 2.1 断言复位 (GPIO 低)
    gpio_set_value(ep->rst_gpio, 0);
    dev_dbg(ep->dev, "Reset asserted (GPIO low)\n");
    
    // 2.2 保持复位时间 (符合 PCIe 规范)
    delay_ms(100);  // Tperst-min = 100ms
    
    // 2.3 释放复位 (GPIO 高)
    gpio_set_value(ep->rst_gpio, 1);
    dev_dbg(ep->dev, "Reset deasserted (GPIO high)\n");
    
    // 2.4 等待设备初始化 (Tperst-relax)
    delay_ms(100);  // 100ms 等待
    
    // 阶段3: 启动链路训练
    dev_info(ep->dev, "Phase 3: Starting link training\n");
    
    // 3.1 EP 主动发送 EIOS
    pcie_send_eios(ep);
    dev_dbg(ep->dev, "EIOS sent\n");
    
    // 3.2 进入 Detect.Active 状态
    pcie_enter_detect_active(ep);
    
    // 3.3 开始训练序列
    ret = pcie_start_link_training(ep);
    if (ret) {
        dev_err(ep->dev, "Link training failed: %d\n", ret);
        return ret;
    }
    
    // 阶段4: 验证和恢复
    dev_info(ep->dev, "Phase 4: Verification\n");
    
    // 4.1 等待链路建立
    ret = wait_for_link_up(ep, 1000);  // 1秒超时
    if (ret) {
        dev_err(ep->dev, "Link did not come up\n");
        return ret;
    }
    
    // 4.2 验证链路参数
    verify_link_parameters(ep);
    
    dev_info(ep->dev, "Reset sequence completed successfully\n");
    return 0;
}

5.2 时序图对比

复制代码
RC-EP 同步时序:
时间     RC                 EP
-----------------------------------------
t0     拉低 PERST#        检测到 PERST# 低
t1     保持 100ms         保持复位状态
t2     释放 PERST#        检测到 PERST# 高
t2+ε   发送 EIOS          发送 EIOS
t3     开始训练           开始训练

Switch-EP 异步时序:
时间     EP                 Switch
-----------------------------------------
t0     GPIO拉低复位       未知(可能在线)
t1     保持 100ms         持续监测
t2     GPIO释放复位       未知
t3     等待 100ms         持续监测
t4     发送 EIOS          检测到 EIOS
t5     开始训练           响应并开始训练
t6     完成训练          完成训练

6. 关键差异总结

特性 RC-EP (传统) Switch-EP (你的方案)
复位控制 RC 主动控制 EP 通过 GPIO 控制
同步性 同步复位 异步复位
初始状态 双方从复位开始 EP 主动,Switch 被动
开始信号 PERST# 释放 EP 发送 EIOS
时间协调 硬件同步 需要软件协调
复杂性 简单,标准化 复杂,需要定制
可靠性 高,经过验证 取决于实现质量
适用场景 标准 PCIe 系统 定制化/特殊需求

7. 你的方案需要特别注意的问题

7.1 竞争条件 (Race Conditions)

c 复制代码
// 可能出现的竞争条件
void potential_race_condition(void)
{
    // 场景:EP 发送 EIOS 时,Switch 正在其他状态
    // 可能导致 EIOS 被忽略或误解
    
    // 解决方案:状态同步
    synchronize_with_switch() {
        // 1. 先确认 Switch 状态
        while (get_switch_state() != DETECT_QUIET) {
            // 2. 如果不在正确状态,引导它
            if (switch_in_l0()) {
                send_electrical_idle();  // 强制进入空闲
            }
            delay_ms(1);
        }
        
        // 3. 确认 Switch 准备好后再发送 EIOS
        send_eios();
    }
}

7.2 超时处理

c 复制代码
// 更严格的超时处理
struct reset_timeouts {
    u32 switch_prepare_timeout = 500;   // 500ms
    u32 eios_detection_timeout = 100;   // 100ms
    u32 link_training_timeout = 1000;   // 1000ms
    u32 total_operation_timeout = 2000; // 2000ms
};

// 带超时的复位操作
int reset_with_timeouts(struct pcie_ep *ep)
{
    u64 start_time = get_current_time_ms();
    
    // 每个步骤检查总超时
    if (check_total_timeout(start_time)) {
        return -ETIMEDOUT;
    }
    
    // ... 执行复位步骤
}

结论

是的,RC-EP 和 Switch-EP 的复位链路建立过程确实不同:

  1. RC-EP 是同步的:RC 控制 PERST#,双方同时看到信号变化,几乎同时开始训练。

  2. 你的 Switch-EP 方案是异步的

    • EP 通过 GPIO 控制下游设备复位
    • Switch 完全不知道复位何时发生
    • EP 需要主动发送 EIOS 来"唤醒" Switch
    • Switch 被动检测到信号后才开始响应

这带来的挑战

  • 需要确保 Switch 在正确的状态(Detect.Quiet)
  • 需要处理可能的竞争条件
  • 需要更严格的超时和错误处理
  • 链路建立时间可能更长

建议 :在你的 EP 驱动中实现状态检查同步机制,确保 Switch 准备好接收 EIOS,这样可以提高复位可靠性。

相关推荐
Doro再努力1 小时前
Vim 快速上手实操手册:从入门到生产环境实战
linux·编辑器·vim
wypywyp1 小时前
8. ubuntu 虚拟机 linux 服务器 TCP/IP 概念辨析
linux·服务器·ubuntu
Doro再努力2 小时前
【Linux操作系统10】Makefile深度解析:从依赖推导到有效编译
android·linux·运维·服务器·编辑器·vim
senijusene2 小时前
Linux软件编程:IO编程,标准IO(1)
linux·运维·服务器
忧郁的橙子.2 小时前
02-本地部署Ollama、Python
linux·运维·服务器
醇氧2 小时前
【linux】查看发行版信息
linux·运维·服务器
No8g攻城狮2 小时前
【Linux】Windows11 安装 WSL2 并运行 Ubuntu 22.04 详细操作步骤
linux·运维·ubuntu
XiaoFan0123 小时前
免密批量抓取日志并集中输出
java·linux·服务器
souyuanzhanvip3 小时前
ServerBox v1.0.1316 跨平台 Linux 服务器管理工具
linux·运维·服务器
HalvmånEver4 小时前
Linux:线程互斥
java·linux·运维