“ 本文主要分享了在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;
initial
begin
data_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;
//读取函数
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
仿真结果如下图所示:
仿真结果如下所示//************************** 写文件 $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
$fwrite和$fdisplay的区别,$fwrite写下一个数不会自动转行,可以加 来转行,$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
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)
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); //上升沿检测
(~sin_reg0) & sin_reg1; //下降沿检测 =
//--------------------------------------------------------------------------------
仿真后的结果如下图所示,可以看出sout_r为上升沿检测结果,sout_f为下降沿检测结果。
审核编辑:郭婷
全部0条评论
快来发表一下你的评论吧 !