Verilog笔记

硬件描述语言Verilog


综合与仿真

Verilog,用代码的形式描述硬件的功能,最终在硬件电路上实现该功能。

Verilog代码
综合器
FPGA

综合:编写rtl代码,从库里面选择用到的问期间,这些期间会按照“逻辑”搭建成“门”电路。

不可综合或不推荐综合代码:initial, task/function, for, while/repeat/forever, integer, 三态门, casex/casez, force/wait/fork, #n

推荐使用的代码:reg/wire, parameter, assign/always, if else/case, 算术运算符(+ - * / %), 赋值运算符(组合逻辑= 时序逻辑<=), 关系运算符(<= != >), 逻辑运算符(&& || !), 位运算符(~ | ^ &), 移位运算符(<< >>), 拼接运算符({})

模块结构

模块(module)是Verilog的基本描述单位,是用于描述某个设计功能或结构及与其他模块通信的外部接口。

Verilog HDL模块
模块声明
端口定义
数据类型声明
逻辑功能描述

例:端口定义

%端口定义
module block1(a,b,c,d)
    %I/O说明
    input a,b,c;
    output d;
    %信号类型声明
    wire x;
    %功能描述
    assign d=a|x;
    assign x=(b&~c);
endmodule

三种描述电路的逻辑功能方法:

  • assign语句:assign x=(b&~c);
  • 用元件例化(instantiate):and myand1(f,a,b,c);
  • always块语句:需要用begin end语句成对出现。

需要先建立以下概念:

  • 只有寄存器类型的信号才可以在always和initial 语句中进行赋值,类型定义通过reg语句实现。
  • always 语句是一直重复执行,由敏感表(always 语句括号内的变量)中的变量触发。
  • always 语句从0 时刻开始。
  • 在begin 和end 之间的语句是顺序执行,属于串行语句。

例:计数器

%计数器
always @(posedge clk)
    begin
        if(load)
            out=data;
        else
            out=data+1+cin;
    end

例:8位全加器

module adder8(cont,sum,a,b,cin);
    output cout;
    output [7:0]sum;
    input [7:0]a,b;
    input [7:0]cin;
    assign{cout,sum}=a+b+cin;
endmodule

例:8位计数器

module counter8(ouot,cout,data,load,clk);
    output [7:0]out;
    input cout;
    input [7:0]data;
    input load,cin,clk;
    reg [7:0]out;
    always @(posedge clk)
        begin
            if(load)
                out<=data; //同步预置数据
            else
                out<=out+cin;//加1计数
        end
    assign cout=&out&cin;//如果out和cin所有位为1,则cout为1
endmodule         

基本语法

符号

  • 空白:空格\b、tab\t、换行\n
  • 注释://
  • 关键字:小写

image-20210814200930803

  • 标识符:首字母必须是字母或下划线,关键字不能用来做标识符

常量

程序运行中不能改变的

  • 整数

    • 简单十进制格式

    • 基数格式

      如果定义的长度比为常量指定的长度长,通常在左边填0 补位。但是如果数最左边一位为x 或z ,就相应地用x 或z 在左边补位。例如:

      10'b10 左边添0 占位, 0000000010
      10'bx0x1 左边添x 占位, x x x x x x x 0 x 1
      如果长度定义得更小,那么最左边的位相应地被截断。例如:
      3 ' b1001 _ 0011 与3'b011 相等
      5'H0FFF 与5'H1F 相等
      
  • parameter常量(或符号常量)

    • 格式 parameter 参数名1=表达式,参数名2=表达式;

      image-20210814201636055

  • x和z值

    • x表示不确定值(就是我可以不在乎他是0和1,如卡诺图中有的值)
    • z表示高阻。一个为z 的值总是意味着高阻抗,一个为0 的值通常是指逻辑0 。在门的输入或一个表达式中的为“z ”的值通常解释成“x ”。 x 值和z 值都是不分大小写的,也就是说,值0x1z 与值0X1Z 相同。
  • 实数:十进制格式和科学计数法两种,Verilog中实数通过四舍五入的方法变为整数

image-20210814201422965

变量

nets型

image-20210814202520213

wire型向量(总线)

wire [7:0] wire1,wire2;表示线位宽为8,共有2条总线

  • wire 和 tri 定义
    线网类型主要有wire 和tri 两种。线网类型用于对结构化器件之间的物理连线的建模。如器件的管脚,内部器件如与门的输出等。以上面的加法器为例,输入信号A, B是由外部器件所驱动,异或门X1的输出S1是与异或门X2输入脚相连的物理连接线,它由异或门X1所驱动。

  • 两者区别:tri 主要用于定义三态的线网。

  • 由于线网类型代表的是物理连接线,因此它不存贮逻辑值。必须由器件所驱动。通常由assign进行赋值。如 assign A = B ^ C

  • 当一个wire 类型的信号没有被驱动时,缺省值为Z(高阻)。

  • 信号没有定义数据类型时,缺省为 wire 类型。如一位全加器的端口信号 A, B, SUM等,没有定义类型,故缺省为wire 线网类型

reg型
reg 是最常用的寄存器类型,寄存器类型通常用于对存储单元的描述,如D型触发器、 ROM等。存储器类型的信号当在某种触发机制下分配了一个值,在分配下一个值之时保留原值。但必须注意的是, reg 类型的变量,不一定是存储单元,如在always 语句中进行描述的必须用reg 类型的变量。
reg 类型定义:`reg [msb:lsb] reg1, reg2, ... ,reg N`,msb 和lsb 定义了范围,并且均为常数值表达式。范围定义是可选的;如果没有定义范围,缺省值为1 位寄存器。在过程中被赋值的信号,往往代表触发器。

​ 寄存器类型的值可取负数,但若该变量用于表达式的运算中,则按无符号类型处理。

  • reg:常代表触发器;
  • integer:32位带符号整型
  • real:64位带符号实数型变量
  • time:无符号时间变量

例:用reg型变量生成逻辑组合举例

module rw1(a,b,out1,out2);    input a,b;    output out1,out2;    reg out1;    reg out2;    assign out2=a;    always @(b)        out1<=~b;endmodule

reg型变量既可以生成触发器,也可以生成组合逻辑,wire型变量只能生成组合逻辑。

语句

image-20210814204306343

过程语句

initial语句:仅执行一次,常用于仿真初始化

always语句:不断重复执行,只要满足规定条件。

​ always块有过的敏感信号是,一定要用if-else-if语句,而不能采用多个if语句!!!

always@(敏感信息列表如:posedge clk or negedge rst)//clk上升沿或rst下降沿    begin        只能使用regester型变量        一个变量只能在一个always中赋值    end
块语句

begin-end语句:顺序

fork-join语句:并行

例:用顺序块和延迟控制组合产生一个时序波型(8位)

parameter d=50;reg [7:0] r;begin    #d r='h45';    #d r='hF1';    #d r='h10';    #d r='hB2';end

例:用并行块和延迟控制组合产生一个时序波形(8位)

reg [7:0] r;fork    #20 r='h45';    #40 r='hF1';    #60 r='h10';    #80 r='hB2';join
赋值语句

assign语句只能给wire类型赋值

always通常用register类型赋值

非阻塞赋值:b<=a,在块结束时才完成赋值操作

always @(posedge clk)    begin        b<=a;        c<=b;    end

​ 在一次正跳变后b=a,c=!=a;上述代码相当于两个串联的触发器,有一个时钟周期的落后。

阻塞赋值:b=a,在该语句结束时就完成赋值操作

条件语句

使用条件语句应注意列出所有条件分支,否则当条件不满足时,编译器会生成一个锁存器保持原值

在组合逻辑电路设计中,应避免生成隐含锁存器时序电路设计则不会。

image-20210814214501193

if-else语句

例:十进制计数器

module count10(clk,reset,load,a,q,cout);    input clk,reset,load;    input[3:0] a,q;    output cout;    reg[3:0] q;    reg cout;    always@(posedge clk)        begin            if(reset)//同步复位                begin                    q<=0;                    cout<=0;                end            else if(load)//同步置数                q<=a;            else if(q==9)//计数                begin                    q<=0;                    cout<=1;                end            else                begin                    q<=q+1;                    cout<=0;                end        endendmodule

例:

module code4_2(in,out);    input [3:0] in;    output [1:0] out;    reg [1:0] out;    always @(in)	//不是用时钟,赋值用的阻塞方式,当in变化是触发        begin            if(in[3]) out=3;            else if(in[2]) out=2;            else if(in[1]) out=1;            else out=0;        endendmodule

case语句:case/casex/casez三种

例:

module code4_3(P,Q):    input [3:0] P;    output [1:0] Q;    reg [1:0] Q;    always@(P)        begin            case(P)                4'b0001:Q=2'b11;	//4位的二进制数0001                4'b0010:Q=2'b10;                4'b0011:Q=2'b01;                4'b0100:Q=2'b00;                default:Q=2'bXX;	//xx是无所谓的值            endcase        endendmodule
循环语句

四种形式的循环语句for while repeat forever,只能在initial always中使用。

例:用for语句描述的7人投票表决器

module vote7(pass,vote);    output pass;    input [6:0] vote;    reg [2:0] sum;    integer i;    reg pass;    always @(vote)        begin            sum=0;            for(i=0;i<=6;i=i+1)                if(vote[i]) sum =sum+1;            if(sum[2]) pass=1;            else pass=0;        endendmodule

例:用repeat循环语句实现4位乘法器

module multiplier4_repeat(a,b,out);    parameter width=4;    input [width:1] a,b;    output [2*wisth:1] out;    reg[2*width:1] out;    reg[2*wisth:1] regb;    reg[2*wisth:1] rega;    integer i;    always@(a or b)        begin            out=0;            rega=0;            regb=0;            repeat(width)                begin                    if(regb[1]==1)                        out=out+rega;                    rega=rega<<1                    regb=regb<<1                end        endendmodule

例:用while语句对一个8位的二进制数中值为1的位进行计数

module count1s_while(count,rega,clk);
    output [3:0] count;
    input [7:0] rega;
    reg [3:0] count;
    always@(posedge clk)
        begin:count1
            reg[7:0] tempreg;
            count = 0;
            tempreg = rega;
            while(tempreg)
                begin
                    if(tempreg[0])
                        count =count+1;
                        tempreg = tempreg>>1;
                end
        end
endmodule

forever:无条件执行,一般用在initial中,常用在仿真。

task任务

任务的关键字为taskendtask

image-20210814221202708

add1(d1,d2,out)对上述任务调用

函数

函数的关键字为functionendfunction

image-20210814221832978

out=decode(d)对上述函数进行调用

例:用function模块设计ALU单元

image-20210814222246501

编译预处理

“编译预处理”是指在编译时,先对这些语句进行预处理,然后再将结果与Verilog HDL源程序一起进行编译。

``define`语句

``define`语句:用一个简单的标识符(或称宏名)来代表一个复杂的字符串(即宏内容)。

`define width 8	//用width代替数字8reg[`width-1:0] a,b,c;

define宏定义+`inlude "file.v"文件包含来实现参数模块化设计:

  1. 新建参数模块文件(我命名为para.v);

  2. 在para.v文件中使用’define宏定义参数(部分、有错误):

//`define+name+参数  `define   STATE_INIT  3'd0`define   STATE_IDLE  3'd1``define   STATE_WRIT  3'd2`define   STATE_READ  3'd3`define   STATE_WORK   3'd4`define   STATE_RETU  3'd5

3. 在需要调用参数的文件init.v中使用``include “para.v”`:

``include “para.v”`

4. 在init.v文件需要参数的地方使用`name 调用(部分):

​ state_init <= `STATE_INIT;

​ 若直接在module中通过localparam或者parameter进行参数定义的话,会带来两个问题:代码长度增加,不够美观;不利于参数和代码修改;

define` 与`localparam`和`parameter`最大的区别就是define可以跨文件传递参数;parameter只能在模块间传递参数;localparam`只能在其所在的module中起作用,不能参与参数传递。

``include`语句

``include`语句:一个源文件可将另一个源文件的内容调用。vivado在一个project中可以自动形成调用关系,无需再include。

关于"文件包含"处理的四点说明:

  1. 一个include命令只能指定一个被包含的文件,如果要包含n个文件,要用n个include命令。注意下面的写法是非法的``include"aaa.v"“bbb.v”`

  2. ``include命令可以出现在Verilog HDL源程序的任何地方,被包含文件名可以是相对路径名,也可以是绝对路径名。例如:’include"parts/count.v"`

  3. 可以将多个include命令写在一行,在include命令行,只可以出空格和注释行。例如下面的写法是合法的。

'include "fileB" 'include "fileC" //including fileB and fileC

  1. 如果文件1包含文件2,而文件2要用到文件3的内容,则可以在文件1用两个``include`命令分别包含文件2和文件3,而且文件3应出现在文件2之前。

这个``includel`里面定义的主要是一些参数,然后给另外的文件进行一个调用

``timescale`语句

``timescale`语句:时间尺度语句,用于定义时间和时间精度。

//格式 `timescale 时间单位/时间精度
`timescale 1ns/10ps

时间单位:用于定义模块中仿真时间和延迟时间的基准单位。

时间精度:用来声明该模块的仿真时间和延迟时间的精确程度。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值