赞
踩
目录
参考《Verilog 编程艺术》魏家明著
Verilog行为模型包含有控制仿真和操作变量的过程语句,它们包含在过程快内。每个过程块都有一个与他相联系的活动流。
活动从initial 和 always开始,每个initial和always都开始各自的活动流。所有活动流都是并发的,用于模拟硬件固有的并发行为。
例子:
- module behave();
- reg a, b;
- initial begin
- a = 1'b1;
- b = 1'b0;
- end
-
- always begin
- #50 a = ~a;
- end
- always begin
- #100 b = ~b;
- end
- endmodule
所有由initial和always定义的活动流在仿真0时刻同时开始,initial只执行一次,always重复执行。
过程赋值用于修改reg,integer,time,real和realtime类型的变量,过程赋值和连续赋值有很大的不同。
1/连续赋值驱动线网,当输入发生变化时就计算并更改线网
2/过程赋值更改变量,在包含它的过程流的控制下更改变量
Verilog 有两种过程赋值,阻塞赋值和非阻塞赋值。
在顺序块内,阻塞赋值语句必须在它的后续语句执行之前执行。但是在并行块(fork join)内,阻塞赋值语句不能阻止它的后续语句的执行。
variable_lvalue = [delay_or_event_control] expression;
说明:
1.= 是阻塞赋值的操作符。
2.delay_or_event_control是可选的赋值间时序操作,既可以是delay_control(例如 #6),也可以是event_control(例如 @(posedge clk))。
3.当LHS使用到了变量(例如数组的索引),那么此变量的值就是在执行赋值语句时刻的值。
4.=也被过程连续赋值和连续赋值使用
阻塞赋值例子:
- rega = 0;
- rega[3] = 1;
- rega[3:5] = 7;
- mema[address] = 8'hff;
- {carry, acc} = rega + regb;
- a = #5 b;
- a = @(posedge clk) b;
- a = repeat(3) @(posedge clk) b;
能够在不阻塞过程流的情况下允许赋值调度。当需要对几个变量在同一个 time-step赋值,而且不用考虑赋值语句的顺序和依赖关系时,就可以使用非阻塞赋值。
variable_lvalue <= [delay_or_event_control] expression;
说明:
1.非阻塞赋值和阻塞赋值语法一样,只不过<=是非阻塞赋值的操作符
2.delay_or_event_control是可选的赋值间时序操作,既可以是delay_control(例如 #6),也可以是event_control(例如 @(posedge clk))。
3.当LHS使用到了变量(例如数组的索引),那么此变量的值就是在执行赋值语句时刻的值。
4.注意:<=也是比较操作符,要根据其出现的位置判断是做非阻塞赋值,还是比较操作。
5.不像阻塞赋值中的事件或延迟控制,非阻塞赋值不能阻塞过程流。在非阻塞赋值计算RHS和调度LHS的更改时,它不会阻塞同一块内后续语句的执行。
6.非阻塞赋值的执行分为两步,
1)在执行非阻塞赋值时,仿真器计算RHS,然后把更改的RHS调度到非阻塞赋值更改队列(NBAU_EQ)的尾部。
2)在time-step的最后,仿真器激活NBAU_EQ时,更改每个赋值语句的LHS
例子:a,b交换
- module evaluates2(
- output out
- );
- reg a, b, c;
- initial begin
- a = 0;
- b = 1;
- c = 0;
- end
- always c = #5 ~c;
- always @(posedge clk) begin
- a <= b;
- b <= a;
- end
- endmodule
是允许用表达式对变量和线网连续驱动的过程赋值,包括assign/deassign语句和force/release语句。
LHS可以是reg 和 net,不能是reg的位选和域选。
例子:assign 和 deassign可以用来模拟D触发器的异步复位/置位
- module dff(
- output reg q,
- input d, clear, preset, clock
- );
- always @(clear or preset) begin
- if (!clear) assign q = 0;
- else if (!preset) assign q = 1;
- else deassign q;
- end
- always @(posedge clock) begin
- q <= d;
- end
- endmodule
如果clear或preset为0,那么输出q保持为0或1,posedge clock对q没影响。当clear和preset都为1时,取消assign过程连续赋值,然后posedge clock对q有影响。
与c语言的if语句一样。
- if (index > 0) begin
- if (rega > regb)
- result = rega;
- else
- result = regb;
- end
Verilog有4中循环语句:
1.forever:持续不断的执行,就是死循环
2.repeat:执行括号内表达式指定的循环次数,如果表达式是x或z,就不执行
3.while:与c语言的while循环一样,当括号内表达式为true时就执行,否则不进入循环或跳出循环
4.for:与c语言的for一样。
通常:
1.forever,用在需要死循环的地方,例如生成时钟
2.repeat,可以不用定义循环变量,直接使用。
3.while,循环中的判断条件可以很简单,也可以很复杂
4.for,常用于固定的次数或可变次数的循环,要定义一个循环变量。
例子:
- initial begin
- clk <= 0;
- forever # (PERIOD / 2.0) clk = ~clk;
- end
-
-
- repear (3) @(posedge clk);
-
-
- begin : counts
- reg [7;0] tempreg;
- counr = 0;
- tempreg = rega;
- while (tempreg) begin
- if (tempreg[0])
- count = count + 1;
- tempreg = tempreg >> 1;
- end
- end

for循环中,如果循环个数是变量的时候,那么任何综合工具都综合不出来,这是因为硬件规模必须是有限的,固定的。当综合工具遇到循环变量时,就把它们展开成若干条顺序执行的语句,然后再综合出电路。若循环个数是常数,则展开的语句数是确定的,所以可以综合;若循环个数是变量,则展开的语句数是不确定的,对应的硬件电路数量也不能确定,所以无法综合。
disable语句:
再命名块中使用,当disable执行的时候,命名块就被终止执行,所以它可以用于停止块,退出循环或退出task和function。
例子:
- module test;
- integer i;
- initial begin
- begin : break_block
- for (i = 0; i < 10; i = i + 1) begin : continue_block
- if (i == 5) disable continue_block;
- if (i == 8) disable break_block;
- $display("i= %-d", i)
- end
- end
- end
- endmodule
延迟控制:直到过了指定的时间延迟,才允许后续语句的执行。延迟控制表示从开始遭遇语句到最后执行语句之间的时间。由 # 引入
事件控制:直到某些仿真事件发生,才允许后续语句的执行。仿真时间既可以是线网值或变量值的变化,也可以是由其他过程触发的命名时间。由 @ 引入
延迟控制后面的语句要根据指定的延迟时间推迟执行。规则:
1/如果延迟表达式是x或z,那么就当作0延迟
2/如果延迟表达式是负数,那么就把它解释成同样位长的无符号整数。
3/specify parameters也可用再延迟表达式中,这些参数再做SDF反标时会被替换掉。
例子:
- #10 rega = regb;
- #d rega = regb;
- #((d+e)/2) rega = regb;
- #regr regr = rege + 1
过程语句的执行可以和线网,变量变化或命名事件的发生同步,事件控制的规则如下:
1/线网或变量的变化可以当作触发事件使用,称为隐含事件
2/事件可以基于方向的变化,包括posedge和negedge
3/negedge:the transition form 1 to x z or 0,and from x or z to 0
4/posedge:the transition form 0 to x z or 1,and from x or z to 1
5/当表达式的值发生变化时,就检测到隐含事件。但是当表达式内操作数发生变化,但表达式的结果没变时,就检测不到隐含事件。
例子:
- @r rega = regb;
- @(posedge clock) rega = regb;
- forever @(negedge clock) rega = regb;
必须先声明后使用。命名事件是要明确触发的,用于控制过程语句的执行。用户可以声明event事件类型变量,并触发该变量来识别该事件是否发生
触发信号用 -> 表示。
逻辑或在一起的多个事件用于表示:这些多个事件中只要任意事件发生,那么就可以触发后续语句的之心。or或‘,’被当作事件逻辑或操作符,它们的作用是一样的。
例子:使用逻辑或操作符
- @(trig or enable) rega = regb;
- @(posedge clk_a or posedge clk_b or trig) rega = regb;
- always @(a, b, c, d, e)
事件列表不全会引起bug,使用@(*)可以自动建立隐含事件列表,可以消除由事件列表不全引起的bug。
- always @(*)
- y = (a & b) | (c & d);
-
- equ to always @(a or b or c or d)
过程语句可以通过wait语句延迟执行,直到等待的条件变为true,wait语句是一种特殊形式的事件控制。wait语句的本质是电平敏感的,而基本事件控制其实是边沿敏感的。
wait语句计算条件值,如果是false,那么后续语句就被阻塞,直到条件位true
例子:
- begin
- wait (!enble) #10 a = b;
- #10 c = d;
- end
如果进入块时enable是1,wait语句就会推迟后续语句的执行,直到enable变成1.如果进入块时enable是0,那么再延迟10个时间单位后就执行a=b,不会有附加延迟。
赋值间的延迟控制和事件控制包含在赋值语句里面,以不同的方式执行活动流。
赋值间延迟控制和事件控制会推迟LHS的更改,但是RHS是在延迟之前计算,而不是再延迟之后。
例子:同时对a和b采样和更新值,会导致竞争条件
- fork
- #5 a = b;
- #5 b = a;
- join
通过赋值间时序控制可以避免竞争,因为赋值间延迟使a和b的值在延迟前被计算,然后a和b的值在延迟后被更改为新值
- fork
- a = #5 b;
- b = #5 a;
- join
- fork
- a = @(posedge clk) b;
- c = @(posedge clk) d;
- join
a <= repeat (5) @(posedge clk) data;
在这个例子中,在执行语句时,data被计算并保存到临时存储中,过了5个posedge clk 之后,a才被更改为data的值。
位于begin和end之间的语句会按照指定的顺序执行。
特性:
1.语句按顺序执行,执行一条之后,再执行下一条
2.每条语句的延迟值是相对于它前面语句完成时的仿真时间
3.在最后一条语句执行完后,控制就从对应的顺序块离开
位于fork和join之间的语句会并发执行。
特性:
1.所有语句并发执行
2.每条语句的延迟值都是相对于进入并行块时的仿真时间
3.可以使用延迟控制,从而为赋值提供time-ording
4.在最后一条time-ording语句执行完后,控制就从对应的并行块离开
顺序快和并行块可以被命名,就是在begin或fork后面加上“: name_of_block”
1.允许为块声明local variables,parameters,named events
2.允许其他地方引用块。例如,使用disable语句终止命名块
所有块内声明的变量都是静态的,既所有local变量都有自己单独的存储空间,进入和离开块都不会影响保存在变量中的值。
对于顺序块,开始时间是在执行第一条语句时,结束时间是在最后一条语句执行完
对于并行块,开始时间对所有语句是相同的,结束时间是在最后的time-ording语句执行完
顺序块和并行块可以互相包含。整个块已经执行完成,块后面的语句才能执行下去。
Verilog中的过程要用以下4种语句表示:initial,always,task和function
1.initial和always在仿真开始的时候被使能
2.initial块只执行一次,当它里面的语句都执行完后,initial块就停止
3.always块要重复执行,只有仿真结束,always才停止
4.在initial和always块之间,没有执行顺序的要求
5.模块内对initial块和always块的数量没有限制。
注意:在写综合代码时,当always块包含异步行为时,例如always@(posedge clk or negedge reset),那么此always块只能包含一个独立的if块。但是当always块只包含同步行为,例如always@(posedge clk),那么此always块就可以包含多个独立的if块。
1.综合工具对没有posedge和negedge的always推导出组合逻辑或latch逻辑
2.对于组合always块,组合逻辑是从块中的逻辑推导出来的,与敏感列表没有任何关系,但是综合工具会检查敏感列表是否完整,如果综合工具发现敏感列表不完整,就发出警告;可能导致前后仿真不一致
3.当敏感列表不包含edge表达式,通常生成组合逻辑,但是如果输出变量没有在每个分支上都赋值,就会出现latch,所以设计者要注意检查是否生成了不想要的latch
4.当敏感列表中有edge表达式的时候,就不能在出现non-edge表达式。如果在敏感列表中把edge和non-edge混杂在一起,综合工具就会报告error
5.在always敏感列表中存在但没有使用的信号不会导致前后仿真不一致,但这些额外的信号会使前后仿真运行变慢。
always块中前仿真赋值是按照顺序执行的,如果变量在被赋值之前使用,那么就导致错误顺序的赋值,因为变量将保持上一次always块执行时的值。
在code2a中,temp在赋值之前就被调用,那么上次always执行时的赋值给temp的值就被用于计算变量的赋值。在下一行temp被赋值一个对应于本次always执行的新值。前仿真时temp就像一个latch,值被保留用于下一次计算,然后在综合时,综合工具起是按照temo = c & d;在前面考虑的,不会生成latch,这就导致前后仿真不一致。code2b给出了正确的编码顺序。
- module code2a (
- output reg o,
- input a, b, c, d
- );
- reg temp;
- always @(*) begin
- o = a & b | temp;
- temp = c & d;
- end
- endmodule
-
- module code2b (
- output reg o,
- input a, b, c, d
- );
- reg temp;
- always @(*) begin
- temp = c & d;
- o = a & b | temp;
- end
- endmodule

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。