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,这样可以提高复位可靠性。

相关推荐
HABuo2 小时前
【Linux进程(五)】进程地址空间深入剖析-->虚拟地址、物理地址、逻辑地址的区分
linux·运维·服务器·c语言·c++·后端·centos
wdfk_prog2 小时前
WIN11如何可以安装ISO
linux·笔记·学习
旖旎夜光2 小时前
Linux(10)(中)
linux
牛奶咖啡132 小时前
shell脚本编程(六)
linux·shell脚本编程·shell中的判断·if/else结构·if/elif/else结构·case选择语句·shell各种判断的语法及示例
sim20202 小时前
创建FTP账号
linux
Ha_To2 小时前
2026.1.12 Linux提升账户权限
linux·运维·服务器
OopspoO2 小时前
Linux内存分析工具
linux·运维
oMcLin2 小时前
如何在Ubuntu 20.04上配置并调优Kubernetes集群,确保在多租户环境下的高可用性与资源分配?
linux·ubuntu·kubernetes
石头5303 小时前
Service 详解
linux