VLSI:verilog模块自动测试与功能验证

发表于:2023-4-18 09:36

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:王小云    来源:知乎

  当作者完成模块的verilog代码编写并进行模块功能验证时,常面临两个问题:
  (1)数据量不够:难以覆盖所有情况。
  (2)手动比对复杂:耽误时间,且可能出错。
  因此“自动产生大量的、随机的参考输入和参考输出,并与模块的输出进行对比”是一个非常有效的解决上述问题的方式。这里,作者以4级8比特流水线乘法器为例,展示如何进行模块的自动测试与功能验证。
  整体流程如图1所示:包含参考数据的生成、待测模块(DUT)、Testbench的搭建和Makefile编写和最终运行。供各位读者参考,欢迎大家在评论区交流。
  首先,在testbench中自动读取Golden Brick所生成的若干对参考输入和参考输出。接着,将参考输入送给DUT后产生输出。最后,对比DUT的输出和参考输出,从而判定整个模块的是否正常工作。工作流程由Makefile控制。
图1. 整体流程图
  第一步:参考数据(Golden brick)的生成
  生成Golden brick的方式多种多样。这里我们以C语言为例,goldenbrick.c代码如下所示。其中M为Golden brick的行数,即M对参考输入和参考输出:
  #include <stdio.h>
  #include <stdlib.h>
  #include <time.h>
  #include <assert.h>
  #include <math.h>
  #include <string.h>
  #define M 16
  #define Nj 8
  #define Ni 8
  #define N (Ni*Nj)
  #define eps (1e-7)
  int main(int argc, char **argv)
  {
    unsigned int A, B, Ai, Bi, Ri;
    unsigned int R, R_pipe0, R_pipe1, R_pipe2, R_pipe3;
    int i,t;
    
    srand48(time(NULL));
    assert (Ni<(sizeof(int)*8));
    assert (Nj<(sizeof(int)*8));
    R       = 0;
    R_pipe0 = 0;
    R_pipe1 = 0;
    R_pipe2 = 0;
    R_pipe3 = 0;
    for(t=0; t<M; t++) {
      /* generate uniformly random integers in correct range */
      A = rand() % 256;
      B = rand() % 256; 
      
      R_pipe3 = R_pipe2; 
      R_pipe2 = R_pipe1; 
      R_pipe1 = R_pipe0; 
      R_pipe0 = R; 
      R = A*B;
      printf("A = ");
      for(i=Ni-1; i>=0; i--) {
        Ai = ((A&(1<<i))!=0)?1:0;
        printf("%i",Ai);
      }
      printf("     B = ");
      for(i=Ni-1; i>=0; i--) {
        Bi = ((B&(1<<i))!=0)?1:0;
        printf("%i",Bi);
      }
      printf("     RESULT = ");
      for(i=Ni+Nj-1; i>=0; i--) {
        Ri = ((R_pipe3&(1<<i))!=0)?1:0;
        printf("%i",Ri);
      }
      printf("\n");
    }
    return 0;
  }
  用以下两条命令在终端编译并运行上述代码:
  gcc -Wall -ggdb -o goldenbrick goldenbrick.c
  goldenbrick > goldenbrick.txt
  可得如下goldenbrick.txt文件,包含M=16行输入和输出:
  A = 01100111 B = 11000110 RESULT = 0000000000000000 
  A = 01101001 B = 01110011 RESULT = 0000000000000000 
  A = 01010001 B = 11111111 RESULT = 0000000000000000 
  A = 01001010 B = 11101100 RESULT = 0000000000000000 
  A = 00101001 B = 11001101 RESULT = 0100111110101010 
  A = 10111010 B = 10101011 RESULT = 0010111100101011 
  A = 11110010 B = 11111011 RESULT = 0101000010101111 
  A = 11100011 B = 01000110 RESULT = 0100010000111000 
  A = 01111100 B = 11000010 RESULT = 0010000011010101 
  A = 01010100 B = 11111000 RESULT = 0111110000111110 
  A = 00011011 B = 11101000 RESULT = 1110110101000110 
  A = 11100111 B = 10001101 RESULT = 0011111000010010 
  A = 01110110 B = 01011010 RESULT = 0101110111111000 
  A = 00101110 B = 01100011 RESULT = 0101000101100000 
  A = 00110011 B = 10011111 RESULT = 0001100001111000 
  A = 11001001 B = 10011010 RESULT = 0111111100111011 
  第二步:待测模块(DUT)
  整个4级8比特流水线乘法器的代码mult.v如下所示。输入为clk,reset,a 和 b,输出为result。这里我们采用了同步复位的方式,即reset仅在clk的上升沿起作用。
  module mult(clk, reset, a, b, result);
  input [7:0] a, b;
  input clk, reset;
  output [15:0] result;
  reg [3:0] P00, P01, P02, P03, P10, P11, P12, P13, P20, P21, P22, P23, P30, P31, P32, P33;
  reg [9:0] C00, C01, C02, C03;
  reg [11:0] G0, G1;
  reg [15:0] result;
  
  always@(posedge clk)
  begin
   P00 <= (reset == 1'b1) ? a[1:0] * b[1:0] : 4'h0 ;
   P01 <= (reset == 1'b1) ? a[1:0] * b[3:2] : 4'h0 ;
   P02 <= (reset == 1'b1) ? a[1:0] * b[5:4] : 4'h0 ;  
   P03 <= (reset == 1'b1) ? a[1:0] * b[7:6] : 4'h0 ;
   
   P10 <= (reset == 1'b1) ? a[3:2] * b[1:0] : 4'h0 ;
   P11 <= (reset == 1'b1) ? a[3:2] * b[3:2] : 4'h0 ;
   P12 <= (reset == 1'b1) ? a[3:2] * b[5:4] : 4'h0 ;
   P13 <= (reset == 1'b1) ? a[3:2] * b[7:6] : 4'h0 ;
   
   P20 <= (reset == 1'b1) ? a[5:4] * b[1:0] : 4'h0 ;
   P21 <= (reset == 1'b1) ? a[5:4] * b[3:2] : 4'h0 ;
   P22 <= (reset == 1'b1) ? a[5:4] * b[5:4] : 4'h0 ;
   P23 <= (reset == 1'b1) ? a[5:4] * b[7:6] : 4'h0 ;
   
   P30 <= (reset == 1'b1) ? a[7:6] * b[1:0] : 4'h0 ;
   P31 <= (reset == 1'b1) ? a[7:6] * b[3:2] : 4'h0 ;
   P32 <= (reset == 1'b1) ? a[7:6] * b[5:4] : 4'h0 ;
   P33 <= (reset == 1'b1) ? a[7:6] * b[7:6] : 4'h0 ;
  end
  always @(posedge clk) 
  begin
   C00 <= (reset == 1'b1) ? (P00 + {P01, 2'b0}) + {(P02 + {P03, 2'b0}), 4'h0} : 10'h000 ;
   C01 <= (reset == 1'b1) ? (P10 + {P11, 2'b0}) + {(P12 + {P13, 2'b0}), 4'h0} : 10'h000 ;
   C02 <= (reset == 1'b1) ? (P20 + {P21, 2'b0}) + {(P22 + {P23, 2'b0}), 4'h0} : 10'h000 ;
   C03 <= (reset == 1'b1) ? (P30 + {P31, 2'b0}) + {(P32 + {P33, 2'b0}), 4'h0} : 10'h000 ;
  end
  always @(posedge clk) 
  begin
  G0  <= (reset == 1'b1) ? {C01 + C00[9:2], C00[1:0]} : 12'h000 ;
  G1  <= (reset == 1'b1) ? {C03 + C02[9:2], C02[1:0]} : 12'h000 ;
  end
  always @(posedge clk) 
  begin
  result <= (reset == 1'b1) ? {G0[11:4] + G1, G0[3:0]} : 16'h0000;
  end
  endmodule
  第三步:Testbench搭建
  mult_testbench.v可以实现以下功能:(1)打开输入文件goldbenbrick.txt和输出文件testbench.txt(2)设置时钟和复位信号(3)读取输入参考信号,对比模块输出和参考输出,正确输出pass,错误输出fail(4)例化DUT
  module mult_testbench();
  reg [7:0] a,b;
  reg [15:0] ref_result,result;
  reg clk;
  reg reset;
  /////////////////////////////////////////
  // Open Files
  integer read_file;
  integer write_file;
  initial 
  begin
  read_file  = $fopen("goldenbrick.txt", "r");
  write_file = $fopen("testbench.txt", "w");
  /////////////////////////////////////////
  // Set up signals
  clk = 1'b0;
  reset = 1'b0;
  #10
  reset = 1'b1;
  end
  /////////////////////////////////////////
  // Do file IO 
  always @(posedge clk) 
  begin
  $fscanf(read_file, "A = %b\t B = %b\t RESULT = %b", a, b, ref_result); 
  if (!$feof(read_file))
  begin
  if (ref_result == result)
  $fdisplay(write_file, "A = %b\t B = %b\t RESULT = %b pass", a, b, result); 
  else
  $fdisplay(write_file, "A = %b\t B = %b\t RESULT = %b fail", a, b, result); 
  end
  else
  begin
  $fclose(read_file);
  $fclose(write_file);
  $finish;
  end
  end
  
  /////////////////////////////////////////
  // Set up DUT
  mult mult0(.clk(clk), .a(a), .b(b), .result(result), .reset(reset));
  /////////////////////////////////////////
  // Run Simulation
  always @(*)
  begin
  #5 clk <= ~ clk;
  end
  备注:
  (1)在上述代码中可以引入right_data和total_data两个变量,从而计算正确率。
  (2)$feof的位置需要注意,之后我会就这个问题写文章进行分析与讨论。
  第四步:Makefile编写和文件结构
  Makefile代码如下所示,分别执行goldenbrick.c和Synopsys VCS仿真。
  TESTBENCH = mult_testbench.v
  SIM_FILES =  mult.v
  VV         = vcs
  VVOPTS     = -o $@ +v2k +vc -sverilog -timescale=1ns/1ps +vcs+lic+wait +multisource_int_delays                    \
         +neg_tchk +incdir+$(VERIF) +plusarg_save +overlap +warn=noSDFCOM_UHICD,noSDFCOM_IWSBA,noSDFCOM_IANE,noSDFCOM_PONF -full64 -cc gcc +libext+.v+.vlib+.vh 
  all: c_compile sim
  c_compile:
  cd goldenbrick; gcc -Wall -ggdb -o goldenbrick goldenbrick.c
  cd goldenbrick; ./goldenbrick > goldenbrick.txt
  sim:
  cp goldenbrick/goldenbrick.txt verilog/goldenbrick.txt
  cd verilog; $(VV) $(VVOPTS) $(SIM_FILES) $(TESTBENCH); ./$@
  如图2所示,Makefile放在主文件夹中,goldenbrick.v放在goldenbrick文件夹中,mult.v和mult_testbech.v放置在verilog文件中。
图2. 文件结构
  在图2处运行
  make
  我们即可在/verilog中看到testbench.txt,所有结果均正确。这说明模块功能化运行正常。
  A = 01100111 B = 11000110 RESULT = 0000000000000000 pass
  A = 01101001 B = 01110011 RESULT = 0000000000000000 pass
  A = 01010001 B = 11111111 RESULT = 0000000000000000 pass
  A = 01001010 B = 11101100 RESULT = 0000000000000000 pass
  A = 00101001 B = 11001101 RESULT = 0100111110101010 pass
  A = 10111010 B = 10101011 RESULT = 0010111100101011 pass
  A = 11110010 B = 11111011 RESULT = 0101000010101111 pass
  A = 11100011 B = 01000110 RESULT = 0100010000111000 pass
  A = 01111100 B = 11000010 RESULT = 0010000011010101 pass
  A = 01010100 B = 11111000 RESULT = 0111110000111110 pass
  A = 00011011 B = 11101000 RESULT = 1110110101000110 pass
  A = 11100111 B = 10001101 RESULT = 0011111000010010 pass
  A = 01110110 B = 01011010 RESULT = 0101110111111000 pass
  A = 00101110 B = 01100011 RESULT = 0101000101100000 pass
  A = 00110011 B = 10011111 RESULT = 0001100001111000 pass
  A = 11001001 B = 10011010 RESULT = 0111111100111011 pass
  我们也可在图2处分别运行:
  make c_compile
  或者
  make sim
  从而分别进行goldenbrick的产生和vcs仿真。
  本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号