news 2026/9/23 20:14:58

TVM VTA 设计与开发指南:可配置深度学习加速器的软硬件全栈解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
TVM VTA 设计与开发指南:可配置深度学习加速器的软硬件全栈解析
  • 编译器
  • 深度学习
  • 模型优化

【免费下载链接】tvm

Open deep learning compiler stack for cpu, gpu and specialized accelerators

项目地址:https://gitcode.com/gh_mirrors/tvm7/tvm
点击查看免费下载

VTA(Versatile Tensor Accelerator,通用张量加速器)是 TVM 内置的一个开放、通用、可定制的深度学习加速器,其设计目标是暴露主流深度学习加速器最常见、最具代表性的特征,并与 TVM 编译器栈共同构成一套覆盖硬件设计、驱动、JIT 运行时与优化编译器的端到端软硬件系统。本文以仓库中 VTA Design and Developer Guide 及其子文档(VTA Configuration、VTA Hardware Guide)为主体,系统讲解 VTA 的顶层配置方式、四模块硬件架构、指令集与微架构设计,并结合vta-hw硬件子模块与 VTA Python 软件栈 中的源码实现进行佐证。读完本文,你将掌握如何通过修改vta_config.json重新参数化 VTA,理解 VTA 的 LOAD/GEMM/ALU/STORE 指令与 load-compute-store 数据流流水线,并具备阅读 VTA HLS 硬件源码与 TVM 侧 tensorization 内建函数的能力。

VTA 全栈概览:从编译器到 FPGA 的一体化设计

VTA 的核心定位在 VTA 索引页 中有明确表述:它是一个开放、通用、可定制的深度学习加速器,并附带一套基于 TVM 的完整编译器栈。TVM 与 VTA 合在一起构成了一个端到端的深度学习软硬件系统栈,包括:

  • 硬件设计:基于 Vivado HLS C++(Xilinx 工具链)或 Chisel(Intel 工具链)描述的加速器,硬件源码位于3rdparty/vta-hw/hardware子模块;
  • 驱动程序:面向 Pynq / DE10-Nano 等 FPGA 开发板的设备驱动;
  • JIT 运行时:根据硬件参数即时生成加速器可执行代码的运行时;
  • 优化编译器栈:基于 TVM 的完整编译流水线。

VTA 的关键特性包括:通用、模块化、开源的硬件设计;面向 FPGA 部署的流畅工作流;支持在普通工作站上用模拟器(sim)原型化编译流水线;面向模拟与 FPGA 两种后端的 Pynq 驱动与 JIT 运行时;以及与 TVM 的端到端集成。

在软件侧,VTA 的 Python 栈位于仓库的 vta/python/vta 目录,其中 environment.py 中Environment类承载了针对特定 VTA 后端编译所需的全部硬件配置信息。Environment支持以临时作用域的方式切换配置(with vta.Environment(new_cfg)),这使得同一套编译栈可以在不同参数化的 VTA 之间复用,体现了软硬件协同设计(co-design)的核心理念。

VTA 顶层配置:通过 vta_config.json 参数化整个栈

VTA 栈同时包含硬件加速器栈与基于 TVM 的软件栈,并且开箱即用地具备灵活性:通过修改高层配置文件3rdparty/vta-hw/config/vta_config.json,用户可以改变张量内建函数(tensor intrinsic)的形状、时钟频率、流水线深度、数据类型位宽以及片上缓冲区大小。该文件由3rdparty/vta-hw子模块提供(当前仓库中该子模块未展开内容,需要执行git submodule update --init --recursive拉取)。

这份配置不仅是硬件的规格说明,也向 TVM 编译器栈参数化了加速器的架构规格。install.rst明确指出:配置文件还指定了 TVM 编译目标,当TARGET设为sim时,所有 TVM 工作负载都在 VTA 模拟器上执行。修改配置后重建 VTA 的流程为:编辑3rdparty/vta-hw/config/vta_config.json,然后回到 TVM 根目录执行make

参数总览表

config.rst给出了vta_config.json中所有参数的说明:

属性格式说明
TARGETStringTVM 设备目标。
HW_VERStringVTA 硬件版本号。
LOG_INP_WIDTHInt (log2)输入数据类型有符号整数位宽。
LOG_WGT_WIDTHInt (log2)权重数据类型有符号整数位宽。
LOG_ACC_WIDTHInt (log2)累加器数据类型有符号整数位宽。
LOG_BATCHInt (log2)VTA 矩阵乘内建函数的输入/输出第 0 维。
LOG_BLOCKInt (log2)VTA 矩阵乘内维。
LOG_UOP_BUFF_SIZEInt (log2)微操作(micro-op)片上缓冲区大小(字节)。
LOG_INP_BUFF_SIZEInt (log2)输入片上缓冲区大小(字节)。
LOG_WGT_BUFF_SIZEInt (log2)权重片上缓冲区大小(字节)。
LOG_ACC_BUFF_SIZEInt (log2)累加器片上缓冲区大小(字节)。

关于 LOG 前缀的约定

config.rst特别强调了一个命名约定:参数名以LOG开头时,表示该值只能表示为 2 的幂,因此这些参数以 log2 值描述。例如:

  • 输入数据类型位宽为 8 bit 时,LOG_INP_WIDTH应设为 3(即 8 的 log2);
  • 要描述一个 64 kB 的微操作缓冲区,LOG_UOP_BUFF_SIZE应设为 16(2^16 = 65536 字节 = 64 kB)。

关键参数详解

  • TARGET:可取值"pynq""ultra96""sim"(快速模拟器)、"tsim"(基于 Verilator 的周期精确模拟)。
  • HW_VER:硬件版本号,每当 VTA 硬件设计发生变化时递增,用于唯一标识硬件比特流(bitstream)。
  • LOG_BATCH:等价于形状为 (A, B) × (B, C) 乘法中的 A,即内部张量计算的批(batch)维度。
  • LOG_BLOCK:等价于形状为 (A, B) × (B, C) 乘法中的 B 和 C,即内部张量计算的输入/输出通道维度。

这两个参数直接决定了 GEMM 内建函数的形状。在 TVM 侧,intrin.py 的gemm(env, mock)函数按如下方式把配置映射为内建函数的张量形状:

  • 权重张量形状为(env.BLOCK_OUT, env.BLOCK_IN),且要求WGT_ELEM_BITS // WGT_WIDTH == BLOCK_OUT * BLOCK_IN(即权重按位打包后的通道数必须匹配 GEMM 核的单周期乘法维度);
  • 输入张量形状为(env.BATCH, env.BLOCK_IN),要求INP_ELEM_BITS // INP_WIDTH == BATCH * BLOCK_IN
  • 输出张量形状为(env.BATCH, env.BLOCK_OUT),要求ACC_ELEM_BITS // ACC_WIDTH == BATCH * BLOCK_OUT

从源码结构看,这些assert保证了配置参数的一致性,任何修改都必须满足上述位宽与形状的整除约束,否则编译期就会报错。

VTA 硬件设计总览

hardware.rst对 VTA 硬件进行了两个层次的描述:一是 VTA 设计及其 ISA 软硬件接口的架构级概览;二是 VTA 硬件模块与计算核微码规范的微架构级概览

顶层架构:RISC 风格处理器 + 解耦访问执行

VTA 是为快速、高效的稠密线性代数(dense linear algebra)而设计的通用深度学习加速器。它包含一个简单的类 RISC 处理器,可以对秩为 1 或 2 的张量寄存器执行稠密线性代数运算。此外,设计采用了**解耦访问-执行(decoupled access-execute)**架构来隐藏内存访问延迟。从更广的角度看,VTA 可以作为全栈优化的深度学习加速器模板设计,向编译器栈暴露一个通用的张量计算接口。

VTA 由四个模块组成,它们通过 FIFO 队列和本地存储块(SRAM)相互通信,从而实现任务级流水线并行:

  • 取指模块(fetch):负责从 DRAM 加载指令流,并解码指令将其路由到三条命令队列之一;
  • 加载模块(load):负责将输入张量和权重张量从 DRAM 加载到数据专用的片上存储器;
  • 计算模块(compute):用 GEMM 核执行稠密线性代数计算,用张量 ALU 执行通用计算;同时负责将数据从 DRAM 加载到寄存器文件,以及将微操作核加载到微操作缓存;
  • 存储模块(store):将计算核产生的结果写回 DRAM。

HLS 硬件源码组织

VTA 设计目前用 Vivado HLS C++ 描述(仅受 Xilinx 工具链支持),硬件源码位于3rdparty/vta-hw/hardware/xilinx/sources

  • vta.cc:包含每个 VTA 模块的定义,以及顶层 VTA 设计的行为级模型;
  • vta.h:包含使用 Xilinxap_int类型定义的类型定义与函数原型声明。

此外,预处理宏定义在3rdparty/vta-hw/include/vta/hw_spec.h中。这些宏大多由3rdparty/vta-hw/config/vta_config.json中的参数推导而来。JSON 文件由3rdparty/vta-hw/config/vta_config.py处理,生成一串定义预处理宏的编译标志字符串,供 Makefile 使用,从而在 HLS 硬件综合编译器和构建 VTA 运行时的 C++ 编译器两边同时设置这些高层参数。这正是"改一份 JSON、软硬件同步生效"的实现机制。

在 Python 侧,environment.py 的pkg_config(cfg)通过执行3rdparty/vta-hw/config/pkg_config.py来解析配置,而get_vta_hw_path()默认指向3rdparty/vta-hw(可用环境变量VTA_HW_PATH覆盖),说明软件栈与硬件栈共享同一份配置源。

HLS 模块示例:fetch 模块

hardware.rst给出了其中一个 VTA 模块的 C++ 定义,作为 HLS 编码示例:

void fetch( uint32_t insn_count, volatile insn_T *insns, hls::stream<insn_T> &load_queue, hls::stream<insn_T> &gemm_queue, hls::stream<insn_T> &store_queue) { #pragma HLS INTERFACE s_axilite port = insn_count bundle = CONTROL_BUS #pragma HLS INTERFACE m_axi port = insns offset = slave bundle = ins_port #pragma HLS INTERFACE axis port = load_queue #pragma HLS INTERFACE axis port = gemm_queue #pragma HLS INTERFACE axis port = store_queue #pragma HLS INTERFACE s_axilite port = return bundle = CONTROL_BUS INSN_DECODE: for (int pc = 0; pc < insn_count; pc++) { #pragma HLS PIPELINE II = 1 // Read instruction fields insn_T insn = insns[pc]; // Do some partial decoding opcode_T opcode = insn.range(VTA_INSN_MEM_0_1, VTA_INSN_MEM_0_0); memop_id_T memory_type = insn.range(VTA_INSN_MEM_5_1, VTA_INSN_MEM_5_0); // Push to appropriate instruction queue if (opcode == VTA_OPCODE_STORE) { store_queue.write(insn); } else if (opcode == VTA_OPCODE_LOAD && (memory_type == VTA_MEM_ID_INP || memory_type == VTA_MEM_ID_WGT)) { load_queue.write(insn); } else { gemm_queue.write(insn); } } }

这段代码中,VTA_OPCODE_*VTA_MEM_ID_*VTA_INSN_MEM_*正是由hw_spec.h根据vta_config.json宏定义生成。在软件侧,environment.py 的DevContext也维护了同源的内存 ID 常量(MEM_ID_UOP = 0MEM_ID_WGT = 1MEM_ID_INP = 2MEM_ID_ACC = 3MEM_ID_OUT = 4等)和任务队列 ID(QID_LOAD_INP/QID_LOAD_WGT = 1QID_LOAD_OUT/QID_COMPUTE = 2QID_STORE_OUT = 3),与 fetch 模块的解码路由逻辑一一对应。

HLS 编码要点

对上述示例,hardware.rst总结了三条观察结论:

  1. 参数(Parameters):每个函数的参数列表,加上接口 pragma,共同定义了生成硬件模块对外暴露的硬件接口。

    • 按值传递的参数表示只读的硬件内存映射寄存器,主机可以写入。例如insn_count会被综合为一个内存映射寄存器,供主机写入以设定某段 VTA 指令序列的长度。
    • 指针参数的含义取决于所用接口 pragma:配合m_axi接口 pragma 时,会生成 AXI 请求方接口以提供对 DRAM 的 DMA 访问;配合bram接口 pragma 时,会生成 BRAM 接口,向 FPGA 块 RAM 暴露读/写端口。
    • 按引用传递的 HLS 流(stream)配合axis接口 pragma,会产生通向模块的 FIFO 接口。硬件 FIFO 在模块之间提供了有用的同步机制。
  2. Pragma:编译器 pragma 对定义每个模块的硬件实现至关重要,VTA 设计中用到了几种 pragma:

    • HLS INTERFACE:指定综合后硬件模块的接口;
    • HLS PIPELINE:通过设定 initiation interval(II)目标来定义硬件流水线性能目标。当设定II == 1时,告诉编译器综合出的硬件流水线应能做到每周期执行一次循环迭代;
    • HLS DEPENDENCE:指示编译器忽略给定循环中的某些类型依赖检查。例如当循环体对同一个 BRAM 结构既写又读、又要达到 II 为 1 时,HLS 编译器必须假设最坏情况(读发生在上一周期写更新地址的下一周期),这在 BRAM 时序特性下无法实现(看到更新后的值至少需要 2 个周期)。因此要达到 II 为 1,必须放宽依赖检查。开启这一优化后,需要软件栈来防止对同一地址的"写后读"。

指令集架构(ISA)

VTA 的 ISA 由 4 条变延迟 CISC 指令组成,其中两条执行微码指令序列来完成计算:

  • LOAD指令:将二维张量从 DRAM 加载到输入缓冲区、权重缓冲区或寄存器文件;也可以将微内核加载到微操作缓存;在加载输入与权重 tile 时支持动态填充(padding);
  • GEMM指令:对输入张量与权重张量执行矩阵-矩阵乘法的微操作序列,并将结果累加到寄存器文件张量上;
  • ALU指令:对寄存器文件张量数据执行矩阵-矩阵 ALU 操作的微操作序列;
  • STORE指令:将输出缓冲区的二维张量存储到 DRAM。

执行单元分配如下:LOAD指令根据目标存储缓冲区的位置,由加载模块或计算模块执行;GEMMALU指令由计算模块的 GEMM 核和张量 ALU 执行;STORE指令完全由存储模块执行。

需要注意:VTA 的 ISA 会随架构参数(GEMM 核形状、数据类型、存储大小等)的变化而变化,ISA 并不保证在所有 VTA 变体间兼容。这本身是可接受的,因为 VTA 运行时适配参数变化,为生成的加速器版本定制二进制代码。这体现了 VTA 栈拥抱软硬件接口流动性的协同设计哲学。

数据流执行与流水线扩展

基于依赖 FIFO 的数据流执行

VTA 依靠硬件模块之间的依赖 FIFO 队列来同步并发任务的执行。每个模块通过**读后写(RAW)写后读(WAR)**依赖队列与其消费者、生产者相连,从而与生产者/消费者模块并发执行,形成数据流(dataflow)式的执行方式。伪代码执行逻辑如下:

  1. 在硬件中解码每条指令内的依赖标志;
  2. 若指令有传入的 RAW 依赖,则执行以收到生产者模块的 RAW 依赖令牌为条件;
  3. 若任务有传入的 WAR 依赖,则执行以收到消费者模块的 WAR 依赖令牌为条件;
  4. 任务完成时,检查传出的 RAW 与 WAR 依赖,分别通知消费者与生产者模块。

这里的依赖令牌是无信息量的(information-less),因为各模块执行的指令按 FIFO 顺序到达、设计上不允许重排。

流水线可扩展性

默认 VTA 设计由四个模块构成3 级 load-compute-store 任务流水线。遵循数据流硬件组织原则,可以将 VTA 流水线扩展到更多级。例如,可以设想把张量 ALU 从 GEMM 核中分离出来以最大化 GEMM 核利用率,形成load-gemm-activate-store任务流水线(接近 TPU 的设计)。但增加流水线级数会带来存储与额外逻辑开销,因此默认采用 3 级流水线。

微架构详解

Fetch 模块

VTA 由线性指令流编程。fetch 模块是 VTA 面向 CPU 的入口,通过三个内存映射寄存器编程:

  • 可读写的control寄存器:启动 fetch 模块,并可读回以检查是否完成;
  • 只写的insn_count寄存器:设置要执行的指令数;
  • 只写的insns寄存器:设置 DRAM 中指令流的起始地址。

CPU 在由 VTA 运行时准备的物理连续缓冲区内布置指令流。就绪后,CPU 把起始物理地址写入insns寄存器,把指令流长度写入insn_count寄存器,并在control寄存器中断言启动信号,从而启动 VTA 通过 DMA 从 DRAM 读取指令流。

fetch 模块访问指令流后,对指令进行部分解码,推入喂给 load、compute、store 三个模块的命令队列:

  • STORE指令推入 store 命令队列;
  • GEMMALU指令推入 compute 命令队列;
  • 描述微操作内核或寄存器文件数据加载的LOAD指令推入 compute 命令队列;
  • 描述输入或权重数据加载的LOAD指令推入 load 命令队列。

当某个命令队列变满时,fetch 模块会停驻(stall)直到队列不再满。因此命令队列被设计得足够深,以允许较宽的执行窗口,并让多个任务在 load-compute-store 流水线上并发在飞(in-flight)。

Compute 模块

VTA 的计算模块扮演一个对张量寄存器(而非标量寄存器)执行计算的 RISC 处理器角色。两个功能单元修改寄存器文件:张量 ALU 与 GEMM 核。

计算模块从微操作缓存执行 RISC 微操作,有两类计算微操作:ALU 与 GEMM 操作。为了最小化微操作内核的占用空间,同时避免条件跳转等控制流指令,计算模块在两级嵌套循环内执行微操作序列,通过仿射函数计算每个张量寄存器位置的地址。这种压缩方式帮助减小了微内核指令占用空间,适用于神经网络算子中常见的矩阵乘法与二维卷积。

GEMM 核通过执行上图所示的两级嵌套循环中的微码序列来评估 GEMM 指令。GEMM 核每周期可执行一次输入-权重矩阵乘法。单周期矩阵乘法的维度定义了一个硬件张量化内建函数(tensorization intrinsic),TVM 编译器必须将计算调度 lower 到该内建函数上。该内建函数由输入、权重与累加器张量的维度定义;每种数据类型可有不同的整数精度——典型情况下权重与输入类型是低精度的(8 bit 或更少),而累加器张量用更宽的类型(32 bit)防止溢出。为了让 GEMM 核保持忙碌,输入缓冲区、权重缓冲区和寄存器文件都必须暴露足够的读/写带宽。

张量 ALU支持一组标准操作以实现常见的激活、归一化和池化算子。VTA 是模块化设计,张量 ALU 支持的算子范围可以扩展以提高算子覆盖率,代价是更高的资源占用。张量 ALU 可以执行张量-张量操作,以及对立即数执行张量-标量操作。张量 ALU 的操作码和立即数由高层 CISC 指令指定;张量 ALU 计算场景中的微码只负责指定数据访问模式。

需要指出的是,在计算吞吐方面,张量 ALU 无法达到每周期一个操作的速率。限制来自读端口的缺乏:由于每周期只能读取一个寄存器文件张量,张量 ALU 的 initiation interval 至少为 2(即每 2 个周期至多执行 1 个操作)。此外,单次执行张量-张量操作可能很昂贵,因为寄存器文件类型很宽(典型为 32 bit 整数)。为平衡张量 ALU 与 GEMM 核的资源占用,默认情况下张量-张量操作通过跨多周期的向量-向量操作执行。

在软件侧,environment.py 的DevContext定义了与硬件对应的 ALU 操作码(ALU_OPCODE_MINALU_OPCODE_MAXALU_OPCODE_ADDALU_OPCODE_SHRALU_OPCODE_MUL),编译栈据此为张量 ALU 生成对应的微操作序列。

Load 与 Store 模块

load 与 store 模块执行从 DRAM 到 SRAM 的、带跨步访问模式的 2D DMA 加载。此外,load 模块可以在加载过程中即时插入 2D 填充,这在分块二维卷积时非常有用。这意味着 VTA 可以对二维卷积输入分块,而无需在 DRAM 中重新布局数据来为输入与权重 tile 插入空间填充,省去了额外的数据重排开销。

配置驱动的实战流程参考

结合 VTA Installation Guide,配置与部署 VTA 的关键流程如下:

  1. 设置环境变量export VTA_HW_PATH=<tvm根目录>/3rdparty/vta-hw(VTA 功能仿真库);
  2. 启用仿真后端构建 TVM:在build/config.cmake中追加set(USE_VTA_FSIM ON),然后cmake .. && make -j4
  3. 加入 VTA Python 包路径export PYTHONPATH=<tvm根目录>/vta/python:${PYTHONPATH}
  4. 验证安装:运行python <tvm根目录>/vta/tests/python/integration/test_benchmark_topi_conv2d.py(二维卷积测试台);
  5. 修改配置并重建:编辑3rdparty/vta-hw/config/vta_config.json后执行make。当TARGET设为sim时,所有 TVM 工作负载都在模拟器上执行;
  6. FPGA 部署:对于 Pynq 等硬件目标,可将TARGET设为"pynq",使用pynq_sample.json作为配置模板,并通过 apps/vta_rpc/start_rpc_server.sh 在板端启动 RPC 服务器(监听0.0.0.0:9091),主机侧设置VTA_RPC_HOSTVTA_RPC_PORT后远程编程 FPGA 比特流并运行测试台。

总结

VTA 的设计精髓在于软硬件协同与参数化:一份vta_config.json同时驱动 HLS 硬件综合、C++ 运行时构建与 TVM 编译栈(vta_config.py→ 编译宏 →hw_spec.hpkg_config.pyEnvironment→ tensorization 内建函数),改配置即改架构。硬件侧的四模块(fetch/load/compute/store)通过 FIFO 与依赖令牌实现解耦访问执行与 load-compute-store 数据流流水线;ISA 随参数流动,运行时以 JIT 方式为具体硬件生成二进制,正是"软硬件接口保持流动"的协同设计哲学的具体体现。对希望深入全栈加速器设计的开发者而言,dev/index.rst 指向的配置与硬件两份文档,配合 vta/python/vta 软件栈源码与3rdparty/vta-hw硬件源码,构成了完整的学习路径。

  • 编译器
  • 深度学习
  • 模型优化

【免费下载链接】tvm

Open deep learning compiler stack for cpu, gpu and specialized accelerators

项目地址:https://gitcode.com/gh_mirrors/tvm7/tvm
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/23 20:13:48

舌头分割数据集实战:从2类掩码到U-Net训练与避坑指南

简介&#xff1a;本资源面向图像分割方向的算法学习者与工程开发者&#xff0c;提供一套完整的舌头分割数据集&#xff0c;可用于语义分割模型的训练、验证与效果对比。数据涵盖训练集与测试集两部分&#xff1a;训练集含2127张jpg原图及2127张对应png掩膜&#xff0c;测试集含…

作者头像 李华
网站建设 2026/9/23 20:10:50

DeepSeek工业视觉质检:缺陷检测与工艺闭环优化方案

简介&#xff1a;本资源是一份面向工业AI工程师、视觉算法研究员及智能制造系统集成人员的深度技术方案&#xff0c;聚焦工业视觉质检中缺陷识别精度低、工艺反馈滞后等核心痛点&#xff0c;提出基于DeepSeek大模型与DLIA系统的全流程闭环优化方法。文档共455页、52章&#xff…

作者头像 李华
网站建设 2026/9/23 20:07:56

改进型果蝇优化算法Matlab实现与三参数调优指南

简介&#xff1a;本资源是一套面向智能优化算法研究者与MATLAB实践者的改进型果蝇优化算法&#xff08;FOA&#xff09;实现方案&#xff0c;聚焦于解决多模态函数优化、模型参数调优等复杂全局寻优问题。资源包含1个核心MATLAB源码文件&#xff08;LGMS_FOA.m&#xff09;与1份…

作者头像 李华
网站建设 2026/9/23 19:59:42

豆瓣TOP250爬虫实战:Python数据工程最小闭环

简介&#xff1a;本资源是一套完整的豆瓣电影TOP250数据采集与可视化分析实战项目&#xff0c;面向Python初学者及数据分析入门者&#xff0c;解决网页爬虫、结构化存储、多维统计与前端可视化等典型数据工程问题。压缩包共86个文件&#xff0c;总计11.17MB&#xff0c;包含3个…

作者头像 李华