“ 本文主要分享了在Verilog设计过程中一些经验与知识点,主要包括Verilog仿真时常用的系统任务、双向端口的使用(inout)、边沿检测”
01
—
仿真时常用的系统任务($display,$fopen,$fscanf,$fwrite($fdisplay),$fclose,$random,$stop)
在RTL设计过程中,仿真的时候需要用一些系统函数,这边笔整理了部分Verilog设计中常用的系统函数:$display,$fopen,$fscanf,$fwrite($fdisplay),$fclose,$random,$stop。
这个函数系统任务的作用是用来在控制台输出信息。
$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;initialbegindata_display = 32'd100;flag = 0;$display("!!! Start Simulation !!!");//显示16进制 10进制$display("data_display = %h hex %d decimal", 100, 100);//显示8进制 2进制$display("data_display = %o otal %b binary", 100, 100);//ASCII码$display("data_display has %c ascii character value",64);//显示10进制 换行 2进制$display("data_display = %d otal next line %b binary", 100, 100);//显示系统仿真时间$display("simulation time is %t",$time);flag = 1;end
仿真结果如下图所示:

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


//****************************** 读文件 $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;//读取函数initialbegin#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
仿真结果如下图所示:

仿真结果如下所示//************************** 写文件 $fwrite($fdisplay) *************************//****** $fwrite 写下一个数不会自动转行,所以要加//将读取的Sig0,Sig1重新写进两个新的txt中//定义句柄integer data_wr0;integer data_wr1;integer m;//读取函数initialbegin#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

$fwrite和$fdisplay的区别,$fwrite写下一个数不会自动转行,可以加 来转行,$fdisplay则会自动转行。
仿真结果如下图所示://****** $fdisplay//将读取的Sig0,Sig1重新写进两个新的txt中//定义句柄integer data_wr2;integer data_wr3;integer j;//读取函数initialbegin#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

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

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

同样,Xilinx也有三态门的源语assign io = ( !t ) ? i : 1'bz ;assign o = io;
参考: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);

可以看出:
当T=1的时候,O端电平=IO端电平;
当T=0的时候,O端电平=IO端电平=I端电平。
03
—
边沿检测
在程序设计过程中,经常需要检测一个脉冲信号的上升沿或者下降沿,下面给大家介绍如何使用Verilog实现对脉冲信号的边沿进行检测。时钟信号与脉冲信号如下图所示。

Verilog代码如下:
上述程序经过综合后,其RTL结构如下图所示,由两个D触发器和两个与门组成。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;//--------------------------------------------------------------------------------clk or posedge rst)beginif(rst)beginsin_reg0 <= 0;sin_reg1 <= 0;endelsebeginsin_reg0 <= sin_pulse;sin_reg1 <= sin_reg0;endend//--------------------------------------------------------------------------------assign sout_r = sin_reg0 & (~sin_reg1); //上升沿检测= (~sin_reg0) & sin_reg1; //下降沿检测//--------------------------------------------------------------------------------

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

审核编辑:郭婷
全部0条评论
快来发表一下你的评论吧 !