Verilog硬件编程基础语法笔记

Verilog笔记

行为仿真

assign:

assign 相当于连线,一般是将一个变量的值不间断地赋值给另一个变量,就像把这两个变量连在一起,所以习惯性的当做连线用,比如把一个模块的输出给另一个模块当输入。

wire型变量赋值,wire 是线网,相当于实际的连接线,如果要用assign直接连接,就用wire型变量。wire型变量的值随时变化。

(1)在Verilog module中的所有过程块(如initial块和always块)、连续赋值语句(如assign语句)和实例引用都是并行的。在同一module中这三者出现的先后顺序没有关系

(2)只有连续赋值语句assign实例引用语句可以独立于过程块而存在于module的功能定义部分。

(3)连续赋值assign语句独立于过程块,所以不能在always过程块中使用assign语句。只有连续赋值语句assign实例引用语句可以独立于过程块而存在于模块的功能定义部分

通过连续赋值语句描述3位加法器

module  adder ( count,sum,a,b,cin );
input [2:0] a,b;
input   cin;
output  count;
output [2:0] sum;
      assign {count,sum} = a + b + cin;
endmodule
通过连续赋值语句描述一个比较器

module compare ( equal,a,b );
output  equal;    //声明输出信号equal
input [1:0] a,b;  //声明输入信号a,b
  assign  equal =(a = = b)?1:0;
/*如果a、b 两个输入信号相等,输出为1。否则为0*/
endmodule

//版本2
module compare_2 ( equal,a,b );
output  equal;    //声明输出信号equal
input [1:0] a,b;  //声明输入信号a,b
reg equal;//

  always @(a,b)
    if(a==b)
      equal=1;
    else
      equal=0;
/*如果a、b 两个输入信号相等,输出为1。否则为0*/
endmodule


//版本3
module compare_3 ( equal,a,b );
output  equal;    //声明输出信号equal
input [1:0] a,b;  //声明输入信号a,b
reg equal;//

  always @(a,b)
  begin   //
    if(a==b)
      equal=1;
    else
      equal=0;
  end     //
assign  equal =(a = = b)?1:0;
/*如果a、b 两个输入信号相等,输出为1。否则为0*/
endmodule

always:

always @(a)表示如果a有变化就执行下面的语句

always @(sl or a or b)表示只要slab,其中若有一个变化时就执行下面的语句

Verilog程序包括4个主要部分:

  • 端口定义
  • I/O说明
  • 内部信号声明
  • 功能定义

I/O说明的格式:

  • 输入口 input[范围];
  • 输出口 output [范围];
  • 输入/输出口 inout [范围];
  • I/O说明也可以写在端口声明里。

内部信号说明:

  • reg[范围] 变量1,变量2…;
  • wire[范围] 变量1,变量2…;

模块中实现逻辑功能的3种方法:

(1)assign: assign c=a&b;

(2)用实例元件 :and #2 u1(q,a,b);

(3)用always

  • assign语句是描述组合逻辑最常用的方法之一。
  • always块既可用于描述时序逻辑,又可用于组合逻辑。

D触发器:

module new_dff(q,clk,d);
    input clk,d;
    output q;
    reg q;

      always @(posedge clk)
        q<=d;
endmodule

D触发器(带异步清除端)

module new_dff2(q,clk,d,clr);
input clk,d,clr;
output q;
reg q;

  always @(posedge clk or  posedge clr)
  begin
    if(clr)
      q<=0;
    else
      q<=d;
  end
endmodule

D触发器(带异步清除端和使能端)

module new_dff3(q,clk,d,clr,en);
output q;
input clk,d,clr,en;
reg q;

  always @(posedge clk or  posedge clr)
  begin
    if(clr)
      q<=0;
    else if (en)
      q<=d;
  end
endmodule

数据类型及其常量和变量:

4种逻辑值 : 0 1 z(高阻) x (不定值)

常量:在程序运行过程中,其值不能被改变的量称为常量。


整数:

  • 二进制整数 bB
  • 十进制整数 dD
  • 八进制整数 oO
  • 十六进制整数 hH

数字表达方式: <位宽>'<进制><数字>,默认位宽是32bit

4'b1110   //4位二进制数
12'habc  //12位十六进制数
16'd255  //16位十进制数

负数: 一个数字可以被定义为负数,只需在位宽表达式前加一个减号,减号必须写在数字定义表达式的最前面。

-8'd5 //这个表达式代表5的补数(用八位二进制数表示)


参数(Parameter)型:

parameter来定义一个标识符 代表一个常量,称为符号常量,即标识符形式的常量,采用标识符代表一个常量可提高程序的可读性和可维护性。是一种常数型的数据,其说明格式如下:

parameter 参数名1=表达式,参数名2=表达式, …, 参数名n=表达式;

parameter是参数型数据的确认符,确认符后跟着一个用逗号分隔开的赋值语句表。在每一个赋值语句的右边必须是一个常数表达式。也就是说,该表达式只能包含数字或先前已定义过的参数。

parameter msb=7; //定义参数msb为常量7
parameter e=25, f=29; //定义二个常数参数
parameter r=5.7; //声明r为一个实型参数
parameter byte_size=8, byte_msb=byte_size-1; //用常数表达式赋值
parameter average_delay = (r+f)/2; //用常数表达式赋值

参数型常数经常用于定义延迟时间和变量宽度。在实例引用时可通过参数传递改变在被引用模块中已定义的参数

module two_delay(a,b,c,d);
output c,d;
input a,b;
reg c,d;
parameter delay1=2,delay2=2;

  always @(a)
    c<= #delay1 a;

  always @(b)
    d<=#delay2 b;

endmodule

//调用版本1
module top_delay(a1,b1,c1,d1);
input a1,b1;
output c1,d1;

  two_delay #(.delay1(5),.delay2(10)) u1(.a(a1),.b(b1),.c(c1),.d(d1));

endmodule

//调用版本2
module top_delay2(a1,b1,c1,d1);
input a1,b1;
output c1,d1;

  two_delay #(5,10) u1(.a(a1),.b(b1),.c(c1),.d(d1));

endmodule

//调用版本3
module top_delay3(a1,b1,c1,d1);
input a1,b1;
output c1,d1;

  two_delay #(.delay2(10)) u1(.a(a1),.b(b1),.c(c1),.d(d1));

endmodule

//调用版本4
module top_delay4(a1,b1,c1,d1);
input a1,b1;
output c1,d1;

  two_delay #(10) u1(.a(a1),.b(b1),.c(c1),.d(d1));  //参数被传给了delay1

endmodule

变量:

wire变量:

wire [n-1:0] 数据名1,数据名2,…数据名i;  //共有i条总线,每条总线内有n条线路
wire [n:1] 数据名1,数据名2,…数据名i;
wire a;     //定义了一个一位的wire型数据
wire [7:0] b;     //定义了一个八位的wire型数据
wire [4:1] c, d;     //定义了二个四位的wire型数据

///////////////////////////////////////////////////////

reg是最常用的寄存器型数据。默认初始值是x:

reg [n-1:0] 数据名1,数据名2,… 数据名i;
reg [n:1] 数据名1,数据名2,… 数据名i;
reg rega; //定义了一个一位的名为rega的reg型数据
reg [3:0] regb; //定义了一个四位的名为regb的reg型数据
reg [4:1] regc, regd; //定义了两个四位的名为regc和regd的reg型数据

运算符按其功能可分为以下几类:

1) 算术运算符(+,-,×,/,%)
2) 赋值运算符(=,<=)
3) 关系运算符(>,<,>=,<=)
4) 逻辑运算符(&&,||,!)
5) 条件运算符(?:)
6) 位运算符(~,|,^,&,^~)
7) 移位运算符(<<,>>)
8) 拼接运算符({ })
9) 其它

优先级

下面是Verilog HDL中使用的关键词:
always, and, assign,begin,buf,bufif0,bufif1,case,casex,casez,cmos,deassign,default,defparam,disable,edge,else,end,endcase,endmodule,endfunction,endprimitive,endspecify, endtable, endtask, event, for, force, forever, fork, function,highz0,highz1, if,initial, inout, input,integer,join,large,macromodule,medium,module,nand,negedge,nmos,nor,not,notif0,notifl, or, output, parameter, pmos, posedge,primitive, pull0, pull1, pullup, pulldown, rcmos, reg, releses, repeat, mmos, rpmos,rtran, rtranif0,rtranif1,scalared,small,specify,specparam,strength,strong0, strong1,supply0, supply1, table, task, time, tran, tranif0, tranif1, tri, tri0, tri1, triand,trior, trireg,vectored,wait,wand,weak0,weak1,while, wire,wor, xnor, xor

块语句:

begin_end语句 通常用来标识顺序执行的语句,用它来标识的块称为顺序块

顺序块有以下特点:

(1) 块内的语句是按顺序执行的,即只有上面一条语句执行后下面的语句才能执行。
(2) 每条语句的延迟时间是相对于前一条语句的仿真时间而言的。
(3) 直到最后一条语句执行完,程序流程控制才跳出该语句块。

块内声明语句可以是参数声明语句、reg型变量声明语句、integer型变量声明语句、real型变量声明语句。

module ex4_5;
parameter d=50;
reg[7:0] r;
reg[3:0] a;
event end_wave;

  always @(end_wave)
    a=4'hf;
    initial
       begin
         a=4'h2;
       end

    initial
      begin
        #d r='h35;
        #d r='hE2;
        #d r='h00;
        #d r='hF7;
        #d ->end_wave;
      end

endmodule

逻辑判断:

module counter10b(q,clk,clr);
output[3:0] q;
input clk,clr;
reg[3:0] q=4'b0000;//ModelSim仿真用,QuartusII不用初值
  always @(posedge clk or posedge clr)
  begin
    if(clr)
      q<=4'b0000;
    else
    begin
      if (q==4'b1001)
        q<=4'b0000;
      else
        q<=q+1'b1;
    end
  end
endmodule

case语句:

case语句是一种多分支选择语句,if语句只有两个分支可供选择,而实际问题中常常需要用到多分支选择,Verilog语言提供的case语句直接处理多分支选择。

case语句的所有表达式的值的位宽必须相等,只有这样控制表达式和分支表达式才能进行对应位的比较。一个经常犯的错误是用’bx, ‘bz 来替代 n’bx, n’bz,这样写是不对的,因为信号x, z的缺省宽度是机器的字节宽度,通常是32位(此处 n 是case控制表达式的位宽)

module test_case;
reg[1:0] select;
  initial
  begin
    select=2'b01;
    case (select)
      2'b01:$display("second 2b01...");
      2'b01:$display("third 2b01...");
      2'b01,2'b00:$display("2b01");
      2'b11:$display("2b11");
      default:$display("default...");
    endcase
  end
endmodule

initialalways 说明语句在仿真的一开始即开始执行。

initial 语句只执行一次。相反,always 语句则是不断地重复执行,直到仿真过程结束。

always 语句后面跟着的过程块是否运行,则要看它的触发条件是否满足,如满足则运行过程块一次,再次满足再运行一次,直至仿真过程结束。

在一个模块中,使用initialalways 语句的次数是不受限制的。

taskfunction 语句可以在程序模块中的一处或多处调用。

@*@(*),它们都表示对其后面语句块中所有输入变量的变化时敏感的。

发表评论 / Comment

用心评论~