当前位置: 首页 > news >正文

从 HLS 到 Verilog 的转变解析1:以 AXI 接口为例

从 HLS 到 Verilog 的核心技术解析1:以 AXI 接口为例

这篇文档将作为你的学习指南,帮助你彻底理解 HLS 是如何将一个简单的 C++ 函数 add 转换为一个带有标准 AXI 接口的、可被处理器控制的硬件 IP 核。

第一步:理解“为什么”——为何不是简单的 input/output

你可能会想,c=a+b 最直观的 Verilog 不应该是这样吗?

module add (input  [31:0] a,input  [31:0] b,output [31:0] c
);assign c = a + b;
endmodule

HLS 当然可以生成这样的代码(如果使用 ap_fifo 等接口类型)。但你选择的 #pragma HLS INTERFACE s_axilite 是在告诉 HLS 一个至关重要的信息:

“我设计的这个硬件模块,不是一个独立的芯片,而是一个需要被嵌入式处理器(如 ARM 核)控制的硬件加速器(IP Core)。”

在现代片上系统(SoC)中,处理器是主(Master),硬件加速器是从(Slave)。它们之间需要一套标准的“沟通语言”和“交通规则”,而 AXI (Advanced eXtensible Interface) 就是业界最通用的规则之一。s_axilite 是 AXI 的轻量级版本,用于控制和状态寄存器的读写。

核心思想:HLS 生成 AXI 接口,是为了让你的 C++ 算法无缝地接入到一个以处理器为核心的系统中。处理器可以像读写内存一样,通过总线来设置输入参数 ab,并读取结果 c

第二步:HLS 的“分而治之”策略——数据路径 vs 控制接口

现在看你的 Verilog 文件,HLS 做了一个非常漂亮的设计分离:

  1. 数据路径 (Data Path) - 核心算法的硬件化
    • 位置: add.v 模块中的 assign c = (b + a);
    • 作用: 这是对你 C++ 算法 c = a + b; 最直接、最纯粹的硬件翻译。它是一个简单的组合逻辑加法器。HLS 分析出这个运算不需要时钟周期(零延迟),所以就给出了最高效的实现。这是计算的核心
  2. 控制/接口逻辑 (Control/Interface Logic) - 与外部世界沟通的桥梁
    • 位置: add_control_s_axi.v 模块。
    • 作用: 这个模块不执行任何加法运算。它的唯一职责是处理复杂的 AXI4-Lite 总线协议。它像一个“翻译官”或“秘书”,把处理器通过 AXI 总线发来的“命令”(读/写请求)翻译成对内部寄存器的简单操作。这是控制的核心

add.v 作为顶层模块,将这两部分粘合在一起。它例化了 add_control_s_axi,并把加法器电路和这个“翻译官”连接起来。

第三步:深入 AXI 接口的核心——add_control_s_axi.v 精解

这是我们学习的重点。我们来解剖这个“翻译官”是如何工作的。

核心1:地址映射 - 从软件变量到硬件寄存器

处理器如何区分 abc?答案是地址。HLS 已经为你做好了地址映射:

// 0x10 : Data signal of a
// 0x18 : Data signal of b
// 0x20 : Data signal of c

这意味着:

  • 当处理器向 0x10 地址写入数据时,它意图设置 a 的值。
  • 当处理器向 0x18 地址写入数据时,它意图设置 b 的值。
  • 当处理器从 0x20 地址读取数据时,它想要获取 c 的值。

这是软件思维到硬件思维转变的第一座桥梁:软件中的变量,变成了硬件中具有唯一地址的寄存器。

核心2:写操作流程 - 处理器如何“设置”输入

当处理器想要写入 a=5 时,AXI 总线上会发生一连串被称为“握手”的事件。我们来看 add_control_s_axi.v 中的写状态机(Write FSM):

  1. 等待地址 (WRIDLE 状态):

    • 处理器将地址 0x10 放到地址总线 AWADDR 上,并拉高 AWVALID(表示地址有效)。
    • 我们的 IP 核在 WRIDLE 状态下,看到 AWVALID 后,拉高 AWREADY 作为回应(表示地址已接收)。
    • 握手成功! IP 核在内部锁存住地址 0x10 (waddr <= AWADDR),然后状态机进入 WRDATA
  2. 等待数据 (WRDATA 状态):

    • 处理器将数据 5 放到数据总线 WDATA 上,并拉高 WVALID(表示数据有效)。

    • 我们的 IP 核在 WRDATA 状态下,看到 WVALID 后,拉高 WREADY 作为回应。

    • 握手成功! 此时,关键的寄存器更新逻辑被触发:

      if (w_hs && waddr == ADDR_A_DATA_0) // w_hs是WVALID和WREADY同时为高int_a[31:0] <= WDATA[31:0];
      

      因为之前锁存的地址是 0x10 (ADDR_A_DATA_0),所以总线上的数据 5 被存入了内部寄存器 int_a

    • 状态机进入 WRRESP

  3. 发送响应 (WRRESP 状态):

    • IP 核拉高 BVALID,告诉处理器:“你的写操作我已经完成了。”
    • 处理器看到 BVALID 后,会拉高 BREADY 来确认收到响应。
    • 握手成功! 状态机返回 WRIDLE,一次完整的写操作结束。

写入 b 的流程完全一样,只是地址换成了 0x18

核心3:读操作流程 - 处理器如何“获取”结果
  1. 等待地址 (RDIDLE 状态):

    • 处理器将地址 0x20 放到 ARADDR 上,并拉高 ARVALID

    • 我们的 IP 在 RDIDLE 下看到后,拉高 ARREADY

    • 握手成功! IP 核在内部锁存读地址 0x20,并进入 RDDATA 状态。同时,根据锁存的地址,准备要发送的数据:

      case (raddr)ADDR_C_DATA_0: beginrdata <= int_c[31:0]; // 准备发送int_c的值end
      endcase
      
  2. 发送数据 (RDDATA 状态):

    • IP 核将准备好的数据 rdata(现在是 int_c 的值)放到 RDATA 总线上,并拉高 RVALID,告诉处理器:“你要的数据准备好了!”
    • 处理器看到 RVALID 后,从 RDATA 总线上取走数据,并拉高 RREADY 作为回应。
    • 握手成功! 状态机返回 RDIDLE,一次完整的读操作结束。

第四步:连接一切 - 两个模块如何协同工作

现在,我们把两部分串联起来,看看 5+10 的完整数据流:

  1. 输入阶段:

    • 处理器 -> AXI总线 -> control_s_axi_U 经过写操作,将 5 存入 int_a,将 10 存入 int_b
  2. 计算阶段(在 add.v 中发生):

    • control_s_axi_U 模块的 int_aint_b 通过输出端口 ab 连接到 add.v 顶层的 wire awire b

      // 在 add.v 中
      wire [31:0] a;
      wire [31:0] b;
      // ...
      control_s_axi_U(.a(a), // control_s_axi_U的输出a连接到顶层的wire a.b(b)  // control_s_axi_U的输出b连接到顶层的wire b
      );
      
    • wire awire b 作为输入,送入加法器电路:

      // 在 add.v 中
      assign c = (b + a); // 计算器立即得出结果 15
      
  3. 结果锁存阶段(数据从 add.v 回到 add_control_s_axi.v):

    • 计算结果 15wire c 上。这个 wire c 被连接到 control_s_axi_U 的输入端口 c

    • add_control_s_axi.v 内部,每个时钟周期都会执行:

      always @(posedge ACLK) beginif (c_ap_vld) // c_ap_vld 在顶层被硬编码为1,所以总为真int_c <= c; // 将计算结果15锁存到内部寄存器int_c中
      end
      
  4. 输出阶段:

    • 处理器 -> AXI总线 -> control_s_axi_U 经过读操作,从 int_c 中读取到结果 15 并返回。

至此,一次完整的“软件设置 -> 硬件计算 -> 软件读取”流程就完成了。

总结与下一步

  • HLS 的本质: HLS 是一个高级的代码生成器。它为你封装了标准化的、经过验证的接口逻辑(如 AXI FSM),让你能专注于核心算法的 C/C++ 实现。
  • 解耦: add.vadd_control_s_axi.v 的分离是典型的硬件设计思想——将数据处理与控制逻辑解耦,使得两部分都可以独立修改和验证。
  • 你的机会: 现在你完全理解了这个流程。如果你发现 HLS 生成的 AXI 逻辑在某些方面(如延迟、吞吐率)不满足你的精细化需求,你现在拥有了足够的基础知识去:
    1. 修改 add_control_s_axi.v: 例如,改变一些状态转换条件,或者添加流水线寄存器来优化时序。
    2. 完全手写你自己的 AXI 模块: 当你成为专家后,你可以完全抛开 HLS 的接口生成,手写一个为你的特定应用量身定制的、最高效的 AXI 接口,然后与 HLS 生成的数据路径(或者你手写的数据路径)进行集成。

建议的下一步:

尝试一个带循环的 HLS 项目,比如向量加法。你会发现 HLS 会在 AXI 地址映射中增加一些控制位,比如 ap_start (地址 0x00) 和 ap_done (地址 0x00 的某个状态位)。去分析 HLS 是如何用这些控制位来启动、监控你的硬件的,这将是你精通 HLS 到 Verilog 设计的下一个关键里程碑。

http://www.lryc.cn/news/575086.html

相关文章:

  • 云原生灰度方案对比:服务网格灰度(Istio ) 与 K8s Ingress 灰度(Nginx Ingress )
  • jenkins 越用越卡,打开网页缓慢
  • CLion 调试时 Command Timed Out 问题解决方案
  • 深入剖析 Spring AOP
  • 红外图像增强(dde):基于“基础层-细节层”分解的增强算法
  • 5. Pytest失败重跑机制pytest-rerunfailures
  • LE AUDIO---Chapter 2. The Bluetooth® LE Audio architecture
  • AR/VR 显示画质失真?OAS 体全息光栅案例来解决
  • Linux系统之Nginx反向代理与缓存
  • 鸿蒙Next仓颉开发语言中的数据类型总结分享
  • 【计算机网络】第二章:物理层
  • 掌握多门计算机语言之后,如何高效学习新语言与切换编程思维
  • 在 GitLab CI 中配置多任务
  • 《从0到1:C/C++音视频开发自学指南》
  • SQL学习笔记2
  • 论文阅读:arxiv 2025 ThinkSwitcher: When to Think Hard, When to Think Fast
  • 通过 HTML 子图和多尺度卷积 BERT 的双向融合实现可解释的恶意 URL 检测
  • npm 报错:“无法加载文件 ...npm.ps1,因为在此系统上禁止运行脚本” 解决方案(附执行策略说明)
  • SpringBoot使用admin+actuator实现日志可视化
  • 曼昆《经济学原理》第九版 宏观经济学 第三十二章宏观经济政策的六个争论
  • Spring 容器核心扩展实战:Spring Boot中三大扩展问题解析
  • 亚远景-ASPICE与ISO 26262:汽车安全与软件质量的协同
  • JVM 中的 GC 算法演进之路!(Serial、CMS、G1 到 ZGC)
  • 7.Spring框架
  • 【机器人编程基础】Python模块的定义和导入
  • 融合聚类与分类的退役锂电智能分选技术:助力新能源汽车产业可持续发展
  • Spring学习笔记【8】
  • 【嘉立创EDA】PCB 如何按板框轮廓进行铺铜
  • JVM调优实战 Day 6:JVM性能监控工具实战
  • Redis大规模Key遍历实战:性能与安全的最佳实践