FPGA学习-关于延迟的用法-FPGA常见问题社区-FPGA CPLD-ChipDebug

FPGA学习-关于延迟的用法

1.0延时

Verilog和VHDL是一种基于实际电路进行设计的硬件描述语言,所以在设计时,要更多的基于实际电路去考虑延时的添加。

 

在实际电路中,延时分为:惯性延时和传输延时。

 

1.1惯性延时:

通常发生在信号通过逻辑门时发生。

图片[1]-FPGA学习-关于延迟的用法-FPGA常见问题社区-FPGA CPLD-ChipDebug

如图1所示,输入Wirein有两个高电平脉冲,第一个宽度为 3ns,第二个宽度为9ns。

当第一个高电平脉冲到达与非门时,因为与非门的延迟是5ns大于第一个脉冲信号宽度3ns,输出还来不及建立低电平,脉冲信号就已经过去了,导致在输出信号Wireout上没有输出。

当第二个脉冲信号到达与非门时,因其脉冲宽度大于与非门延迟宽度,所以输出信号Wireout有输出并整体延迟5ns。

这种延时就被成为惯性延时,如果输入变化过快,则不会体现在输出上。

 

1.2传输延时:

传输延迟一般为输入信号变化到对应输出信号变化经过的时间,不会对输入信号进行滤除处理,所以传输延迟是一种绝对延迟,这种延迟类似于物理传输线的延迟,在仿真中用于模拟连线延迟。如下图所示,输入一般不会被滤除。

 

图片[2]-FPGA学习-关于延迟的用法-FPGA常见问题社区-FPGA CPLD-ChipDebug

2.0仿真延时

在仿真中,如果在仿真的elaboration过程中没有显示指定延迟的类型,那么仿真器一般都会将代码中指定的延迟作为惯性延迟处理,即此时小于指定延迟宽度的脉冲将被滤除。

如果仿真过程中需要模拟传输延迟,需要在elaboration时指定相应的传输延迟参数,即使能传输延迟模拟功能,此时输入信号原则上都能通过电路单元。

 

2.1传输延迟参数

在仿真elaboration时,增加如下仿真参数:

“+transport_path_delays +pulse_r/<滤除百分比> +pulse_e/<滤除百分比>”

等可在仿真阶段实现传输延迟的模拟,并且配置不同的滤除百分比,输出的结果也会有差异,一般输出结果为三种情况:滤除、通过、不定态,如下图所示:

图片[3]-FPGA学习-关于延迟的用法-FPGA常见问题社区-FPGA CPLD-ChipDebug

其中“+pulse_r”和“+pulse_e”后指定的参数表示允许通过和滤除的信号的宽度占指定延迟的百分比。

例如,如果指定延迟为5ns,参数为“+pulse_r/40”和“+pulse_e/80”(其中的40和80为百分比),那么小于2ns(5x(40%))的信号将被滤除,大于等于4ns(5x(80%))的信号将可以通过,介于两者之间的信号将输出不定态。

Note:如果仅使用“+transport_path_delays”而不使用对脉冲宽度约束的参数(“+pulse_r/e”),那么仿真器将按照惯性延迟处理方式对输入信号进行处理,即小于指定延迟宽度的信号不能通过电路单元。

同时在EDA工具中还有其他参数(“+pulse_r/e”与“+pulse_int__r/e”等),不同的参数将有不同的仿真效果,具体可参考相关EDA工具手册。

 

3.0延时方式

延迟的添加方法有两种:内定延迟或者正规延迟

内定延迟:C = #2 A+B;

正规延迟:#2 C = A+B;

 

4.0阻塞赋值延时

阻塞赋值:

 

always @(a)

y = ~a;

 

阻塞赋值+正规延迟:

 

always @(a)

#5 y = ~a;

 

阻塞赋值+内定延迟:

 

always @(a)

y = #5 ~a;

 

4.1阻塞赋值+正规延迟

module adder_t1 (co, sum, a, b, ci);

    output             co;

    output   [3:0]   sum;

    input      [3:0]   a, b;

    input                ci;

  reg co;

  reg [3:0] sum;

  always @(a or b or ci)

           #12 {co, sum} = a + b + ci;

 

 

对于adder_t1示例,输出应在输入更改后12ns更新。

如图3所示,如果a输入在时间15变化,

在接下来的12ns期间a,b和ci输入发生变化,则输出将使用a,b和ci的最新值进行更新。

图片[4]-FPGA学习-关于延迟的用法-FPGA常见问题社区-FPGA CPLD-ChipDebug

 

以adder_t7a和adder_t7b为例:

 

module adder_t7a (co, sum, a, b, ci);

output co;

output [3:0] sum;

input [3:0] a, b;

input ci;

reg co;

reg [3:0] sum;

reg [4:0] tmp;

always @(a or b or ci) begin

#12 tmp = a + b + ci;

{co, sum} = tmp;

end

endmodule

 

module adder_t7b (co, sum, a, b, ci);

output co;

output [3:0] sum;

input [3:0] a, b;

input ci;

reg co;

reg [3:0] sum;

reg [4:0] tmp;

always @(a or b or ci) begin

tmp = a + b + ci

#12 {co, sum} = tmp;

end

endmodule

 

adder_t7a的结果与adder_t1一致

adder_t7b的结果由于阻塞赋值的特性,将对第一个输入变化的输入进行采样,并将输出分配到临时位置,直到完成指定的阻塞赋值。然后输出将使用不再有效的旧临时输出值写入。

12ns延迟时间内的其他输入变化将不会被采样,这意味着旧的错误值将保留在输出上,直到发生更多输入变化。

 

4.1.1准则

1.要对组合逻辑的阻塞赋值进行正规延迟。

2. Testbench指南:在测试平台中对阻塞赋值进行正规是合理的,因为延迟仅用于时间空间顺序输入激励事件。

 

4.2阻塞赋值+内定延迟

module adder_t6 (co, sum, a, b, ci);

output co;

output [3:0] sum;

input [3:0] a, b;

input ci;

reg co;

reg [3:0] sum;

always @(a or b or ci)

{co, sum} = #12 a + b + ci;

endmodule

 

对于adder_t6,输出应在输入更改后12ns更新。

如果a输入在时间15发生变化,则RHS输入值将被采样,输出更新,而在接下来的12ns期间所有其他a,b和ci输入变化将不会被采样。

这意味着旧的错误值将保留在输出上,直到发生更多输入变化。

 

module adder_t11a (co, sum, a, b, ci);

output co;

output [3:0] sum;

input [3:0] a, b;

input ci;

reg co;

reg [3:0] sum;

reg [4:0] tmp;

always @(a or b or ci) begin

tmp = #12 a + b + ci;

{co, sum} = tmp;

end

endmodule

 

module adder_t11b (co, sum, a, b, ci);

output co;

output [3:0] sum;

input [3:0] a, b;

input ci;

reg co;

reg [3:0] sum;

reg [4:0] tmp;

always @(a or b or ci) begin

tmp = a + b + ci;

{co, sum} = #12 tmp;

end

endmodule

 

 

当在赋值语句的RHS上发生延迟时,存在多个阻塞赋值的相同问题。

adder_t11a与adder_t6的问题是一样的。

adder_t11b的输出将采用最新的值进行输出。

 

4.2.1准则

不要在模型组合逻辑的阻塞分配的RHS上设置延迟。

Testbench指南:不要在测试平台中对阻塞赋值进行内定延迟。

 

4.3结论

阻塞赋值+延迟,在语句启动之后,输出传输延时当前时刻或语句启动时刻的逻辑结果,既不能模拟传输延时,也不能模拟惯性延时,所以不建议在阻塞赋值中添加延时。

 

5.0非阻塞赋值

always @(a)

y <= ~a;

非阻塞赋值+正规延迟

 

always @(a)

#5 y <= ~a;

 

非阻塞赋值+内定延迟

 

always @(a)

y <= #5 ~a;

 

5.1非阻塞赋值+正规延迟

module adder_t2 (co, sum, a, b, ci);

output co;

output [3:0] sum;

input [3:0] a, b;

input ci;

reg co;

reg [3:0] sum;

always @(a or b or ci)

#12 {co, sum} <= a + b + ci;

endmodule

 

 

可以看出,输出会直接输出延迟后当前时刻的逻辑结果,并不能反映中间的输入变化。

 

5.2非阻塞赋值+内定延迟

module adder_t3 (co, sum, a, b, ci);

output co;

output [3:0] sum;

input [3:0] a, b;

input ci;

reg co;

reg [3:0] sum;

always @(a or b or ci)

{co, sum} <= #12 a + b + ci;

endmodule

 

 

向非阻塞赋值的右侧(RHS)添加延迟(如图9所示)将准确地模拟具有传输延迟的组合逻辑。

图片[5]-FPGA学习-关于延迟的用法-FPGA常见问题社区-FPGA CPLD-ChipDebug

在的adder_t3示例中,如果a输入在时间15发生变化,如图10(下一页)所示,则将采样所有输入,并且新输出值将排队等待12ns后分配。

在输出排队(计划用于将来分配)但尚未分配之后,将立即再次设置始终块以在下一个输入事件上触发。这意味着所有输入事件将在12ns延迟后将新值排队到输出上。

这种编码风格模拟了具有传输延迟的组合逻辑。

 

这种编码风格将精确地模拟具有纯传输延迟的传输延迟;

但是,这种编码风格通常会导致模拟速度变慢。

Testbench指南:当必须在未来的时钟边缘或设定的延迟之后安排刺激时,此编码样式通常用于测试平台,同时不阻止在同一程序块中分配后续刺激事件。

 

多个非阻塞赋值和内定延迟

module adder_t9c (co, sum, a, b, ci);

output co;

output [3:0] sum;

input [3:0] a, b;

input ci;

reg co;

reg [3:0] sum;

reg [4:0] tmp;

always @(a or b or ci or tmp) begin

tmp <= #12 a + b + ci;

{co, sum} <= tmp;

end

endmodule

 

module adder_t9d (co, sum, a, b, ci);

output co;

output [3:0] sum;

input [3:0] a, b;

input ci;

reg co;

reg [3:0] sum;

reg [4:0] tmp;

always @(a or b or ci or tmp) begin

tmp <= a + b + ci;

{co, sum} <= #12 tmp;

end

endmodule

 

 

组合逻辑是有缺陷的,除非所有RHS输入标识符都列在灵敏度列表中,包括仅在always块内分配和使用的中间临时值,如adder_t9c和adder_t9d。

对于adder_t9c和adder_t9d示例,非阻塞赋值是并行执行的,并且在更新tmp之后,由于tmp在灵敏度列表中,因此将再次触发始终块,评估RHS方程并使用更新LHS方程正确的值(在第二次通过always块时)。

建模指南:通常,不要将非阻塞赋值的内定延迟放在模型组合逻辑上。这种编码风格可能令人困惑,并且模拟效率不高。

将非阻塞赋值的内定延迟置于时序逻辑上的时钟到输出行为模型是一种常见且有时有用的做法。

Testbench:有一些多时钟设计验证套件可以从使用RHS延迟的多个非阻塞赋值中受益;

但是,这种编码风格可能会令人困惑,因此通常不建议在测试平台中对非阻塞赋值的RHS进行延迟。

 

5.3结论

对于非阻塞赋值,当只有一个时,内定延迟可以很好的模拟传输延迟的情况。

当非阻塞赋值变为多个时,需要将变量放入敏感事件表中。

因此,非阻塞赋值+内定延迟更适合于设计时序逻辑,而不适合组合逻辑。

 

6.0连续赋值

 

图片[6]-FPGA学习-关于延迟的用法-FPGA常见问题社区-FPGA CPLD-ChipDebug

6.1连续赋值+正规延迟

module adder_t4 (co, sum, a, b, ci);

output co;

output [3:0] sum;

input [3:0] a, b;

input ci;

assign #12 {co, sum} = a + b + ci;

endmodule

 

 

增加连续赋值的延迟可以精确地模拟具有惯性延迟的组合逻辑,是一种推荐的编码方式。

对于adder_t4示例,输出在最后一次输入更改后12ns内不会改变(所有输入稳定后12ns)。任何间隔小于12ns的输入变化序列将导致任何未来的预定输出事件(具有相应分配时间的输出值)被替换为新的输出事件。

图14显示了在图13中所示的adder_t4代码上运行模拟的输出波形。第一个a输入变化发生在时间15,这导致输出事件被安排在时间27,但是b输入上的变化并且在时间17,19和21分别对输入进行了两次更改,导致安排三个新的输出事件。只有最后一个输出事件实际完成,输出在时间33分配。连续分配不会“排队”输出分配,它们只跟踪下一个输出值以及何时发生;因此,连续分配模型具有惯性延迟的组合逻辑。

 

图片[7]-FPGA学习-关于延迟的用法-FPGA常见问题社区-FPGA CPLD-ChipDebug

6.2多个连续赋值+延迟

module adder_t10a (co, sum, a, b, ci);

output co;

output [3:0] sum;

input [3:0] a, b;

input ci;

wire [4:0] tmp;

assign tmp = a + b + ci;

assign #12 {co, sum} = tmp;

endmodule

 

module adder_t10b (co, sum, a, b, ci);

output co;

output [3:0] sum;

input [3:0] a, b;

input ci;

wire [4:0] tmp;

assign #12 tmp = a + b + ci;

assign {co, sum} = tmp;

endmodule

 

可以类似地表明,通过向连续分配添加延迟来建模逻辑功能,其输出用于驱动具有延迟的其他连续分配的输入,也精确地模拟具有惯性延迟的组合逻辑。

 

6.3混合无延迟always块和连续分配

module adder_t5 (co, sum, a, b, ci);

output co;

output [3:0] sum;

input [3:0] a, b;

input ci;

reg [4:0] tmp;

always @(a or b or ci) begin

tmp = a + b + ci;

end

assign #12 {co, sum} = tmp;

endmodule

 

在没有延迟的始终块中建模逻辑功能,然后将始终块中间值传递给具有延迟的连续分配,如adder_t5所示,将精确地模拟具有惯性延迟的组合逻辑。

在上例中,tmp变量在任何输入事件之后更新。

连续分配输出在tmp变量最后一次更改后12ns内不会改变。始终块输入信号的任何变化都将导致tmp更改,这将导致在连续分配输出上调度新的输出事件。直到连续赋值输出才会更新。

这种编码风格模拟了具有惯性延迟的组合逻辑。

建模指南:使用具有延迟的连续赋值来建模简单的组合逻辑。这种编码风格将准确地模拟具有惯性延迟的组合逻辑。

 

Testbench指南:可以在测试平台的任何位置使用连续赋值来将激励值驱动到输入端口和实例化模型的双向端口。

 

结论

1 ) 模拟惯性延迟的方法:连续赋值+正规延迟

2 ) 模拟传输延迟的方法:.非阻塞赋值+内定延迟

3)阻塞赋值不建议添加延迟。

 

请登录后发表评论

    没有回复内容