|
|
|
|
| |
积分:161 帖子:124
精华:1 等级:学徒 注册时间:2007-11-30 最后登录:2011-06-19 |
|
| |
|
| Verilog:TASK 中是否可以用非阻塞赋值语句(高手请进) |
| |
很多资料支持两个观点: 1,TASK 模块中只有时序逻辑能被综合 2,非阻塞赋值语句用来描述时序逻辑,阻塞赋值语句用来描述组合逻辑. 但是从以下资料看来,比较矛盾,大家看看啊. 夏宇闻著作:从算法设计到硬线逻辑的实现 ------ 47 任务: task <任务名>; <端口及数据类型声明语句> <语句1> <语句2> ..... <语句n> endtask 这些声明语句的语法与模块定义中的对应声明语句的语法是一致的。 2) 任务的调用及变量的传递 启动任务并传递输入输出变量的声明语句的语法如下: 任务的调用: <任务名>(端口1,端口2,...,端口n); 下面的例子说明怎样定义任务和调用任务: 任务定义: task my_task; input a, b; inout c; output d, e; … <语句> //执行任务工作相应的语句 … c = foo1; //赋初始值 d = foo2; //对任务的输出变量赋值t e = foo3; endtask 任务调用: my_task(v,w,x,y,z); 任务调用变量(v,w,x,y,z)和任务定义的I/O变量(a,b,c,d,e)之间是一一对应的。当任务启动时,由 v,w,和x.传入的变量赋给了a,b,和c,而当任务完成后的输出又通过c,d和e赋给了x,y和z。下面是一 个具体的例子用来说明怎样在模块的设计中使用任务,使程序容易读懂: 47-48页 module traffic_lights; reg clock, red, amber, green; parameter on=1, off=0, red_tics=350, amber_tics=30,green_tics=200; //交通灯初始化 initial red=off; initial amber=off; initial green=off; //交通灯控制时序 always begin red=on; //开红灯 light(red,red_tics); //调用等待任务 green=on; //开绿灯 light(green,green_tics); //等待 amber=on; //开黄灯 light(amber,amber_tics); //等待 -------------------------------------------------------------------------------------------------------------------------------------------- ------ end //定义交通灯开启时间的任务 task light(color,tics); output color; input[31:0] tics; begin repeat(tics) @(posedge clock);//等待tics个时钟的上升沿 color=off;//关灯 end endtask //产生时钟脉冲的always块 always begin #100 clock=0; #100 clock=1; end endmodule 这个例子描述了一个简单的交通灯的时序控制,并且该交通灯有它自己的时钟产生器。 第 182 页开始: 状态机的VerilogHDL 程序见下面模块: //------------------------------------------------------------------------------ module machine( inc_pc, load_acc, load_pc, rd,wr, load_ir, datactl_ena, halt, clk1, zero, ena, opcode ); output inc_pc, load_acc, load_pc, rd, wr, load_ir; output datactl_ena, halt; input clk1, zero, ena; input [2:0] opcode; reg inc_pc, load_acc, load_pc, rd, wr, load_ir; reg datactl_ena, halt; reg [2:0] state; parameter HLT = 3 'b000, SKZ = 3 'b001, ADD = 3 'b010, ANDD = 3 'b011, XORR = 3 'b100, LDA = 3 'b101, STO = 3 'b110, JMP = 3 'b111; always @( negedge clk1 ) begin if ( !ena ) //接收到复位信号RST,进行复位操作 begin ---------------------------------------------------------------------------------------------------------------------- 183 state<=3'b000; {inc_pc,load_acc,load_pc,rd}<=4'b0000; {wr,load_ir,datactl_ena,halt}<=4'b0000; end else ctl_cycle; end //-----------------begin of task ctl_cycle--------- task ctl_cycle; begin casex(state)
3’b000:
//load high 8bits in struction begin {inc_pc,load_acc,load_pc,rd}<=4'b0001; {wr,load_ir,datactl_ena,halt}<=4'b0100; state<=3’b001; end 3’b001: //pc increased by one
then load low 8bits instruction begin {inc_pc,load_acc,load_pc,rd}<=4'b1001; {wr,load_ir,datactl_ena,halt}<=4'b0100; state<=3’b010; end 3’b010: //idle begin {inc_pc,load_acc,load_pc,rd}<=4'b0000; {wr,load_ir,datactl_ena,halt}<=4'b0000; state<=3’b011; end 3’b011: //next instruction
address setup 分析指令从这里开始 begin if(opcode==HLT) //指令为暂停HLT begin {inc_pc,load_acc,load_pc,rd}<=4'b1000; {wr,load_ir,datactl_ena,halt}<=4'b0001; end else begin {inc_pc,load_acc,load_pc,rd}<=4'b1000; {wr,load_ir,datactl_ena,halt}<=4'b0000; end state<=3’b100; end 3’b100: //fetch
oprand begin if(opcode==JMP) begin {inc_pc,load_acc,load_pc,rd}<=4'b0010; {wr,load_ir,datactl_ena,halt}<=4'b0000; end else 184 if( opcode==ADD || opcode==ANDD || opcode==XORR || opcode==LDA) begin {inc_pc,load_acc,load_pc,rd}<=4'b0001; {wr,load_ir,datactl_ena,halt}<=4'b0000; end else if(opcode==STO) begin {inc_pc,load_acc,load_pc,rd}<=4'b0000; {wr,load_ir,datactl_ena,halt}<=4'b0010; end else begin {inc_pc,load_acc,load_pc,rd}<=4'b0000; {wr,load_ir,datactl_ena,halt}<=4'b0000; end state<=3’b101; end 3’b101: //operation begin if ( opcode==ADD||opcode==ANDD|| opcode==XORR||opcode==LDA ) begin //过一个时钟后与累加器的内容进行运算 {inc_pc,load_acc,load_pc,rd}<=4'b0101; {wr,load_ir,datactl_ena,halt}<=4'b0000; end else if( opcode==SKZ && zero==1) begin {inc_pc,load_acc,load_pc,rd}<=4'b1000; {wr,load_ir,datactl_ena,halt}<=4'b0000; end else if(opcode==JMP) begin {inc_pc,load_acc,load_pc,rd}<=4'b1010; {wr,load_ir,datactl_ena,halt}<=4'b0000; end else if(opcode==STO) begin //过一个时钟后把wr变1就可写到RAM中 {inc_pc,load_acc,load_pc,rd}<=4'b0000; {wr,load_ir,datactl_ena,halt}<=4'b1010; end else begin {inc_pc,load_acc,load_pc,rd}<=4'b0000; {wr,load_ir,datactl_ena,halt}<=4'b0000; end state<=3’b110; end 3’b110: //idle begin if ( opcode==STO ) begin {inc_pc,load_acc,load_pc,rd}<=4'b0000; {wr,load_ir,datactl_ena,halt}<=4'b0010; end else if ( opcode==ADD||opcode==ANDD|| opcode==XORR||opcode==LDA) begin {inc_pc,load_acc,load_pc,rd}<=4'b0001; {wr,load_ir,datactl_ena,halt}<=4'b0000; end else begin {inc_pc,load_acc,load_pc,rd}<=4'b0000; {wr,load_ir,datactl_ena,halt}<=4'b0000; end state<=3’b111; end 3’b111: // begin if( opcode==SKZ && zero==1 ) begin {inc_pc,load_acc,load_pc,rd}<=4'b1000; {wr,load_ir,datactl_ena,halt}<=4'b0000; end else begin {inc_pc,load_acc,load_pc,rd}<=4'b0000; {wr,load_ir,datactl_ena,halt}<=4'b0000; end state<=3’b000; end default: begin {inc_pc,load_acc,load_pc,rd}<=4'b0000; {wr,load_ir,datactl_ena,halt}<=4'b0000; state<=3’b000; end endcase end endtask //-----------------end of task ctl_cycle--------- endmodule //------------------------------------------------------------------------------ 状态机和状态机控制器组成了状态控制器。它们之间的连接关系很简单。见本小节的图8.2.8。 The Verilog® Golden Reference Guide 95页: Rules Names used in a task that are not declared in the task refer to
names in the calling module. A task may have any number (including none) of input, output and
inout arguments. Arguments (including inputs and inouts) may also be declared as
registers. If it is not declared explicitly, an argument is declared implicitly
as a reg with the same range as the corresponding argument. When a task is enabled, the values of the argument expressions corresponding to the task’s inputs and inouts are copied into the corresponding argument registers. When the task completes, the
values of the inout and output argument registers are copied to the
corresponding expressions in the task enable statement. Gotchas! Unlike module ports, the arguments of a task are not defined in
brackets after the work task. Tasks containing more than one statement must use a begin-end block
or a fork-join block to group the statements. Task inputs, inouts and outputs, and any local registers are stored
statically. This means that even if a task is enabled (i.e. called) more than
once, there is only one copy of these registers. If a task is enabled a second
time before the first enable has completed, the values of the input and inout
registers, and possibly the local registers too, will be overridden. 94 The values of the outputs and inouts are only copied to the
corresponding register expressions in the task enable when the task completes. If
there is a timing control in the task after an assignment to an output or
inout, the corresponding registers in the task enable will only be updated
after the timing control delay. Similarly, a non-blocking assignment to an output or inout may not
work, because the assignment may not have taken effect when the task
returns. Synthesis For synthesis, a task may not contain timing controls. Task enables
are synthesized as combinational logic. Tips Complex RTL code is often structured by writing many always’s.
Consider instead using one always that enables several tasks. Use tasks in test fixtures to apply repetitive sequences of
stimulus. For example, to read and write data from a memory (See Examples). Tasks to be used in more than one module can be defined in a
separate module, containing only tasks, and referenced using a hierarchical
name. Example This shows a simple RTL task, which can be synthesized. task Counter; inout [3:0] Count; input Reset; if (Reset) // Synchronous Reset Count = 0; // Must use non-blocking for RTL (有点矛盾) else Count = Count + 1; endtask 周立功的 <Verilog®黄金参考指南 > V1.0 1996 年 8 月 61页: - 61 - 规则 • 不在任务中声明但在任务中使用的名字对应在调用模块中的名字 • 任务可以有任意数量 包括没有 的输入 输出和输入输出变量 • 变量 包括输入和输入输出 可以作为寄存器声明 如果它们没有被明确声明 变量就作为与对 应的变量有相同范围的 reg 声明 • 当使能一个任务后 对应于任务输入和输入输出的变量表达式的值被复制到相应的变量寄存器 当任务完成后 输入输出和输出变量寄存器的值被复制到任务使能语句相应的表达式 注意 • 不像模块端口 任务的变量不在 task 后面的括号中定义 • 如果任务包含超过一个语句 这些语句要用 begin-end 块或 fork-join 块包含起来 • 任务的输入 输出和输入输出以及所有局部寄存器都被静态保存 这就是说甚至任务被使能 即 调用 超过一次 这些寄存器也只有一个副本 如果任务在第一次使能没有完成前再次使能 输 入和输入输出寄存器以及可能的局部寄存器的值将会被覆盖 • 当任务完成后 输出和输入输出的值只能被复制到使能任务的对应寄存器表达式 如果在赋值到 输出或输入输出后任务有定时控制 使能任务中的相应寄存器将只在定时控制延时后更新 • 相似地 不能非阻塞性赋值到输出或输入输出 因为任务返回时赋值仍无效 合并 要合并 任务就不能包含定时控制 任务使能作为组合逻辑合并 提示 • 程序有太多 always 会使 RTL 代码复杂化 考虑用一个 always 使能几个任务 • 在测试设备中使用任务可以重复应用的激励序列 例如 从存储器读或写数据 见举例 • 被超过一个模块使用的任务可以在一个独立的模块中定义 它只包含任务而且用层次名字引用 举例 下面是一个可被合并的简单 RTL 任务 task Counter; inout [3:0] Count; input Reset; if (Reset) // 同步复位 Count = 0; // RTL 必须使用非阻塞性赋值 (这句比较有意思,很矛盾) else Count = Count + 1; Endtask 周立功比较有个性,把 "Synthesis"翻译成"合并". Verilog HDL A guide to Digital Design and
Synthesis, Samir Palnitkar ,SunSoft Press,
1996 中的例子 D:\My Documents\My Pictures\task.JPG TASK例子来自FPGA Compiler II /FPGA Express Verilog HDL Reference Manual
Version 1999.05, May 1999 Example 5-37 Using the task Statement module task_example (a,b,c); input [7:0] a,b; output [7:0] c; reg [7:0] c; task adder; input [7:0] a,b; output [7:0] adder; reg c; integer i; begin c = 0; for (i = 0; i <= 7; i = i+1) begin adder[i] = a[i] ^ b[i] ^ c; c = (a[i] & b[i]) | (a[i] & c) | (b[i] & c); end end endtask always adder (a,b,c); //c is a reg endmodule Note: Only reg variables can receive output values from a task; wire variables cannot. ieee-std-1364-1995 规范里边关于TASK的描述如下附件的125-128页,没有支持"TASK
中可以用非阻塞赋值语句"的例子 我比较同意TASK 中不可以用非阻塞赋值语句,只是猜测,想确定一下,所以想请高手指点. XIILINX官方FORUMS帖子连接: http://forums.xilinx.com/xlnx/board/message?board.id=SYNTHBD&thread.id=491
[最后修改于2009-04-27 18:09]
附件:请先登陆查看附件!
|
| | |
| |
|
相关主题
春江钓徒
|
|
|
|
|
|
| |
头衔:版主
积分:397 帖子:534
精华:1 等级:工程员 注册时间:2007-09-09 最后登录:2011-08-02 |
|
| |
|
| RE:Verilog:TASK 中是否可以用非阻塞赋值语句(高手请进) |
| |
我的理解中,task中组合逻辑是可综合的,时序不行, 而我学的verilog,没有task,因为它部分不可综合,即使可以综合,也不太容易理解以及很多工具综合支持都不太好, 所以貌似,我从来没有在我工程中用过task。。
|
| | |
| |
|
Walkie
|
|
|
|
|
|
| |
积分:161 帖子:124
精华:1 等级:学徒 注册时间:2007-11-30 最后登录:2011-06-19 |
|
| |
|
| RE:Verilog:TASK 中是否可以用非阻塞赋值语句(高手请进) |
| |
看了ieee-std-1364-1195 规范和超过10本E文的教材,没有发现夏宇闻著作的那种写法.尤其是没有支持"TASK 中可以用非阻塞赋值语句"的例子.
除了上边的参考文献外,还有下边两本: The Verilog® Hardware Description Language, Fifth Edition 91-97页: 3.5 Functions and Tasks In software programming, functions and procedures are used to break
up large pro- grams into more-manageable pieces, and to allow commonly used
functionality to be called from multiple places. In Verilog, modules provide the means
of partitioning a design into more-manageable parts; the use of modules implies
that there are struc- tural boundaries being described. These
boundaries may in fact model the logical structure or the physical packaging boundaries of the design.
Verilog provides func- tions and tasks as constructs analogous to software functions and
procedures that allow for the behavioral description of a module to be broken into
more-manageable parts. module mark1Mult; reg[15:0] signed m [0:8191]; / / signed 8192 x 16 bit
memory reg [12:0] signed pc; // signed 13 bit program counter reg [12:0] signed acc; // signed 13 bit accumulator reg [15:0] ir; // 16 bit instruction
register reg ck; // a clock signal always begin @(posedge ck) ir <= m [pc]; @(posedge ck) case (ir [15:13]) 3'b000 : pc <= m [ir [12:0]]; 3'b001: pc <= pc + m [ir [12:0]]; 3'b010 : acc <= -m [ir [12:0]]; 3'b011: m [ir [12:0]] <= acc; 3'b100, 3'b101: acc <= acc - m [ir [12:0]]; 3'b110 : if (acc < 0) pc <= pc + 1; 3'b111: acc <= acc * m [ir [12:0]]; //multiply endcase pc <= pc + 1; end endmodule Example 3.8 The Mark-1 With a Multiply Instruction Functions and tasks allow often-used behavioral sequences to be
written once and called when needed. They also allow for a
cleaner writing style; instead of long sequences of behavioral statements, the sequences can be broken
into more readable pieces, regardless of whether they are called one or many times.
Finally, they allow for data to be hidden from other parts of the design. Indeed, functions
and tasks play a key role in making a behavioral description more readable and
maintainable. Consider defining opcode 7 of the Mark-1 description in the
previous sections to be a multiply instruction. Early in the behavioral modeling
process, we could use the multiply operator as shown in Example 3.8. This is a perfectly
reasonable behavioral model for early in the design process in that the functionality is
thoroughly described. However, we may want to further detail the multiply algorithm
used in the design. Our first approach will be to use functions and tasks
to describe the multiply algo- rithm. Later, we will contrast this approach to that of
describing the multiply as a sep- arate module. Table 3.1 contrasts the differences between tasks and
functions. 3.5.1 Tasks A Verilog task is similar to a software procedure. It is called
from a calling statement. After execution, control returns to the next statement. It cannot
be used in an expres- sion. Parameters may be passed to it and results
returned. Local variables may be declared within it and their scope will be the task.
Example 3.9 illustrates how mod- ule Mark-1 could be rewritten using a task to describe a
multiply algorithm. A task is defined within a module using the task and endtask
keywords. This task is named multiply and is defined to have one inout (a) and one input
(b). This task is called from within the always statement. The order of task
parameters at the calling site must correspond to the order of definitions within the
task. When multiply is called, acc is copied into task variable a, the value read from
memory is copied into b, and the task proceeds. When the task is ready to return, prod is
loaded into a. On return, a is then copied back into acc and execution continues
after the task call site. Although not illustrated here, a task may include timing and
event control state- ments. Twice within Example 3.9 named begin-end blocks are used,
illustrating that within these blocks, new register identifiers may be defined. The
scope of these names (ir, mcnd, mpy, and prod) is the named begin-end block. The general
form of the task declaration is: task_declaration task [ automatic] task_identifier ; {task_item_declaration} statement_or_null endtask | task [ automatic] task_identifier (task_port_list) ; {block_item_declaration} statement endtask | task_item_declaration block_item_declaration | tf_output_declaration | tf_input_declaration | inout_declaration module mark1Task; reg [15:0] signed m [0:8191];
//signed 8192 x 16 bit memory reg [12:0] signed pc; // signed 13 bit program counter reg [12:0] signed
acc; // signed 13 bit
accumulator reg ck; // a clock signal always begin: executeInstructions reg [15:0] ir; // 16 bit
instruction register @(posedge ck) ir <= m [pc]; @(posedge ck) case (ir [15:13]) // other case expressions as before 3'b111: multiply (acc, m [ir [12:0]]); endcase pc <= pc + 1; end task multiply (inout [12:0] a, input [15:0] b); begin: serialMult reg [5:0] mcnd, mpy; //multiplicand and multiplier reg [12:0] prod; //product mpy = b[5:0]; mcnd = a[5:0]; prod = 0; repeat (6) begin if (mpy[0]) prod = prod + {mcnd, 6'b000000}; prod = prod >> 1; mpy = mpy >> 1; end a = prod; end endtask endmodule Example 3.9 A Task Specification block_item_declaration block_register_declaration reg_declaration parameter_declaration local_parameter_declaration integer_declaration real_declaration time_declaration realtime_declaration event_declaration The input and output tf declarations take the form of: tf_output_declaration output [reg] [signed] [range]
list_or_port_identifiers | output [task_port_type] list_or_port_identifiers The first shows the style of making multiple
definitions in a single statement. The second allows the specification of the
task_port_type (to be time, real, realtime,
or integer). The multiply algorithm uses a shift and add technique. The
low-order sixteen bits of the operands are multiplied producing a 32-bit
result that is returned. The state- ment mpy = b[5:0]; does a part-select on b and loads the low order six bits into
mpy. Six times, the low- order bit of the multiplier (mpy) is checked. If it is one,
then the multiplicand (mcnd) is concatenated (using the “{ , }” operator) with six bits of zeros
on its right and added to the product (prod). The product and multiplier are both shifted
right one place and the loop continues. The general forms of a concatenation are shown below: concatenation { expression {, expression} } multiple_ concatenation { constant_expression concatenation } The first form is that shown in the example.
The second allows for the concatenation in the inner braces to be repeated n times where n is given by the
constant expression. The input, output, and inout names declared in tasks
(and as we will later see, functions) are local variables separate from the
variables named at the calling site. Their scope is the task-endtask block. When a task
is called, the internal variables declared as inputs or inouts receive copies of the values named at
the calling site. The task proceeds executing. When it is done, then all of the variables
declared as inouts or outputs are copied back to the variables listed at the call
site. When copying values to and from the call site, the variables at the call
site are lined up left-to-right with order of the input, output, and inout declarations at the task
definition site. A task may call itself, or be called from tasks that it has
called. Unless the task is declared to be automatic, there is only one set of registers to
hold the task variables. Thus, the registers used after the second call to the task are the
same physical entities as those in the previous call(s). The simulator maintains the
thread of control so that the returns from a task called multiple times are handled
correctly. Further, a task may be called from separate processes (i.e., always and initial
statements) and the task may even be stopped at an event control when the task is enabled
from another process. There is still only one set of variables for the two
invocations of the task to use. How- ever, the simulator does keep track of the control tokens so that
the appropriate return to the calling process is made when the task exits. When a task is declared automatic, the task is re-entrant.
That is, there may be calls to it from several concurrent processes. Indeed, since tasks
may have timing and event controls, several processes may be waiting in
the task. Each call to the task, whether from concurrent processes or from itself, works
with its own copies of the internally declared storage. Upon exit from the task, this storage
is released and thus in unavailable to any other scope. Thus inputs, outputs, and
other internal variables cannot be assigned values using non-blocking assignments, or
traced with statements, for instance. The point is that
non-automatic tasks have static storage allocation. Automatic tasks have dynamic storage allocation
which only exists during a particular instance’s execution. The general form of a task call is: task_enable hierarchical_task_identifier [ (expression {, expression } ) ]; It is useful to comment on the concatenation operation in the
example. The “{ , }” characters are used to express concatenation of values. In
this example, we concate- nate two 6-bit values together to add to the 13-bit prod.
mcnd has 6 binary zeros (expressed here in binary format) concatenated onto its right-hand
side. Notice that in this case, an exact bit width must be specified for
the constant so that mcnd is properly aligned with prod for the add. References: functions 3.5.2; Identifiers B.5, G.10 Design Through Verilog HDL 第九章FUNCTION AND TASK. 等等
[最后修改于2009-04-27 08:39]
|
| | |
| |
|
春江钓徒
|
|
|
|
|
|
| |
积分:161 帖子:124
精华:1 等级:学徒 注册时间:2007-11-30 最后登录:2011-06-19 |
|
| |
|
| RE:Verilog:TASK 中是否可以用非阻塞赋值语句(高手请进) |
| |
ieee-1364-2001第44页说: Use of variables (both reading the value of and writing a value to)
that are defined outside a task declaration but within the enclosing module declaration shall be supported. 第三十八页说: A variable shall not be assigned using a blocking assignment and a
non-blocking assignment in the same module. Only those event expressions used in modeling hardware elements as
shown in section 5 shall be supported. 根据这两条,夏宇闻的代码在模块内申明TASK,然后直接使用TASK外而在模块内定义的变量.由于state变量在TASK外使用了non-blocking
assignment,在TASK里边就不能使用blocking assignment了.
|
| | |
| |
|
春江钓徒
|
|
|
|
|
|
| |
头衔:版主
积分:397 帖子:534
精华:1 等级:工程员 注册时间:2007-09-09 最后登录:2011-08-02 |
|
| |
|
| 回复:Verilog:TASK 中是否可以用非阻塞赋值语句(高手请进) |
| |
|
|
| | |
| |
|
Walkie
|
|
|
|
|
|
| |
积分:161 帖子:124
精华:1 等级:学徒 注册时间:2007-11-30 最后登录:2011-06-19 |
|
| |
|
| 回复:Verilog:TASK 中是否可以用非阻塞赋值语句(高手请进) |
| |
|
|
| | |
| |
|
春江钓徒
|
|
|
|
|
|
| |
积分:161 帖子:124
精华:1 等级:学徒 注册时间:2007-11-30 最后登录:2011-06-19 |
|
| |
|
| RE:Verilog:TASK 中是否可以用非阻塞赋值语句(高手请进) |
| |
如果夏的代码没有问题,那下边的观点就有错了. 非阻塞赋值语句用来描述时序逻辑,阻塞赋值语句用来描述组合逻辑.
|
| | |
| |
|
春江钓徒
|
|
|
|
|
|
| |
积分:161 帖子:124
精华:1 等级:学徒 注册时间:2007-11-30 最后登录:2011-06-19 |
|
| |
|
| RE:Verilog:TASK 中是否可以用非阻塞赋值语句(高手请进) |
| |
没有道理去怀疑IEEE 1364的标准,如果夏的代码没有问题,那下边这两个观点就至少有一个错了: 1,TASK 模块中只有时序逻辑能被综合 2,非阻塞赋值语句用来描述时序逻辑,阻塞赋值语句用来描述组合逻辑.
|
| | |
| |
|
春江钓徒
|
|
|
|
|
|
| |
积分:161 帖子:124
精华:1 等级:学徒 注册时间:2007-11-30 最后登录:2011-06-19 |
|
| |
|
| RE:Verilog:TASK 中是否可以用非阻塞赋值语句(高手请进) |
| |
解决了.我错了.下边的说法不对: 2,非阻塞赋值语句用来描述时序逻辑,阻塞赋值语句用来描述组合逻辑. 找了个帖子,有如下结论: 指导方针和结论(概要):
#1: 当为时序逻辑建模,使用“非阻塞赋值”。 #2: 当为锁存器(latch)建模,使用“非阻塞赋值”。 #3: 当用always块为组合逻辑建模,使用“阻塞赋值” #4: 当在同一个always块里面既为组合逻辑又为时序逻辑建模,使用“非阻塞赋值”。 #5: 不要在同一个always块里面混合使用“阻塞赋值”和“非阻塞赋值”。 #6: 不要在两个或两个以上always块里面对同一个变量进行赋值。 #7: 使用$strobe以显示已被“非阻塞赋值”的值。 #8: 不要使用#0延迟的赋值。 详细如下:
9.0 阻塞赋值 & 简单例子 有许多将Verilog和Verilog综合的书,它们举了很多成功地利用“阻塞赋值”为一些简单的时序电路建模的小例子。例13是一个在大多数Verilog书本里用来为一个触发器(flip-flop)建模的例子(这是简单而有缺陷的阻塞赋值建模,但是它确实可以工作):
module dffb (q, d, clk, rst); output q; input d, clk, rst; reg q; always @(posedge clk) if (rst) q = 1'b0; else q = d; endmodule Example 13 - Simple flawed blocking-assignment D-flipflop model -
but it works!
如果工程师们想把所有的模块(module)都集中到一个always里面描述,“阻塞赋值”可以用来正确地为所需要的逻辑建模、仿真和综合。但是不幸的是这个原因导致了喜欢在其它情况下也使用“阻塞赋值”的习惯,并且更复杂的时序always块将会产生竞争条件------在前面已经详细阐述过。
module dffx (q, d, clk, rst); output q; input d, clk, rst; reg q; always @(posedge clk) if (rst) q <= 1'b0; else q <= d; endmodule Example 14 - Preferred D-flipflop coding style with nonblocking
assignments
应该努力养成使用“非阻塞赋值”为 所有的
时序逻辑建模的习惯------象上面的例14一样------即使是为了对付任何一个简单的模块。
下面考虑一下一个稍微复杂的时序逻辑,一个线性反馈移位寄存器(Linear Feedback
shift-Register)或称之为LFSR。
10.0 为时序反馈建模 (Sequential feedback modeling) 一个LFSR是一种带反馈环路(feedback loop)的时序逻辑。反馈环路(feedback
loop)为工程师们带来了一个难题使得他们试图使用细心组织次序的“阻塞赋值”来为它正确建模,如下面的例子:
module lfsrb1 (q3, clk, pre_n); output q3; input clk, pre_n; reg q3, q2, q1; wire n1; assign n1 = q1 ^ q3; always @(posedge clk or negedge pre_n) if (!pre_n) begin q3 = 1'b1; q2 = 1'b1; q1 = 1'b1; end else begin q3 = q2; q2 = n1; q1 = q3; end endmodule Example 15 - Non-functional LFSR with blocking assignments (外注:综合报告―――> Register equivalent to has been removed Found 1-bit register for signal . Found 1-bit xor2 for signal . Found 1-bit register for signal .) Summary: inferred 2 D-type flip-flop(s). 没有办法通过调整描述次序的方法来正确建模除非引入一个临时的变量( 外注:例如引入“wire n――> module xxxxx (q3, clk, pre_n); output q3; input clk, pre_n; reg q3, q2, q1; wire n1,n2; assign n1 = q1 ^ q3; assign n2 = q3; always @(posedge clk or negedge pre_n) if (!pre_n) begin q3 = 1'b1; q2 = 1'b1; q1 = 1'b1; end else begin q3 = q2; q2 = n1; q1 = n2; end endmodule
这样可以得到正确的综合结果: Found 1-bit register for signal . Found 1-bit xor2 for signal . Found 1-bit register for signal . Found 1-bit register for signal . Summary: inferred 3 D-type flip-flop(s).)。
可以通过把所有赋值弄到一个等式的方式(one-line
equations)来避免使用临时变量,例如下面的例16所示。但是现在编码显得更难于理解尤其当涉及的表达式更大更长时,编写代码和调试都变得比较困难,因此不鼓励使用这种风格。
module lfsrb2 (q3, clk, pre_n); output q3; input clk, pre_n; reg q3, q2, q1; always @(posedge clk or negedge pre_n) if (!pre_n) {q3,q2,q1} = 3'b111; else {q3,q2,q1} = {q2,(q1^q3),q3}; endmodule Example 16 - Functional but cryptic LFSR with blocking assignments
如果把例15和例16的阻塞赋值(blocking assignment)都替换为非阻塞赋值(nonblocking
assignment),如下面例17和18所示,那么所有的仿真都将如我们对一个LFSR所期望的那样。 module lfsrn1 (q3, clk, pre_n); output q3; input clk, pre_n; reg q3, q2, q1; wire n1; assign n1 = q1 ^ q3; always @(posedge clk or negedge pre_n) if (!pre_n) begin q3 <= 1'b1; q2 <= 1'b1; q1 <= 1'b1; end else begin q3 <= q2; q2 <= n1; q1 <= q3; end endmodule Example 17 - Functional LFSR with nonblocking assignments
module lfsrn2 (q3, clk, pre_n); output q3; input clk, pre_n; reg q3, q2, q1; always @(posedge clk or negedge pre_n) if (!pre_n) {q3,q2,q1} <= 3'b111; else {q3,q2,q1} <= {q2,(q1^q3),q3}; endmodule Example 18 - Functional but cryptic LFSR with nonblocking
assignments
根据8.0段例子pipeline和10.0段例子LFSR,我们推荐对所有时序逻辑建模时使用非阻塞赋值(nonblocking
assignment)。相似的分析也将显示出对latch建模时使用非阻塞赋值(nonblocking
assignment)是最安全的。
#1: 当为时序逻辑建模,使用“非阻塞赋值”。 #2: 当为锁存器(latch)建模,使用“非阻塞赋值”。
11.0 组合逻辑―使用阻塞赋值(blocking assignment) 用Verilog可以有很多种方法为组合逻辑建模,但是当使用always块来为组合逻辑建模时,应该使用阻塞赋值(blocking
assignment)。
如果在某个always块里面只有一个赋值(表达),那么使用阻塞或者非阻塞赋值都可以正确工作。但是如果您对养成好的编码习惯有兴趣的话,还是要“总是用阻塞赋值对组合逻辑建模”。
一些设计师建议非阻塞赋值不应该只为编写时序逻辑,它也可以用来编写组合逻辑。当然对于简单的组合逻辑always块这是可以的,但是对于在一个always块里面含有多个赋值陈述,例如例19含有and-or的陈述,使用了不含延迟(delay)的非阻塞赋值会造成仿真不正确,或者要使仿真正确您需要另外的添加敏感事件列表(sensitivity
list entries),和“多登入路径”(multiple passes)来贯穿always
块以使得仿真正确。接下来的问题是从仿真需要多长时间来看,这是低效率的(外注:即降低仿真的performance)。
例19的y输出建立在3个依次执行的陈述上(外注:tmp1 <= a & b; tmp2 <= c & d;
y <= tmp1 |
tmp2;)。由于非阻塞赋值的LHS变量值更新是在对RHS表达式估值之后,所以tmp1和tmp2的值仍然是该always块上一个登入口的值而不是在这一个仿真时间步(simulation
time step)结束时被更新的值。因此y的值将受旧的tmp1和tmp2影响,而不是这次扫描过的always块内被更新的值。
module ao4 (y, a, b, c, d); output y; input a, b, c, d; reg y, tmp1, tmp2; always @(a or b or c or d) begin tmp1 <= a & b; tmp2 <= c & d; y <= tmp1 | tmp2; end endmodule Example 19 - Bad combinational logic coding style using nonblocking
assignments
例20与例19是一样的,不同之处在于tmp1和tmp2被添加到事件列表中去了。如第7段(section
7.0)中所述,在“非阻塞赋值更新事件队列”中当非阻塞赋值更新LHS变量时,always块将会“自触发”并使用最新的tmp1和tmp2来更新y输出。现在y输出值正确了因为增加使用了两条“登入路径”(two
passes)贯穿整个always块。使用更多的“登入路径”来贯穿always块等于降低仿真器的性能,因此如果可以有合理的一些代码变化可以取代这种用法的话,就尽量避免这种用法。
module ao5 (y, a, b, c, d); output y; input a, b, c, d; reg y, tmp1, tmp2; always @(a or b or c or d or tmp1 or tmp2) begin tmp1 <= a & b; tmp2 <= c & d; y <= tmp1 | tmp2; end endmodule Example 20 - Inefficient multi-pass combinational logic coding
style with nonblocking assignments
0/0发展一个好的习惯可以避免使用“多登入路径”(multiple passes)贯穿always块,即使用阻塞赋值为组合逻辑建模。
module ao2 (y, a, b, c, d); output y; input a, b, c, d; reg y, tmp1, tmp2; always @(a or b or c or d) begin tmp1 = a & b; tmp2 = c & d; y = tmp1 | tmp2; end endmodule Example 21 - Efficient combinational logic coding style using
blocking assignments
例21与例19一样,不同之处只在于用阻塞赋值替代了非阻塞赋值。这保证了在一个“登入路径”贯穿always后y输出的正确(guarantee
that the y-output assumes the correct value after only one pass
through the always block?)。因此有下面的编码方针:
#3: 当用always块为组合逻辑建模,使用“阻塞赋值”
12.0 时序-组合混合逻辑建模:使用非阻塞赋值
很多时候为了方便我们把时序和一些简单的组合逻辑放在一起。当我们把时序和组合编码放在一个always块的时候,像编写时序逻辑一样使用非阻塞赋值为这种混合逻辑建模,如下面的例22:
module nbex2 (q, a, b, clk, rst_n); output q; input clk, rst_n; input a, b; reg q; always @(posedge clk or negedge rst_n) if (!rst_n) q <= 1'b0; else q <= a ^ b; endmodule Example 22 - Combinational and sequential logic in a single always
block
与例22相同的逻辑也可以使用两个分立的always块------一个是纯粹的时序逻辑(使用非阻塞赋值),另一个是纯粹的组合逻辑(使用阻塞赋值)------建模,例如下面的例23:
module nbex1 (q, a, b, clk, rst_n); output q; input clk, rst_n; input a, b; reg q, y; always @(a or b) y = a ^ b; always @(posedge clk or negedge rst_n) if (!rst_n) q <= 1'b0; else q <= y; endmodule Example 23 - Combinational and sequential logic separated into two
always blocks
#4: 当在同一个always块里面既为组合逻辑又为时序逻辑建模,使用“非阻塞赋值”。
13.0 其它混合“阻塞”与“非阻塞”赋值建模方针
Verilog允许在一个always块里面自由混合“阻塞”与“非阻塞”赋值。一般情况下在同一个always块里面混合“阻塞”与“非阻塞”赋值是“衰婆”风格(poor
coding style,呵呵,借用电影《钢琴教师》里的翻译“衰婆”,刚好poor发音与“婆”有些相近。不过可能引 起大家一阵反胃,女士们一阵痛恨------向导演,可不要向我!),尽管Verilog允许这样做。
下面的例24的仿真和综合都将是正确的,因为“阻塞”与“非阻塞”赋值不是针对同一个变量来的。尽管这可以“正常工作”,但是作者不推荐这种风格。
module ba_nba2 (q, a, b, clk, rst_n); output q; input a, b, rst_n; input clk; reg q; always @(posedge clk or negedge rst_n) begin: ff reg tmp; if (!rst_n) q <= 1'b0; else begin tmp = a & b; q <= tmp; end end endmodule Example 24 - Blocking and nonblocking assignment in the same always
block - generally a bad idea!
下面的例25在大多数情况下仿真是正确的,但是新思(Synopsys)工具会报告语法错误因为针对同一个既进行了“阻塞赋值”又进行了“非阻塞赋值”。这样的编码必须进行修改才可以综合。(Error:Cannot
mix blocking and non blocking assignments on signal .)
module ba_nba6 (q, a, b, clk, rst_n); output q; input a, b, rst_n; input clk; reg q, tmp; always @(posedge clk or negedge rst_n) if (!rst_n) q = 1'b0; // blocking assignment to "q" else begin tmp = a & b; q <= tmp; // nonblocking assignment to "q" end endmodule Example 25 - Synthesis syntax error - blocking and nonblocking
assignment to the same variable
为了养成好的编写习惯,作者推荐始终坚持: #5: 不要在同一个always块里面混合使用“阻塞赋值”和“非阻塞赋值”。
14.0 对同一变量多处赋值(Multiple assignments to the same variable)
对同一变量在二个以上(包括二个)always块里面进行赋值就是一种Verilog竞争生成环境------即使使用非阻塞赋值。
在下面例26里,二个always块对q进行赋值,同时使用非阻塞赋值。因为这些always块可以以同一次序安排执行,仿真输出呈竞争条件。
module badcode1 (q, d1, d2, clk, rst_n); output q; input d1, d2, clk, rst_n; reg q; always @(posedge clk or negedge rst_n) if (!rst_n) q <= 1'b0; else q <= d1; always @(posedge clk or negedge rst_n) if (!rst_n) q <= 1'b0; else q <= d2; endmodule Example 26 - Race condition coding style using nonblocking
assignments 当新思(Synopsys)工具读这段编码时,会产生如下警告: Warning: In design 'badcode1', there is 1 multiple-driver net with
unknown wired-logic type. 当忽略这个警告并编译上面的例子时,推断结果是二个触发器的输出将作为一个and门的输入。在这个例子里综合前(pre-synthesis)仿真结果与综合后(post-synthesis)仿真结果不匹配。
#6: 不要在两个或两个以上always块里面对同一个变量进行赋值。
15.0 常见的“非阻塞”神话(外注:指与事实不符或严格说不正确的见解、想法)
15.1 非阻塞赋值和$display 神话:“对‘非阻塞赋值’使用$display命令不起作用。” 事实:非阻塞赋值在$display命令之后才被更新赋值。 module display_cmds; reg a; initial $monitor("\$monitor: a = %b", a); initial begin $strobe ("\$strobe : a = %b", a); a = 0; a <= 1; $display ("\$display: a = %b", a); #1 $finish; end endmodule 下面的仿真输出结果显示出$display命令在“激活事件列”(the active event
queue)里被执行的情形:在“非阻塞赋值更新”这个事件被执行之前。 $display: a = 0 $monitor: a = 1 $strobe : a = 1
15.2 赋“零延迟” 神话:“‘零延迟’#0 使得该赋值事件在时间步结束时发生” 事实:零延迟’#0 使得赋值事件处于“非激活事件列” module nb_schedule1; reg a, b; initial begin a = 0; b = 1; a <= b; b <= a; $monitor ("%0dns: \$monitor: a=%b b=%b", $stime, a, b); $display ("%0dns: \$display: a=%b b=%b", $stime, a, b); $strobe ("%0dns: \$strobe : a=%b b=%b\n", $stime, a, b); #0 $display ("%0dns: #0 : a=%b b=%b", $stime, a, b); #1 $monitor ("%0dns: \$monitor: a=%b b=%b", $stime, a, b); $display ("%0dns: \$display: a=%b b=%b", $stime, a, b); $strobe ("%0dns: \$strobe : a=%b b=%b\n", $stime, a, b); $display ("%0dns: #0 : a=%b b=%b", $stime, a, b); #1 $finish; end endmodule 下面的仿真输出结果显示出$display命令在“非激活事件列”(the inactive event
queue)里被执行的情形:在“非阻塞赋值更新”这个事件被执行之前。
0ns: $display: a="0" b="1" 0ns: #0 : a="0" b="1" 0ns: $monitor: a="1" b="0" 0ns: $strobe : a="1" b="0" 1ns: $display: a="1" b="0" 1ns: #0 : a="1" b="0" 1ns: $monitor: a="1" b="0" 1ns: $strobe : a="1" b="0"
#7: 使用$strobe以显示已被“非阻塞赋值”的值。
15.3 对同一变量多处进行“非阻塞赋值” 神话:“‘在同一个always块里对同一变量多处进行非阻塞赋值’ 没有被明确定义。” 事实:Verilog标准定义了以上操作。最后一个非阻塞赋值操作将赢得最后结果。引用IEEE1364-1995 Verilog
Standard [2], pg. 47, section – Determinism 如下:
“非阻塞赋值由它们被陈述的次序决定被执行的情况,考虑下面的例子: initial begin a <= 0; a <= 1; end When this block is executed, there will be two events added to the
nonblocking assign update queue. The previous rule requires that
they be entered on the queue in source order; this rule requires
that they be taken from the queue and performed in source order as
well. Hence, at the end of time-step 1, the variable a will be
assigned 0 and then 1." 换句话说:“最后一个非阻塞赋值操作将赢得优先权。”
指导方针和结论(概要):
#1: 当为时序逻辑建模,使用“非阻塞赋值”。 #2: 当为锁存器(latch)建模,使用“非阻塞赋值”。 #3: 当用always块为组合逻辑建模,使用“阻塞赋值” #4: 当在同一个always块里面既为组合逻辑又为时序逻辑建模,使用“非阻塞赋值”。 #5: 不要在同一个always块里面混合使用“阻塞赋值”和“非阻塞赋值”。 #6: 不要在两个或两个以上always块里面对同一个变量进行赋值。 #7: 使用$strobe以显示已被“非阻塞赋值”的值。 #8: 不要使用#0延迟的赋值。
谨遵这些方针可以帮助Verilog设计者减少所遇到的90-100%的Verilog竞争。
16.0 最后注意:“nonblocking”的拼写 “nonblocking”经常被拼错为“non-blocking”。作者认为这是“微软化”的拼写方式。工程师们在“non”和“blocking”之间插入一个“-”是为了满足微软的拼写检查不致报错。在IEEE
1364-1995里正确的拼写应该是:nonblocking。(外注:呵呵,你已经看到了,在这 个文档里面所有的nonblocking都被下划了红色波浪线。)
参考文献: [1] IEEE P1364.1 Draft Standard For Verilog Register Transfer Level
Synthesis [2] IEEE Standard Hardware Description Language Based on the
Verilog Hardware Description Language, IEEE Computer Society, IEEE Std 1364-1995 [3] Clifford Cummings, "Correct Methods For Adding Delays To
Verilog Behavioral Models," International HDL Conference 1999 Proceedings, pp. 23-29,
April 1999.
(外注:以上参考文献原文的。译文中第4页“事件轴”引自中科院计算所张亮编著、人民邮电出版社2000年10月出版的《数字电路设计与Verilog
HDL》;一些综合报告来自Xilinx公司的综合软件XST。)
作者和联系方式 (外注:偷懒省略。总之“Mr. Cummings, a member of the IEEE 1364 Verilog
Standards Group (VSG) since
,虽然文章所涉很使我们感到“离”众书“叛道”,但其实是足够可信的【并且使我疑团大释,所以拿出来让使用Verilog的家伙一齐“开怀”,译文中太多错误和缺点请大家不吝指正!】。 http://www.hqwic.com/bbs/topic.aspx?topicid=12980
|
| | |
| |
|
春江钓徒
|
|
|
|
|
|
| |
积分:161 帖子:124
精华:1 等级:学徒 注册时间:2007-11-30 最后登录:2011-06-19 |
|
| |
|
| 回复:Verilog:TASK 中是否可以用非阻塞赋值语句(高手请进) |
| |
对于以下著作的说法 The Verilog® Golden Reference Guide 95页: Similarly, a non-blocking assignment to an output or inout may not
work, because the assignment may not have taken effect when the task returns. 我的理解是这样的,"may not" 是"可能不能"的意思, 当non-blocking assignment
以描述组合逻辑的时候就可以WORK了. 大家的意见呢?
|
| | |
| |
|
春江钓徒
|
|
|
|
|
|
| |
积分:161 帖子:124
精华:1 等级:学徒 注册时间:2007-11-30 最后登录:2011-06-19 |
|
| |
|
| 回复:Verilog:TASK 中是否可以用非阻塞赋值语句(高手请进) |
| |
在经典的文章Nonblocking Assignments in Verilog Synthesis, Coding Styles That Kill! Clifford E. Cummings Sunburst Design, Inc. (SNUG-2000 San Jose, CA Voted Best Paper 1st Place ) 的第2页中发现了这么一段话: 1.0 Introduction Two well known Verilog coding guidelines for modeling logic are: • Guideline: Use blocking assignments in always blocks that
are written to generate combinational logic [1]. • Guideline: Use nonblocking assignments in always blocks
that are written to generate sequential logic [1]. But why? In general, the answer is simulation related. Ignoring the above guidelines can still infer the correct synthesized logic, but the pre-synthesis
simulation might not match the behavior of the synthesized circuit. To understand the reasons behind the above guidelines, one needs to
have a full understanding of the functionality and scheduling of Verilog blocking and
nonblocking assignments. This paper will detail the functionality and scheduling of blocking and
nonblocking assignments. SUNBURST公司也认可"非阻塞赋值语句用来描述时序逻辑,阻塞赋值语句用来描述组合逻辑"这个是个指导思想,但是这个仅仅是指导思想而非语法. 所以夏宇闻的写法虽然可能可以用,但是将可能面临红色文字所描述的问题.
[最后修改于2009-04-28 16:33]
|
| | |
| |
|
春江钓徒
|
|
|
|
|
|
| |
积分:0 帖子:5
精华:0 等级:学徒 注册时间:2009-03-19 最后登录:2009-04-29 |
|
| |
|
| 回复:Verilog:TASK 中是否可以用非阻塞赋值语句(高手请进) |
| |
引用第 8 楼 hxxfff于2009-04-27 14:36:43发表: 没有道理去怀疑IEEE 1364的标准,如果夏的代码没有问题,那下边这两个观点就至少有一个错了: 1,TASK 模块中只有时序逻辑能被综合 2,非阻塞赋值语句用来描述时序逻辑,阻塞赋值语句用来描述组合逻辑. 1.根据task的调用方式,综合后,执行任务的电路既可以是组合逻辑,也可以是时序逻辑。 2.非阻塞赋值常用于时序逻辑电路设计;阻塞赋值可用于时序逻辑电路设计,但常用于组合逻辑电路设计。
|
| | |
| |
|
|
|
|
|
|
|
| |
积分:27 帖子:6
精华:0 等级:学徒 注册时间:2008-10-25 最后登录:2009-11-27 |
|
| |
|
| RE:Verilog:TASK 中是否可以用非阻塞赋值语句(高手请进) |
| |
刚开始学verilog的时候,师兄告诉我,在实际工程中,不推荐实用task。在testbench中用还不错吧
|
| | |
| |
|
|
|
|
|
|
|
| |
积分:161 帖子:124
精华:1 等级:学徒 注册时间:2007-11-30 最后登录:2011-06-19 |
|
| |
|
| 回复:Verilog:TASK 中是否可以用非阻塞赋值语句(高手请进) |
| |
回楼上:看来夏闻宇不太听你师兄的话啊
|
| | |
| |
|
春江钓徒
|
|
|
|
|
|
| |
积分:2 帖子:4
精华:0 等级:学徒 注册时间:2009-11-12 最后登录:2010-02-15 |
|
| |
|
| New found! |
| |
Dofus est un [url=http://www.bawwgt.com/fr]dofus kamas[/url], le
joueur incarne un ou plusieurs personnages. On y retrouve une
multitude [url=http://www.bawwgt.com/fr]acheter des dofus
kamas[/url] et d'¨¦quipements en tout genre, une vingtaine de
m¨¦tiers diff¨¦rents et plus d'une centaine de monstres r¨¦partis
en diff¨¦rentes zones sur les 10 000
[url=http://www.bawwgt.com/fr]dofus kamas pas cher[/url] (portions
de carte, sur lesquelles l'on se d¨¦place d'ailleurs comme sur une
carte) formant l'univers de [url=http://www.bawwgt.com/fr]achat
dofus kamas[/url], dont 99% ne sont accessibles qu'aux abonn¨¦s.
|
| | |
| |
|
|
|
|