input 赋值_FPGA基础设计:Verilog行为级建模(过程赋值)

来源:CSDN文章

使用逻辑门和连续赋值对电路建模,是相对详细的描述硬件的方法。使用过程块可以从更高层次的角度描述一个系统,称作行为级建模(behavirol modeling)。

1. 过程赋值

阻塞赋值和非阻塞赋值的区别都很熟悉了。这里记录两个特性。

1.1 特性1

绝大多数情况下,非阻塞赋值都是一个时间点处最后执行的赋值语句。看下面的示例代码:

module test
(
input clk,
output reg a, b
);
always @ (posedge clk) begin
a = 0;
b = 1;
a <= b;
b <= a;
end
endmodule

非阻塞赋值可以认为包括两步:
(1)求值和调度(evaluate and schedule),先得到非阻塞赋值等式右侧的值,并将这次赋值安排在当前时间点的结束时刻。
(2)当前时间点结束时,更新左侧的值。因此这段代码的结果是 a = 1,b = 0。

1.2 特性2

如果过程块内,有针对同一个变量的多个非阻塞赋值,那么这些非阻塞赋值会按顺序执行(但我认为不能简单地说过程块内是“顺序执行的”,容易造成误导,应该说具有一定的“顺序性”特点)。

看下面的示例代码:

module test
(
input clk,
output reg a, b
);
always @ (posedge clk) begin
a <= 0;
a <= 1;
end
endmodule

always块内有两条对于变量a的赋值语句,但由于顺序性特点,a的赋值结果应该是1。利用这个特性,会经常见到下面这种代码写法:

always @ (posedge clk) begin
a <= 0;
if (flag == 1)
a <= 1;
end

只有当flag=1时,a才为1。

2. 过程连续赋值

这种赋值方式允许在过程块中连续地驱动网络或变量。但这种建模方法不可综合,因此这里只简单记录一下两种过程连续赋值方式的作用。

assign和deassign:assign连续赋值会优先占用一个变量,让其它对这个变量进行赋值的过程块无效。deassign连续赋值会解除占用关系。

看下面的示例代码:

`timescale 1ns / 1ps
module sim();
reg clk = 0, rst_n = 0, d = 1;
reg q;
//test i1
//(
// .clk (clk),
// .rst_n(rst),
// .q(q),
// .d(d)
//);
always @ (rst_n)
if (!rst_n) assign q = 0;
else deassign q;
always @ (posedge clk)
q <= d;
always #5 clk <= ~clk;
initial begin
#50 rst_n = 1;
end
endmodule

当rst_n=0时,asssign连续赋值占用了q,q的值恒为0;当rst_n=1时,deassign解除了占用,q的值由其它过程赋值决定,在clk上升沿随d变化而变化。

force和release:功能和assign、deassign相同,只是赋值对象可以是变量也可以是网络。force过程赋值的对象为网络时,会使其它所有对该网络的驱动无效。

3. case语句

case语句的default分支不是必须的,只要设计者清楚设计意图即可。记录一下case两个比较少见但有时候特别有用的用法。

3.1 do-not-cares

包括两种:
  •  casez表示不关心高阻状态(z);
  •  casex表示不关心高祖状态(z)和未知状态(x)。

在不关心的bit位上使用“?”表示要更加方便。casex和casez完全是可以综合的,例如下面的代码可以实现优先编码器:

module test
(
input clk,
input [3:0] d,
output reg [15:0] q
);
always @ (posedge clk)
casez (d)
4'b1??? : q <= 1;
4'b01?? : q <= 2;
4'b001? : q <= 3;
4'b0001 : q <= 4;
endcase
endmodule

如果想使用casex和casez,还是要从“设计上”能否综合的角度考虑一下,并且做好综合后的仿真。

比如上面的代码,使用case语句+16条分支可以实现同样的效果,这个设计完全是可以综合实现的。使用casez更多还是起到简化代码设计的作用。

3.2 常数case

case语句中可以使用常数表达式,这个常数会和每个分支中的表达式进行比较。如下面的代码:

module test
(
input clk,
input [3:0] d,
output reg [15:0] q
);
always @ (posedge clk)
casez (1)
d[0] : q <= 1;
d[1] : q <= 2;
d[2] : q <= 3;
d[3] : q <= 4;
endcase
endmodule

可以实现,根据d中的哪个bit为1,执行相应的代码。如果d中多个bit同时为1,此时多条分支同时满足,会执行顺序最前面的一条。总之还是要清楚设计意图,做好仿真工作。

4. 循环语句

forever 和 repeat 是完全不可综合的,只用于仿真文件的设计。

循环语句 for 和 while 并不是完全不能综合的,但因为Verilog是对硬件进行建模,for和while的使用肯定不像在软件编程语言中使用一样灵活。还是上面的老话,要从“设计上”能否综合的角度进行考虑。

如果循环语句的使用出现问题,综合工具会给出提示,如Vivado的提示信息如下:

[Synth 8-3380] loop condition does not converge after 2000 iterations

用好循环,可以简化代码设计。一个寄存器链的示例如下(使用for也能达到同样的效果):

module test
(
input clk, rst_n,
input [15:0] d,
output reg [15:0] q
);
integer i;
(* keep = "true" *)reg [15:0] mem [7:0];
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
i = 0;
while(i < 8) begin
mem[i] <= 0;
i = i + 1'b1;
end
end
else begin
i = 0;
mem[0] <= d;
while(i < 7) begin
mem[i+1] <= mem[i];
i = i + 1'b1;
end
end
q <= mem[7];
end
endmodule

相应的RTL原理图如下 :

82143d5bec6056c7031206214eaa1bb1.png

按软件编程思维考虑,循环语句是一条一条执行的一个过程。而从上面的设计结果来说,显然while循环中的所有语句是“同时”执行的,代码只是将很多具有重复性特点的赋值语句改用 while/for 的形式来编写。

5. 过程块

过程块(procedure)包括四种:initial结构、always结构、任务(task)、函数(function)。这里只记录两个不太熟悉的特性。

5.1 零延迟无限循环

always块在仿真文件中,都要与一些时序控制配合使用。如果always块中没有任何推动仿真时间的控制,仿真会卡在一个时间点。比如经常用如下语句创建时钟信号:

always #10 clk = ~clk;

如果写成了如下的形式:

always clk = ~clk;

相当于形成了一个零延迟的无限循环,仿真时间会卡在0s无法前进。如果运行这个代码,轻则程序卡死,重则系统奔溃只能重启。

5.2 initial用于初始化

initial块是可以综合的,只不过不能添加时序控制语句,因此作用有限,一般用于变量的初始化。如下面代码:

reg [15:0] mem [7:0];
integer i;
initial begin
for (i = 0; i < 8; i=i+1)
mem[i] = i;
end

6. 过程块时序控制

此特性主要用于仿真文件中,部分在硬件设计中也会涉及。Verilog有两种明确的时序控制类型:延时控制和事件表达式。仿真时间正是靠过程块中的延时控制、事件控制以及wait语句来推动的。

6.1 延时控制

用于控制语句的执行时间,比如描述激励的波形。延迟值可以是表达式,比如“#d rega = regb;”,这条赋值语句会在延迟d个时间单位后执行。
(1)如果d的计算结果是高阻(z)或未知(x),则当作0处理;
(2)如果d的计算结果为负数,也会将其视作无符号数来看待,如下面的代码:

parameter [7:0] delay = -50;
initial begin
rst_n = 0;
#(-delay) rst_n = 1;
#delay rst_n = 0;
end

rst_n先延迟50个时钟后变为1;由于8bit -50的二进制补码当作无符号数看时值为206,因此在延时206个时钟后,rst_n值又变为0。

6.2 事件表达式

直到某些仿真事件发生时,语句才会只执行。网络或变量的值发生变化,称作隐式事件(implict event);设计者设置一些命名事件,可能会由其它过程块触发,称作显式事件(explicit event)。

值的变化、或变化的方向(上升沿posedge或下降沿negedge)都是隐式事件。虽然在硬件设计中经常和always配合使用(比如 always @ (posedge clk) ),但在仿真文件中有更多灵活的使用方法。看下面的代码示例:

// example1:clk上升沿,语句执行
reg [7:0] delay = 0;
initial begin
forever @(posedge clk) delay = delay + 1'b1;
end
// example2:clk的值发生变化,语句执行
reg [7:0] delay = 0;
always begin
@(clk) delay = delay + 1'b1;
end

如果 posedge 和 negedge 检测的对象是一个表达式或多位宽的数据,则只会检测LSB上的边沿变化。如下:

// example3
reg [2:0] cnt = 0;
always @ (posedge clk)
cnt <= cnt + 1'b1;
reg [7:0] delay = 0;
always begin
@(posedge cnt) delay = delay + 1'b1;
end

检测3bit变量cnt的上升沿,相当于检测cnt[0]的边沿事件。

事件(event) 是除了变量和网络外Verilog中的另一种数据类型,如果一个标识符被申明为事件类型,则称作“命名事件”,需要显示地触发。虽然事件是一种数据类型,但它本身又没有任何“数据”。如下面的示例:

event trig;        // 命名事件申明
reg [2:0] cnt = 0;
always @ (posedge clk) begin
cnt <= cnt + 1'b1;
if (cnt == 7) -> trig; // 事件显示触发
end
reg [7:0] delay = 0;
always begin
@(trig) delay = delay + 1'b1; // 事件捕获
end

使用命名事件可以有效的实现多个过程块之间的通信和同步。

如果过程块语句的执行同时对多个事件敏感,可以使用事件的逻辑或特性。在事件敏感列表中使用 “or” 或 “,”(这两个符号含义等价),如“always @ (posedge clka or posedge clkb, trig)”。

还有一个特性称作隐式事件表达式,符号为“@*”,会把过程时序控制语句中所有读取的变量和网络添加到事件表达式中

6.3 wait语句

上面的事件控制方法都是边沿敏感型的。还可以使用wait语句控制过程块的时序,直到某项条件为true时才执行相应语句,这种方法称作电平敏感型。

如果wait中的条件为false,则过程块会一直阻塞,直到条件变为真时,才会执行后面的语句。比如下面的代码:

reg [7:0] cnta = 0, cntb = 0;
initial begin
wait(en) #10 cnta <= 60;
#10 cntb <= 70;
end

对于begin…end(顺序块) 而言,wait会阻止顺序块的执行,直到en为1时,cnta和cntb的两条赋值语句才会执行。如果使用fork…join(并行块),则上述代码中的wait只会对cnta的赋值语句有效,此时最好也为wait语句加上块声明(begin…end或fork…join)。

6.4 赋值间(Intra-assignment)时序控制

赋值间延迟和事件控制是另一种时序控制方法,如

 a = #5 b;

与“ #5 a = b; ”不同,赋值语句右边的表达式会马上求值,延迟和事件只是控制这个值赋值给赋值语句左边的时间。比如上面的代码等效于:

begin
temp = b;
#5 a = temp;
end

利用赋值间时序控制的特性,可以巧妙地完成一些行为建模。比如下面的代码可以避免赋值语句间的“竞争”,达到数据交换的效果:

fork       // 并行块,存在竞争
#5 a = b;
#5 b = a;
join
fork // 数据交换
a = #5 b;
b = #5 a;
join

赋值间延迟之前会先求等式右边的值,延迟后才会把这个值赋到左边,因此上面代码相当于交换了a和b的值。很多工具在实现Verilog的赋值间时序控制这个特性时,都会使用临时存储来存放右边表达式的值。

也可以用事件控制:

a = @(posedge clk) b;
//等效于
begin
temp = b;
@(posedge clk) a = temp;
end

赋值间时序控制还有一个特点是可以用repeat来控制延迟或事件执行的次数,如:

a = repeat(3) @(posedge clk) b;
//等效于
begin
temp = b;
@(posedge clk);
@(posedge clk);
@(posedge clk); a = temp;
end

要注意如果采用变量的形式 “ repeat (num) ”:

  •   若num是无符号数:当num为负数时,相当于二进制补码对应的无符号数。比如num = -1,repeat(num) 相当于 repeat(7) 。

  •   若num是带符号数:当num为0或负数时,这条语句将永远不会被执行。

7. 块(block)

块(block)是一些赋值语句的组合,包括:

  •   顺序块begin-end:块中语句按照给定的顺序执行,因此块中的延迟、事件控制相当于起到了隔断的作用。顺序块的开始时间是第一条语句开始执行的时刻,结束时间是最后一条语句执行完的时刻。

  •   并行块fork-join:块中语句同时执行,即所有语句的开始时间相同。并行块的结束时间是所有语句都执行完的时刻。

7.1 嵌套块

通常要使用多个块的嵌套实现更复杂的控制逻辑,因此最好要理解各个块的开始时间和结束时间。下面给出几个例子:

// Example1
begin
fork
@Aevent;
@Bevent;
join
areg = breg;
end

由于fork-join的并行性,A和B两个事件可以以任意的顺序出现,fork-join块结束后执行赋值语句 areg = breg。

// Example2
begin
begin
@Aevent;
@Bevent;
end
areg = breg;
end

如果换成begin-end,事件的触发必须按照给定的顺序。如果B事件先出现,再出现A,那么内部嵌套的begin-end还要再等待B事件的发生。

// Example3
fork
@Aevent;
begin #ta wa = 0; #ta wa = 1; end
@Bevent;
begin #tb wb = 1; #tb wb = 0; end
join

fork-join中的两个顺序块的执行分别受到两个事件的控制。由于fork-join的并行性,两个begin-end的触发和执行同样也是并行的。

7.2 命名块

每个块都可以在begin和fork后面为其附加名字,称为命名块。其它语句可以通过这个名字来引用命名块,最常见的是“命名块+disable”的用法。

disable语句可以终止命名块的运行,一般用于处理异常情况,比如下面的代码:

begin : block_name
...
if (a == 0)
disable block_name;
...
end

当满足a == 0时,begin-end块会终止运行。disable会终止整个命名块的运行,包括命名块中的其它所有块和已调用的任务。利用这个特性可以实现两个功能:
  •   中止一个循环语句(相当于C语言中的break)
  •   跳过循环中的某些状态(相当于C语言中的continue)

虽然Verilog没有直接提供类似于C语言中break和continue的关键词,但可以使用“命名块+disable”来实现此特性。看下面的示例代码:

reg [7:0] cnt = 0;
always @ (posedge clk) cnt <= cnt + 1'b1;
reg [7:0] data;
integer i;
initial begin : break
for (i = 0; i < 100; i = i + 1) begin : continue
@(posedge clk)
if (cnt == 5)
disable break;
data <= cnt;
end
end

for循环中,当满足一定条件时," disable break; "会终止initial之后的begin-end块的执行,整个循环也就终止了。

如果改成" disable continue; ",当满足条件时,会终止for之后的begin-end块的执行,这样只会终止当前的循环状态,而不会影响循环的下一次迭代。

原文链接:https://bestfpga.blog.csdn.net/article/details/102784167

‧  END  

68961e7acb00d446c66d7326215243fc.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/266265.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

本地配置DNS服务器(MAC版)

转自https://www.cnblogs.com/skylor/p/7483959.html作为一个前端开发者&#xff0c;会遇到使用cookie的情况&#xff0c;常见的如&#xff1a;登录&#xff0c;权限控制&#xff0c;视频播放&#xff0c;图形验证码等&#xff0c;这时候本地开发者在PC上会使用修改hosts的方式…

全国计算机等级考试题库二级C操作题100套(第72套)

第72套&#xff1a; 给定程序中&#xff0c;函数fun的功能是计算下式 例如&#xff0c;若形参e的值为1e-3&#xff0c;函数的返回值2.735678。 请在程序的下划线处填入正确的内容并把下划线删除,使程序得出正确的结果。 注意&#xff1a;源程序存放在考生文件夹下的BLANK1.C中…

JAVA操作Excel表格

一、JExcelApi 可以从文件或者输入流进行读取操作。 基本步骤&#xff1a; 1.由文件或者输入流创建一个workbook; 2.由workbook的getSheet()方法创建一个工作表Sheet&#xff08;两种方法&#xff0c;下标和名字&#xff0c;下标从0开始&#xff09;&#xff1b; 3.由Sheet的ge…

Oracle监听注册和sqlnet,Oracle监听的动态注册与静态注册

Oracle监听介于Oracle的数据库和客户端之间的通道。因为数据库本身不对外提供服务&#xff0c;所以需要通过监听器来实现。几个相关的参数&#xff1a;local_listenerdb_domainremote_listenerservice_names相关命令&#xff1a;监听命令&#xff1a;lsnrctl参数&#xff1a;1、…

mme设备内部错误_华为拟安装“俄版安卓”;百度回应内部贪腐;Android Studio 3.5 RC2 发布 | 极客头条...

快来收听极客头条音频版吧&#xff0c;智能播报由标贝科技提供技术支持。「CSDN 极客头条」&#xff0c;是从 CSDN 网站延伸至官方微信公众号的特别栏目&#xff0c;专注于一天业界事报道。风里雨里&#xff0c;我们将每天为朋友们&#xff0c;播报最新鲜有料的新闻资讯&#x…

[spring mvc]Hello World入门

1.新建项目 File->New->Other,选择Dynamic web project&#xff1a; 项目建好之后&#xff0c;目录结构如下&#xff1a; 2.WEB-INF/web.xml 中配置 dispatcherServlet <?xml version"1.0" encoding"UTF-8"?> <web-app xmlns:xsi"ht…

Mysql日期函数使用大全

为什么80%的码农都做不了架构师&#xff1f;>>> 1.1 获得当前日期时间&#xff08;date time&#xff09;函数&#xff1a;now() 除了 now() 函数能获得当前的日期时间外&#xff0c;MySQL 中还有下面的函数&#xff1a; current_timestamp() current_timestamp …

全国计算机等级考试题库二级C操作题100套(第73套)

第73套&#xff1a; 给定程序中&#xff0c;函数fun的功能是计算下式 例如&#xff1a;若形参e的值为1e-3&#xff0c;函数的返回值为0.551690。 请在程序的下划线处填入正确的内容并把下划线删除,使程序得出正确的结果。 注意&#xff1a;源程序存放在考生文件夹下的BLANK1.C…

oracle修改数据库国际字符集,Oracle修改数据库字符集

1.(Window&#xff1a;)cmd下输入set ORACLE_SID你想进入的数据库的那个sid(Linux&#xff1a;)切换到oracle用户&#xff1a;export ORACLE_SID你想进入的数据库的那个sid2.输入 sqlplus /nolog3.将数据库启动到RESTRICTED模式下做字符集更改&#xff1a;SQL> conn /as sy…

input点击事件不能用_用js简单写一个计算器

嗨&#xff0c;大家好&#xff0c;今天给大家带来的是一个计算器首先要知道我们常用的计算器都有哪些按键&#xff0c;有0~9&#xff0c; , - , *, /&#xff0c;还有 和.&#xff0c;基本是这些按键&#xff0c;然后我们去创建一些按键。之后我们去设置样式&#xff0c;设置完…

全国计算机等级考试题库二级C操作题100套(第74套)

第74套&#xff1a; 人员的记录由编号和出生年、月、日组成,N名人员的数据已在主函数中存入结构体数组std中。函数fun的功能是&#xff1a;找出指定出生年份的人员&#xff0c;将其数据放在形参k所指的数组中&#xff0c;由主函数输出&#xff0c;同时由函数值返回满足指定条件…

入驻支付宝开放平台并创建应用的基本流程

https://www.csweigou.com/article/1990.html 转载于:https://www.cnblogs.com/DixinFan/p/9519876.html

php判断信用卡,php校验信用卡卡号代码

[php]代码库// This function will take a credit card number and check to make sure it// contains the right amount of digits and uses the Luhn Algorithm to// weed out made up numbersfunction validateCreditcard_number($credit_card_number){// Get the first dig…

浮点数的表示和基本运算 【转载】

1 浮点数的表示通常&#xff0c;我们可以用下面的格式来表示浮点数 SPM其中S是符号位&#xff0c;P是阶码&#xff0c;M是尾数对于IBM-PC而言&#xff0c;单精度浮点数是32位&#xff08;即4字节&#xff09;的&#xff0c;双精度浮点数是64位&#xff08;即8字节&#xff09;的…

python list长度_python的入门阶段 编程思维100题 我跟罗志祥没关系

"""python的入门阶段 编程思维100题&#xff08;适合学习了1周--1个月的新人&#xff09; 第五题&#xff1a;我跟罗志祥没关系 朋友圈大家每天都看&#xff0c;那用python如何实现它那&#xff1f; 很简单&#xff0c;朋友的信息&#xff08;名字&#xff0c;头…

全国计算机等级考试题库二级C操作题100套(第75套)

第75套&#xff1a; 给定程序中&#xff0c;函数fun的功能是&#xff1a;对形参ss所指字符串数组中的M个字符串按长度由短到长进行排序。ss所指字符串数组中共有M个字符串&#xff0c;且串长<N。请在程序的下划线处填入正确的内容并把下划线删除&#xff0c;使程序得出正确…

php7过滤,PHP7过滤unserialize()

PHP7引入了过滤 unserialize()函数以在反序列化不受信任的数据对象时提供更好的安全性。它可以防止可能的代码注入&#xff0c;使开发人员能够使用序列化白名单类。示例class MyClass1 {public $obj1prop;}class MyClass2 {public $obj2prop;}$obj1 new MyClass1();$obj1->…

windows系统查找文件-通配符的使用

在windows中可以使用通配符“* ”、“? ”查找文件。对于相同字符开头的单词和相同字符结尾的单词可以用“<”和“ >”通配符查找单词。1、如果要查找&#xff1a; 任意单个字符 &#xff1a;键入 ? 例如&#xff0c;s?t 可查找“sat”和“set”……。2、任意字符串 :…

python为什么是动态语言_python为什么是动态语言

首先要理解什么是动态语言&#xff1a;通俗地说&#xff1a;能够在运行时修改自身程序结构的语言&#xff0c;就属于动态语言。那怎样才算是“运行时修改自身程序结构”呢&#xff1f;比如下面这几个例子都算&#xff1a;在运行时给某个类增加成员函数及成员变量&#xff1b;在…

Android ----中文Api 百度地图

Android中文API最新中文版 [复制链接] kupekupe当前离线在线时间1 小时e望0 点最后登录2011-2-17注册时间2011-2-17积分12阅读权限10UID1341171 主题 0 好友 12 积分No.1 开发小菜鸟 No.1 开发小菜鸟, 积分 12, 距离下一级还需 88 积分升级 12% 当前用户组为 No.1 开发小菜鸟 …