第七周开发日志(3.8-3.14)
-
工作进展:本周主要做的是规划,无实习任务相关的实质性代码工作
-
本周做出了对下周工作的详细规划以及个人对aarch64的详细学习
-规划如下:
NPU 协处理器化架构设计 (NPU as FPU)
1. 概述
本项目提出并实现一种创新的异构计算架构,旨在将 NPU 从传统的、基于 VFS/ioctl 的“设备”模型,提升为与 CPU 核心紧耦合的“协处理器”。其设计目标是通过极致的软硬件协同,实现 NPU 算力调度
2. 核心设计哲学
彻底废弃传统基于 VFS(虚拟文件系统)和 ioctl 的松耦合设备驱动模型。将 NPU 提升为与 CPU 平级的计算协处理器(类比 FPU 浮点运算单元)。通过共享 DMA 内存、定制化系统调用(Syscall)与“懒汉式”上下文切换(Lazy Context Switch)实现算力调度。
3. 内核数据结构扩展 (TCB 升级)
为了让操作系统的调度器(Scheduler)能够接管 NPU 资源,必须扩展内核的任务控制块(TCB / task_struct):
-
npu_isdirty: bool- 含义:NPU 硬件上下文“脏”标识位。
true:表示当前物理 NPU 硬件的内部 SRAM 和寄存器中,依然保留着该任务的数据和状态。false:表示该任务的 NPU 状态已被换出,或者尚未使用过 NPU。
-
npu_context: struct- 含义:NPU 硬件上下文结构体。追加在原生 CPU 任务上下文之后。
- 作用:用于存储 NPU 被强行打断或切换时的完整硬件状态机。需查阅具体 NPU 芯片的闭源驱动源码及手册,提取并保存完整的寄存器集合(如命令队列指针、当前算子偏移量、中断屏蔽字等)。
4. 用户态基础框架 (libnpu)
在用户层封装底层细节,向应用程序开发者提供极简的 C/C++ 接口。
-
专属内存分配 (
dma_malloc系统调用)- 抛弃标准
malloc。用户通过dma_malloc直接向内核申请一段物理地址连续的 DMA 内存。 - 目的:绕过 IOMMU 的复杂页表配置,让 NPU 硬件的 DMA 控制器可以直接通过物理地址(PA)高速拉取数据。
- 抛弃标准
-
强制中断注入
- 用户将计算参数、模型权重写入申请好的 DMA 内存区域。
- 在构建 NPU 任务描述符(Task Descriptor)链表时,
libnpu会在底层介入,将每个微小可配置算子任务结构体中的中断控制位(IRQ Enable)强制设为开启。这是后续内核能够精准捕获算子边界、实现状态保存的关键。
-
内联汇编触发 (
npu_workAPI)- 利用 AArch64 调用约定,将装载了任务描述符链表的 DMA 内存物理地址指针塞入约定的寄存器(如 X0)。
- 执行定制的内联汇编(包含特定的
SVC陷入指令),直接触发定制的resolve_npu系统调用。
5. 内核态调度核心 (resolve_npu 系统调用)
这是整个架构的调度心脏。当 CPU 通过 SVC 陷入内核态后,严格按照以下流水线执行:
阶段 1:内存转换与前置检查
- 地址翻译:将 X0 寄存器传入的用户态虚拟地址(VA),通过查询页表或 DMA 分配记录,转换为 NPU 硬件所需的物理地址(PA)。
- 硬件状态机检查:检查全局 NPU 硬件状态(
IDLE或BUSY)。
阶段 2:懒汉式上下文切换 (Lazy Context Switch) 调度器需要仲裁当前物理 NPU 硬件的归属权。
- 碰撞检测:检查系统中是否存在一个“前任任务”,其
npu_isdirty == true,且该任务并非当前请求任务。 - 等待安全边界:如果存在这样的“前任任务”(说明 NPU 硬件中存有他人数据),内核绝不能强行复位硬件。必须检查 NPU 当前是否为
IDLE。如果是BUSY,当前 CPU 线程必须挂起睡眠(加入等待队列),直到 NPU 算完当前算子并触发硬件中断。 - 保存前任状态:当 NPU 确认处于
IDLE后,内核立刻将 NPU 硬件内的全量寄存器状态抽取出来,保存到那个“前任任务”的npu_context中。 - 剥夺前任所有权:将“前任任务”的
npu_isdirty标志位修改为false。
阶段 3:恢复当前状态并启动硬件
- 检查当前记录:检查当前请求任务的 TCB 中,是否存有尚未跑完的
npu_context(即之前被切换出去的状态)。 - 状态恢复:如果存在,则将
npu_context里的数据原封不动地写回 NPU 的物理控制寄存器。 - 宣誓主权:将当前任务的
npu_isdirty标志位置为true。 - 硬件启动 (Kick the Doorbell):将阶段1中转换好的任务描述符物理地址指针,写入 NPU 的“任务执行寄存器”,并写入特定的启动命令(“踢门铃”)。
- 状态标记与返回:将全局 NPU 状态标记为
BUSY,CPU 退出系统调用,返回用户态继续执行或调度其他普通线程。
6. 硬件中断处理程序 (NPU IRQ Handler)
由于 libnpu 强制开启了每个算子的中断,NPU 每算完一个微小算子就会发出硬件 IRQ。
- 进入 ISR:CPU 陷入内核中断服务例程。
- 确认完成:读取 NPU 硬件状态寄存器,确认当前算子计算完成。
- 释放路权:将全局 NPU 状态强制标记为
IDLE(空闲)。 - 唤醒等待者:唤醒在
resolve_npu系统调用中,因为 NPUBUSY而挂起睡眠的其他任务线程,让它们得以重新参与 NPU 的抢占与上下文切换流程。