0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

FPGA学习-使用逻辑门和连续赋值对电路建模

FPGA设计论坛 来源:未知 2023-03-17 21:50 次阅读

使用逻辑门和连续赋值对电路建模,是相对详细的描述硬件的方法。使用过程块可以从更高层次的角度描述一个系统,称作行为级建模(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原理图如下 :

按软件编程思维考虑,循环语句是一条一条执行的一个过程。而从上面的设计结果来说,显然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块的执行,这样只会终止当前的循环状态,而不会影响循环的下一次迭代。







精彩推荐



至芯科技-FPGA就业培训来袭!你的选择开启你的高薪之路!3月28号北京中心开课、欢迎咨询!
基于FPGA的快速傅立叶变换
FPGA职业生涯的五个层次
扫码加微信邀请您加入FPGA学习交流群




欢迎加入至芯科技FPGA微信学习交流群,这里有一群优秀的FPGA工程师、学生、老师、这里FPGA技术交流学习氛围浓厚、相互分享、相互帮助、叫上小伙伴一起加入吧!


点个在看你最好看





原文标题:FPGA学习-使用逻辑门和连续赋值对电路建模

文章出处:【微信公众号:FPGA设计论坛】欢迎添加关注!文章转载请注明出处。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • FPGA
    +关注

    关注

    1629

    文章

    21729

    浏览量

    603016

原文标题:FPGA学习-使用逻辑门和连续赋值对电路建模

文章出处:【微信号:gh_9d70b445f494,微信公众号:FPGA设计论坛】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    TTL逻辑的种类及应用

    在数字电子领域,TTL(晶体管-晶体管逻辑逻辑是构建复杂数字系统的基石。TTL技术以其可靠性、成本效益和广泛的应用而闻名。 TTL逻辑
    的头像 发表于 11-18 10:36 405次阅读

    FPGA数的计算方法

    我们在比较FPGA的芯片参数时经常说某一款FPGA是多少万的,也有的说其有多少个LE,那么二者之间有何关系呢? FPGA等效数的计
    的头像 发表于 11-11 09:45 268次阅读
    <b class='flag-5'>FPGA</b><b class='flag-5'>门</b>数的计算方法

    逻辑电路的类别和性能参数

    除了分立元件逻辑(二极管和晶体管),对于集成电路逻辑大致可以分为两类。
    的头像 发表于 11-06 09:44 539次阅读
    <b class='flag-5'>逻辑</b>门<b class='flag-5'>电路</b>的类别和性能参数

    常用逻辑芯片有哪些

    逻辑是数字电路中的基本构建块,它们执行基本的逻辑运算,如与(AND)、或(OR)、非(NOT)、异或(XOR)等。逻辑
    的头像 发表于 09-24 10:48 2064次阅读

    常用的组合逻辑电路有哪些

    : 基本逻辑 基本逻辑是构成组合逻辑电路的基础,包括与门(AND)、或(OR)、非门(NO
    的头像 发表于 07-30 14:41 1500次阅读

    数字系统的核心:逻辑电路

    数字逻辑是一种电子电路,它根据输入端的数字信号组合做出逻辑决策。数字逻辑可以有多个输入,例如
    的头像 发表于 07-16 10:04 1200次阅读
    数字系统的核心:<b class='flag-5'>逻辑</b>门<b class='flag-5'>电路</b>

    FPGA学习笔记-关于FPGA资源

    在和别人讨教FPGA的知识时,初步得到的有关FPGA的印象是:通过程序或者其它设置,把集成在芯片中的各种逻辑电路组合起来使用。就像用CMOS、TTL门电路搭建处理
    发表于 05-22 18:27

    FPGA基础知识学习

    可以执行各种逻辑操作(如与、或、非、异或等),将输入信号转换为输出信号。这些逻辑通过FPGA内部的互连网络相互连接,从而形成一个复杂的逻辑电路
    发表于 04-29 23:26

    如何快速入门FPGA

    学习Verilog的语法、结构以及如何使用它进行算法级、级、开关级等多种抽象设计层次的建模。 掌握FPGA开发工具: 选择一款适合你的FPGA
    发表于 04-28 09:06

    如何快速入门FPGA

    学习Verilog的语法、结构以及如何使用它进行算法级、级、开关级等多种抽象设计层次的建模。 掌握FPGA开发工具: 选择一款适合你的FPGA
    发表于 04-28 08:54

    FPGA学习笔记-入门

    大概10年前,大学同学建议我学习DSP。当因为工作忙,也只是简单学习了DSP的一些基础知识,没有进一步深入学习和时间。结果现在,好像DSP已经不再是主流了,现在有了FPGA。 现在想想
    发表于 04-09 10:55

    fpga开发是什么意思

    配置内部的逻辑和连接关系来实现特定的电路功能。因此,FPGA开发实质上是一种将软件算法或硬件电路转化为可编程
    的头像 发表于 03-15 14:28 1180次阅读

    数字电路逻辑电路符号图

    把基本逻辑运算的电子电路称之为逻辑电路。在数字电路关系应用中,逻辑
    的头像 发表于 02-04 14:58 2993次阅读
    数字<b class='flag-5'>电路</b>中<b class='flag-5'>逻辑</b><b class='flag-5'>门</b>的<b class='flag-5'>电路</b>符号图

    异或门的逻辑符号和逻辑电路组成

    异或门(XOR gate)是数字逻辑电路中常用的一种逻辑。它的作用是对两个输入信号进行逻辑运算,输出一个结果。
    的头像 发表于 02-04 14:18 1w次阅读
    异或门的<b class='flag-5'>逻辑</b>符号和<b class='flag-5'>逻辑电路</b>组成

    组合逻辑电路之与或逻辑

    逻辑电路由多个逻辑组成且不含存储电路,对于给定的输入变量组合将产生确定的输出,则这种逻辑电路称为组合
    的头像 发表于 02-04 11:46 1682次阅读
    组合<b class='flag-5'>逻辑电路</b>之与或<b class='flag-5'>逻辑</b>