编程语言——Verilog基本语句

过程语句

在一个模块内部可以有任意多个initial语句和always语句,两者都是从仿真的起始时
刻开始执行的, 但是initial语句后面的块语句只执行一次,而always语句则循环地重复执
行后面的块语句,直到仿真结束。

  1. initial
    initial语句后面的块语句只执行一次,格式为:
initial begin
	语句 1;
	语句 2;
	…
	语句 n;
end

initial 语句举例:

initial begin
	rst_n = 0;		// 设置寄存器初始值
	data  = 0;
	addr  = 0;
    #5;		 		// 延时5个时间长度,(`timescale 时间 * 5)== 实际延时时间
    rst_n = 1;		// 拉高复位信号
    #2;				// 延时2个时间长度
    data  = 8'h86;  // 写入数据
	addr  = 8'h01;
    $stop			// 退出仿真
end
  1. always
    always语句后面的块语句是不断重复执行的,格式为:
always <时序表达式> <进程语句>;

其中最常用的描述格式:
always @(敏感信号表达式) begin
	...
end

always 语句举例:

always @ (posedge clk or negedge rst_n) begin   // 敏感列表 上升沿时钟信号 和 下降沿复位信号
	if (rst_n == 1'b0) // 低电平复位,初始化寄存器
		byte_cnt <= 2'b0;
	else	// 当每隔时钟沿的到来时,byte_cnt + 1
		byte_cnt <= byte_cnt + 1'b1;
end

赋值语句

赋值语句是Verilog HDL中对线型和寄存器型变量赋值的主要方式,根据赋值对象的不同分为连续赋值语句和过程赋值语句,两者的主要区别是:
(1) 赋值对象不同: 连续赋值语句用于对线型变量的赋值;过程赋值语句完成对寄存器变量的赋值。

(2) 赋值过程实现方式不同:线型变量一旦被连续赋值语句赋值后,赋值语句右端表达式中的信号有任何变化,都将实时地反映到左端的线型变量中;过程赋值语句只有在语句被执行到时,赋值过程才能够进行一次,而且赋值过程的具体执行时间还受到各种因素的影响。

(3) 语句出现的位置不同:连续赋值语句不能够出现在任何一个过程块中;过程赋值语句只能够出现在过程块中。

(4) 语句结构不同:连续赋值语句以关键词assign为先导;过程赋值语句不需要任何先导的关键词,但是,语句的赋值分为阻塞型和非阻塞型。

  1. 连续赋值语句(Continuous assignments)
    assign为连续赋值语句,用于对wire型变量进行赋值。其基本的描述语法为:
    assign <线型变量> = <表达式> ;
// 比较器
module add
(
	input		[ 3:0]	di_a,
	input		[ 3:0]	di_b,
	output		[ 3:0]  di_max
);

assign di_max= (di_a > di_b) ? di_a : di_b;

endmodule
  1. 过程赋值语句(Procedural assignments)
    过程赋值语句用于对寄存器类变量赋值,没有任何先导的关键词,而且只能够在always语句或initial语句的过程块中赋值。其基本的描述语法为:
    <寄存器型变量> = <表达式> (阻塞型过程赋值);
    <寄存器型变量> <= <表达式>(非阻塞性过程赋值)

  2. 阻塞型赋值语句与非阻塞型赋值语句( Blocking & nonblocking procedural assignment)
    阻塞与非阻塞型赋值语句的基本区别是,阻塞型赋值语句的执行受到前后顺序的影响,只有在第一条语句执行完之后才可以执行第二条语句,而在非阻塞型赋值语句中,则是在某一规定时刻同时完成,不受先后顺序的影响.从某个角度讲,非阻塞型赋值语句的执行顺序与并行块的执行十分相象。

  3. 块语句(Block statements)
    语句块用来将两条或多条语句组合在一起,使其在格式上更象一条语句。块语句有两种,
    一种是begin-end语句,通常用来标识按照给定顺序执行的串行块( Sequential block) ;一种
    是fork-join语句,用来标识并行执行的并行块(Parallel block)。

(1).串行块(begin-end)
串行块具有如下特点:

  • 串行块中的每条语句都是依据块中的排列次序顺序执行。
  • 串行块中每条语句的延时都是相对于前一条语句执行结束的相对时间。
  • 串行块的起始执行时间是块中第一条语句开始执行的时间,结束时间是最后一条语句执行结束的时间。

(2). 并行块(fork-join)
并行块具有如下特点:

  • 并行块中的每条语句都是同时并行执行的,与排列次序无关。
  • 并行块中每条语句的延时都是相对于整个并行块开始执行的绝对时间。
  • 并行块的起始执行时间是流程控制转入并行块的时间,结束时间是并行块 中按执行时
    间排序,最后执行的那条语句结束的时间。

条件语句

  1. if-else
    if-else 语句是用来判断所给的条件是否满足,根据判定的结果(真或假)决定执行给
    出的两种操作之一。 Verilog HDL语言共提供了3种形式的if-else语句。
    ( 1) if(表达式) 块语句1;
    ( 2) if(表达式) 块语句1;
    else 块语句2;
    ( 3) if (表达式1) 块语句1;
    else if(表达式2) 块语句2;
    else if(表达式3) 块语句3;

    else if (表达式n)块语句n ;
    else 块语句n+1

  2. case语句
    case语句构成了一个多路条件分支的结构,多用于多条件译码电路的描述中,如译码器、数据选择器、状态机及微处理器的指令译码等。 Verilog HDL语言共提供了3种形式的case语句,这里只介绍一种常用形式。
    ( 1) case(敏感表达式)
    值1: 块语句1;
    值2: 块语句2;
    … 值
    n: 块语句n;
    default: 块语句n+1;
    endcase

case语句与if-else语句的功能十分接近,可以全部转化为if-else语句描述。当控制条件集中在某个敏感表达式的变化上时,同样也可以将if-else语句改写成case语句。只是, if-else语句可以实现优先权的描述,而case无法实现。

循环语句

  1. for语句
    与C语言完全相同, for语句的描述格式为:
    for ( 循环变量赋初值;循环结束条件;循环变量增值 ) 块语句;
    即在第一次循环开始前,对循环变量赋初值;循环开始后,判断初值是否符合循环结束条件,如果不符合,执行块语句,然后给循环变量增值;再次判断是否符合循环结束条件,如果符合循环结束条件,循环过程终止。

  2. repeat语句
    repeat语句可以连续执行一条语句若干次,描述格式为:
    repeat (循环次数表达式)
    块语句;

  3. while语句
    while语句是不停地执行某一条语句,直至循环条件不满足时退出。描述格式为:
    while(循环执行条件表达式)
    块语句;
    while语句在执行时,首先判断循环执行表达式是否为真,如果为真,执行后面的块语句,然后再返回判断循环执行条件表达式是否为真,依据判断结果确定是否需要继续执行。while语句与for语句十分类似,都需要判断循环执行条件是否为真,以此确定是否需要继续执行块语句。

  4. forever语句
    forever语句可以无条件地连续执行语句,多用在“initial”块中,生成周期性输入波形,通常为不可综合语句。描述格式为:
    forever 块语句;

任务与函数

Verilog HDL分模块对系统加以描述,但有时这种划分并不一定方便或显得勉为其难。因此, Verilog HDL还提供了任务和函数的描述方法。通常在描述设计的开始阶段,设计者更多关注总体功能的实现,之后再分阶段对各个模块的局部进行细化实现,任务和函数对这种设计思路的实现有很大的帮助。任务和函数,是在模块内部将一些重复描述或功能比较单一的部分,作为一个相对独立地进行描述,在设计中可以多次调用。

  1. task任务
    任务可以在源代码中的不同位置执行共同的代码段,这些代码段已经用任务定义编写成
    任务,因此,能够从源代码的不同位置调用任务。
    任务的定义与引用都在一个模块内部完成,任务内部可以包含时序控制,即时延控制,
    并且任务也能调用任何任务(包括其本身)和函数。
    定义格式为:
    task <任务名>;
    <端口及数据类型定义语句>
    <语句1>
    <语句2>

    <语句n>
    endtask
    调用格式为:
    <任务名>(端口1,端口2, ……) ;

使用任务时,要注意以下几点:

  • 定义任务与调用任务必须在同一个模块内,任务调用语句应该在always块或者task-endtask块中。
  • 定义任务时,没有端口名列表,但要进行端口与数据类型的声明。
  • 调用任务时,与调用模块一样,要列出端口名列表,但是顺序要与定义中的排序完全一致。
  • 任务中可以调用其他的任务或者函数,且调用的个数不受限制。
  1. function函数
    函数与task任务一样,也可以在模块中的不同位置执行同一段代码;不同之处是函数只能返回一个值,它不能包含任何时间控制语句。函数可以调用其它函数,但是不能调用任务。此外,函数必须至少带有一个输入端口,在函数中允许没有输出或输入输出说明。
    函数的定义格式为:
    function <位宽说明>函数名;
    <输入端口与类型说明>
    <局部变量说明>
    begin
    <语句1>
    <语句2>
    ……
    <语句n>
    end
    endfunction
    函数的调用是通过将函数作为表达式中的操作数来实现的。其调用格式为:
    <函数名>(<表达式1>, <表达式2>, ……)

任务与函数的区别

  • 函数需要在一个仿真时间单位内完成;而任务定义中可以包含任意类型的定时控制
    部分及wait语句等。
  • 函数不能调用任务,而任务可以调用任何任务和函数。
  • 函数只允许有输入变量且至少有一个,不能够有输出端口和输入输出端口;任务可以没有任何端口,也可以包括各种类型的端口。
  • 函数通过函数名返回一个值;任务则不需要。
相关推荐
©️2020 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页