一、平台与环境
1、仿真环境:ModelSim-altera 10.3
2、FPGA设计环境:Quartus ii 15.0
3、操作系统:Windows 10
二、问题叙述
最近笔者在调试Altrera FPGA自带的FFT核,在使用ModelSim仿真的时候出现了以下错误:

Instantiation or 'FFT_out' failed.The design unit was not found.
大概意思就是:例化失败,找不到这个设计单元,下面就是搜索路径。在网上找了很多的资料。这些回答差不多都是大同小异——1、需要添加的XXX.vo文件而不是XXX.v文件。2、缺少了相关的库,要手动添加的……
当时我用的版本是Quartus ii 16.0。在我的工程文件里面我根本找不到一个叫XXX.vo的文件(其实是找到了一个.vo文件,但是不是它)。所以我认为是我的软件问题,所以我就重新安装了15.0的版本,果然,还是没有。而且作为一个小白初学者,上面提到的缺少库,我也不知道是指的除了安装目录下之外的什么库。
在之后,我找到了一个文档,里面也记载了一个解决办法,就是不使用Quartus ii调用ModelSim,而是手动添加库,然后直接使用ModelSim进行门级仿真。但是这个办法我还是失败了,原因依旧是我找不到.vo文件。然后我就重新建了一个工程,注意到了一个我一直忽视的弹框,才解决了问题。
三、解决办法
1、首先新建IP核,虽然各个版本的Qsys工具的界面不太一样,但是也都算是大同小异了。

2、设置完之后点击右下方的Generate HDL。在弹出的框框里面中,simlation的一栏里面选择上verilog(如果你用VHDL的话就选择VHDL),选择完成之后点击generate生成IP核。生成完毕点击close关闭,右下键点击finish关闭Qsys工具。

3、这时弹出了一个框,这就是我一直忽视的框框,框框里面的大概意思就是:你生成了一个IP核,但是你需要添加以下两个文件,这两个文件分别是XXX.qip和XXX.sip。这两个文件一个都不能少!笔者在仿真报错的原因其实就是因为没有添加sip这个文件,只添加了IP核的文件。

4、最后,写一个驱动IP核的文件,然后在写一个testbench文件,在settings--->simlation里面设置一下需要仿真的testbench然后点击RTL simlation就可以进行门极仿真了。下面我把相关代码附上。
FFT_test.v
module FFT_test(
clk,
rst_n,
data_in, //AD采集数据输入
amp //FFT计算结果
);
input clk;
input rst_n;
input signed[11:0]data_in;
output signed[24:0] amp;
/********** 定义FFT IP核使用端口 **********/
wire inverse; // 输入,为1时进行IFFT,为0时进行FFT
wire sink_ready; // 输出,FFT引擎准备好接收数据时该信号置位
wire source_ready; // 输入,下传流模块在可以接收数据时将该信号置位
reg sink_valid; // 输入,有效标记信号,sink_valid和sink_ready都置位时开始数据传输
reg sink_sop; // 输入,高电平表示1帧数据载入开始
reg sink_eop; // 输入,高电平表示1帧数据载入结束
wire signed [11:0]sink_imag; // 输入,输入数据的虚部,二进制补码数据
wire [1:0] sink_error; // 输入,表示载入数据状态,一般置0
wire [1:0] source_error; // 输出,表示FFT转换出现的错误
wire source_sop; // 输出,高电平表示一帧数据转换开始
wire source_eop; // 输出,高电平表示一帧数据转换结束
wire [5:0]source_exp;
wire source_valid;
wire signed [11:0] xkre; // 输出,输出数据的实部,二进制补码数据
wire signed [11:0] xkim; // 输出,输出数据的虚部
assign sink_error = 2'b00;
assign source_ready = 1'b1; // 该信号置1表示永远准备好接收FFT数据
assign inverse = 1'b0; // 进行FFT正变换
assign sink_imag = 12'd0; // 输入数据虚部接地
reg [10:0] count;
always @ (posedge clk or negedge rst_n)
if (!rst_n) begin
sink_eop <= 'b0;
sink_sop <= 'b0;
sink_valid <= 'b0;
count <= 'b0;
end
else begin
count <= count + 1'd1;
if (count == 1) sink_sop <= 1'b1; //计数1,置位sop,开始载入AD数据
else sink_sop <= 1'b0;
if (count == 512) sink_eop <= 1'b1;
else sink_eop <= 1'b0;
if (count>=1 & count<=512) sink_valid <= 1'b1; //载入数据期间,置位sink_valid
else sink_valid <= 1'b0;
end
/********** 调用IP核进行FFT变换,Burst模式 **********/
FFT u0 (
.clk (clk),
.reset_n (rst_n),
.sink_valid (sink_valid),
.sink_ready (sink_ready),
.sink_error (sink_error),
.sink_sop (sink_sop),
.sink_eop (sink_eop),
.sink_real (data_in),
.sink_imag (sink_imag),
.inverse (inverse),
.source_valid (source_valid),
.source_ready (source_ready),
.source_error (source_error),
.source_sop (source_sop),
.source_eop (source_eop),
.source_exp (source_exp),
.source_real (xkre),
.source_imag (xkim)
);
/********** 计算频谱的幅值信号 **********/
wire signed [23:0] xkre_square, xkim_square;
assign xkre_square = xkre * xkre;
assign xkim_square = xkim * xkim;
assign amp = xkre_square + xkim_square;
endmodule
TB_test.v
`timescale 1ns/1ps//时钟刻度
`define clock_period 20//宏定义时钟刻度
module TB_test();
reg clk;
reg Rst_n;
wire clk_100M;
reg signed [11:0]data_in;
wire signed [24:0]amp;
FFT_test FFT_0(
.clk(clk), //时钟
.rst_n(Rst_n), //低电平有效复位
.data_in(data_in), //AD采集数据输入
.amp(amp) //FFT计算结果
);
initial clk = 1;
always #(`clock_period/2) clk = ~clk;//50M时钟 (调用宏定义的数据除以2)
initial begin
Rst_n = 1'b0;//复位
#200;
Rst_n = 1'b1;//复位完成
#2000000;
$stop;//停止仿真
end
reg [11:0]Count;
always@(posedge clk or negedge Rst_n)
if(!Rst_n)
Count <= 12'd0;
else if(Count == 100)
Count <= 12'd0;
else
Count <= Count+1'd1;
//30M采样 512个采样点 256个高电平 256个低电平
always@(posedge clk or negedge Rst_n)
if(!Rst_n)
data_in <= 12'd0;
else if(Count < 50)
data_in <= 12'd0;
else
data_in <= 12'HFF;
endmodule
四、仿真效果

五、总结
笔者也是个接触没多久的小白,本文主要记录了使用Qsys工具生成的IP核在仿真的时候遇到的一些问题和本人忽视的点。如果有别的问题还望指出,共同学习,共同进步。