首页>>论坛
|
我要发帖  | 我要投票  | 我要回复  | 收藏
1

Verilog:TASK 中是否可以用非阻塞赋值语句(高手请进)

版主: 暂无
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]
附件:请先登陆查看附件!
 
相关主题
春江钓徒
回复 链接 收藏
 
RE:Verilog:TASK 中是否可以用非阻塞赋值语句(高手请进)
 
我的理解中,task中组合逻辑是可综合的,时序不行,
而我学的verilog,没有task,因为它部分不可综合,即使可以综合,也不太容易理解以及很多工具综合支持都不太好,
所以貌似,我从来没有在我工程中用过task。。
 
Walkie
回复 链接 收藏
 
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]
 
春江钓徒
回复 链接 收藏
 
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了.
 
春江钓徒
回复 链接 收藏
 
回复:Verilog:TASK 中是否可以用非阻塞赋值语句(高手请进)
 

然后你的结论是?

 
Walkie
回复 链接 收藏
 
回复:Verilog:TASK 中是否可以用非阻塞赋值语句(高手请进)
 

have no ideal

 
春江钓徒
回复 链接 收藏
 
RE:Verilog:TASK 中是否可以用非阻塞赋值语句(高手请进)
 

如果夏的代码没有问题,那下边的观点就有错了.
非阻塞赋值语句用来描述时序逻辑,阻塞赋值语句用来描述组合逻辑.
 
春江钓徒
回复 链接 收藏
 
RE:Verilog:TASK 中是否可以用非阻塞赋值语句(高手请进)
 
没有道理去怀疑IEEE 1364的标准,如果夏的代码没有问题,那下边这两个观点就至少有一个错了:
1,TASK 模块中只有时序逻辑能被综合
2,非阻塞赋值语句用来描述时序逻辑,阻塞赋值语句用来描述组合逻辑.
 
春江钓徒
回复 链接 收藏
 
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
 
春江钓徒
回复 链接 收藏
 
回复: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了.

大家的意见呢?

 
春江钓徒
回复 链接 收藏
 
回复: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]
 
春江钓徒
回复 链接 收藏
 
回复:Verilog:TASK 中是否可以用非阻塞赋值语句(高手请进)
 
引用第 8 楼 hxxfff于2009-04-27 14:36:43发表:
没有道理去怀疑IEEE 1364的标准,如果夏的代码没有问题,那下边这两个观点就至少有一个错了:
1,TASK 模块中只有时序逻辑能被综合
2,非阻塞赋值语句用来描述时序逻辑,阻塞赋值语句用来描述组合逻辑.
 

 

 

1.根据task的调用方式,综合后,执行任务的电路既可以是组合逻辑,也可以是时序逻辑。

2.非阻塞赋值常用于时序逻辑电路设计;阻塞赋值可用于时序逻辑电路设计,但常用于组合逻辑电路设计。

 

 
回复 链接 收藏
 
RE:Verilog:TASK 中是否可以用非阻塞赋值语句(高手请进)
 
刚开始学verilog的时候,师兄告诉我,在实际工程中,不推荐实用task。在testbench中用还不错吧
 
回复 链接 收藏
 
回复:Verilog:TASK 中是否可以用非阻塞赋值语句(高手请进)
 

回楼上:看来夏闻宇不太听你师兄的话啊

 
春江钓徒
回复 链接 收藏
 
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.
 
回复 链接 收藏
 
我要发帖  | 我要投票  | 我要回复  | 收藏
1