结构说明语句

always肯定必考,感觉task和function会二选一。

Verilog 语言中的任何过程模块都从属于以下 4 种结构的说明语句:

  • initial 说明语句
  • always 说明语句
  • task 说明语句
  • function 说明语句

always

always 语句在仿真过程中是不断活动着的。always语句后跟着的过程块是否执行,则要看它的触发条件是否满足,如满足则运行过程块一次;如不断满足,则不断地循环执行。

1
2
always clk = ~clk;    //将无限循环,周期为0,仿真时间无法推进
always #5 clk = ~clk; //周期为10的时钟信号
  • 沿触发的always块常描述时序行为,如有限状态机。可将其转换为表示寄存器组和门级组合逻辑的结构,而该结构应具有时序所要求的行为。
  • 电平触发的 always 块常用来描述组合逻辑的行为。可将其转换为表示组合逻辑的门级逻辑结构或带锁存器的组合逻辑结构,而该结构应具有所要求的行为。
  • 一个模块中可以有多个always块,它们都是并行运行的,并没有前后之分。

wait

always @(...)的敏感列表很像。

仿真器连续监视 count_enable 的值,若其值为 0,则不执行后面的语句,仿真会停顿下来;如果其值为 1,则在 20 个时间单位之后执行这条语句。

如果 count_enable始终为 1,那么 count 将每过 20 个时间单位加 1。

1
2
3
always
wait (count_enable)
#20 count = count +1;

感觉不如敏感列表感觉

task&function

平时用的也很少,得看看概念和语法。只在作业里出现了两次。

任务和函数相比

  • 函数只能与主模块共用同一个仿真时间单位,而任务可以定义自己的仿真时间单位
  • 函数不能启动任务,而任务能启动其他任务和函数
  • 函数至少要有一个输入变量,而任务可以没有或有多个任何类型的变量
  • 函数返回一个值,而任务则不返回值

task

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 将数据 input_array 连续循环左移 rotate_by 次
task rotate_left;
inout [16:1] input_array;
input [0:3] start_bit, stop_bit, rotate_by;
reg fill_value;
//局部变量
integer mac1, mac3;
begin
for(mac3=1; mac3<=rotate_by; mac3=mac3+1)
begin
fill_value=input_array[stop_bit];
for(mac1=stop_bit; mac1>=start_bit+1; mac1=mac1-1)
input_array[mac1] = input_array[mac1-1];
input_array[start_bit] = fill_value;
end
end
endtask
  • task调用时需要按顺序传递输入参数和输出参数
  • task调用语句是过程语句,在initial或always语句中使用
  • task调用中的输出和inout参数必须是reg类型

function

  • 函数带至少一个输入参数,但不能有inout或output说明
  • 函数只能返回一个值,与函数名相同的变量必须在函数中赋值
  • 函数不能包含任何延时或者时序控制(必须立即执行)
  • 函数表达的是组合逻辑

如果不说明函数取值范围或者类型,函数值为1位二进制数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 阶乘函数
module tryfact;
// 函数的定义

function [31:0] factorial;
input [3:0] operand;
reg [3:0] index;
begin
factorial = 1;
//0的阶乘为1, 1的阶乘也为 1
for (index = 2; index <= operand; index = index + 1)
factorial = index * factorial;
end
endfunction

// 函数的测试-----------------
reg [31:0] result;
reg [ 3:0] n;
initial begin
result = 1;
for (n = 2; n <= 9; n = n + 1) begin
$display("Partial result n=%d result=%dn", n, result);
result = n * factorial(n) / ((n * 2) + 1);
end
$display("Finalresult=%d", result);
end
endmodule //模块结束

递归函数

Verilog 中的函数是不能够进行递归调用的。设计模块中若某函数在两个不同的地方被同时并发调用,由于这两个调用同时对同一块地址空间进行操作, 那么计算结果将是不确定的。若在函数声明时使用了关键字automatic,那么该函数将成为自动的或可递归的,即仿真器为每一次函数调用动态地分配新的地址空间,每一个函数调用对各自的地址空间进行操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
module top ; //递归函数完成阶乘计算
…………
function automatic integer
factorial;
input [ 31 : 0 ] oper;
integer i ;
begin
if ( operand >= 2 )
factorial=factorial(oper-1)*oper;
else
factorial = 1 ;
end
endfunction
integer result;
initial
begin
result=factorial(4); // 4 的阶乘
$display ("Factorial of 4 is %0d", result ); //显示 24
end
endmodule

常量函数

常量函数实际上是一个带有某些限制的常规 Verilog 函数。 这种函数能够用来引用复杂的值, 因而可用来代替常量。

以下为计算模块中地址总线的宽度。

1
2
3
4
5
6
7
8
9
10
11
12
13
module ram(......);
parameter RAM_DEPTH = 256;
input [ clogb2(RAM_DEPTH) - 1 : 0 ] addr_bus;
.........
//
function integer clogb2 ( input integer depth ) ;
begin
for ( clogb2 = 0; depth > 0 ; clogb2 = clogb2 + 1 )
depth = depth >> 1 ;
end
endfunction
.........
endmodule