背景:
通常RAM存储的数据表示一组特有的信息,但是存在这样的情况,如下:
一组信息包含多个相同含义的信息,例如RAM存储位宽是8bit,每bit表示一个port端口的link状态(1:linkup;0:linkdonw)。这样每8个端口用一个地址将端口link状态存储在RAM中。
反过来考虑,要是每个端口占用一个地址,仅仅存储端口的link状态,这样会非常浪费资源。
代码实现:
module sp_ram_mask #(
parameter U_DLY = 1 ,
parameter ADDR_WIDTH = 3 ,
parameter DATA_WIDTH = 8 ,
parameter BIT_SIZE = 1 ,
parameter MASK_WIDTH = DATA_WIDTH / BIT_SIZE
)(
input clk ,
input rst_n ,
input wr_en ,
input [DATA_WIDTH-1:0] wr_data ,
input [MASK_WIDTH-1:0] mask ,
input rd_en ,
input [ADDR_WIDTH-1:0] addr ,
output reg [DATA_WIDTH-1:0] rd_data
);
// 定义本地参数
localparam DEPTH = (1 << ADDR_WIDTH);
// 定义信号量
reg [DATA_WIDTH-1:0] memory [DEPTH-1:0];
reg [DATA_WIDTH-1:0] currt_data;
reg [DATA_WIDTH-1:0] wr_data_real;
// 获取写数据时当前的RAM中的数据
always @ ( * ) begin
if (wr_en == 1'b1) begin
currt_data = memory[addr];
end
else begin
currt_data = {DATA_WIDTH{1'b0}};
end
end
//构造掩码位有效的写数据,其它位不变
always @ ( * ) begin : MASK_WRITE_DATA
integer i;
if (wr_en == 1'b1) begin
for (i = 0; i < MASK_WIDTH; i = i + 1) begin
if (mask[i] == 1'b1) begin
wr_data_real[i * BIT_SIZE +: BIT_SIZE] = wr_data[i * BIT_SIZE +: BIT_SIZE];
end
else begin
wr_data_real[i * BIT_SIZE +: BIT_SIZE] = currt_data[i * BIT_SIZE +: BIT_SIZE];
end
end
end
else begin
wr_data_real = {DATA_WIDTH{1'b0}};
end
end
always @ (posedge clk or negedge rst_n) begin : WRITE_DATA
integer i;
if (!rst_n) begin
for (i = 0; i < DEPTH; i = i + 1) begin
memory[i] <= #U_DLY {DATA_WIDTH{1'b0}};
end
end
else if (wr_en == 1'b1) begin
memory[addr] <= #U_DLY wr_data_real;
end
else;
end
always @ (posedge clk or negedge rst_n) begin : READ_DATA
if (!rst_n) begin
rd_data <= #U_DLY {DATA_WIDTH{1'b0}};
end
else if (rd_en == 1'b1) begin
rd_data <= #U_DLY memory[addr];
end
else;
end
endmodule
仿真代码:
`timescale 1ns/1ps
module tb_sp_ram_mask #(
parameter ADDR_WIDTH = 3 ,
parameter DATA_WIDTH = 8 ,
parameter BIT_SIZE = 1 ,
parameter MASK_WIDTH = DATA_WIDTH / BIT_SIZE
)();
reg clk ;
reg rst_n ;
reg wr_en ;
reg [DATA_WIDTH-1:0] wr_data ;
reg [MASK_WIDTH-1:0] mask ;
reg rd_en ;
reg [ADDR_WIDTH-1:0] addr ;
wire [DATA_WIDTH-1:0] rd_data ;
`ifdef FSDB_ON
initial begin
$fsdbAutoSwitchDumpfile(1000,"wave.fsdb",20,"fsdb_dump.log");
//$fsdbDumpfile("tb_sp_ram_mask.fsdb"); //上面一行可用这个简单的系统任务
$fsdbDumpvars(0); //0表示所有层次都dump 从xxx_top开始dump
$fsdbDumpMDA(); //dump 二维数组,vcs 编译时加上 -debug_all 才行
$fsdbDumpon; //开始dump时间
$fsdbDumpflush; //结束dump时间 ,如果没有开始和结束时间默认从仿真开始到结束
end
`endif
initial begin
clk = 1;
forever #0.5 clk = ~clk;
end
initial begin
rst_n = 0;
wr_en = 1'b0;
rd_en = 1'b0;
wr_data = 0;
mask = 0;
addr = 0;
#9 rst_n = 1;
#1 wr_en = 1'b1; wr_data = 8'b1111_1111; mask = 8'b1111_1111; addr = 3'd2;
#1 wr_en = 1'b0;
#1 rd_en = 1'b1; addr = 3'd2;
#1 rd_en = 1'b0;
#1 wr_en = 1'b1; wr_data = 8'b0010_0010; mask = 8'b0001_0001; addr = 3'd2;
#1 wr_en = 1'b0;
#1 rd_en = 1'b1; addr = 3'd2;
#1 rd_en = 1'b0;
#1 wr_en = 1'b1; wr_data = 8'b0111_1101; mask = 8'b1000_0010; addr = 3'd2;
#1 wr_en = 1'b0;
#1 rd_en = 1'b1; addr = 3'd2;
#1 rd_en = 1'b0;
#10000 $finish;
end
sp_ram_mask #(
.ADDR_WIDTH ( ADDR_WIDTH ),
.DATA_WIDTH ( DATA_WIDTH ),
.BIT_SIZE ( BIT_SIZE ),
.MASK_WIDTH ( MASK_WIDTH )
)
u_sp_ram_mask (
.clk ( clk ), // i
.rst_n ( rst_n ), // i
.wr_en ( wr_en ), // i
.wr_data ( wr_data ), // i
.mask ( mask ), // i
.rd_en ( rd_en ), // i
.addr ( addr ), // i
.rd_data ( rd_data ) // o
);
endmodule
用例过程:
步骤1:地址2所有端口状态置为UP(1),然后读出结果,预期结果0xff;
步骤2:地址2的0和4端口状态置为DOWN(0),然后读出结果,预期结果0xee;
步骤3:地址2的1和7端口状态置为DOWN(0),然后读出结果,预期结果0x6c。
仿真结果:
从波形图看,结果完全符合预期。
需要读取端口的状态时,只需将端口换算成对应的地址,然后取对应bit位的值。
代码实现已经参数化,容易扩展。例如:8bit数据存储4个端口的信息,每2bit是一组,掩码位宽是4。每2bit中,高位表示是否由光模块,低位表示link状态。
若有不正确的地方,欢迎大家指正。