Verilog系统函数和边沿检测

描述

“ 本文主要分享了在Verilog设计过程中一些经验与知识点,主要包括Verilog仿真时常用的系统任务、双向端口的使用(inout)、边沿检测”

01

仿真时常用的系统任务($display,$fopen,$fscanf,$fwrite($fdisplay),$fclose,$random,$stop)

在RTL设计过程中,仿真的时候需要用一些系统函数,这边笔整理了部分Verilog设计中常用的系统函数:$display,$fopen,$fscanf,$fwrite($fdisplay),$fclose,$random,$stop。

  • $display

这个函数系统任务的作用是用来在控制台输出信息

  • $display("!!! Start   Simulation !!!"); 直接显示字符串

  • $display("data_display = %h hex %d decimal",100, 100); //显示data_display 的16进制 ,10进制

  • $display("data_display = %o otal %b binary",100, 100);//显示data_display 的8进制   2进制

  • $display("data_display = %d otal next line %bbinary", 100, 100);//主要展示换行操作

 

  • $display("simulation time is %t",$time);//显示系统仿真时间

具体代码如下:

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  reg        flag;//******************************  系统显示  $display  *******************************  reg  [31:0]   data_display;  initial    begin      data_display  =  32'd100;      flag   =   0;          $display("!!! Start  Simulation !!!");      #10;        //显示16进制  10进制        $display("data_display = %h hex %d decimal", 100, 100);      #10;        //显示8进制   2进制        $display("data_display = %o otal %b binary", 100, 100);      #10;        //ASCII码        $display("data_display has %c ascii character value",64);      #10;        //显示10进制  换行 2进制        $display("data_display = %d otal next line 
 %b binary", 100, 100);      #10         //显示系统仿真时间        $display("simulation time is %t",$time);      flag   =   1;      end

仿真结果如下图所示:

Verilog

在第五行展示了换行功能;为了验证系统仿真时间,笔者这边用flag参数拉高来测试时间,时间结果如下图显示,和显示时间一致;

 

Verilog

  • $fopen

用法:<文件句柄>=$fopen("<文件名>"); 句柄就是任务$fopen返回的多通道描述符,默认为32位,最低位(第0位)默认被设置1,默认开放标准输出通道,即transcript窗口。 每一次使用$fopen函数后都打开了一个新的通道,并且返回了一个设置为1的位相对应。默认应该是0001,以上每调用分别设置为0010 ,0100,1000(只考虑最低四位)。  

Verilog

 

  • $fscanf

函数功能:读取txt(.dat)文件数据,具体代码如下:
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
//******************************   读文件  $fscanf  *******************************  //宏定义,定义数据长度  `define  DATA_LENGTH 8  //定义RAM大小  reg  signed   [15:0]  Sig0  [`DATA_LENGTH-1:0];  reg            [15:0]  Sig1  [`DATA_LENGTH-1:0];  //定义句柄  integer   data_file0;  integer   data_file1;  integer   i;  //读取函数  initial    begin      #200;      //打开句柄      data_file0 = $fopen("file/rd_data0_fpga.txt","r");       data_file1 = $fopen("file/rd_data1_fpga.txt","r");       for(i = 0;i < `DATA_LENGTH; i = i + 1)         begin          $fscanf(data_file0,"%d",Sig0[i]); //读取十进制          $fscanf(data_file1,"%h",Sig1[i]); //读取十六进制        end      $fclose(data_file0); ////关闭这个句柄      $fclose(data_file1); ////关闭这个句柄    end

仿真结果如下图所示:

Verilog

 

  • $fwrite$fdisplay

代码如下所示:

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
//**************************   写文件  $fwrite($fdisplay)  *************************  //******  $fwrite  写下一个数不会自动转行,所以要加
  //将读取的Sig0,Sig1重新写进两个新的txt中  //定义句柄  integer   data_wr0;  integer   data_wr1;  integer  m;  //读取函数  initial    begin      #400;        //打开句柄      data_wr0 = $fopen("file/wr_data1_fpga.txt","w");       data_wr1 = $fopen("file/wr_data2_fpga.txt","w");       for(m = 0;m < `DATA_LENGTH; m = m + 1)         begin          @(clk);          $fwrite(data_wr0,"%d
",Sig0[m]); //向txt写十进制    写下一个数不会自动转行,所以要加
          $fwrite(data_wr1,"%h
",Sig1[m]); //向txt写十六进制   写下一个数不会自动转行,所以要加
        end      //关闭这个句柄      $fclose(data_wr0);      $fclose(data_wr1);    end
仿真结果如下所示

Verilog

 

$fwrite和$fdisplay的区别,$fwrite写下一个数不会自动转行,可以加 来转行,$fdisplay则会自动转行。
  • $fdisplay

代码如下所示:
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
//******  $fdisplay  //将读取的Sig0,Sig1重新写进两个新的txt中  //定义句柄  integer   data_wr2;  integer   data_wr3;  integer  j;  //读取函数  initial    begin      #600;      //打开句柄      data_wr2 = $fopen("file/wr_data3_fpga.txt","w");       data_wr3 = $fopen("file/wr_data4_fpga.txt","w");         for(j = 0;j < `DATA_LENGTH; j = j + 1)         begin          @(clk);          $fdisplay(data_wr2,"%d",Sig0[j]); //向txt写十进制    写下一个数会自动转行,所以不需要加
          $fdisplay(data_wr3,"%h",Sig1[j]); //向txt写十六进制   写下一个数会自动转行,所以不需要加
        end      //关闭这个句柄      $fclose(data_wr2);      $fclose(data_wr3);    end
仿真结果如下图所示:

Verilog

  • $fclose

fclose();关闭文件,为所获得的句柄。
  •  $stop

暂停仿真。

02

双向端口的使用(inout)

  根据Verilog的语法定义,IO的端口可以定义为三种类型input、output和inout,其中inout为双向端口。双向端口通过控制三态门来实现,其结构框图如下所示。

Verilog

  • 当T为1的时候,I端忽略(高阻),O端电平 = IO端电平;

  • 当T为0的时候,IO端电平=I端电平=O端电平;

真值表如下:  

Verilog

实现代码如下:
  •  
  •  
  assign    io  = ( !t ) ? i : 1'bz ;  assign    o   =  io;
同样,Xilinx也有三态门的源语

参考:Xilinx 7 Series FPGA Libraries Guide for HDL Design

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  IOBUF #(    .DRIVE         (  12         ), // Specify the output drive strength    .IBUF_LOW_PWR  (  "TRUE"     ), // Low Power - "TRUE", High Performance = "FALSE"    .IOSTANDARD    (  "DEFAULT"  ), // Specify the I/O standard    .SLEW          (  "SLOW"     ) // Specify the output slew rate  ) IOBUF_inst (    .O             (  o1         ), // Buffer output    .IO            (  io         ), // Buffer inout port (connect directly to top-level port)    .I             (  i1         ), // Buffer input    .T             (  t          ) // 3-state enable input, high=input, low=output  );
仿真结果如下:

Verilog

可以看出:

  • T=1的时候,O端电平=IO端电平;

  • T=0的时候,O端电平=IO端电平=I端电平。

 高阻,即可以认为没有输出,作为输出端口,对下级电路没有任何影响。悬空,是针对输入口而言,就是说没有接输入。在HDL语言中,高阻和悬空都是Z。  

03

边沿检测

在程序设计过程中,经常需要检测一个脉冲信号的上升沿或者下降沿,下面给大家介绍如何使用Verilog实现对脉冲信号的边沿进行检测。时钟信号与脉冲信号如下图所示。

 

Verilog

Verilog代码如下:

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
module edge_detection(  input        wire        clk,  input        wire        rst,  input        wire        sin_pulse,  output      wire        sout_r,  //上升沿检测  output      wire        sout_f   //下降沿检测    );//--------------------------------------------------------------------------------  reg              sin_reg0,sin_reg1;  //--------------------------------------------------------------------------------  always@(posedge  clk or posedge    rst)    begin      if(rst)        begin          sin_reg0 <= 0;          sin_reg1 <= 0;        end      else        begin          sin_reg0 <= sin_pulse;          sin_reg1 <= sin_reg0;        end    end//--------------------------------------------------------------------------------  assign  sout_r = sin_reg0 & (~sin_reg1); //上升沿检测  assign  sout_f = (~sin_reg0) & sin_reg1; //下降沿检测//--------------------------------------------------------------------------------
上述程序经过综合后,其RTL结构如下图所示,由两个D触发器和两个与门组成。

 

Verilog

 

仿真后的结果如下图所示,可以看出sout_r为上升沿检测结果,sout_f为下降沿检测结果。

 

Verilog

审核编辑:郭婷

 
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分