用于仿真的Verilog

发布于 2021-01-16  188 次阅读


1.时间尺度

`timescale

`timescale 时间单位 /时间精度
时间精度不能大于时间单位,时间单位表示一个单位时间的时间长度,时间精度决定了在哪一位进行四舍五入

`timescale 1ns/100ps
module tb_adder4b;
reg [8:0] sti;
wire [3:0] t_sum;
wire t_c_out;
adder4b xadder4b( // instance DUV
  .a     (sti[8:5] ),
  .b     (sti[4:1] ),
  .c_in  (sti[0]   ),
  .sum   (t_sum    ),
  .c_out (t_c_out  )
);
initial //stimulus generation
begin
  sti[9:0]=9'b0000_0000_0;//     @ 0ns
  #10 sti[9:0]=9'b1111_0000_1;// @ 10ns
  #10 sti[9:0]=9'b0000_1111_1;// @ 20ns
  #10 sti[9:0]=9'b1111_0001_1;// @ 30ns
  #10 sti[9:0]=9'b0001_1111_0;// @ 40ns
  #10 $finish;
end
endmodule

2.块语句

块内赋值语句,被赋值信号均为reg类型,块内不可能出现assign语句

顺序块

begin-end

块内语句按顺序执行

`timescale 1ns /1ns
initial
begin
  #5   ... //@ 5ns
  #10  ... //@ 15ns
  #15  ... //@ 30ns
end

并行块

fork-join

块内语句并发执行

`timescale 1ns /1ns
initial
fork
  #5  ... //@ 5ns
  #10 ... //@ 10ns
  #15 ... //@ 15ns
join

3.结构说明语句

always语句

  • 不断重复,直至仿真结束
  • 满足出发条件时,运行过程块一次
  • 需和一定的时序控制结合: @,#

initial语句

  • 只执行一次,仿真必有
  • 多个initial begin-end等效于一个initial fork-join
always // deadclock
  haha = !haha;

always #half_period
  haha = !haha

`timescale 1ns /1ns
initial
begin
  #5  ... //@ 5ns
end
initial
begin
  #10 ... //@ 10ns
end
initial
begin
  #15 ... //@ 15ns
end
//=======等效于========//
`timescale 1ns /1ns
initial
fork
  #5  ... //@ 5ns
  #10 ... //@ 10ns
  #15 ... //@ 15ns
join

task语句

格式:

task 任务名称
   输出、输出声明:
   语句:
endtask

例如一个CPU读写的任务

task Read;
output [7:0] Rtask_data; //data read out
input  [5:0] Rtask_Addr;
begin
    uP_rw = 0;
    # SLOW_PERIOD;
    uP_addr = Rtask_Addr;
    uP_rw = 1;
    # SLOW_PEROID;
end
endtask

调用task时的代码如下

Read (Data, Addr);

function语句

格式:

function [BITWIDTH -1 : 0] 函数名称;
    输入声明;
    语句;
endfunction

与task不同的是,函数将返回一个值。以上代码会返回一个BITWIDTH宽度的值,例如

function [7:0] Product;
input [3:0] Sig_A;
input [3:0] Sig_B;
begin
    Product = Sig_A * Sig_B;
end
endfunction

调用function的格式如下:

ProductResult = Product (A,B); //将A和B的乘积的值赋给ProductResult变量

4.循环语句

forever

必须写在initial块中,常用于监控和生成时钟,可以替代always块

always @(posedge req) begin
 ...
end
//=========等效于===========//
initial begin
  forever begin
    @(posedge req);
    ...
  end
end
//=====生成时钟=====//
initial begin
  clk = 1'b0;
  #5 clk = 1'b1;
  #5 clk = 1'b0;
  #5 clk = 1'b1;
  ...
//================//
initial begin
  clk = 1'b0;
  forever #HALF_PERIOD
    clk = !clk;
end
//==================//
initial
  clk = 1'b0;
always #HALF_PERIOD
  clk = !clk;

repeat

连续执行一条语句N次,常用来做延迟若干时钟周期
repeat (次数) (限定条件)
repeat (N) @(posedge clk)

//wait for 20 clock cycles
repeat (20) @(posedge clk)

while

循环变量赋初值;
while(循环条件) begin
执行语句;
循环变量变化;
end

for

for(循环变量赋值;循环条件;循环变量变化)
执行语句;

跳转操作

  • continue: 跳至下一次循环
  • break: 跳出循环
  • disable: 结束某个块(已被命名)或任务
initial begin
   ...
   for(i=0;i<64;i=i+1) begin: loop1
     for(j=0;j<64;j=j+1) begin: loop2
       if(a[i][j]==0)
         continue;
       if(b[i][j]==0)
         //break;
         disable loop2
       ...
     end
   end
end

5.事件语句

wait(signal)

等待电平事件

initial begin
  wait(intr);
  clear_intr = 1'b1;
  ...
end

@(event)

等待边沿事件,@(posedge clk)常用于延迟一个时钟周期,或于时钟边沿对齐

initial begin
  @(poseedge rst_n);
  @(posedge clk);
  start = 1'b1;
  ...
end

6.后门操作

上帝视角

通过层次结构,直接访问某一信号,用.分割例化名

initial
  @(posedge tb.rst_n);
  U_itcm.U_sram.mem[0] = 32'b0;
  ...
begin

force-release语句

强迫赋值语句,对变量实行强制赋值,用于制造激励难以达到的情况,只能在结构说明语句中使用

initial
  @(posedge start);
  force din = 32'hdeadbeef;
  @(posedge finish);
  release din;
begin

7.常用系统任务与系统函数

系统任务

$time: 当前仿真时刻
$random: 生成随机数

  • $random(): 生成一个32位有符号整数,可以指定范围和符号
  • $random % a: 给出[-a+1,a-1]范围内的随机整数
  • {$random} % a: 给出[0,a-1]范围内的随机整数,这里{}相当于绝对值

系统函数

打印到屏幕

$display和$write: 功能上相同,但$display可以自动换行
$monitor(参数1,参数2,...,参数n); 监控、输出参数列表中的表达式或变量的值,有一个发生变化就输出

$monitor($time,"p1=%x p2=%b",p1,p2);

输出到文件

$fopen

功能: 打开文件,文件句柄一般为integer类型的变量
格式: 文件句柄=$fopen("文件名");

$fmonitor,$fdisplay,$fwrite

功能: 写入已打开的文件
格式: $fdisplay(文件句柄,正常的$display语句);

$fclose

功能: 关闭文件
格式: $fclose(文件句柄);

in_handler = $fopen("golden_input.txt");
for(i=0;i<=`SRAM_DEPTH-1;i++)
begin
  wdata[i] = {$random} % (2**(`SRAM_DEPTH)); //256
  `SRAM_PATH.array[i] = wdata[i];
  $fdisplay(in_handler, "@%h\n%h",i,wdata[i]);
end
$fclose(in_handler);

从文件获取数据

$readmemb,$readmemh
功能: 从文件读取数据到存储器中
格式:

  • readmemh("<文件名>",<存储器名>);
  • readmemh("<文件名>",<存储器名>,<起始地址>);
  • readmemh("<文件名>",<存储器名>,<起始地址>,<结束地址>); 注意,如果有地址,要以@<十六进制数>的形式表示
module dir_exchange();
reg [`SRAM_DATAW-1:0] ori_data [`SRAM_DEPTH-1:0];
reg [`SRAM_DATAW-1:0] final_data [`SRAM_DEPTH-1:0];
integer i;
integer j;
integer in_handler;
integer out_handler;

intial
begin
  $readmemh("../golden_input.txt",ori_data);
  $readmemh("../golden_output.txt",final_data);


追求理性 独立思考 不做韭菜