FPGA学习之疑难杂症(二)——综合仿真引脚分配都没问题,为什么上板效果不对呢?

问题根源:位宽错误!

工程目标:设计5个灯,分别以2s,3s,4s,5s,6s的频率闪烁(针对计数器、参数化设计练习)

正确的设计代码:(注意里面的参数化设计)

子模块:

//设计5个灯,分别以2s,3s,4s,5s,6s的频率闪烁
//先设计子模块,底层模块,一个灯以2s的频率闪烁

module led_flash_1led (clk,rst_n,led);

input clk;
input rst_n;
output reg led;
reg [27:0] cnt;

parameter CNTE=26'd50000000;

//计数1s(5000_0000)
always @(posedge clk or negedge rst_n) begin
  if(!rst_n)
    cnt<=0;
    else if(cnt==CNTE-1'd1)
    cnt<=0;
    else
    cnt<=cnt+1; 
end
//LED灯输出
always @(posedge clk or negedge rst_n) begin
  if(!rst_n)
   led<=0;
   else if(cnt==CNTE-1'd1)
   led<=~led;
end
endmodule

顶层模块:

//设计5个灯,分别以2s,3s,4s,5s,6s的频率闪烁
//这里是顶层模块

module led_flash_5led (clk,rst_n,led5);

input clk;
input rst_n;
output  [4:0] led5;

led_flash_1led                     u0(.clk(clk),.rst_n(rst_n),.led(led5[0]));
led_flash_1led #(.CNTE(27'd7500_0000)) u1(.clk(clk),.rst_n(rst_n),.led(led5[1]));
led_flash_1led #(.CNTE(27'd10000_0000)) u2(.clk(clk),.rst_n(rst_n),.led(led5[2]));
led_flash_1led #(.CNTE(27'd1_2500_0000)) u3(.clk(clk),.rst_n(rst_n),.led(led5[3]));
led_flash_1led #(.CNTE(28'd1_5000_0000)) u4(.clk(clk),.rst_n(rst_n),.led(led5[4]));

endmodule

正确的测试代码:(注意里面的参数化设计)

`timescale 1ns/1ns

module led_flash_5led_tb();
reg clk;
reg rst_n;
wire [4:0] led5;

led_flash_5led led_inst (.clk(clk),.rst_n(rst_n),.led5(led5));

defparam led_inst.u0.CNTE=50;
defparam led_inst.u1.CNTE=75;
defparam led_inst.u2.CNTE=100;
defparam led_inst.u3.CNTE=125;
defparam led_inst.u4.CNTE=150;


initial clk=1;
always #10 clk=~clk;

initial begin
rst_n=0;
#2;
rst_n=1;
#3000;
$stop;
end

endmodule

引脚分配:

仿真波形:

错误原因记录:

在错误的一版子模块设计中,考虑到计时1s后灯翻转,所以计时变量cnt(即需要计时间到1s/20ns=26'd50000000)的位宽设为26位,reg [25:0] cnt;然而,这没有考虑到顶层模块通过参数化设计对子模块进行例化时,里面的cnt已经超过了1s对应的计时次数,所以位宽也要改变!!!最大的是计时3s后灯翻转对应的次数(3s/20ns=28'd1_5000_0000),所以cnt的位宽设为28位,reg [27:0] cnt。否则就会出现,后面的4个灯无法亮的情况,也就是上板后只有第1个灯闪烁,而后4个灯没动静的错误情况。

那么问题来了,为什么在仿真中没有检查出来呢?这是因为在仿真中,为了节省仿真时间,提高效率,仿真代码中对计时进行了参数化设计。所以仿真中没有看出问题。

这个问题还是值得考虑的!

注意:位宽!!!以后设计中对于每一个数字都用标准的<位宽>’<进制><数字>形式来写,当对数字进行修改时,也要关注位宽的变化。当进行模块例化时,也要考虑模块中参数和变量的位宽大小。比如:5个led分别以1s,2s,3s,4s,5s的频率闪烁,用1s的模块作为子模块,顶层模块对子模块例化的时候。

附加如何进行参数化设计:(下面的模块是另一个工程里的,只是为了说明参数化设计这个问题而已)

方法一:使用defparam语句重新定义子模块参数内容

子模块counter中

parameter CNT=25’d2499_9999;

上层模块LED_flicker

counter counter0(.clk(clk),.rst_n(rst_n),.led(led[0]));
counter counter1(.clk(clk),.rst_n(rst_n),.led(led[1]));

defparam couter0.CNT=22’d249_9999;
defparam counter1.CNT=18’d24_9999;

虽然模块中设定了有默认值,但是使用defparam修改的值比原始设计文件中的值拥有更高的编译优先级。当使用defparam修改了原始文件中的参数值后,原始文件中的默认参数值即被忽略。

方法二:使用参数例化方式修改参数值

子模块counter中

parameter CNT=25’d2499_9999;

上层模块LED_flicker_inst

counter #(.CNT(22’d249_9999)) counter0(.clk(clk),.rst_n(rst_n),.led(led[0]));
counter #(.CNT(18’d24_9999)) counter1(.clk(clk),.rst_n(rst_n),.led(led[1]));//参数例化+端口例化

能够避开对于同一个参数多次使用defparam来重定义值而引发的冲突。

测试代码中:

仿真中,为了节约仿真时间,我们希望仿真中再次修改参数值。

总结,为了同时保证实际逻辑设计和仿真验证时对参数的修改能够共存,互不影响,在实体模块设计中,使用参数例化的方式修改被例化模块中的参数(设计中用方法二)。在仿真验证用的testbench中,使用defparam语句来修改对应的参数(设计中用方法二时,测试中用方法一)。

  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值