FPGA/verilog 学习笔记(3)—— verilog程序框架

一、注释

  • verilog中有两种注释方式,同C语法

二、关键字

  • 全部关键字[
    在这里插入图片描述

  • 常用关键字
    在这里插入图片描述

三、 Verilog程序框架

1. 模块

(1)基础概念

  1. Verilog程序的基本单元是 “模块(block)” (类似C的函数)

  2. 一个模块由两部分组成

    • 接口描述

      • 端口定义

      • I/O说明

    • 逻辑功能描述

      • 内部信号声明

      • 功能定义

  3. 可综合和不可综合

    • 可综合的模块:可以用综合工具生成实际电路结构的模块

    • 不可综合的模块:不能生成实际电路结构的模块,常用于testbench

(2)定义一个模块

  • 示例代码
module block(a,b,c,d);	//端口定义
   	input a,c;			//I/O说明
   	output c,d;
   	
   	assign c = a | b;		//功能定义
   	assign d = a & b;
   
endmodule
  1. 模块用 mouduleendmodule 包起来

  2. moudle后接模块名

  3. 模块名后是参数列表,每个参数是一个端口,别忘了;

  4. I/O说明

    • input 指定端口为输入端口,默认是wire类型。写成input reg则为reg类型

    • ouput 指定端口为输出端口,默认是wire类型。写成output reg则为reg类型

    • I/O说明语句也可以和端口定义写在一起

      module block(
          input a,
          input b,
          output c,
          output d
      	);	
      
  5. 这段程序对应的接口描述和逻辑功能

    • 接口描述
      在这里插入图片描述

    • 逻辑功能
      在这里插入图片描述

  6. 模块基本结构
    在这里插入图片描述

(3)功能定义的三种方法

  • 示例代码
//端口定义 + I/O说明---------------------------------------------
module flow_led(
    input               sys_clk  ,  //系统时钟
    input               sys_rst_n,  //系统复位,低电平有效 
    output  reg  [3:0]  led         //4个LED灯,这是一个1位位宽reg的数组,数组长4
    );

//内部信号声明--------------------------------------------------
reg [23:0] counter;					//reg define,这是一个24位位宽reg类型变量
                                                                                                                                                                           //功能定义-----------------------------------------------------                 
//计数器对系统时钟计数,计时0.2秒
always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n)
        counter <= 24'd0;
    else if (counter < 24'd10)
        counter <= counter + 1'b1;
    else
        counter <= 24'd0;
end

//通过移位寄存器控制IO口的高低电平,从而改变LED的显示状态
always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n)
        led <= 4'b0001;
    else if(counter == 24'd10) 
        led[3:0] <= {led[2:0],led[3]};
    else
        led <= led;
end

endmodule 
  • 功能定义的三种方法(以下三种逻辑功能是并行的)
    • assign语句:描述组合逻辑
    • always语句:描述组合/时序逻辑
      • always块中,逻辑是顺序执行
      • always块之间,逻辑是并行的
    • 实例化元件:如and #2 u1(q,a,b); //and例化一个与门模块

(4)模块的调用

  1. 模块调用类似C中函数调用,信号通过模块端口在模块之间传递

  2. 示例

    • 被调模块(子模块)

      在这里插入图片描述

    • 主调模块(主模块)

      在这里插入图片描述

      • 这里最下面就是例化time_count模块

        • #后接的是:传入的parameter 类型数据
        • ()中的是:传递的wirereg 类型数据
      • 注意

        • 子模块的输入端口(input端口),连接的信号可以是**wire 或者 reg 类型**

        • 子模块的输出端口(output端口),连接的信号必须是wire 类型

        • 传递的信号位宽必须保持一致

    • 另外一种连接方式:直接把主模块的信号放到子模块参数列表中,这要求信号排列顺序一致

      在这里插入图片描述

2. 结构语句

(1)initial语句

  1. initial语句在模块中只在最开始执行一次

  2. 常用于测试文件的编写,用来产生仿真测试信号(激励信号),或者用来对存储器变量赋值

  3. 如果initial语句有多行,需要用beginend 把它们括起来(类似C的大括号)

  4. 示例

//给输入信号的初始值
initial begin					
    //这三条同时执行
	sys_clk			<= 	1'b0;	
	sys_rst_n		<= 	1'b0;
	touch_key		<= 	1'b0;
    
	//#是延时
    #20 sys_rst_n	<= 1'b1;	//过20ns,拉高sys_rst_n
	#10 touch_key	<= 1'b1;	//再过10ns,touch_key
	#30 touch_key	<= 1'b0;	//再过30ns....
	#110 touch_key	<= 1'b1;
end

(2)always语句

  1. always语句一直在不断地重复活动

  2. 只有和一定的时间控制结合在一起才有作用(比如下面那个#10

  3. 如果always语句有多行,需要用beginend 把它们括起来(类似C的大括号)

  4. 示例

//产生50Mhz时钟,周期20us
always #10 sys_clk <= ~sys_clk;	
  1. 波形(最上面一行是clk信号)

    在这里插入图片描述

  2. always的时间控制(触发控制)

    • 触发方式

      • 简单延时always #10 sys_clk <= ~sys_clk;

      • 沿触发:常用于描述时序逻辑行为

        在这里插入图片描述

      • 电平触发:常用来描述组合逻辑行为

        在这里插入图片描述
        如果组合逻辑的输入变量很多,编写敏感列表会很麻烦,这时可以@(*)表示对后面语句块中所有输入变量的变化都是敏感的
        在这里插入图片描述

    • 触发信号

      • 单个信号
      • 多个信号,中间需要or连接
    • 引起触发的多个事件名/信号名组成的列表称为 “敏感列表

(3)组合逻辑电路和时序逻辑电路

  • 数字电路分为组合逻辑电路和时序逻辑电路两类

    • 组合逻辑电路

      • 任意时刻的输出仅仅决定于此时刻的输入信号,与电路原来的状态无关
    • 时序逻辑电路

      • 任意时刻的输出决定于此时刻的输入信号以及电路原来的状态,因此时序逻辑电路必须具有记忆功能

    在这里插入图片描述

3. 赋值语句

  • reg只能在initial和always块中赋值
  • wire只能通过assign或通过模块输入输出端口赋值

(1)阻塞赋值

  1. 格式:b = [delay] a; (这里的delay是语句内延时)

  2. 操作:计算右值,内部延时,更新左值。

  3. 所谓 “阻塞” :指同一个always块中,后面的赋值语句是在前一句赋值语句结束之后才开始赋值的

  4. 示例

  • 示例(1)
    在这里插入图片描述

    • 由于采用了阻塞赋值,执行过程如下
      • a = 0; 这时a=0,b=2,c=3
      • b = a; 这时a=0,b=0,c=3
      • c = b; 这时a=0,b=0,c=0
    • 从波形图来看,就好像三条语句在同一时刻执行完一样
  • 示例(2)

initial begin 
	t = #5 0;	// 时间为(0,5]时,t=x
	t = #4 1;	// 时间为(5,9]时,t=0
	t = #10 0;  // 时间为(9,19]时,t=1
end

(2)非阻塞赋值

  1. 格式:b <= [delay] a;(这里的delay是语句内延时)

  2. 操作:赋值开始的时候,计算右值;进行内部延时;赋值结束时,更新左值

  3. 所谓 “非阻塞”:指在计算右值和更新左值期间,允许其他的非阻塞赋值语句同时计算右值和左值

  4. 非阻塞赋值只能用于对reg类型的变量进行赋值,因此只能用在 initial块always块

  5. 示例

  • 示例(1)
    在这里插入图片描述
    • 由于采用了非阻塞赋值,执行过程如下
      • a = 0; b = a; c = b; 三行同时开始计算右值,然后同时更新左值,导致a的值变为0,b的值变为一开始a的值(1),c变为一开始b的值(2)
  • 示例(2)
initial begin 
	t = #5 1;	
	t = #4 0;	
	t = #10 0;  
end
// 时间为(0,4]时,t=x,时间为(4,5]时,t=0,时间为(5,10]时,t=1,时间为(10,正无穷]时,t=0,
  • 示例(3)
    • 这里我需要在一个时钟周期内先读再写一个reg类型变量,通过非阻塞赋值来实现它
    • 分析以下代码,当clk的下降沿来到时,两个always语句同时执行。reg_a的值在第二条always中被读取,在此时刻结束前的最后一刻,reg_a的值在第一条always语句中被刷新
always @(negedge clk)
	reg_a <= data;			// 写
always @(negedge clk)
	reg_b <= reg_a;			// 读

(3)使用原则

  1. 对always语句块外用到的变量进行赋值时,使用<=,计算中间结果时,使用=

  2. 注意

    • 同一个always块中不要两种赋值混用(不知道该生成哪种电路了)
    • 不允许在多个always块中对同一个变量进行赋值(因为是并行执行,这样会冲突)

(4)assign

  • Verilog语言使用一个或多个模块对数字电路建模,通常可以用三种方式:

    1. 结构描述方式:即调用其它已定义好的低层模块或直接调用Verilog内部度基本门级元件描述电路结构和功能。

    2. 数据流描述方式:连续使用赋值语句(assign)对电路的逻辑功能进行描述。

    3. 行为风格描述方式:使用过程块语句结构(initial和always语句)和比较抽象的高级程序语句对电路的逻辑功能进行描述。

    4. 也可以把这些方式混用,称为混合设计风格描述方式

  • assign语句就属于第二种。

    • 连续赋值语句用于对wire型变量进答行赋值,它由关键字内assign开始,后面跟着由操作数和运算符组成的逻辑表达式。

    • assign语句中,左边变量的数据类型必须是wire模块的input和output如果容不特别声明类型,默认是wire类型。

    • 格式assgin [delay]LHS_net = RHS_expression

    • assign右边的操作数无论何时发生变化,右边的表达式都会被重新计算,并且在指定的延时后(默认0),计算得到的值被赋予等号左边的线网变量。因此也称为 “连续赋值语句”

    • assign、always、initial等是并发执行的

    • 例如:

      wire A,B,SEL,L;//声明4个线型变量
      assign L=(A&~SEL)|(B&SEL);//连续赋值
      
  • 总之

    • assign常习惯用在模块的output赋值
      • 如果直接写out = ...,这是把内部的reg信号直接作为输出,这里其实暗含了用根导线直接把reg的输出与out连接起来
      • 如果写assign out = ...,这是把上面的隐含的线显式表示了
      • 这二者的RTL没有太大变化
      • 注意out是一个wire类型
    • 从实用角度来说,assign意义比较大。当内部有多个信号需要输出,可是输出引脚只有一个,那么这时就可以进行选择。如下:assign lholda= (条件)? (lholda_ra): lholda_rb; 可以嵌套使用。(这个真的很很很很常用!!)

4. 条件语句

  • 条件语句必须在过程块(即由 initialalways 引导的块语句)中使用

(1)if_else语句

  1. 格式
//写法1--------------------------------------
if(a > b)
   	out = data_1;
   	
//写法2--------------------------------------
if(a > b)
	out = data_1;
else
   	out = data_2;
   	
//写法3--------------------------------------
if(表达式1)
   	语句1;
else if(表达式2)
   	语句2;
   ...
else if(表达式n)
   	语句n;
else
   	语句n+1;

  1. 说明

    • 允许一定程度的简写

      • if(a) 等同于 if(a==1)

      • if(!a) 等同于 if(a!=1)

    • if 语句对表达式的值进行判断

      • 若为 0 ,x ,z 则按假处理
      • 若为 1 则按真处理
    • ifelse 语句后面的操作语句可以用 beginend 包含多行语句

    • 允许 if 语句的嵌套

(2)case/casez/casex语句

  1. 格式
    在这里插入图片描述

  2. 说明

    • 分支表达式的值互不相同
    • 所有表达式的位宽必须相等
      • 不要省略位宽用'bx代替n'bx,因为省略写法默认位宽32位,如果case的信号不是32位易出错
    • casez语句
      • 比较时不考虑表达式中的高阻值z

      • 示例分析
        在这里插入图片描述

        • 第一个case,只要求高四位是1100
        • 第二个case,要求高六位是1100XX
    • casex语句
      • 比较时不考虑表达式中的高阻值z和不定值x
      • 如果程序还是上图那样,这时第一个和第二个case判断的一样了,报错
  • 1
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 《FPGA自学笔记——设计与验证》是一本关于FPGA设计和验证的入门教材。本书以VHDL和Verilog两种HDL语言为主要工具,通过实例讲解FPGA的基本概念、设计流程和验证方法。同时,本书还通过实例演示了如何使用Xilinx ISE和ModelSim这两个主流软件工具进行FPGA设计和验证。 本书的内容分为五个部分,分别是FPGA基础知识、FPGA设计流程、FPGA验证方法、FPGA性能优化以及FPGA应用实例。其中,FPGA基础知识部分介绍了FPGA的基本构成、组成部件以及通用数字电路设计知识;FPGA设计流程部分从设计输入、RTL设计、布局布线、实现生成等方面详细介绍了FPGA设计流程;FPGA验证方法部分主要介绍了功能验证和时序验证这两个方面的知识;FPGA性能优化部分介绍了FPGA的几种性能指标以及如何通过一定的优化方法提高FPGA性能;FPGA应用实例部分通过几个实例演示了如何应用FPGA进行数字电路设计。 本书的难度适中,适合初学者学习和参考,同时也可以作为FPGA初学者的参考书籍。本书涉及的知识点较为全面,可以为初学者提供一个全面的FPGA设计和验证入门指南。其内容易于理解,实例讲解深入浅出,对于想要学习FPGA设计和验证的人群来说是一本很好的参考书。 ### 回答2: 《FPGA自学笔记——设计与验证》PDF是一本很好的自学FPGA的书籍。这本书包含了FPGA基本概念、设计流程、Verilog HDL语言、开发工具、测试方法等多个主题,非常详尽地介绍了FPGA的基本知识和开发技巧。读这本书可以帮助我们更好地理解FPGA的原理和功能,从而更加熟练地掌握FPGA的设计和验证。 此外,这本书还提供了很多实例来帮助我们更好地理解FPGA的设计和验证。这些实例包含多种应用场景,例如数字逻辑、时序控制、通信等,能够帮助我们从不同角度学习FPGA的相关知识。而且,这本书还提供了实验指导,通过做实验来让我们更深入地理解FPGA的各种知识和技能。 总之,这本书《FPGA自学笔记——设计与验证》PDF是一本非常好的FPGA自学指南,通过阅读这本书,我们可以掌握FPGA基本知识和开发技能,更好地应用FPGA进行各种应用开发。我相信,读完这本书,你一定能够对FPGA有更深刻的认识,并且能够灵活运用FPGA进行各种应用开发。 ### 回答3: 《FPGA自学笔记——设计与验证》是一本以FPGA为研究对象的书籍。它详细介绍了FPGA的诸多特性和应用。该书主要分为两部分,第一部分介绍了FPGA的基本概念,并讲解了Verilog语法和使用方法。第二部分是实践性较强的部分,通过编写案例代码进行实际操作。 该书着重强调了FPGA设计流程,通过案例演示了FPGA设计的全过程。该书还提供了大量的练习题和案例代码,读者可以通过反复练习和实际操作,逐渐掌握FPGA的设计和验证技能。 总体来说,《FPGA自学笔记——设计与验证》是一本非常实用的FPGA入门教材。它从基础知识入手,循序渐进地讲解了FPGA的各个方面。并且,该书重点讲解了如何运用Verilog语言进行FPGA设计,这对FPGA初学者来说是一个非常实用的指南。 如果你对FPGA领域感兴趣,且希望通过自学来掌握FPGA的基本操作和设计方法,那么《FPGA自学笔记——设计与验证》是一本非常值得推荐的书籍。  

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云端FFF

所有博文免费阅读,求打赏鼓励~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值