第十章.设计练习进阶 目的:1.明确掌握阻塞赋值与非阻塞赋值的概念和区别 2.了解阻塞赋值的使用情况 阻塞赋值与非阻塞赋值,在教材中我们已经了解了它们之间在语法上的区别以及综合后 所得到的电路结构上的区别。在 always块中,阻塞赋值可以理解为赋值语句是顺序执行的, 而非阻塞赋值可以理解为赋值语句是并发执行的。实际的时序逻辑设计中,一般的情况下非 阻塞赋值语句被更多地使用,有时为了在同一周期实现相互关联的操作,也使用了阻塞赋值 语句。(注意:在实现组合逻辑的 assign结构中,无一例外地都必须采用阻塞赋值语句。 下例通过分别采用阻塞赋值语句和非阻塞赋值语句的两个看上去非常相似的两个模块 blocking.v和non_ blocking.v来阐明两者之间的区别。 模块源代码 blockingv module blocking(clk, a, b, c) output [3: 0] b,c: input [3: 0] a: clk reg [3: 0] b,c always @(posedge clk) b Sdisplay( blocking: a =%d, b= %d, c=%d",a, b, c) endmodule non blocking v module non blocking(clk, a, b,c) output [3: 0]b,c: [3:0] t clk reg [3: 0]b, c l ways @(posedge clk begin b <e Sdisplay non blocking: a =%d, b=%d, c=%d", a, b, c)
第十章.设计练习进阶 目的:1.明确掌握阻塞赋值与非阻塞赋值的概念和区别; 2.了解阻塞赋值的使用情况。 阻塞赋值与非阻塞赋值,在教材中我们已经了解了它们之间在语法上的区别以及综合后 所得到的电路结构上的区别。在 always 块中,阻塞赋值可以理解为赋值语句是顺序执行的, 而非阻塞赋值可以理解为赋值语句是并发执行的。实际的时序逻辑设计中,一般的情况下非 阻塞赋值语句被更多地使用,有时为了在同一周期实现相互关联的操作,也使用了阻塞赋值 语句。(注意:在实现组合逻辑的 assign 结构中,无一例外地都必须采用阻塞赋值语句。 下例通过分别采用阻塞赋值语句和非阻塞赋值语句的两个看上去非常相似的两个模块 blocking.v 和 non_blocking.v 来阐明两者之间的区别。 模块源代码: // ------------- blocking.v --------------- module blocking(clk,a,b,c); output [3:0] b,c; input [3:0] a; input clk; reg [3:0] b,c; always @(posedge clk) begin b = a; c = b; $display("Blocking: a = %d, b = %d, c = %d.",a,b,c); end endmodule //------------- non_blocking.v ------------------- module non_blocking(clk,a,b,c); output [3:0] b,c; input [3:0] a; input clk; reg [3:0] b,c; always @(posedge clk) begin b <= a; c <= b; $display("Non_Blocking: a = %d, b = %d, c = %d.",a,b,c); end 271
第十章.设计练习进阶 endmodule 测试模块源代码: compare lop.V timescale Ins/100ps include ./blockingv include ./non blockingv module compare Top wIre [3:0]bl,cl,b2,c2; reg initial cIk =0 forever #50 clk = clk nitia begin Display 4’h7 Display( #100 4 hf Display c Display ) 100 Display non blocking non blocking(clk, a, b2, c2) blocking(clk, a, bl, c1) endmodule 272
第十章.设计练习进阶 endmodule 测试模块源代码: //------------- compareTop.v ----------------------------- `timescale 1ns/100ps `include "./blocking.v" `include "./non_blocking.v" module compareTop; wire [3:0] b1,c1,b2,c2; reg [3:0] a; reg clk; initial begin clk = 0; forever #50 clk = ~clk; end initial begin a = 4'h3; $display("____________________________"); # 100 a = 4'h7; $display("____________________________"); # 100 a = 4'hf; $display("____________________________"); # 100 a = 4'ha; $display("____________________________"); # 100 a = 4'h2; $display("____________________________"); # 100 $display("____________________________"); $stop; end non_blocking non_blocking(clk,a,b2,c2); blocking blocking(clk,a,b1,c1); endmodule 272
第十章.设计练习进阶 仿真波形(部分) 1111 /compare Top/c1 /compare Top/b2 0011 0111 Y1111 compare Top/c2 0011 0111 11 思考:在 blocking模块中按如下写法,仿真与综合的结果会有什么样的变化?作出仿真 波形,分析综合结果 always @(posedge clk lways @(posedge clk)b=a always @(posedge clk)c= 练习五.用 always块实现较复杂的组合逻辑电路 目的:1.掌握用 always实现组合逻辑电路的方法; 2.了解 assign与 always两种组合逻辑电路实现方法之间的区别。 仅使用 assign结构来实现组合逻辑电路,在设计中会发现很多地方会显得冗长且效率低 下。而适当地采用 always来设计组合逻辑,往往会更具实效。已进行的范例和练习中,我们 仅在实现时序逻辑电路时使用 always块。从现在开始,我们对它的看法要稍稍改变。 下面是一个简单的指令译码电路的设计示例。该电路通过对指令的判断,对输入数据执 行相应的操作,包括加、减、与、或和求反,并且无论是指令作用的数据还是指令本身发生 变化,结果都要作出及时的反应。显然,这是一个较为复杂的组合逻辑电路,如果采用 assign 语句,表达起来非常复杂。示例中使用了电平敏感的 always块,所谓电平敏感的触发条件是 指在@后的括号内电平列表中的任何一个电平发生变化,(与时序逻辑不同,它在@后的括号内 没有沿敏感关键词,如 posedge或 negedge)就能触发 always块的动作,并且运用了case 结构来进行分支判断,不但设计思想得到直观的体现,而且代码看起来非常整齐、便于理解。 define plus 3 do define minus3’d1 define band 3 d2 ne bor ne negate 3 d4 module alu(out, opcode, a, b)
第十章.设计练习进阶 仿真波形(部分): 思考:在 blocking 模块中按如下写法,仿真与综合的结果会有什么样的变化?作出仿真 波形,分析综合结果。 1. always @(posedge clk) begin c = b; b = a; end 2. always @(posedge clk) b=a; always @(posedge clk) c=b; 练习五. 用 always 块实现较复杂的组合逻辑电路 目的: 1.掌握用 always 实现组合逻辑电路的方法; 2.了解 assign 与 always 两种组合逻辑电路实现方法之间的区别。 仅使用 assign 结构来实现组合逻辑电路,在设计中会发现很多地方会显得冗长且效率低 下。而适当地采用 always 来设计组合逻辑,往往会更具实效。已进行的范例和练习中,我们 仅在实现时序逻辑电路时使用 always 块。从现在开始,我们对它的看法要稍稍改变。 下面是一个简单的指令译码电路的设计示例。该电路通过对指令的判断,对输入数据执 行相应的操作,包括加、减、与、或和求反,并且无论是指令作用的数据还是指令本身发生 变化,结果都要作出及时的反应。显然,这是一个较为复杂的组合逻辑电路,如果采用 assign 语句,表达起来非常复杂。示例中使用了电平敏感的 always 块,所谓电平敏感的触发条件是 指在@后的括号内电平列表中的任何一个电平发生变化,(与时序逻辑不同,它在@后的括号内 没有沿敏感关键词,如 posedge 或 negedge)就能触发 always 块的动作,并且运用了 case 结构来进行分支判断,不但设计思想得到直观的体现,而且代码看起来非常整齐、便于理解。 //--------------- alu.v -------------------------- `define plus 3'd0 `define minus 3'd1 `define band 3'd2 `define bor 3'd3 `define unegate 3'd4 module alu(out,opcode,a,b); 273
第十章.设计练习进阶 output[7: 0]out reg[7:0] input[7: 0] a, b //操作数。 always@( opcode or a or b)//电平敏感的 always块 case(opcode) a+b;//加操作 minus:out=a-b;//减操作。 band:out=a&b;//求与。 bor:out=a|b:/求或。 negate:out=a;//求反。 default:out=8'hx://未收到指令时,输出任意态。 endcase 同一组合逻辑电路分别用 always块和连续赋值语句 assign描述时,代码的形式大相径 庭,但是在 always中适当运用 default(在case结构中)和else(在if.else结构中), 通常可以综合为纯组合逻辑,尽管被赋值的变量一定要定义为reg型。不过,如果不使用 default或else对缺省项进行说明,则易生成意想不到的锁存器,这一点一定要加以注意 指令译码器的测试模块源代码: timescale Ins/Ins include "./alu module alutest e[7:0] 7:0]a,b parameter times=5 initial egin a= Random)%256; //Give a radom number blongs to [o, 255 b=Random //G opcode3 ho a=Random/%256: //Give a radom number. rando //Give a radom number 274
第十章.设计练习进阶 output[7:0] out; reg[7:0] out; input[2:0] opcode; input[7:0] a,b; //操作数。 always@(opcode or a or b) //电平敏感的 always 块 begin case(opcode) `plus: out = a+b; //加操作。 `minus: out = a-b; //减操作。 `band: out = a&b; //求与。 `bor: out = a|b; //求或。 `unegate: out=~a; //求反。 default: out=8'hx;//未收到指令时,输出任意态。 endcase end endmodule 同一组合逻辑电路分别用 always 块和连续赋值语句 assign 描述时,代码的形式大相径 庭,但是在 always 中适当运用 default(在 case 结构中)和 else(在 if…else 结构中), 通常可以综合为纯组合逻辑,尽管被赋值的变量一定要定义为 reg 型。不过,如果不使用 default 或 else 对缺省项进行说明,则易生成意想不到的锁存器,这一点一定要加以注意。 指令译码器的测试模块源代码: //------------- alu_Top.v ----------------- `timescale 1ns/1ns `include "./alu.v" module alutest; wire[7:0] out; reg[7:0] a,b; reg[2:0] opcode; parameter times=5; initial begin a={$random}%256; //Give a radom number blongs to [0,255] . b={$random}%256; //Give a radom number blongs to [0,255]. opcode=3'h0; repeat(times) begin #100 a={$random}%256; //Give a radom number. b={$random}%256; //Give a radom number. opcode=opcode+1; end 274