当前位置: 首页 > news >正文

简单网站开发帝国cms商城

简单网站开发,帝国cms商城,陕西城乡住房建设部网站,工程公司简介模板3.1 Verilog 连续赋值 关键词#xff1a;assign#xff0c; 全加器 连续赋值语句是 Verilog 数据流建模的基本语句#xff0c;用于对 wire 型变量进行赋值。#xff1a; assign LHS_target RHS_expression #xff1b;LHS#xff08;left hand side#xff09;…3.1 Verilog 连续赋值 关键词assign 全加器 连续赋值语句是 Verilog 数据流建模的基本语句用于对 wire 型变量进行赋值。 assign LHS_target RHS_expression LHSleft hand side 指赋值操作的左侧RHSright hand side指赋值操作的右侧。 assign 为关键词任何已经声明 wire 变量的连续赋值语句都是以 assign 开头例如 wire Cout, A, B ; assign Cout A B ; //实现计算A与B的功能LHS_target 必须是一个标量或者线型向量而不能是寄存器类型。 RHS_expression 的类型没有要求可以是标量或线型或存器向量也可以是函数调用。 只要 RHS_expression 表达式的操作数有事件发生值的变化时RHS_expression 就会立刻重新计算同时赋值给 LHS_target。 Verilog 还提供了另一种对 wire 型赋值的简单方法即在 wire 型变量声明的时候同时对其赋值。 wire 型变量只能被赋值一次因此该种连续赋值方式也只能有一次。例如下面赋值方式和上面的赋值例子的赋值方式效果都是一致的。 全加器 下面采用数据流描述方式来设计一个 1bit 全加器。 设 AiBiCi 分别为被加数、加数和相邻低位的进位数So, Co 分别为本位和与向相邻高位的进位数。 真值表如下 全加器的表达式为 So Ai ⊕ Bi ⊕ Ci ; Co AiBi Ci(AiBi)rtl 代码full_adder1.v如下 module full_adder1(input Ai, Bi, Ci,output So, Co);assign So Ai ^ Bi ^ Ci ;assign Co (Ai Bi) | (Ci (Ai | Bi)); endmodule当然更为贴近加法器的代码描述可以为 module full_adder1(input Ai, Bi, Cioutput So, Co);assign {Co, So} Ai Bi Ci ; endmodule testbenchtest.sv参考如下 timescale 1ns/1nsmodule test ;reg Ai, Bi, Ci ;wire So, Co ;initial begin{Ai, Bi, Ci} 3b0;forever begin#10 ;{Ai, Bi, Ci} {Ai, Bi, Ci} 1b1;endendfull_adder1 u_adder(.Ai (Ai),.Bi (Bi),.Ci (Ci),.So (So),.Co (Co));initial beginforever begin#100;//$display(---gyc---%d, $time);if ($time 1000) begin$finish ;endendendendmodule 代码解释 这段代码是用Verilog HDL硬件描述语言编写的用于描述一个数字电路模块。Verilog常用于模拟和描述电子系统尤其是数字电路和系统。以下是对代码的逐行解释 时序和模块声明 timescale 1ns/1ns module test ;timescale 1ns/1ns定义时间单位和时间精度都为1纳秒。 module test ;定义一个名为test的模块。 变量声明 reg Ai, Bi, Ci ; wire So, Co ;reg Ai, Bi, Ci ;声明三个寄存器变量Ai、Bi和Ci。 wire So, Co ;声明两个线网变量So和Co。 初始化块1 initial begin {Ai, Bi, Ci} 3b0; forever begin #10 ; {Ai, Bi, Ci} {Ai, Bi, Ci} 1b1; end endinitial begin … end定义了一个初始化块该块中的代码在仿真开始时执行一次。 {Ai, Bi, Ci} 3’b0;将Ai、Bi和Ci的初始值设为二进制0。 forever begin … end一个无限循环。 #10 ;等待10个时间单位即10纳秒。 {Ai, Bi, Ci} {Ai, Bi, Ci} 1’b1;将Ai、Bi和Ci的值加1二进制进位。 实例化全加器 full_adder1 u_adder( .Ai (Ai), .Bi (Bi), .Ci (Ci), .So (So), .Co (Co));实例化一个名为full_adder1的全加器模块并命名为u_adder。 .Ai (Ai), .Bi (Bi), .Ci (Ci), .So (So), .Co (Co)将模块的端口与前面声明的变量连接起来。 初始化块2 initial begin forever begin #100; // $display(---gyc---%d, $time); if ($time 1000) begin $finish ; end end end这是第二个初始化块。 #100;等待100个时间单位即100纳秒。 if ( t i m e 1000 ) b e g i n . . . e n d 如果仿真时间达到或超过 1000 纳秒则执行 time 1000) begin ... end如果仿真时间达到或超过1000纳秒则执行 time1000)begin...end如果仿真时间达到或超过1000纳秒则执行finish命令结束仿真。 模块结束 endmoduleendmodule表示模块定义结束。 这个模块主要描述了一个全加器full_adder1的使用。通过两个初始化块一个用于改变全加器的输入另一个用于控制仿真的结束时间。 然而注意这里没有显示full_adder1模块的具体实现它应该在其他地方定义。 3.2 Verilog 时延 时延 惯性时延 连续赋值延时语句中的延时用于控制任意操作数发生变化到语句左端赋予新值之间的时间延时。 时延一般是不可综合的。 寄存器的时延也是可以控制的这部分在时序控制里加以说明。 连续赋值时延一般可分为普通赋值时延、隐式时延、声明时延。 下面 3 个例子实现的功能是等效的分别对应 3 种不同连续赋值时延的写法。 //普通时延AB计算结果延时10个时间单位赋值给Z wire Z, A, B ; assign #10 Z A B ;//隐式时延声明一个wire型变量时对其进行包含一定时延的连续赋值。 wire A, B; wire #10 Z A B;//声明时延声明一个wire型变量是指定一个时延。因此对该变量所有的连续赋值都会被推迟到指定的时间。除非门级建模中一般不推荐使用此类方法建模。 wire A, B; wire #10 Z ; assign Z A B惯性时延 A 或 B 任意一个变量发生变化那么在 Z 得到新的值之前会有 10 个时间单位的时延。 如果在这 10 个时间单位内即在 Z 获取新的值之前A 或 B 任意一个值又发生了变化那么计算 Z 的新值时会取 A 或 B 当前的新值。 所以称之为惯性时延即信号脉冲宽度小于时延时对输出没有影响。 因此仿真时时延一定要合理设置防止某些信号不能进行有效的延迟。 对一个有延迟的与门逻辑进行时延仿真。 module time_delay_module(input ai, bi,output so_lose, so_get, so_normal);assign #20 so_lose ai bi ;assign #5 so_get ai bi ;assign so_normal ai bi ; endmoduletestbench 参考如下: timescale 1ns/1nsmodule test ;reg ai, bi ;wire so_lose, so_get, so_normal ;initial beginai 0 ;#25 ; ai 1 ;#35 ; ai 0 ; //60ns#40 ; ai 1 ; //100ns#10 ; ai 0 ; //110nsendinitial beginbi 1 ;#70 ; bi 0 ;#20 ; bi 1 ;endtime_delay_module u_wire_delay(.ai (ai),.bi (bi),.so_lose (so_lose),.so_get (so_get),.so_normal (so_normal));initial beginforever begin#100;//$display(---gyc---%d, $time);if ($time 1000) begin$finish ;endendendendmodule仿真结果如下: 信号 so_normal 为正常的与逻辑。 由于所有的时延均大于 5ns所以信号 so_get 的结果为与操作后再延迟 5ns 的结果。 信号 so_lose 前一段是与操作后再延迟 20ns 的结果。 由于信号 ai 第二个高电平持续时间小于 20nsso_lose 信号会因惯性时延而漏掉对这个脉冲的延时检测所以后半段 so_lose 信号仍然为 0。 4.1 Verilog 过程结构 过程结构语句有 2 种initial 与 always 语句。它们是行为级建模的 2 种基本语句。 一个模块中可以包含多个 initial 和 always 语句但 2 种语句不能嵌套使用。 这些语句在模块间并行执行与其在模块的前后顺序没有关系。 但是 initial 语句或 always 语句内部可以理解为是顺序执行的非阻塞赋值除外。 每个 initial 语句或 always 语句都会产生一个独立的控制流执行时间都是从 0 时刻开始。 initial语句 initial 语句从 0 时刻开始执行只执行一次多个 initial 块之间是相互独立的。 如果 initial 块内包含多个语句需要使用关键字 begin 和 end 组成一个块语句。 如果 initial 块内只要一条语句关键字 begin 和 end 可使用也可不使用。 initial 理论上来讲是不可综合的多用于初始化、信号检测等。 对上一节代码稍作修改进行仿真代码如下。 timescale 1ns/1nsmodule test ;reg ai, bi ;initial beginai 0 ;#25 ; ai 1 ;#35 ; ai 0 ; //absolute 60ns#40 ; ai 1 ; //absolute 100ns#10 ; ai 0 ; //absolute 110nsendinitial beginbi 1 ;#70 ; bi 0 ; //absolute 70ns#20 ; bi 1 ; //absolute 90nsend//at proper time stop the simulationinitial beginforever begin#100;//$display(---gyc---%d, $time);if ($time 1000) begin$finish ;endendendendmodule仿真结果如下: 可以看出2 个 initial 进程语句分别给信号 aibi 赋值时相互间并没有影响。 信号 aibi 的值按照赋值顺序依次改变所以 initial 内部语句也可以看做是顺序执行。 always 语句 与 initial 语句相反always 语句是重复执行的。 always 语句块从 0 时刻开始执行其中的行为语句当执行完最后一条语句后便再次执行语句块中的第一条语句如此循环反复。 由于循环执行的特点always 语句多用于仿真时钟的产生信号行为的检测等。 下面用 always 产生一个 100MHz 时钟源并在 1010ns 时停止仿真代码如下。 代码如下: timescale 1ns/1nsmodule test ;parameter CLK_FREQ 100 ; //100MHzparameter CLK_CYCLE 1e9 / (CLK_FREQ * 1e6) ; //switch to nsreg clk ;initial clk 1b0 ; //clk is initialized to 0always # (CLK_CYCLE/2) clk ~clk ; //generating a real clock by reversingalways begin#10;if ($time 1000) begin$finish ;endendendmodule仿真结果如下: 可见时钟周期是我们想要得到的 100MHz。而且仿真在 1010ns 时停止。 4.2 Verilog 过程赋值 过程性赋值是在 initial 或 always 语句块里的赋值赋值对象是寄存器、整数、实数等类型。 这些变量在被赋值后其值将保持不变直到重新被赋予新值。 连续性赋值总是处于激活状态任何操作数的改变都会影响表达式的结果 过程赋值只有在语句执行的时候才会起作用。这是连续性赋值与过程性赋值的区别。 Verilog 过程赋值包括 2 种语句阻塞赋值与非阻塞赋值。 阻塞赋值 阻塞赋值属于顺序执行即下一条语句执行前当前语句一定会执行完毕。 阻塞赋值语句使用等号 作为赋值符。 前面的仿真中initial 里面的赋值语句都是用的阻塞赋值。 非阻塞赋值 非阻塞赋值属于并行执行语句即下一条语句的执行和当前语句的执行是同时进行的它不会阻塞位于同一个语句块中后面语句的执行。 非阻塞赋值语句使用小于等于号 作为赋值符。 利用下面代码对阻塞、非阻塞赋值进行仿真来说明 2 种过程赋值的区别。 timescale 1ns/1nsmodule test ;reg [3:0] ai, bi ;reg [3:0] ai2, bi2 ;reg [3:0] value_blk ;reg [3:0] value_non ;reg [3:0] value_non2 ;initial beginai 4d1 ; //(1)bi 4d2 ; //(2)ai2 4d7 ; //(3)bi2 4d8 ; //(4)#20 ; //(5)//non-block-assigment with block-assignmentai 4d3 ; //(6)bi 4d4 ; //(7)value_blk ai bi ; //(8)value_non ai bi ; //(9)//non-block-assigment itselfai2 4d5 ; //(10)bi2 4d6 ; //(11)value_non2 ai2 bi2 ; //(12)end//stop the simulationalways begin#10 ;if ($time 1000) $finish ;endendmodule仿真结果如下 语句1-8都是阻塞赋值按照顺序执行。 20ns 之前信号 aibi 值改变。由于过程赋值的特点value_blk ai bi 并没有执行到所以 20ns 之前value_blk 值为 X不确定状态。 20ns 之后信号 aibi 值再次改变。执行到 value_blk ai bi信号 value_blk 利用信号 aibi 的新值得到计算结果 7。 语句9-12都是非阻塞赋值并行执行。 首先9-12虽然都是并发执行但是执行顺序也是在8之后所以信号 value_non ai bi 计算是也会使用信号 aibi 的新值结果为 7。 其次10-12是并发执行所以 value_non2 ai2 bi2 计算时并不关心信号 ai2bi2 的最新非阻塞赋值结果。即 value_non2 计算时使用的是信号 ai2bi2 的旧值结果为 4’hF。 使用非阻塞赋值避免竞争冒险 上述仿真代码只是为了让读者更好的理解阻塞赋值与非阻塞赋值的区别。实际 Verilog 代码设计时切记不要在一个过程结构中混合使用阻塞赋值与非阻塞赋值。两种赋值方式混用时时序不容易控制很容易得到意外的结果。 更多时候在设计电路时always 时序逻辑块中多用非阻塞赋值always 组合逻辑块中多用阻塞赋值在仿真电路时initial 块中一般多用阻塞赋值。 如下所示为实现在时钟上升沿交换 2 个寄存器值的功能在 2 个 always 块中使用阻塞赋值。 因为 2 个 always 块中的语句是同时进行的但是 ab 与 ba 是无法判定执行顺序的这就造成了竞争的局面。 但不管哪个先执行和编译器等有关系不考虑 timing 问题时他们执行顺序总有先后最后 a 与 b 的值总是相等的。没有达到交换 2 个寄存器值的效果。 always (posedge clk) begina b ; endalways (posedge clk) beginb a; end但是如果在 always 块中使用非阻塞赋值则可以避免上述竞争冒险的情况。 如下所示2 个 always 块中语句并行执行赋值操作右端操作数使用的是上一个时钟周期的旧值此时 ab 与 ba 就可以相互不干扰的执行达到交换寄存器值的目的。 always (posedge clk) begina b ; endalways (posedge clk) beginb a; end当然利用下面代码也可以实现交换寄存器值的功能但是显然不如在 always 块中直接用非阻塞赋值简单直观。 always (posedge clk) begintemp a ;a b ;b temp ; end4.3 Verilog 时序控制 Verilog 提供了 2 大类时序控制方法时延控制和事件控制。 事件控制主要分为边沿触发事件控制与电平敏感事件控制。 时延控制 基于时延的时序控制出现在表达式中它指定了语句从开始执行到执行完毕之间的时间间隔。 时延可以是数字、标识符或者表达式。 根据在表达式中的位置差异时延控制又可以分为常规时延与内嵌时延。 常规时延 遇到常规延时时该语句需要等待一定时间然后将计算结果赋值给目标信号。 格式为#delay procedural_statement例如 reg value_test ; reg value_general ; #10 value_general value_test ;该时延方式的另一种写法是直接将井号 # 独立成一个时延执行语句例如 #10 ; value_ single value_test ;内嵌时延 遇到内嵌延时时该语句先将计算结果保存然后等待一定的时间后赋值给目标信号。 内嵌时延控制加在赋值号之后。例如 reg value_test ; reg value_embed ; value_embed #10 value_test ;需要说明的是这 2 种时延控制方式的效果是有所不同的。 当延时语句的赋值符号右端是常量时2 种时延控制都能达到相同的延时赋值效果。 当延时语句的赋值符号右端是变量时2 种时延控制可能会产生不同的延时赋值效果。 例如下面仿真代码 timescale 1ns/1nsmodule test ;reg value_test ;reg value_general, value_embed, value_single ;//signal sourceinitial beginvalue_test 0 ;#25 ; value_test 1 ;#35 ; value_test 0 ; //absolute 60ns#40 ; value_test 1 ; //absolute 100ns#10 ; value_test 0 ; //absolute 110nsend//(1)general delay controlinitial beginvalue_general 1;#10 value_general value_test ; //10ns, value_test0#45 value_general value_test ; //55ns, value_test1#30 value_general value_test ; //85ns, value_test0#20 value_general value_test ; //105ns, value_test1end//(2)embedded delay controlinitial beginvalue_embed 1;value_embed #10 value_test ; //0ns, value_test0value_embed #45 value_test ; //10ns, value_test0value_embed #30 value_test ; //55ns, value_test1value_embed #20 value_test ; //85ns, value_test0end//(3)single delay controlinitial beginvalue_single 1;#10 ;value_single value_test ; //10ns, value_test0#45 ;value_single value_test ; //55ns, value_test1#30 ;value_single value_test ; //85ns, value_test0#20 ;value_single value_test ; //105ns, value_test1endalways begin#10;if ($time 150) begin$finish ;endendendmodule仿真结果如下由图可知 1一般延时的两种表达方式执行的结果都是一致的。2一般时延赋值方式遇到延迟语句后先延迟一定的时间然后将当前操作数赋值给目标信号并没有惯性延迟的特点不会漏掉相对较窄的脉冲。3内嵌时延赋值方式遇到延迟语句后先计算出表达式右端的结果然后再延迟一定的时间赋值给目标信号。 下面分析下内嵌延时的赋值过程 value_embed #10 value_test ; //0ns, value_test00ns 时执行此延时语句。 先将 0 赋值给信号 value_embed, 延迟 10ns 输出为 0 value_embed #45 value_test ; //10ns, value_test010ns 时执行此延时语句。 由于此时 value_test 仍然为 0所以 value_embed 值不变。 即到 55ns 时value_embed 值仍然保持为 0。 value_embed #30 value_test ; //55ns, value_test1同理55ns 时value_test 值为 1将其赋值给 value_embed 并延迟 30ns 输出。 所以 85ns 时value_embed 输出为 1。 value_embed #20 value_test ; //85ns, value_test0同理105ns 时value_embed 输出为 0。 边沿触发事件控制 在 Verilog 中事件是指某一个 reg 或 wire 型变量发生了值的变化。 基于事件触发的时序控制又主要分为以下几种。 一般事件控制 事件控制用符号 表示。 语句执行的条件是信号的值发生特定的变化。 关键字 posedge 指信号发生边沿正向跳变negedge 指信号发生负向边沿跳变未指明跳变方向时则 2 种情况的边沿变化都会触发相关事件。例如 //信号clk只要发生变化就执行qd双边沿D触发器模型 always (clk) q d ; //在信号clk上升沿时刻执行qd正边沿D触发器模型 always (posedge clk) q d ; //在信号clk下降沿时刻执行qd负边沿D触发器模型 always (negedge clk) q d ; //立刻计算d的值并在clk上升沿时刻赋值给q不推荐这种写法 q (posedge clk) d ;命名事件控制 用户可以声明 event事件类型的变量并触发该变量来识别该事件是否发生。 命名事件用关键字 event 来声明触发信号用 - 表示。例如 event start_receiving ; always ( posedge clk_samp) begin- start_receiving ; //采样时钟上升沿作为时间触发时刻 endalways (start_receiving) begindata_buf {data_if[0], data_if[1]} ; //触发时刻对多维数据整合 end敏感列表 当多个信号或事件中任意一个发生变化都能够触发语句的执行时Verilog 中使用或表达式来描述这种情况用关键字 or 连接多个事件或信号。 这些事件或信号组成的列表称为敏感列表。当然or 也可以用逗号 , 来代替。例如 //带有低有效复位端的D触发器模型 always (posedge clk or negedge rstn) begin //always (posedge clk , negedge rstn) begin //也可以使用逗号陈列多个事件触发if! rstnbeginq 1b ; endelse beginq d ;end end当组合逻辑输入变量很多时那么编写敏感列表会很繁琐。 此时更为简洁的写法是 * 或 (*)表示对语句块中的所有输入变量的变化都是敏感的。例如 always (*) begin //always (a, b, c, d, e, f, g, h, i, j, k, l, m) begin //两种写法等价assign s a? bc : d ? ef : g ? hi : j ? kl : m ; end电平敏感事件控制 前面所讨论的事件控制都是需要等待信号值的变化或事件的触发使用 敏感列表 的方式来表示的。 Verilog 中还支持使用电平作为敏感信号来控制时序即后面语句的执行需要等待某个条件为真。 Verilog 中使用关键字 wait 来表示这种电平敏感情况。例如 initial beginwait (start_enable) ; //等待 start 信号forever begin//start信号使能后在clk_samp上升沿对数据进行整合(posedge clk_samp) ;data_buf {data_if[0], data_if[1]} ; end end4.4 Verilog 语句块 Verilog 语句块提供了将两条或更多条语句组成语法结构上相当于一条一句的机制。主要包括两种类型顺序块和并行块。 顺序块 顺序块用关键字 begin 和 end 来表示。 顺序块中的语句是一条条执行的。当然非阻塞赋值除外。 顺序块中每条语句的时延总是与其前面语句执行的时间相关。 在本节之前的仿真中initial 块中的阻塞赋值都是顺序块的实例。 并行块 并行块有关键字 fork 和 join 来表示。 并行块中的语句是并行执行的即便是阻塞形式的赋值。 并行块中每条语句的时延都是与块语句开始执行的时间相关。 顺序块与并行块的区别显而易见下面用仿真说明。 仿真代码如下: timescale 1ns/1nsmodule test ;reg [3:0] ai_sequen, bi_sequen ;reg [3:0] ai_paral, bi_paral ;reg [3:0] ai_nonblk, bi_nonblk ;//////(1)Sequence blockinitial begin#5 ai_sequen 4d5 ; //at 5ns#5 bi_sequen 4d8 ; //at 10nsend//(2)fork blockinitial fork#5 ai_paral 4d5 ; //at 5ns#5 bi_paral 4d8 ; //at 5nsjoin//(3)non-block blockinitial fork#5 ai_nonblk 4d5 ; //at 5ns#5 bi_nonblk 4d8 ; //at 5nsjoinendmodule仿真结果如下: 如图所示顺序块顺序执行第 10ns 时信号 bi_sequen 才赋值为 8。 而并行块ai_paral 与 bi_paral 的赋值是同时执行的所以均在 5ns 时被赋值。 而非阻塞赋值也能达到和并行块同等的赋值效果。 嵌套块 顺序块和并行块还可以嵌套使用。 timescale 1ns/1nsmodule test ;reg [3:0] ai_sequen2, bi_sequen2 ;reg [3:0] ai_paral2, bi_paral2 ;initial beginai_sequen2 4d5 ; //at 0nsfork#10 ai_paral2 4d5 ; //at 10ns#15 bi_paral2 4d8 ; //at 15nsjoin#20 bi_sequen2 4d8 ; //at 35nsendendmodule仿真结果如下: 并行块语句块内是并行执行所以信号 ai_paral2 和信号 bi_paral2 分别在 10ns, 15ns 时被赋值。 而并行块中最长的执行时间为 15ns所以顺序块中的信号 bi_sequen2 在 35ns 时被赋值。 命名块 我们可以给块语句结构命名。 命名的块中可以声明局部变量通过层次名引用的方法对变量进行访问。 timescale 1ns/1nsmodule test;initial begin: runoob //命名模块名字为runoob分号不能少integer i ; //此变量可以通过test.runoob.i 被其他模块使用i 0 ;forever begin#10 i i 10 ; endendreg stop_flag ;initial stop_flag 1b0 ;always begin : detect_stopif ( test.runoob.i 100) begin //i累加10次即100ns时停止仿真$display(Now you can stop the simulation!!!);stop_flag 1b1 ;end#10 ;endendmodule仿真结果如下: 命名的块也可以被禁用用关键字 disable 来表示。 disable 可以终止命名块的执行可以用来从循环中退出、处理错误等。 与 C 语言中 break 类似但是 break 只能退出当前所在循环而 disable 可以禁用设计中任何一个命名的块。 timescale 1ns/1nsmodule test;initial begin: runoob_d //命名模块名字为runoob_dinteger i_d ;i_d 0 ;while(i_d100) begin: runoob_d2# 10 ;if (i_d 50) begin //累加5次停止累加disable runoob_d3.clk_gen ;//stop 外部block: clk_gendisable runoob_d2 ; //stop 当前block: runoob_d2endi_d i_d 10 ;endendreg clk ;initial begin: runoob_d3while (1) begin: clk_gen //时钟产生模块clk1 ; #10 ;clk0 ; #10 ;endendendmodule仿真结果如下: 由图可知信号 i_d 累加到 50 以后便不再累加以后 clk 时钟也不再产生。 可见disable 退出了当前的 while 块。 需要说明的是disable 在 always 或 forever 块中使用时只能退出当前回合下一次语句还是会在 always 或 forever 中执行。 因为 always 块和 forever 块是一直执行的此时的 disable 有点类似 C 语言中的 continue 功能。 4.5 Verilog 条件语句 条件语句 条件if语句用于控制执行语句要根据条件判断来确定是否执行。 条件语句用关键字 if 和 else 来声明条件表达式必须在圆括号中。 条件语句使用结构说明如下 if (condition1) true_statement1 ; else if (condition2) true_statement2 ; else if (condition3) true_statement3 ; else default_statement ; if 语句执行时如果 condition1 为真则执行 true_statement1 如果 condition1 为假condition2 为真则执行 true_statement2依次类推。else if 与 else 结构可以省略即可以只有一个 if 条件判断和一组执行语句 ture_statement1 就可以构成一个执行过程。else if 可以叠加多个不仅限于 1 或 2 个。ture_statement1 等执行语句可以是一条语句也可以是多条。如果是多条执行语句则需要用 begin 与 end 关键字进行说明。 下面代码实现了一个 4 路选择器的功能。 module mux4to1(input [1:0] sel ,input [1:0] p0 ,input [1:0] p1 ,input [1:0] p2 ,input [1:0] p3 ,output [1:0] sout);reg [1:0] sout_t ;always (*) beginif (sel 2b00)sout_t p0 ;else if (sel 2b01)sout_t p1 ;else if (sel 2b10)sout_t p2 ;elsesout_t p3 ;endassign sout sout_t ;endmoduletestbench 代码如下 timescale 1ns/1nsmodule test ;reg [1:0] sel ;wire [1:0] sout ;initial beginsel 0 ;#10 sel 3 ;#10 sel 1 ;#10 sel 0 ;#10 sel 2 ;endmux4to1 u_mux4to1 (.sel (sel),.p0 (2b00), //path0 are assigned to 0.p1 (2b01), //path1 are assigned to 1.p2 (2b10), //path2 are assigned to 2.p3 (2b11), //path3 are assigned to 3.sout (sout));//finish the simulationalways begin#100;if ($time 1000) $finish ;endendmodule仿真结果如下。 由图可知输出信号与选择信号、输入信号的状态是相匹配的。 事例中 if 条件每次执行的语句只有一条没有使用 begin 与 end 关键字。 但如果是 if-if-else 的形式即便执行语句只有一条不使用 begin 与 end 关键字也会引起歧义。 例如下面代码虽然格式上加以区分但是 else 对应哪一个 if 条件是有歧义的。 if(en)if(sel 2b1)sout p1s ;elsesout p0 ;当然编译器一般按照就近原则使 else 与最近的一个 if例子中第二个 if相对应。 但显然这样的写法是不规范且不安全的。 所以条件语句中加入 begin 与 and 关键字就是一个很好的习惯。 例如上述代码稍作修改就不会再有书写上的歧义。 if(en) beginif(sel 2b1) beginsout p1s ;endelse beginsout p0 ;end end4.6 Verilog 多路分支语句 case 语句是一种多路条件分支的形式可以解决 if 语句中有多个条件选项时使用不方便的问题。 case 语句 case 语句格式如下 case(case_expr)condition1 : true_statement1 ;condition2 : true_statement2 ;……default : default_statement ; endcasecase 语句执行时如果 condition1 为真则执行 true_statement1 ; 如果 condition1 为假condition2 为真则执行 true_statement2依次类推。如果各个 condition 都不为真则执行 default_statement 语句。 default 语句是可选的且在一个 case 语句中不能有多个 default 语句。 条件选项可以有多个不仅限于 condition1、condition2 等而且这些条件选项不要求互斥。虽然这些条件选项是并发比较的但执行效果是谁在前且条件为真谁被执行。 ture_statement1 等执行语句可以是一条语句也可以是多条。如果是多条执行语句则需要用 begin 与 end 关键字进行说明。 case 语句支持嵌套使用。 下面用 case 语句代替 if 语句实现了一个 4 路选择器的功能。仿真结果与 testbench 可参考条件语句一章两者完全一致。 module mux4to1(input [1:0] sel ,input [1:0] p0 ,input [1:0] p1 ,input [1:0] p2 ,input [1:0] p3 ,output [1:0] sout);reg [1:0] sout_t ;always (*)case(sel)2b00: begin sout_t p0 ;end2b01: sout_t p1 ;2b10: sout_t p2 ;default: sout_t p3 ;endcaseassign sout sout_t ;endmodulecase 语句中的条件选项表单式不必都是常量也可以是 x 值或 z 值。 当多个条件选项下需要执行相同的语句时多个条件选项可以用逗号分开放在同一个语句块的候选项中。 但是 case 语句中的 x 或 z 的比较逻辑是不可综合的所以一般不建议在 case 语句中使用 x 或 z 作为比较值。 例如对 4 路选择器的 case 语句进行扩展举例如下 case(sel)2b00: sout_t p0 ;2b01: sout_t p1 ;2b10: sout_t p2 ;2b11: sout_t p3 ;2bx0, 2bx1, 2bxz, 2bxx, 2b0x, 2b1x, 2bzx :sout_t 2bxx ;2bz0, 2bz1, 2bzz, 2b0z, 2b1z :sout_t 2bzz ;default: $display(Unexpected input control!!!); endcasecasex/casez 语句 casex、 casez 语句是 case 语句的变形用来表示条件选项中的无关项。 casex 用 “x” 来表示无关值casez 用问号 “?” 来表示无关值。 两者的实现的功能是完全一致的语法与 case 语句也完全一致。 但是 casex、casez 一般是不可综合的多用于仿真。 例如用 casez 语句来实现一个 4bit 控制端的 4 路选择选择器。 module mux4to1(input [3:0] sel ,input [1:0] p0 ,input [1:0] p1 ,input [1:0] p2 ,input [1:0] p3 ,output [1:0] sout);reg [1:0] sout_t ;always (*)casez(sel)4b???1: sout_t p0 ;4b??1?: sout_t p1 ;4b?1??: sout_t p2 ;4b1???: sout_t p3 ; default: sout_t 2b0 ;endcaseassign sout sout_t ;endmodule4.7 Verilog 循环语句 Verilog 循环语句有 4 种类型分别是 whileforrepeat和 forever 循环。循环语句只能在 always 或 initial 块中使用但可以包含延迟表达式。 while 循环 while 循环语法格式如下 while (condition) begin… endwhile 循环中止条件为 condition 为假。 如果开始执行到 while 循环时 condition 已经为假那么循环语句一次也不会执行。 当然执行语句只有一条时关键字 begin 与 end 可以省略。 下面代码执行时counter 执行了 11 次。 timescale 1ns/1nsmodule test ;reg [3:0] counter ;initial begincounter b0 ;while (counter10) begin#10 ;counter counter 1b1 ;endend//stop the simulationalways begin#10 ; if ($time 1000) $finish ;endendmodulefor 循环 for 循环语法格式如下 for(initial_assignment; condition ; step_assignment) begin… endinitial_assignment 为初始条件。 condition 为终止条件condition 为假时立即跳出循环。 step_assignment 为改变控制变量的过程赋值语句通常为增加或减少循环变量计数。 一般来说因为初始条件和自加操作等过程都已经包含在 for 循环中所以 for 循环写法比 while 更为紧凑但也不是所有的情况下都能使用 for 循环来代替 while 循环。 下面 for 循环的例子实现了与 while 循环中例子一样的效果。需要注意的是i i 1 不能像 C 语言那样写成 i 的形式i i -1 也不能写成 i – 的形式。 // for 循环语句 integer i ; reg [3:0] counter2 ; initial begincounter2 b0 ;for (i0; i10; ii1) begin#10 ;counter2 counter2 1b1 ;end endrepeat 循环 repeat 循环语法格式如下 repeat (loop_times) begin… endrepeat 的功能是执行固定次数的循环它不能像 while 循环那样用一个逻辑表达式来确定循环是否继续执行。 repeat 循环的次数必须是一个常量、变量或信号。 如果循环次数是变量信号则循环次数是开始执行 repeat 循环时变量信号的值。 即便执行期间循环次数代表的变量信号值发生了变化repeat 执行次数也不会改变。 下面 repeat 循环例子实现了与 while 循环中的例子一样的效果。 // repeat 循环语句 reg [3:0] counter3 ; initial begincounter3 b0 ;repeat (11) begin //重复11次#10 ;counter3 counter3 1b1 ;end end下面 repeat 循环例子实现了连续存储 8 个数据的功能: always (posedge clk or negedge rstn) beginj 0 ;if (!rstn) beginrepeat (8) beginbuffer[j] b0 ; //没有延迟的赋值即同时赋值为0j j 1 ;endendelse if (enable) beginrepeat (8) begin(posedge clk) buffer[j] counter3 ; //在下一个clk的上升沿赋值j j 1 ;endend end仿真结果如下图。 由图可知rstn 拉高时buffer 的 8 个向量同时赋值为 0。 第二个时钟周期后buffer 依次被 counter3 赋值实现了连续存储 8 个数据的功能。 forever 循环 forever 循环语法格式如下 forever begin… endforever 语句表示永久循环不包含任何条件表达式一旦执行便无限的执行下去系统函数 $finish 可退出 forever。 forever 相当于 while(1) 。 通常forever 循环是和时序控制结构配合使用的。 例如使用 forever 语句产生一个时钟 reg clk ; initial beginclk 0 ;forever beginclk ~clk ;#5 ;end end例如使用 forever 语句实现一个时钟边沿控制的寄存器间数据传输功能 reg clk ; reg data_in, data_temp ; initial beginforever (posedge clk) data_temp data_in ; end4.8 Verilog 过程连续赋值 过程连续赋值是过程赋值的一种。这种赋值语句能够替换其他所有 wire 或 reg 的赋值改写了 wire 或 reg 型变量的当前值。 与过程赋值不同的是过程连续赋值的表达式能被连续的驱动到 wire 或 reg 型变量中即过程连续赋值发生作用时右端表达式中任意操作数的变化都会引起过程连续赋值语句的重新执行。 过程连续性赋值主要有 2 种assign-deassign 和 force-release。 assign, deassign assign过程赋值操作与 deassign 取消过程赋值操作表示第一类过程连续赋值语句。 赋值对象只能是寄存器或寄存器组而不能是 wire 型变量。 赋值过程中对寄存器连续赋值寄存器中的值被保留直到被重新赋值。 例如一个带复位端的 D 触发器可以用下面代码描述 module dff_normal(input rstn,input clk,input D,output reg Q);always (posedge clk or negedge rstn) beginif(!rstn) begin //Q 0 after reset effectiveQ 1b0 ;endelse beginQ D ; //Q D at posedge of clockendendendmodule下面用 assign 与 deassign 改写完成相同的功能。 即在复位信号为 0 时Q 端被 assign 语句赋值始终输出为 0。 复位信号为 1 时Q 端被 deassign 语句取消赋值在时钟上升沿被重新赋值。 module dff_assign(input rstn,input clk,input D,output reg Q);always (posedge clk) beginQ D ; //Q D at posedge of clockendalways (negedge rstn) beginif(!rstn) beginassign Q 1b0 ; //change Q value when reset effectiveendelse begin //cancel the Q value overlay,deassign Q ; //and Q remains 0-value until the coming of clock posedgeendendendmoduleforce, release force 强制赋值操作与 release取消强制赋值表示第二类过程连续赋值语句。 使用方法和效果和 assign 与 deassign 类似但赋值对象可以是 reg 型变量也可以是 wire 型变量。 因为是无条件强制赋值一般多用于交互式调试过程不要在设计模块中使用。 当 force 作用在寄存器上时寄存器当前值被覆盖release 时该寄存器值将继续保留强制赋值时的值。之后该寄存器的值可以被原有的过程赋值语句改变。 当 force 作用在线网上时线网值也会被强制赋值。但是一旦 release 该线网型变量其值马上变为原有的驱动值。 为直观的观察两种类型变量强制赋值的区别利用第一节中的计数器 counter10 作为设计模块testbench 设计如下。 timescale 1ns/1nsmodule test ;reg rstn ;reg clk ;reg [3:0] cnt ;wire cout ;counter10 u_counter (.rstn (rstn),.clk (clk),.cnt (cnt),.cout (cout));initial beginclk 0 ;rstn 0 ;#10 ;rstn 1b1 ;wait (test.u_counter.cnt_temp 4d4) ;(negedge clk) ;force test.u_counter.cnt_temp 4d6 ;force test.u_counter.cout 1b1 ;#40 ;(negedge clk) ;release test.u_counter.cnt_temp ;release test.u_counter.cout ;endinitial beginclk 0 ;forever #10 clk ~ clk ;end//finish the simulationalways begin#1000;if ($time 1000) $finish ;endendmodule // test仿真结果如下。 由图可知在 cnt_temp 等于 4 时80ns, cnt_temp 被强制赋值为 6cout 被强制赋值为 1。 release 时120ns, cnt_temp 为寄存器类型仍然保持原有值不变直到时钟上升沿对其进行加法赋值操作值才变为 7 。 而 120ns 时由于 cout 是线网型变量其值不能保存。原码 counter10 模型中存在驱动语句 assign cout (cnt_temp4’d9) 所以 cout 值变为 0 。
http://www.hkea.cn/news/14525994/

相关文章:

  • wordpress建站多少钱南溪门户网
  • 海安公司网站建设网页设计实训报告小结
  • 网站设计与平面设计区别怎么做一个网站平台
  • 哪个网站可以做自由行地图做的网站百度搜索不出来
  • 建设网站的颜色wordpress注册接口
  • 深圳网站制作首荐祥奔科技做网站做什么赚钱
  • 南通wap网站建设肇庆做网约车
  • 网站推广的方法及特点美丽深圳微信公众号二维码
  • 成都网站制作汕头沈阳高端网站制作
  • 西宁网站网站做附近地图导航
  • 专业网站建设开发代理注册公司注意事项
  • 查看网站的外链重庆网站推广产品企业
  • 上海怎么做网站刚刚地震最新消息今天 刚才
  • seo建设者厦门 网站优化
  • 长春城投建设投资有限公司网站外网网站
  • 网站内部链接怎么做的什么软件可以优化关键词
  • 郑州seo网络营销杭州网站seo外包
  • 专业购物网站教做视频的网站
  • 宁波怎么做外贸公司网站企业网站的首页
  • 网站安全防护怀化同城网站
  • 横沥镇网站仿做佛山市
  • 海口小学网站建设抖音代运营保证金
  • 香奈儿网站设计分析wordpress自定义简单注册
  • 云南省建设培训网站有哪些网站可以用
  • 做娱乐网站少10个页面东莞网站建设 胶粘包装材料
  • 域名解析 网站建设驻马店seo
  • 国外搜索网站建设眉山建设局网站
  • 长沙企业网站建设报价企业vi设计是啥
  • 做网站跟app张家港做网站的
  • 安徽专业做网站的大公司网站建设速成班培训