这是对rom初始化并读取的源码
module rom_nxm(rom_data,rom_addr,clk,rd,load);
parameter M=8,N=4;
input clk,rd;
input load;
input [N-1:0] rom_addr;
output reg [M-1:0] rom_data;
reg[M-1:0]memory[0:2**N-1];
always@(posedge load)
begin:init
integer i;
for(i=0;i<(2**N);i=i+1)
memory[i]=i+1;
end
always@(posedge clk)
end
always@(posedge clk)
begin:read
if(rd)
rom_data=memory[rom_addr];
end
endmodule
testbench为
`timescale 1ns/100ps
module rom_nxm_tb ;
parameter M = 8 ;
parameter N = 4 ;
reg load ;
reg [N-1:0] rom_addr ;
reg rd ;
reg clk ;
wire [M-1:0] rom_data ;
rom_nxm #( M , N )
DUT (
.load (load ) ,
.rom_addr (rom_addr ) ,
.rd (rd ) ,
.clk (clk ) ,
.rom_data (rom_data ) );
initial
begin
clk=0;
load=0;
rd=0;
end
initial
begin
#10 load=1;
#10 rd=1;
rom_addr=0;
end
always #10 clk=~clk;
always@(posedge clk)
begin
$display("addr:%d,data:%d",rom_addr,rom_data);
if(rom_addr==15)
rom_addr=0;//rom_addr<=0
else
rom_addr= rom_addr+1;//rom_addr<= rom_addr+1
end
initial
#35 $strobe($time,"addr:%d,data:%d",rom_addr,rom_data);
endmodule
阻塞赋值的仿真结果:
addr: x,data: x
# addr: 0,data: x
# 35addr: 1,data: 2
# addr: 1,data: 2
# addr: 2,data: 3
# addr: 3,data: 4
非阻塞赋值的结果:
addr: x,data: x
# addr: 0,data: x
# 35addr: 1,data: 1
# addr: 1,data: 1
# addr: 2,data: 2
# addr: 3,data: 3
modelsim的testbench中对always@(posedge)模块的阻塞赋值是在posedge clk之前delta时间进行赋值,而非阻塞赋值是在posedge clk之后delta时间进行赋值,且一般寄存器的采样在时钟前delta时间,而赋值在时钟后delta时间。
若为阻塞赋值,在第二个上升沿的时侯,addr在posedge clk前的delta时间进行赋值变为0001,然后memory采样的是posedge clk之前delta时间的值即0001(虽然显示为0000,),则addr显示为0001的时候,memory显示的是memory【0001】。这时addr与memory对应。
但若用非阻塞赋值的话,在第二个上升沿的时侯,addr的显示与阻塞赋值是一样的,但差别是非阻塞赋值是在posedge clk之后delta时间进行赋值,则memory采样仍是在clk之前,就变成memory【0000】。这时,从图上看,仿佛addr与memory不对应。
但是在testbench中将阻塞赋值用非阻塞赋值替代是有好处的,尤其是你的verilig源文件中有非阻塞赋值(存在寄存器或触发器)的情况,这样才能在时序图中体现非阻塞赋值的功能(因非阻塞的本质是读取触发前的状态,而testbench中的非阻塞赋值起到了延时的作用,不要死板的认为是在下一个时钟触发时才赋值),但你要将testbench中的非阻塞赋值当做阻塞赋值对待,深刻理解modelsim是仿真,可参考:
http://xilinx.eetop.cn/viewthread-282059
有高手说:
写tb时注意信号的生成不要在pos clk的同时用阻塞赋值(当你的模块是pos clk时)。这就是多事件同时发生:如果都是非阻塞赋值就不存在问题。但阻塞赋值时,不同仿真器的处理顺序不一样,会导致结果差异。可用3种方式:1:neg clk 和阻塞赋值;2:pos clk和非阻塞赋值;3:pos clk +单位延时 + 阻塞赋值。
若用testbench中用阻塞赋值,
加载测试向量时,避免在时钟的上下沿变化
为了模拟真实器件的行为,加载测试向量时,避免在时钟的上下沿变化,而是在时钟的上升沿延时一个时间单位后,加载的测试向量发生变化。如:
assign #5 c="a"^b
……
@(posedge clk) #(0.1*`cycle) A=1;