310P windows 驱动支持热复位指南
收藏回复举报
310P windows 驱动支持热复位指南
发表于2026-08-29 09:39:54
0 查看

概述&背景

在 Linux 环境下,310P 驱动通常采用 SBR 方式实现热复位。由于 Windows 系统的架构限制,Windows 驱动无法获取上级 PCI 桥控制器的权限,因此无法通过 SBR 方式触发复位。故当前310P Windows 驱动通过传统 PCIE 方式进行 Hot Reset 不可行,驱动徽标认证受阻。

解决方法

本文介绍了基于 IPC(Inter-Process Communication)的消息机制实现热复位方案:Host 端通过消息机制发送复位命令,Device 端(大核/泰山核)接收到命令后,向 LP 核(小核/R52核)发送复位消息,最终由 LP 核写入安全寄存器来触发 SOC 的软复位操作。

程序调用流程

从 host 到 device,一次完整的热复位流程如下:

host 侧保持不变

关键接口

--HiNpuEvtDevD0Exit

​--D0EntryPostResoureFree

​--HiNpuUninitAllModules

​--HiNpuNotifyDevOnline

device 侧修改

泰山核(大核)接收 host 侧消息通信

大核(drv_pcie)通过接收 Windows驱动消息检测设备离线状态,并通过回调机制通知 ascend_lpm 发送 IPC 指令。

关键接口

--agentdrv_admin_queue_cmd_proc

​ --agentdrv_notify_dev_online

​ --agentdrv_dev_online_proc

  1. drv_pcie检测到设备离线,触发热复位

在 src/drv_pcie_device/pcie_slave/agentdrv_pci.c 的 agentdrv_dev_online_proc() 函数中,当检测到设备离线状态为DEVDRV_DEV_OFFLINE时,触发热复位回调函数:

#define HOT_RESET_REASON_NORMAL  0x1

#define LPM_SOC_ID               8

STATIC void agentdrv_dev_online_proc(struct agentdrv_msg_dev *msg_dev, u32 online_devid, u32 status)

{

    /* ... */

    if (status == DEVDRV_DEV_OFFLINE) {

        msg_dev->dev_status = DEVDRV_DEV_OFFLINE;

     /* ... */

        /* 热复位触发时,通过回调通知 ascend_lpm 发送 IPC */

        if (g_hot_reset_callback != NULL) {

            u32 reset_reason = HOT_RESET_REASON_NORMAL;

            ret = g_hot_reset_callback(dev_id, LPM_SOC_ID, &reset_reason);

            if (ret != 0) {

                devdrv_err("Failed to send hot reset to LP core. (dev_id=%d; ret=%d)\n", dev_id, ret);

            }

        }

    }

}

消息拼装&转发给 LP 核(小核)

由于原设计架构中 ascend_lpm 依赖 drv_pcie,因此采用回调方式。

1. drv_pcie 定义回调接口

在 src/drv_pcie_device/pcie_slave/agentdrv_pci.h 中定义:

typedef int (*agentdrv_hot_reset_callback_t)(u32 dev_id, u32 core_id, u32 *reset_reason);

int agentdrv_register_hot_reset_callback(agentdrv_hot_reset_callback_t callback);

void agentdrv_unregister_hot_reset_callback(void);

2. drv_pcie 实现注册接口

在 src/drv_pcie_device/pcie_slave/agentdrv_pci.c 中实现:

static agentdrv_hot_reset_callback_t g_hot_reset_callback = NULL;

int agentdrv_register_hot_reset_callback(agentdrv_hot_reset_callback_t callback)

{

    if (callback == NULL) {

        devdrv_err("Invalid callback function.\n");

        return -EINVAL;

    }

    g_hot_reset_callback = callback;

    return 0;

}

EXPORT_SYMBOL(agentdrv_register_hot_reset_callback);

void agentdrv_unregister_hot_reset_callback(void)

{

    g_hot_reset_callback = NULL;

}

EXPORT_SYMBOL(agentdrv_unregister_hot_reset_callback);

3. ascend_lpm 注册回调

在 src/ascend_lpm/lpm/dms_lpm.c 中,使用外部申明函数,驱动初始化时把lpm_set_hot_reset_from_ipc() 函数注册到drv_pcie,退出时注销:

typedef int (*agentdrv_hot_reset_callback_t)(u32 dev_id, u32 core_id, u32 *reset_reason);

extern int agentdrv_register_hot_reset_callback(agentdrv_hot_reset_callback_t callback);

extern void agentdrv_unregister_hot_reset_callback(void);

int __init dms_lpm_init(void)

{

    CALL_INIT_MODULE(DMS_LPM_CMD_NAME);

    lpm_dms_info_from_devinfo_init();

#ifndef CFG_SOC_PLATFORM_MDC_V51

    lpm_scan_init();

#endif

    agentdrv_register_hot_reset_callback(lpm_set_hot_reset_from_ipc);

    return 0;

}

void __exit dms_lpm_exit(void)

{

    CALL_EXIT_MODULE(DMS_LPM_CMD_NAME);

#ifndef CFG_SOC_PLATFORM_MDC_V51

    lpm_scan_uninit();

#endif

    lpm_dms_info_from_devinfo_uninit();

    agentdrv_unregister_hot_reset_callback();

}

4. IPC 发送接口实现

在 src/ascend_lpm/lpm/dms_lpm_info_from_ipc.c 中,lpm_set_hot_reset_from_ipc() 函数会先进行参数校验,然后组装 struct devdrv_dev_info 结构体,并通过 lpm_get_info_from_ipc() 接口发送 IPC消息:

#ifdef CFG_FEATURE_HOT_RESET_SUPPORT

int lpm_set_hot_reset_from_ipc(u32 dev_id, u32 core_id, u32 *reset_reason)

{

    int ret;

    u32 len = sizeof(struct devdrv_dev_info);

    struct devdrv_dev_info *reset_arg = NULL;

    ret = dms_lpm_id_check(dev_id, core_id);

    if (ret != 0) {

        lpm_err("Parameter error. (dev_id=%u; ret=%d)\n", dev_id, ret);

        return ret;

    }

    reset_arg = (struct devdrv_dev_info *)reset_reason;

    ret = lpm_get_info_from_ipc(dev_id, LPM_HOT_RESET, core_id, (u8 *)reset_reason, &len);

    if (ret != 0) {

        lpm_err("Failed to send hot reset to ipc. (dev_id=%u; core_id=%u; ret=%d)\n",

            dev_id, core_id, ret);

        return ret;

    }

    lpm_info("Hot reset sent successfully. (dev_id=%u; core_id=%u; reason=0x%x)\n",

        dev_id, core_id, reset_arg->data1);

    return 0;

}

EXPORT_SYMBOL(lpm_set_hot_reset_from_ipc);

#endif

LP 核接收消息

LP 核(R52 核)接收到 device 端通过 IPC 发送的热复位指令后,需通过查询(query)模块来解析并处理该指令。

1. IPC 格式列表配置

在 src/ascend_lpm/ipc/dms_ipc_from_lp.c 中,将热复位的 IPC 格式配置加入到 IPC 格式列表 g_ipc_format_list 中:

/* hot reset */

{LPM_HOT_RESET, LPM_SOC_ID, MSG_HEADS_WORD, MSG_TOTAL_WORD,

    0, 0, NO_REPEATS, IPC_OBJ_LP, DMS_LP_Q_TX_RPID4_ACPU0,

    NULL, lp_ack_check_privte, NULL,

    {SUB_CMD_HOT_RESET, CMD_INQUIRY,  LPR52_TARGET_ID, LPR52_SOURECE_ID}},

其中:

  • 枚举类型:LPM_HOT_RESET
  • 子命令:SUB_CMD_HOT_RESET = 106
  • IPC 对象:IPC_OBJ_LP,队列:DMS_LP_Q_TX_RPID4_ACPU0

LP侧修改

  • 在LP核初始化时,会调用LpQueryInit()函数,该函数里面调用InquiryCmdFunc()——>LpQuery(),然后根据不同的Ipc消息,注册不同的查询函数。
  • LpQuery()中,当前基于已有的策略,新增命令字25,调用QueryHotResetInit(),该函数中通过写寄存器实现热复位。

1. LP 查询函数注册与枚举定义

在 LP 核的查询模块 lpr52/driver/query/query_lp.h 中,添加对应的枚举定义:SUBSYS_QUERY_HOT_RESET_INIT = 25

enum {

/* ... */

#ifdef AS610VER2

    SUBSYS_QUERY_QOS_BW_INIT = 25,

    SUBSYS_QUERY_QOS_BW_DEINIT = 26,

#endif

#ifdef PRODUCT_DC

    SUBSYS_QUERY_HOT_RESET_INIT = 25,

#endif

    QUERY_MAX_NUM,

};

其中:

SUBSYS_QUERY_HOT_RESET_INIT = SUB_CMD_HOT_RESET - SUB_CMD_QUERY_SOC_TEMP = 25

2/driver/query/query_lp.c 中,首先声明查询函数QueryHotResetInit():

static void(* const g_queryFunc[QUERY_MAX_NUM])(struct IpcMsg *) = {

/* ... */

#ifdef AS610VER2

    [SUBSYS_QUERY_QOS_BW_INIT]         = QueryQosBandInit,

    [SUBSYS_QUERY_QOS_BW_DEINIT]       = QueryQosBandDeinit,

#endif

#ifdef PRODUCT_DC

    static void QueryHotResetInit(struct IpcMsg *txMsg);

#endif

};

然后在函数指针数组 g_queryFunc 中进行注册,将新增的查询命令字与对应的处理函数关联起来:

[SUBSYS_QUERY_HOT_RESET_INIT] = QueryHotResetInit,

2. LP 核写安全寄存器&执行复位

当 LP 核的 QueryHotResetInit() 函数被调用后,会执行具体的 SOC 软复位操作。该操作通过向复位控制寄存器写入固定值来完成。具体实现代码位于 lpr52/driver/query/query_lp.c 中:

#ifdef PRODUCT_DC

/*

 * @brief                   : SOC热复位

 * @param [in]              : NA

 * @param [out]  txMsg      : 未使用,保留参数用于接口一致性

 * @return                  : void

 */

#define SC_SYSSOFTRST_CTRL  0x800002C0U

#define SC_SYSSOFTRST_VAL   0x55555555

static void QueryHotResetInit(struct IpcMsg *txMsg)

{

    (void)txMsg;

    __asm__ volatile("isb");

    __asm__ volatile("dsb");

    WRITEL(SC_SYSSOFTRST_VAL, SC_SYSSOFTRST_CTRL);

    __asm__ volatile("isb");

    __asm__ volatile("dsb");

}

#endif

3. 关键执行步骤说明:

  1. 寄存器定义:
    • 软复位控制寄存器地址:SC_SYSSOFTRST_CTRL = 0x800002C0U
    • 复位触发值:SC_SYSSOFTRST_VAL = 0x55555555
  2. 同步指令保障:
  3. 在写入复位寄存器之前和之后,分别使用 isb和 dsb确保指令流和数据流的完全同步,保证复位操作在硬件层面能够可靠、准确地触发 SOC 软复位。

上机验证

1. 构建产物

  • 大核修改与产物:大核主要修改了 device 侧驱动,编译产物为两个 .ko 文件(ascend_lpm.ko 和 drv_pcie.ko),打包在filesystem-le.cpio.gz压缩包中,该压缩包存在于驱动包目录:driver\device\filesystem-le.cpio.gz。
  • 小核修改与产物:小核主要修改了源码,编译产物为 bin 文件(lperika.bin)。该 bin 文件存在于驱动包目录:driver\device\lperika.b

2. 验证结果

  • 验证环境:310P Windows 11

在 Windows 设备管理器 中禁用设备之前 & 禁用-启用设备之后,npu-smi & 大小模型测试验证均通过。

npu-smi —— npu-smi info

小模型 —— resnet50

大模型 —— qwen2-7b-f16.gguf

本帖最后由 匿名用户 于 2026/09/03 09:48:53 编辑

我要发帖子