目录
1. 简介
2. static RAM
2.1 无 reset 的情形
2.2 含 reset 的情形
3. static ROM
4. 总结
1. 简介
本文仍然是讨论阵列的初始化与复位问题,区别于《Vitis HLS 学习笔记--global_array_RAM初始化及复位-CSDN博客》,本文讨论的对象是静态阵列,RAM和ROM。
静态阵列可以映射到BRAM/URAM/LUTRAM,本文并展示了它们如何初始化以及如何重置。
2. static RAM
2.1 无 reset 的情形
无 reset,意味着静态变量存储器的值不会受到 reset 信号的影响。
#include <ap_int.h>
int example(int i) {
static ap_int<10> A[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
#pragma HLS BIND_STORAGE variable = A type = RAM_2P impl = BRAM
static ap_int<10> B[10] = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
#pragma HLS BIND_STORAGE variable = B type = RAM_2P impl = LUTRAM
static ap_int<10> C[10] = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
#pragma HLS BIND_STORAGE variable = C type = RAM_2P impl = URAM
A[i] += B[i] + C[i];
B[i] += 5;
C[i] += 10;
int result = (A[i] + B[i] + C[i]).to_int();
return result;
}
查看存储绑定信息:
================================================================
== Bind Storage Report
================================================================
+-----------+------+------+--------+----------+---------+--------+---------+
| Name | BRAM | URAM | Pragma | Variable | Storage | Impl | Latency |
+-----------+------+------+--------+----------+---------+--------+---------+
| + example | 1 | 1 | | | | | |
| B_V_U | - | - | pragma | B_V | ram_2p | lutram | 1 |
| C_V_U | - | 1 | pragma | C_V | ram_2p | uram | 1 |
| A_V_U | 1 | - | pragma | A_V | ram_2p | bram | 1 |
+-----------+------+------+--------+----------+---------+--------+---------+
与 global_array_RAM 不同的是,static RAM 是支持使用 URAM 来实现的,我们使用下列指令绑定资源。
#pragma HLS BIND_STORAGE variable = C type = RAM_2P impl = URAM
从报告中我们也可以观察到,变量C已被分配为uram资源。
Verilog 代码结构:
以变量C为例,其实现的Verilog代码如下:
module example_C_V_RAM_2P_URAM_1R1W (address0, ce0, q0, address1, ce1, d1, we1, reset,clk);
parameter DataWidth = 10;
parameter AddressWidth = 4;
parameter AddressRange = 10;
input[AddressWidth-1:0] address0;
input ce0;
output reg[DataWidth-1:0] q0;
input[AddressWidth-1:0] address1;
input ce1;
input[DataWidth-1:0] d1;
input we1;
input reset;
input clk;
(* ram_style = "hls_ultra", cascade_height = 1 *)reg [DataWidth-1:0] ram[0:AddressRange-1];
initial begin
$readmemh("./example_C_V_RAM_2P_URAM_1R1W.dat", ram);
end
always @(posedge clk)
begin
if (ce0) begin
q0 <= ram[address0];
end
end
always @(posedge clk)
begin
if (ce1) begin
if (we1)
ram[address1] <= d1;
end
end
endmodule
2.2 含 reset 的情形
含 reset,意味着静态变量存储器的值在 reset 信号有效时,会恢复到初始状态。
需要在源代码中添加如下指令:
#pragma HLS reset variable=A
#pragma HLS reset variable=B
#pragma HLS reset variable=C
Verilog 代码结构:
以变量C为例,其实现的Verilog代码如下:
module example_C_V_RAM_2P_URAM_1R1W
#(parameter
DataWidth = 10,
AddressWidth = 4,
AddressRange = 10
)(
input wire clk,
input wire reset,
input wire [AddressWidth-1:0] address0,
input wire ce0,
output wire [DataWidth-1:0] q0,
input wire [AddressWidth-1:0] address1,
input wire ce1,
input wire we1,
input wire [DataWidth-1:0] d1
);
//------------------------Local signal-------------------
reg [AddressRange-1:0] written = {AddressRange{1'b0}};
wire [DataWidth-1:0] q0_ram;
wire [DataWidth-1:0] q0_rom;
wire q0_sel;
reg [0:0] sel0_sr;
//------------------------Instantiation------------------
example_C_V_RAM_2P_URAM_1R1W_ram #(
.DataWidth(DataWidth),
.AddressWidth(AddressWidth),
.AddressRange(AddressRange))
example_C_V_RAM_2P_URAM_1R1W_ram_u(
.clk ( clk ),
.reset ( reset ),
.ce0 ( ce0 ),
.address0 ( address0 ),
.q0 ( q0_ram ),
.ce1 ( ce1 ),
.address1 ( address1 ),
.we1 ( we1 ),
.d1 ( d1 )
);
example_C_V_RAM_2P_URAM_1R1W_rom #(
.DataWidth(DataWidth),
.AddressWidth(AddressWidth),
.AddressRange(AddressRange))
example_C_V_RAM_2P_URAM_1R1W_rom_u(
.clk ( clk ),
.ce0 ( ce0 ),
.address0 ( address0 ),
.q0 ( q0_rom )
);
//------------------------Body---------------------------
assign q0 = q0_sel? q0_ram : q0_rom;
assign q0_sel = sel0_sr[0];
always @(posedge clk) begin
if (reset)
written <= 1'b0;
else begin
if (ce1 & we1) begin
written[address1] <= 1'b1;
end
end
end
always @(posedge clk) begin
if (ce0) begin
sel0_sr[0] <= written[address0];
end
end
endmodule
这部分内容可以参见《Vitis HLS 学习笔记--global_array_RAM初始化及复位-CSDN博客》。
3. static ROM
#include <ap_int.h>
int example(int i) {
static const ap_int<10> A[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
#pragma HLS BIND_STORAGE variable = A type = ROM_1P impl = BRAM
static const ap_int<10> B[10] = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
#pragma HLS BIND_STORAGE variable = B type = ROM_1P impl = LUTRAM
return A[i] + B[i];
}
由于 ROM 是只读的,所以使用 ROM_1P 来实现,即只有一个读取口。
ROM 存储器实现不可由 URAM 来实现。
查看存储器绑定报告:
================================================================
== Bind Storage Report
================================================================
+-----------+------+------+--------+----------+---------+--------+---------+
| Name | BRAM | URAM | Pragma | Variable | Storage | Impl | Latency |
+-----------+------+------+--------+----------+---------+--------+---------+
| + example | 1 | 0 | | | | | |
| A_V_U | 1 | - | pragma | A_V | rom_1p | bram | 1 |
| B_V_U | - | - | pragma | B_V | rom_1p | lutram | 1 |
+-----------+------+------+--------+----------+---------+--------+---------+
可以看到 ROM 也是通过 bram/lutram 来实现的,不过不影响,没有对外暴露写端口。
Verilog 代码结构:
以变量A为例,其实现的Verilog代码如下:
(* rom_style = "block" *) module example_A_V_ROM_1P_BRAM_1R (
address0, ce0, q0, reset,clk);
parameter DataWidth = 4;
parameter AddressWidth = 4;
parameter AddressRange = 10;
input[AddressWidth-1:0] address0;
input ce0;
output reg[DataWidth-1:0] q0;
input reset;
input clk;
(* ram_style = "block" *)reg [DataWidth-1:0] ram[0:AddressRange-1];
initial begin
$readmemh("./example_A_V_ROM_1P_BRAM_1R.dat", ram);
end
always @(posedge clk)
begin
if (ce0)
begin
q0 <= ram[address0];
end
end
endmodule
4. 总结
在本文中,我们探讨了静态阵列在Vitis HLS中的初始化和复位问题,特别是与全局阵列RAM相比,静态RAM的不同之处。我们了解到静态阵列可以映射到BRAM、URAM或LUTRAM,并且可以通过特定的HLS指令进行初始化和重置。在无reset情形下,静态变量存储器的值不受reset信号影响,而在含reset情形下,存储器的值会在reset信号有效时恢复到初始状态。此外,我们还讨论了静态ROM的实现,它是只读的,通常使用BRAM或LUTRAM实现,但不能使用URAM。通过这些讨论,我们可以更好地理解如何在硬件设计中有效地使用这些存储结构,以及如何通过Vitis HLS工具来优化它们的实现。总的来说,这些知识对于那些希望在FPGA编程中实现高效存储和数据访问的开发者来说是非常宝贵的。