core-v-verif系列之cva6 verilator Model之tc_sram.sv
tc_sram模块模拟了一个可配置的 SRAM 设备,具备多个读写端口,可通过参数化配置其存储容量、数据宽度、字节宽度、端口数量、读取延迟等特性。该模块还支持不同的仿真初始化模式,能在仿真开始时输出配置信息,并对参数和地址访问进行验证。NumWords:SRAM 中的字数,地址宽度可根据该参数计算得出。DataWidth:读写数据端口的宽度。ByteWidth:字节的宽度,用于计算字节使能信号的宽
tc_sram.sv
// Copyright (c) 2020 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
// Author: Wolfgang Roenninger <wroennin@ethz.ch>
// Description: Functional module of a generic SRAM
//
// Parameters:
// - NumWords: Number of words in the macro. Address width can be calculated with:
// `AddrWidth = (NumWords > 32'd1) ? $clog2(NumWords) : 32'd1`
// The module issues a warning if there is a request on an address which is
// not in range.
// - DataWidth: Width of the ports `wdata_i` and `rdata_o`.
// - ByteWidth: Width of a byte, the byte enable signal `be_i` can be calculated with the
// ceiling division `ceil(DataWidth, ByteWidth)`.
// - NumPorts: Number of read and write ports. Each is a full port. Ports with a higher
// index read and write after the ones with lower indices.
// - Latency: Read latency, the read data is available this many cycles after a request.
// - SimInit: Macro simulation initialization. Values are:
// "zeros": Each bit gets initialized with 1'b0.
// "ones": Each bit gets initialized with 1'b1.
// "random": Each bit gets random initialized with 1'b0 or 1'b1.
// "none": Each bit gets initialized with 1'bx. (default)
// - PrintSimCfg: Prints at the beginning of the simulation a `Hello` message with
// the instantiated parameters and signal widths.
// - ImplKey: Key by which an instance can refer to a specific implementation (e.g. macro).
// May be used to look up additional parameters for implementation (e.g. generator,
// line width, muxing) in an external reference, such as a configuration file.
//
// Ports:
// - `clk_i`: Clock
// - `rst_ni`: Asynchronous reset, active low
// - `req_i`: Request, active high
// - `we_i`: Write request, active high
// - `addr_i`: Request address
// - `wdata_i`: Write data, has to be valid on request
// - `be_i`: Byte enable, active high
// - `rdata_o`: Read data, valid `Latency` cycles after a request with `we_i` low.
//
// Behaviour:
// - Address collision: When Ports are making a write access onto the same address,
// the write operation will start at the port with the lowest address
// index, each port will overwrite the changes made by the previous ports
// according how the respective `be_i` signal is set.
// - Read data on write: This implementation will not produce a read data output on the signal
// `rdata_o` when `req_i` and `we_i` are asserted. The output data is stable
// on write requests.
module tc_sram #(
parameter int unsigned NumWords = 32'd1024, // Number of Words in data array
parameter int unsigned DataWidth = 32'd128, // Data signal width
parameter int unsigned ByteWidth = 32'd8, // Width of a data byte
parameter int unsigned NumPorts = 32'd2, // Number of read and write ports
parameter int unsigned Latency = 32'd1, // Latency when the read data is available
parameter SimInit = "none", // Simulation initialization
parameter bit PrintSimCfg = 1'b0, // Print configuration
parameter ImplKey = "none", // Reference to specific implementation
// DEPENDENT PARAMETERS, DO NOT OVERWRITE!
parameter int unsigned AddrWidth = (NumWords > 32'd1) ? $clog2(NumWords) : 32'd1,
parameter int unsigned BeWidth = (DataWidth + ByteWidth - 32'd1) / ByteWidth, // ceil_div
parameter type addr_t = logic [AddrWidth-1:0],
parameter type data_t = logic [DataWidth-1:0],
parameter type be_t = logic [BeWidth-1:0]
) (
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
// input ports
input logic [NumPorts-1:0] req_i, // request
input logic [NumPorts-1:0] we_i, // write enable
input addr_t [NumPorts-1:0] addr_i, // request address
input data_t [NumPorts-1:0] wdata_i, // write data
input be_t [NumPorts-1:0] be_i, // write byte enable
// output ports
output data_t [NumPorts-1:0] rdata_o // read data
);
// memory array
data_t sram [NumWords-1:0];
// hold the read address when no read access is made
addr_t [NumPorts-1:0] r_addr_q;
// SRAM simulation initialization
data_t init_val[NumWords-1:0];
initial begin : proc_sram_init
for (int unsigned i = 0; i < NumWords; i++) begin
case (SimInit)
"zeros": init_val[i] = {DataWidth{1'b0}};
"ones": init_val[i] = {DataWidth{1'b1}};
"random": init_val[i] = {DataWidth{$urandom()}};
default: init_val[i] = {DataWidth{1'bx}};
endcase
end
end
// set the read output if requested
// The read data at the highest array index is set combinational.
// It gets then delayed for a number of cycles until it gets available at the output at
// array index 0.
// read data output assignment
data_t [NumPorts-1:0][Latency-1:0] rdata_q, rdata_d;
if (Latency == 32'd0) begin : gen_no_read_lat
for (genvar i = 0; i < NumPorts; i++) begin : gen_port
assign rdata_o[i] = (req_i[i] && !we_i[i]) ? sram[addr_i[i]] : sram[r_addr_q[i]];
end
end else begin : gen_read_lat
always_comb begin
for (int unsigned i = 0; i < NumPorts; i++) begin
rdata_o[i] = rdata_q[i][0];
for (int unsigned j = 0; j < (Latency-1); j++) begin
rdata_d[i][j] = rdata_q[i][j+1];
end
rdata_d[i][Latency-1] = (req_i[i] && !we_i[i]) ? sram[addr_i[i]] : sram[r_addr_q[i]];
end
end
end
// In case simulation initialization is disabled (SimInit == 'none'), don't assign to the sram
// content at all. This improves simulation performance in tools like verilator
if (SimInit == "none") begin
// write memory array without initialization
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
for (int i = 0; i < NumPorts; i++) begin
r_addr_q[i] <= {AddrWidth{1'b0}};
end
end else begin
// read value latch happens before new data is written to the sram
for (int unsigned i = 0; i < NumPorts; i++) begin
if (Latency != 0) begin
for (int unsigned j = 0; j < Latency; j++) begin
rdata_q[i][j] <= rdata_d[i][j];
end
end
end
// there is a request for the SRAM, latch the required register
for (int unsigned i = 0; i < NumPorts; i++) begin
if (req_i[i]) begin
if (we_i[i]) begin
// update value when write is set at clock
for (int unsigned j = 0; j < BeWidth; j++) begin
if (be_i[i][j]) begin
sram[addr_i[i]][j*ByteWidth+:ByteWidth] <= wdata_i[i][j*ByteWidth+:ByteWidth];
end
end
end else begin
// otherwise update read address for subsequent non request cycles
r_addr_q[i] <= addr_i[i];
end
end // if req_i
end // for ports
end // if !rst_ni
end
end else begin
// write memory array
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
// Fix to avoid runtime space reaching maximum capacity in simulation
foreach (init_val[i]) begin
sram[i] <= init_val[i];
end
for (int i = 0; i < NumPorts; i++) begin
r_addr_q[i] <= {AddrWidth{1'b0}};
// initialize the read output register for each port
if (Latency != 32'd0) begin
for (int unsigned j = 0; j < Latency; j++) begin
rdata_q[i][j] <= init_val[{AddrWidth{1'b0}}];
end
end
end
end else begin
// read value latch happens before new data is written to the sram
for (int unsigned i = 0; i < NumPorts; i++) begin
if (Latency != 0) begin
for (int unsigned j = 0; j < Latency; j++) begin
rdata_q[i][j] <= rdata_d[i][j];
end
end
end
// there is a request for the SRAM, latch the required register
for (int unsigned i = 0; i < NumPorts; i++) begin
if (req_i[i]) begin
if (we_i[i]) begin
// update value when write is set at clock
for (int unsigned j = 0; j < BeWidth; j++) begin
if (be_i[i][j]) begin
sram[addr_i[i]][j*ByteWidth+:ByteWidth] <= wdata_i[i][j*ByteWidth+:ByteWidth];
end
end
end else begin
// otherwise update read address for subsequent non request cycles
r_addr_q[i] <= addr_i[i];
end
end // if req_i
end // for ports
end // if !rst_ni
end
end
// Validate parameters.
// pragma translate_off
`ifndef VERILATOR
`ifndef TARGET_SYNTHESIS
initial begin: p_assertions
assert ($bits(addr_i) == NumPorts * AddrWidth) else $fatal(1, "AddrWidth problem on `addr_i`");
assert ($bits(wdata_i) == NumPorts * DataWidth) else $fatal(1, "DataWidth problem on `wdata_i`");
assert ($bits(be_i) == NumPorts * BeWidth) else $fatal(1, "BeWidth problem on `be_i`" );
assert ($bits(rdata_o) == NumPorts * DataWidth) else $fatal(1, "DataWidth problem on `rdata_o`");
assert (NumWords >= 32'd1) else $fatal(1, "NumWords has to be > 0");
assert (DataWidth >= 32'd1) else $fatal(1, "DataWidth has to be > 0");
assert (ByteWidth >= 32'd1) else $fatal(1, "ByteWidth has to be > 0");
assert (NumPorts >= 32'd1) else $fatal(1, "The number of ports must be at least 1!");
end
initial begin: p_sim_hello
if (PrintSimCfg) begin
$display("#################################################################################");
$display("tc_sram functional instantiated with the configuration:" );
$display("Instance: %m" );
$display("Number of ports (dec): %0d", NumPorts );
$display("Number of words (dec): %0d", NumWords );
$display("Address width (dec): %0d", AddrWidth );
$display("Data width (dec): %0d", DataWidth );
$display("Byte width (dec): %0d", ByteWidth );
$display("Byte enable width (dec): %0d", BeWidth );
$display("Latency Cycles (dec): %0d", Latency );
$display("Simulation init (str): %0s", SimInit );
$display("#################################################################################");
end
end
for (genvar i = 0; i < NumPorts; i++) begin : gen_assertions
assert property ( @(posedge clk_i) disable iff (!rst_ni)
(req_i[i] |-> (addr_i[i] < NumWords))) else
$warning("Request address %0h not mapped, port %0d, expect random write or read behavior!",
addr_i[i], i);
end
`endif
`endif
// pragma translate_on
endmodule
tc_sram.sv 是一个用 SystemVerilog 编写的文件,定义了一个名为 tc_sram 的模块,其主要功能是实现一个通用的静态随机存取存储器(SRAM)的功能模型。以下是对该文件的详细分析和功能总结:
功能概述
tc_sram 模块模拟了一个可配置的 SRAM 设备,具备多个读写端口,可通过参数化配置其存储容量、数据宽度、字节宽度、端口数量、读取延迟等特性。该模块还支持不同的仿真初始化模式,能在仿真开始时输出配置信息,并对参数和地址访问进行验证。
详细分析
1. 参数定义
模块通过一系列参数进行配置,主要参数如下:
NumWords:SRAM 中的字数,地址宽度可根据该参数计算得出。DataWidth:读写数据端口的宽度。ByteWidth:字节的宽度,用于计算字节使能信号的宽度。NumPorts:读写端口的数量,每个端口都是完整的读写端口。Latency:读取延迟,即读取请求发出后,数据可用所需的周期数。SimInit:仿真初始化模式,支持zeros(全零初始化)、ones(全一初始化)、random(随机初始化)和none(不初始化)。PrintSimCfg:是否在仿真开始时打印模块的配置信息。ImplKey:用于引用特定实现的键,可用于查找外部配置文件中的额外参数。
2. 端口定义
模块包含以下端口:
clk_i:时钟信号。rst_ni:异步复位信号,低电平有效。req_i:请求信号,高电平有效。we_i:写请求信号,高电平有效。addr_i:请求地址。wdata_i:写数据,在请求时必须有效。be_i:字节使能信号,高电平有效。rdata_o:读数据,在读取请求且we_i为低电平时,经过Latency周期后有效。
3. 行为描述
- 地址冲突处理:当多个端口对同一地址进行写访问时,写操作从索引最低的端口开始,每个端口会根据其
be_i信号覆盖前一个端口的更改。 - 写时读数据处理:当
req_i和we_i同时有效时,模块不会在rdata_o上产生读数据输出,写请求时输出数据保持稳定。
4. 仿真初始化
根据 SimInit 参数的值,模块会对 SRAM 进行不同的初始化:
zeros:每个位初始化为1'b0。ones:每个位初始化为1'b1。random:每个位随机初始化为1'b0或1'b1。none:每个位初始化为1'bx。
5. 读取数据处理
根据 Latency 参数的值,模块会采用不同的方式处理读取数据:
- 当
Latency为 0 时,读数据直接组合逻辑输出。 - 当
Latency大于 0 时,读数据会经过多个周期的延迟后输出。
6. 写入数据处理
根据 SimInit 参数的值,模块会采用不同的方式处理写入数据:
- 当
SimInit为none时,不进行初始化,直接写入数据。 - 当
SimInit不为none时,在复位时进行初始化,然后再写入数据。
7. 参数验证
在仿真时,模块会对参数进行验证,确保地址、数据和字节使能信号的宽度与参数一致,同时确保 NumWords、DataWidth、ByteWidth 和 NumPorts 都大于 0。
8. 仿真配置信息输出
如果 PrintSimCfg 参数为 1'b1,模块会在仿真开始时输出模块的配置信息,包括端口数量、字数、地址宽度、数据宽度、字节宽度、字节使能宽度、读取延迟和仿真初始化模式等。
总结
tc_sram 模块是一个高度可配置的 SRAM 功能模型,通过参数化配置可以满足不同的设计需求。该模块支持多种仿真初始化模式,能处理地址冲突和写时读数据的情况,并对参数和地址访问进行验证,提供了良好的仿真和设计支持。
tc_sram.sv 是一个用 SystemVerilog 编写的通用 SRAM(静态随机存取存储器)功能模块。下面对该代码进行详细分析:
代码分析
1. 版权和作者信息
// Copyright (c) 2020 ETH Zurich and University of Bologna.
// ...
// Author: Wolfgang Roenninger <wroennin@ethz.ch>
代码开头包含版权声明,表明代码版权归 ETH Zurich 和 University of Bologna 所有,同时给出了作者信息。
2. 模块概述
// Description: Functional module of a generic SRAM
此模块用于模拟一个通用的 SRAM 功能。
3. 参数说明
module tc_sram #(
parameter int unsigned NumWords = 32'd1024, // Number of Words in data array
parameter int unsigned DataWidth = 32'd128, // Data signal width
parameter int unsigned ByteWidth = 32'd8, // Width of a data byte
parameter int unsigned NumPorts = 32'd2, // Number of read and write ports
parameter int unsigned Latency = 32'd1, // Latency when the read data is available
parameter SimInit = "none", // Simulation initialization
parameter bit PrintSimCfg = 1'b0, // Print configuration
parameter ImplKey = "none", // Reference to specific implementation
// DEPENDENT PARAMETERS, DO NOT OVERWRITE!
parameter int unsigned AddrWidth = (NumWords > 32'd1) ? $clog2(NumWords) : 32'd1,
parameter int unsigned BeWidth = (DataWidth + ByteWidth - 32'd1) / ByteWidth, // ceil_div
parameter type addr_t = logic [AddrWidth-1:0],
parameter type data_t = logic [DataWidth-1:0],
parameter type be_t = logic [BeWidth-1:0]
)
NumWords:SRAM 中存储的字数,默认值为 1024。DataWidth:数据信号的宽度,默认值为 128 位。ByteWidth:一个字节的宽度,默认值为 8 位。NumPorts:读写端口的数量,默认值为 2。Latency:读操作的延迟周期数,默认值为 1。SimInit:仿真初始化方式,有"zeros"、"ones"、"random"、"none"四种选项,默认值为"none"。PrintSimCfg:是否打印仿真配置信息,默认值为1'b0(不打印)。ImplKey:指向特定实现的引用,默认值为"none"。- 依赖参数:根据前面的参数计算得出,不建议手动修改。
AddrWidth参数
parameter int unsigned AddrWidth = (NumWords > 32'd1) ? $clog2(NumWords) : 32'd1,
- 功能:该参数用于计算地址总线的宽度。地址总线宽度决定了可以访问的内存地址范围。
- 计算逻辑:
- 如果
NumWords(内存中的字数)大于 1,则使用$clog2函数计算NumWords的以 2 为底的对数,向上取整得到地址总线宽度。 - 如果
NumWords等于 1,则地址总线宽度为 1。
- 如果
- 用途:在后续的代码中,
AddrWidth用于定义地址相关的信号和数组的宽度。
BeWidth参数
parameter int unsigned BeWidth = (DataWidth + ByteWidth - 32'd1) / ByteWidth, // ceil_div
- 功能:该参数用于计算字节使能信号(
be_i)的宽度。字节使能信号用于控制对内存中每个字节的写入操作。 - 计算逻辑:使用
(DataWidth + ByteWidth - 32'd1) / ByteWidth实现向上取整除法,得到字节使能信号的宽度。 - 用途:在后续的代码中,
BeWidth用于定义字节使能信号相关的信号和数组的宽度。
addr_t参数
parameter type addr_t = logic [AddrWidth-1:0],
- 功能:该参数定义了地址类型
addr_t,它是一个宽度为AddrWidth的逻辑类型。 - 用途:在后续的代码中,
addr_t用于定义地址相关的信号和数组,例如addr_i端口。
data_t参数
parameter type data_t = logic [DataWidth-1:0],
- 功能:该参数定义了数据类型
data_t,它是一个宽度为DataWidth的逻辑类型。 - 用途:在后续的代码中,
data_t用于定义数据相关的信号和数组,例如wdata_i和rdata_o端口。
be_t参数
parameter type be_t = logic [BeWidth-1:0]
- 功能:该参数定义了字节使能类型
be_t,它是一个宽度为BeWidth的逻辑类型。 - 用途:在后续的代码中,
be_t用于定义字节使能相关的信号和数组,例如be_i端口。
总结
这些依赖参数的定义使得 tc_sram 模块具有更好的可配置性和灵活性。通过调整 NumWords、DataWidth 和 ByteWidth 等参数,可以方便地改变内存的大小、数据宽度和字节使能信号的宽度,而这些依赖参数会自动根据配置参数进行计算和更新。
优化建议
- 代码可读性:可以添加更多的注释来解释每个参数的计算逻辑和用途,特别是
BeWidth的向上取整除法。 - 错误检查:可以添加一些断言来确保
NumWords、DataWidth和ByteWidth的取值是合理的,避免出现计算错误。例如,确保ByteWidth大于 0。
parameter int unsigned AddrWidth = (NumWords > 32'd1) ? $clog2(NumWords) : 32'd1,
// 计算字节使能信号的宽度,使用向上取整除法
parameter int unsigned BeWidth = (DataWidth + ByteWidth - 32'd1) / ByteWidth,
// 定义地址类型,宽度为 AddrWidth
parameter type addr_t = logic [AddrWidth-1:0],
// 定义数据类型,宽度为 DataWidth
parameter type data_t = logic [DataWidth-1:0],
// 定义字节使能类型,宽度为 BeWidth
parameter type be_t = logic [BeWidth-1:0]
// 断言检查 ByteWidth 是否大于 0
initial begin
assert (ByteWidth > 32'd0) else $fatal(1, "ByteWidth must be greater than 0");
end
通过添加注释和断言,可以提高代码的可读性和健壮性。
4. 端口定义
(
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
// input ports
input logic [NumPorts-1:0] req_i, // request
input logic [NumPorts-1:0] we_i, // write enable
input addr_t [NumPorts-1:0] addr_i, // request address
input data_t [NumPorts-1:0] wdata_i, // write data
input be_t [NumPorts-1:0] be_i, // write byte enable
// output ports
output data_t [NumPorts-1:0] rdata_o // read data
);
clk_i:时钟信号。rst_ni:异步复位信号,低电平有效。req_i:请求信号,高电平有效。we_i:写使能信号,高电平有效。addr_i:请求地址。wdata_i:写数据。be_i:字节使能信号,高电平有效。rdata_o:读数据,在Latency个周期后有效。
5. 内部信号定义
// memory array
data_t sram [NumWords-1:0];
// hold the read address when no read access is made
addr_t [NumPorts-1:0] r_addr_q;
// SRAM simulation initialization
data_t init_val[NumWords-1:0];
sram:用于存储 SRAM 数据的数组。r_addr_q:在无读访问时保存读地址。init_val:用于 SRAM 仿真初始化的值。
6. SRAM 仿真初始化
initial begin : proc_sram_init
for (int unsigned i = 0; i < NumWords; i++) begin
case (SimInit)
"zeros": init_val[i] = {DataWidth{1'b0}};
"ones": init_val[i] = {DataWidth{1'b1}};
"random": init_val[i] = {DataWidth{$urandom()}};
default: init_val[i] = {DataWidth{1'bx}};
endcase
end
end
根据 SimInit 参数的值,对 init_val 数组进行初始化。
7. 读数据输出赋值
data_t [NumPorts-1:0][Latency-1:0] rdata_q, rdata_d;
if (Latency == 32'd0) begin : gen_no_read_lat
for (genvar i = 0; i < NumPorts; i++) begin : gen_port
assign rdata_o[i] = (req_i[i] && !we_i[i]) ? sram[addr_i[i]] : sram[r_addr_q[i]];
end
end else begin : gen_read_lat
always_comb begin
for (int unsigned i = 0; i < NumPorts; i++) begin
rdata_o[i] = rdata_q[i][0];
for (int unsigned j = 0; j < (Latency-1); j++) begin
rdata_d[i][j] = rdata_q[i][j+1];
end
rdata_d[i][Latency-1] = (req_i[i] && !we_i[i]) ? sram[addr_i[i]] : sram[r_addr_q[i]];
end
end
end
- 当
Latency为 0 时,直接根据请求和写使能信号选择读数据。 - 当
Latency不为 0 时,使用组合逻辑进行读数据的延迟处理。
8. 写操作和读地址更新
if (SimInit == "none") begin
// write memory array without initialization
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
for (int i = 0; i < NumPorts; i++) begin
r_addr_q[i] <= {AddrWidth{1'b0}};
end
end else begin
// read value latch happens before new data is written to the sram
for (int unsigned i = 0; i < NumPorts; i++) begin
if (Latency != 0) begin
for (int unsigned j = 0; j < Latency; j++) begin
rdata_q[i][j] <= rdata_d[i][j];
end
end
end
// there is a request for the SRAM, latch the required register
for (int unsigned i = 0; i < NumPorts; i++) begin
if (req_i[i]) begin
if (we_i[i]) begin
// update value when write is set at clock
for (int unsigned j = 0; j < BeWidth; j++) begin
if (be_i[i][j]) begin
sram[addr_i[i]][j*ByteWidth+:ByteWidth] <= wdata_i[i][j*ByteWidth+:ByteWidth];
end
end
end else begin
// otherwise update read address for subsequent non request cycles
r_addr_q[i] <= addr_i[i];
end
end // if req_i
end // for ports
end // if !rst_ni
end
end else begin
// write memory array
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
// Fix to avoid runtime space reaching maximum capacity in simulation
foreach (init_val[i]) begin
sram[i] <= init_val[i];
end
for (int i = 0; i < NumPorts; i++) begin
r_addr_q[i] <= {AddrWidth{1'b0}};
// initialize the read output register for each port
if (Latency != 32'd0) begin
for (int unsigned j = 0; j < Latency; j++) begin
rdata_q[i][j] <= init_val[{AddrWidth{1'b0}}];
end
end
end
end else begin
// read value latch happens before new data is written to the sram
for (int unsigned i = 0; i < NumPorts; i++) begin
if (Latency != 0) begin
for (int unsigned j = 0; j < Latency; j++) begin
rdata_q[i][j] <= rdata_d[i][j];
end
end
end
// there is a request for the SRAM, latch the required register
for (int unsigned i = 0; i < NumPorts; i++) begin
if (req_i[i]) begin
if (we_i[i]) begin
// update value when write is set at clock
for (int unsigned j = 0; j < BeWidth; j++) begin
if (be_i[i][j]) begin
sram[addr_i[i]][j*ByteWidth+:ByteWidth] <= wdata_i[i][j*ByteWidth+:ByteWidth];
end
end
end else begin
// otherwise update read address for subsequent non request cycles
r_addr_q[i] <= addr_i[i];
end
end // if req_i
end // for ports
end // if !rst_ni
end
end
- 当
SimInit为"none"时,不进行 SRAM 初始化,仅在复位时初始化读地址。 - 当
SimInit不为"none"时,在复位时将init_val的值赋给sram,并初始化读输出寄存器。 - 在每个时钟上升沿或复位信号下降沿,根据请求和写使能信号进行读数据的锁存和写操作。
9. 参数验证和仿真信息打印
// Validate parameters.
// pragma translate_off
`ifndef VERILATOR
`ifndef TARGET_SYNTHESIS
initial begin: p_assertions
assert ($bits(addr_i) == NumPorts * AddrWidth) else $fatal(1, "AddrWidth problem on `addr_i`");
assert ($bits(wdata_i) == NumPorts * DataWidth) else $fatal(1, "DataWidth problem on `wdata_i`");
assert ($bits(be_i) == NumPorts * BeWidth) else $fatal(1, "BeWidth problem on `be_i`" );
assert ($bits(rdata_o) == NumPorts * DataWidth) else $fatal(1, "DataWidth problem on `rdata_o`");
assert (NumWords >= 32'd1) else $fatal(1, "NumWords has to be > 0");
assert (DataWidth >= 32'd1) else $fatal(1, "DataWidth has to be > 0");
assert (ByteWidth >= 32'd1) else $fatal(1, "ByteWidth has to be > 0");
assert (NumPorts >= 32'd1) else $fatal(1, "The number of ports must be at least 1!");
end
initial begin: p_sim_hello
if (PrintSimCfg) begin
$display("#################################################################################");
$display("tc_sram functional instantiated with the configuration:" );
$display("Instance: %m" );
$display("Number of ports (dec): %0d", NumPorts );
$display("Number of words (dec): %0d", NumWords );
$display("Address width (dec): %0d", AddrWidth );
$display("Data width (dec): %0d", DataWidth );
$display("Byte width (dec): %0d", ByteWidth );
$display("Byte enable width (dec): %0d", BeWidth );
$display("Latency Cycles (dec): %0d", Latency );
$display("Simulation init (str): %0s", SimInit );
$display("#################################################################################");
end
end
for (genvar i = 0; i < NumPorts; i++) begin : gen_assertions
assert property ( @(posedge clk_i) disable iff (!rst_ni)
(req_i[i] |-> (addr_i[i] < NumWords))) else
$warning("Request address %0h not mapped, port %0d, expect random write or read behavior!",
addr_i[i], i);
end
`endif
`endif
// pragma translate_on
p_assertions:验证端口信号的位宽和参数的合法性,若不满足条件则使用$fatal终止仿真。p_sim_hello:若PrintSimCfg为1'b1,则打印 SRAM 实例的配置信息。gen_assertions:检查每个端口的请求地址是否在有效范围内,若超出范围则发出警告。
总结
该模块实现了一个通用的 SRAM 功能,支持多端口读写操作,可配置读写延迟和仿真初始化方式。通过参数验证和断言,确保了模块的正确性和可靠性。
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)