目录
为何使用BRAM实现延时
在一些设计中需要对数据进行多个周期的延时。延时方法有多种,比如使用SLICEM生成移位寄存器,或者使用FF。但是对于大位宽、深延时的数据,使用上述方法会消耗过多资源,功耗也较大。此时使用BRAM进行延时将是更优的选择。
实现与仿真
下面是简单的实现和仿真代码。其中DELAY_LEV是需要的延时周期数,READ_LATENCY是RAM的读延时,WRITE_LATENCY是RAM写入延时(在本例中是指top文件中对输入din进行的同步处理)。需要注意的是需要将RAM的工作模式设置为Read First,这样可以保证从当前地址读出的数据是之前存储的数据,而不是写入数据。当延时模块开始工作时,在addr第一次累加并归零之前,从RAM读出的都是RAM中的初始数据。只有从第二轮地址累加开始后,从RAM读出的才是需要延时的数据。所以通过控制addr在何处归零,即可实现不同的延时级数。
由于本例中的RAM通过调用XPM库实现,所以可以进一步增加parameter参数,使延时功能参数化程度进一步提高。
`timescale 1ns / 1ps
//
// Company:
// Engineer:
// Yinye
// Create Date: 2020/02/26 22:04:12
// Design Name:
// Module Name: top
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module top
#( parameter DELAY_LEV = 10,
parameter READ_LATENCY = 1,
parameter WRITE_LATENCY = 1)
(
input clk,
input [31: 0] din,
output [31: 0] dout
);
reg [31: 0] din_r=32'h0;
reg [ 5: 0] addr=6'h0;
wire [31: 0] dout_w;
assign dout = dout_w;
always @ (posedge clk) begin
din_r <= din;
if( addr == DELAY_LEV-1 - READ_LATENCY-WRITE_LATENCY ) addr <= 6'h0;
else addr <= addr + 1'b1;
end
xpm_memory_spram #(
.ADDR_WIDTH_A(6), // DECIMAL
.AUTO_SLEEP_TIME(0), // DECIMAL
.BYTE_WRITE_WIDTH_A(32), // DECIMAL
.ECC_MODE("no_ecc"), // String
.MEMORY_INIT_FILE("none"), // String
.MEMORY_INIT_PARAM("0"), // String
.MEMORY_OPTIMIZATION("true"), // String
.MEMORY_PRIMITIVE("auto"), // String
.MEMORY_SIZE(2048), // DECIMAL
.MESSAGE_CONTROL(0), // DECIMAL
.READ_DATA_WIDTH_A(32), // DECIMAL
.READ_LATENCY_A(1), // DECIMAL
.READ_RESET_VALUE_A("0"), // String
.RST_MODE_A("SYNC"), // String
.USE_MEM_INIT(1), // DECIMAL
.WAKEUP_TIME("disable_sleep"), // String
.WRITE_DATA_WIDTH_A(32), // DECIMAL
.WRITE_MODE_A("read_first") // String
)
xpm_memory_spram_inst (
.dbiterra(), // 1-bit output: Status signal to indicate double bit error occurrence
// on the data output of port A.
.douta(dout_w), // READ_DATA_WIDTH_A-bit output: Data output for port A read operations.
.sbiterra(), // 1-bit output: Status signal to indicate single bit error occurrence
// on the data output of port A.
.addra(addr), // ADDR_WIDTH_A-bit input: Address for port A write and read operations.
.clka(clk), // 1-bit input: Clock signal for port A.
.dina(din_r), // WRITE_DATA_WIDTH_A-bit input: Data input for port A write operations.
.ena(1'b1), // 1-bit input: Memory enable signal for port A. Must be high on clock
// cycles when read or write operations are initiated. Pipelined
// internally.
.injectdbiterra(1'b0), // 1-bit input: Controls double bit error injection on input data when
// ECC enabled (Error injection capability is not available in
// "decode_only" mode).
.injectsbiterra(1'b0), // 1-bit input: Controls single bit error injection on input data when
// ECC enabled (Error injection capability is not available in
// "decode_only" mode).
.regcea(1'b1), // 1-bit input: Clock Enable for the last register stage on the output
// data path.
.rsta(1'b0), // 1-bit input: Reset signal for the final port A output register stage.
// Synchronously resets output port douta to the value specified by
// parameter READ_RESET_VALUE_A.
.sleep(1'b0), // 1-bit input: sleep signal to enable the dynamic power saving feature.
.wea(1'b1) // WRITE_DATA_WIDTH_A-bit input: Write enable vector for port A input
// data port dina. 1 bit wide when word-wide writes are used. In
// byte-wide write configurations, each bit controls the writing one
// byte of dina to address addra. For example, to synchronously write
// only bits [15-8] of dina when WRITE_DATA_WIDTH_A is 32, wea would be
// 4'b0010.
);
endmodule
`timescale 1ns / 1ps
//
// Company:
// Engineer:
// Yinye
// Create Date: 2020/02/26 22:58:54
// Design Name:
// Module Name: top_tb
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module top_tb(
);
parameter PERIOD = 10.0;
reg clk;
reg [31: 0] din=32'h1;
wire [31: 0] dout;
reg sim_running=1'b0;
initial begin
clk = 1'b0;
forever begin
#(PERIOD/2) clk = ~clk;
end
end
initial begin
sim_running = 1'b0;
#200 sim_running = 1'b1;
$display("Simulation running");
#500 sim_running = 1'b0;
$display("Simulation end");
end
always @ (posedge clk) begin
if( sim_running ) din <= din + 1'b1;
else din <= 32'h1;
end
top top_i(
.clk (clk),
.din (din),
.dout (dout) );
endmodule