目录
1. 简介
2. 示例
2.1 示例功能介绍
2.2 示例代码
2.3 顶层函数解释
2.4 综合报告(HW Interfaces)
2.5 关于TKEEP和TSTRB
2.6 综合报告(SW I/O Information)
3. 总结
1. 简介
本文通过“<Examples>/Interface/Streaming/axi_stream_to_master/”示例,展示了如何利用 Vitis HLS 工具,将输入的 AXI Stream 格式的数据流转换为并行存储数据,并通过 AXI Master 接口写入到存储器中。
AXI Stream 接口:是一种轻量级的、无协议的、点对点的数据传输接口,常用于在 FPGA 内部或 FPGA 与外部设备之间传输大量数据。它包括一个数据通道和一个控制信号,通过这两个通道传输数据和标记数据包结束。
AXI Master 接口:是一种高级别的存储器接口,用于 FPGA 设备与外部存储器之间的数据传输。它包括数据通道、地址通道、控制信号等,可以支持高性能的数据读写操作。
2. 示例
2.1 示例功能介绍
本示例包含三个函数:
- getinstream 函数
从输入的 AXI Stream 数据流中读取数据,并将其转换为 data 结构后发送到输出流中。在达到最大计数或读取到最后一个数据包时,将计数值发送到输出计数流中。这个函数主要利用了 AXI Stream 接口进行数据的传输和标记数据包结束。
- streamtoparallelwithburst 函数
从输入的 AXI Stream 数据流和计数流中读取数据,并将数据写入到输出的 AXI Master 接口中。这个函数主要利用了 AXI Master 接口进行数据的存储,支持数据的突发传输。
- example 函数
顶层函数,将这两个操作结合起来,首先将输入流转换为数据流和计数流,然后将数据流传递给 streamtoparallelwithburst 函数进行数据存储操作。最终实现了从 AXI Stream 数据流到 AXI Master 存储器接口的数据处理流程。
2.2 示例代码
#include "ap_axi_sdata.h"
#include "ap_int.h"
#include "hls_stream.h"
typedef ap_axiu<64, 0, 0, 0> trans_pkt;
// Expects max bandwidth at 64 beats burst (for 64-bit data)
static constexpr int MAX_BURST_LENGTH = 64;
static constexpr int BUFFER_FACTOR = 64;
// Buffer sizes
static constexpr int DATA_DEPTH = MAX_BURST_LENGTH * BUFFER_FACTOR;
static constexpr int COUNT_DEPTH = BUFFER_FACTOR;
struct data {
ap_int<64> data_filed;
ap_int<1> last;
};
/// Reads from in_stream and in_counts, Write to out_memory
void streamtoparallelwithburst(hls::stream<data>& in_stream,
hls::stream<int>& in_counts,
ap_uint<64>* out_memory) {
data in_val;
do {
int count = in_counts.read();
for (int i = 0; i < count; ++i) {
#pragma HLS PIPELINE
in_val = in_stream.read();
out_memory[i] = in_val.data_filed;
}
out_memory += count;
} while (!in_val.last);
}
void getinstream(hls::stream<trans_pkt>& in_stream,
hls::stream<data>& out_stream, hls::stream<int>& out_counts) {
int count = 0;
trans_pkt in_val;
do {
#pragma HLS PIPELINE
in_val = in_stream.read();
data out_val = {in_val.data, in_val.last};
out_stream.write(out_val);
count++;
if (count >= MAX_BURST_LENGTH || in_val.last) {
out_counts.write(count);
count = 0;
}
} while (!in_val.last);
}
void example(hls::stream<trans_pkt>& inStreamTop, ap_uint<64> outTop[1024]) {
#pragma HLS INTERFACE axis register_mode = both register port = inStreamTop
#pragma HLS INTERFACE m_axi max_write_burst_length = 256 latency = 10 depth = 1024 bundle = gmem0 port = outTop
#pragma HLS INTERFACE s_axilite port = outTop bundle = control
#pragma HLS INTERFACE s_axilite port = return bundle = control
#pragma HLS DATAFLOW
hls::stream<data, DATA_DEPTH> buf;
hls::stream<int, COUNT_DEPTH> count;
getinstream(inStreamTop, buf, count);
streamtoparallelwithburst(buf, count, outTop);
}
2.3 顶层函数解释
顶层函数 example 共有两个参数:
- inStreamTop:这是一个输入参数,类型为 hls::stream<trans_pkt>&,表示一个 AXI Stream 接口的数据流。该参数用于传递输入的数据流,其中 trans_pkt 是一个结构体类型,表示一个 AXI Stream 格式的数据包,包含64位的数据字段和一个标记最后一个数据包的信号。
- outTop:这是一个输出参数,类型为 ap_uint<64> outTop[1024],表示一个 AXI Master 接口的存储器。该参数用于指定存储数据的地址,数据将被写入到这个地址指定的存储器中。这里使用了 ap_uint<64> 类型的数组,长度为1024,表示存储器的容量为1024个64位的数据。
编译器指令,只介绍其中第一个,其余的在之前的文章已有分析。
#pragma HLS INTERFACE axis register_mode = both register port = inStreamTop
- axis:这表示我们正在定义一个AXI Stream接口。
- register_mode = both:这指定了数据流的寄存器模式。在这里,both表示数据流的输入和输出都使用寄存器。
- register:表示数据端口将使用寄存器进行数据缓存。
- port = inStreamTop:表示将指定这个接口指令应用于名为 inStreamTop 的输入端口。这个端口将被识别为 AXI Stream 接口,从而在 HLS 综合过程中正确处理数据流。
2.4 综合报告(HW Interfaces)
================================================================
== HW Interfaces
================================================================
* M_AXI
+-------------+------------+---------------+---------+--------+----------+-----------+--------------+--------------+-------------+-------------+
| Interface | Data Width | Address Width | Latency | Offset | Register | Max Widen | Max Read | Max Write | Num Read | Num Write |
| | (SW->HW) | | | | | Bitwidth | Burst Length | Burst Length | Outstanding | Outstanding |
+-------------+------------+---------------+---------+--------+----------+-----------+--------------+--------------+-------------+-------------+
| m_axi_gmem0 | 64 -> 64 | 64 | 10 | slave | 0 | 0 | 16 | 256 | 16 | 16 |
+-------------+------------+---------------+---------+--------+----------+-----------+--------------+--------------+-------------+-------------+
* S_AXILITE Interfaces
+---------------+------------+---------------+--------+----------+
| Interface | Data Width | Address Width | Offset | Register |
+---------------+------------+---------------+--------+----------+
| s_axi_control | 32 | 5 | 16 | 0 |
+---------------+------------+---------------+--------+----------+
* S_AXILITE Registers
+---------------+----------+--------+-------+--------+----------------------------------+----------------------------------------------------------------------+
| Interface | Register | Offset | Width | Access | Description | Bit Fields |
+---------------+----------+--------+-------+--------+----------------------------------+----------------------------------------------------------------------+
| s_axi_control | CTRL | 0x00 | 32 | RW | Control signals | 0=AP_START 1=AP_DONE 2=AP_IDLE 3=AP_READY 7=AUTO_RESTART 9=INTERRUPT |
| s_axi_control | GIER | 0x04 | 32 | RW | Global Interrupt Enable Register | 0=Enable |
| s_axi_control | IP_IER | 0x08 | 32 | RW | IP Interrupt Enable Register | 0=CHAN0_INT_EN 1=CHAN1_INT_EN |
| s_axi_control | IP_ISR | 0x0c | 32 | RW | IP Interrupt Status Register | 0=CHAN0_INT_ST 1=CHAN1_INT_ST |
| s_axi_control | outTop_1 | 0x10 | 32 | W | Data signal of outTop | |
| s_axi_control | outTop_2 | 0x14 | 32 | W | Data signal of outTop | |
+---------------+----------+--------+-------+--------+----------------------------------+----------------------------------------------------------------------+
* AXIS
+-------------+---------------+-------+-------+-------+--------+-------+--------+
| Interface | Register Mode | TDATA | TKEEP | TLAST | TREADY | TSTRB | TVALID |
+-------------+---------------+-------+-------+-------+--------+-------+--------+
| inStreamTop | both | 64 | 8 | 1 | 1 | 8 | 1 |
+-------------+---------------+-------+-------+-------+--------+-------+--------+
* TOP LEVEL CONTROL
+-----------+------------+-----------+
| Interface | Type | Ports |
+-----------+------------+-----------+
| ap_clk | clock | ap_clk |
| ap_rst_n | reset | ap_rst_n |
| interrupt | interrupt | interrupt |
| ap_ctrl | ap_ctrl_hs | |
+-----------+------------+-----------+
通过 AXIS 报告项,可以清楚的看到 axi stream 接口的构成:
* AXIS
+-------------+---------------+-------+-------+-------+--------+-------+--------+
| Interface | Register Mode | TDATA | TKEEP | TLAST | TREADY | TSTRB | TVALID |
+-------------+---------------+-------+-------+-------+--------+-------+--------+
| inStreamTop | both | 64 | 8 | 1 | 1 | 8 | 1 |
+-------------+---------------+-------+-------+-------+--------+-------+--------+
- TDATA: 这是数据信号,用于传输实际的数据。在您的表格中,TDATA的宽度是64位。
- TKEEP: 这是字节使能信号,每个位对应TDATA中的一个字节。如果TKEEP的某位是1,那么对应的TDATA字节是有效的;如果是0,则该字节无效。在您的表格中,TKEEP的宽度是8位,意味着可以独立控制TDATA中的每个字节。
- TLAST: 这是一个标志信号,用于指示一次传输的最后一个数据包。当TLAST为1时,表示当前的TDATA是当前传输的最后一个数据包。
- TREADY: 这是就绪信号,由接收方控制。当TREADY为1时,表示接收方准备好接收数据;当TREADY为0时,表示接收方未准备好接收数据。
- TSTRB: 这是字节选通信号,与TKEEP类似,用于指示有效的数据字节。在您的表格中,TSTRB的宽度是8位。
- TVALID: 这是有效信号,由发送方控制。当TVALID为1时,表示发送方正在发送有效的数据;当TVALID为0时,表示当前没有数据被发送。
这些信号共同工作,以确保数据能够可靠地在主设备和从设备之间传输。例如,数据传输只有在TVALID和TREADY都为1时才会发生,这确保了发送方和接收方都准备好进行数据交换。
2.5 关于TKEEP和TSTRB
TKEEP和TSTRB在AXI Stream接口中都是字节使能信号,但它们的用途略有不同。
- TKEEP是用来指示哪些字节是有效的。如果TKEEP的某一位是1,那么对应的TDATA字节是有效的;如果是0,则该字节无效。这个信号通常用于数据包的开始和结束,以及中间的所有字节(如果TKEEP全部为1,则表示所有字节都是有效的)。
- TSTRB也是一个字节使能信号,但它更多地用于指示数据的位置或时序。当TSTRB的某一位是1时,表示对应的TDATA字节在当前时刻是有效的。TSTRB可以用来传输空字节,即使TKEEP为高,TSTRB也可以为低,这意味着需要发送一个空字节。
在大多数情况下,只使用TKEEP信号,因为它可以满足大部分接口的需求。然而,在某些特定的应用中,可能会同时使用TKEEP和TSTRB来提供更精细的控制。
2.6 综合报告(SW I/O Information)
================================================================
== SW I/O Information
================================================================
* Top Function Arguments
+-------------+-----------+---------------------------------------------+
| Argument | Direction | Datatype |
+-------------+-----------+---------------------------------------------+
| inStreamTop | in | stream<hls::axis<ap_uint<64>, 0, 0, 0>, 0>& |
| outTop | out | ap_uint<64>* |
+-------------+-----------+---------------------------------------------+
* SW-to-HW Mapping
+-------------+---------------+-----------+----------+------------------------------------+
| Argument | HW Interface | HW Type | HW Usage | HW Info |
+-------------+---------------+-----------+----------+------------------------------------+
| inStreamTop | inStreamTop | interface | | |
| outTop | m_axi_gmem0 | interface | | |
| outTop | s_axi_control | register | offset | name=outTop_1 offset=0x10 range=32 |
| outTop | s_axi_control | register | offset | name=outTop_2 offset=0x14 range=32 |
+-------------+---------------+-----------+----------+------------------------------------+
从 Top Function Arguments 可轻松分析参数类型和方向。
3. 总结
本文详细介绍了如何利用 Vitis HLS 工具将 AXI Stream 格式的数据流转换为并行存储数据,并通过 AXI Master 接口写入到存储器中。通过示例代码和编译器指令的解释,读者可以了解到 AXI Stream 接口和 AXI Master 接口的特点以及在 FPGA 设计中的应用。同时,本文还分析了示例中各个函数的功能和参数,以及综合报告中的重要信息。