一、新建工程
1、建立工程管理文件夹;
2、双击quartus,打开软件;
3、选择新建工程→选择工程文件夹→工程名→不 add files→选择器件→选择仿真工具;







4、至此新建工程完毕,可以进行设计输入;
二、quartus与notepad++的关联
1、tools→options;

2、点击Preferred Text Editor,选择notepad++,并且关联到notepad++的安装路径;

三、分析与综合
代码编写完毕后,需要对语法进行检查;
四、打开RTL视图
方法1:双击左侧

方法2:工具栏 Tools → Netlist Viewers → RTL Viewer;


五、modelsim仿真
代码编写完成后,就可以进行仿真操作,验证代码的逻辑性;
5.1 quartus与modelsim联合仿真
步骤1 :选择仿真软件;


其实步骤1在新建工程时,已经选择过了;
步骤2:设置modelsim软件路径;


步骤3:添加vt文件
代码编写好后,需要将 tb_xxx.v 文件添加到工程中。点击“File”切换到文件模式,在该模式下可以看到所有属于该工程下的文件。


步骤4:仿真设置


步骤5:NativeLink 的设置



步骤3中,
①
②是Testbench 顶层的名字;
③是设置打开 ModelSim时波形运行多久后停止,一般设置1us;
④选择 sim 文件夹下的 tb_led.v 文件;
步骤6:打开 ModelSim 观察波形
5.2 testbench语法
通常,编写测试文件的过程如下:
• 产生模拟激励(波形);
• 将产生的激励加入到被测试模块中并观察其响应;
• 将输出响应与期望值相比较。
1、Testbench是一个测试文件,取名就在模块前加 tb_xxx;模块的实例化,是在Testbench里的代码,取名是u_xxx;
2、在sim文件夹下,编写 tb_xxx.v 文件;
3、tb_xxx.v文件写完后,可运行分析与综合,进行代码语法检查;
5.2.1 testbench结构
通常,一个完整的测试文件其结构为
`timescale 仿真单位/仿真精度
module Test_bench();//通常无输入无输出
信号或变量声明定义
逻辑设计中输入对应 reg 型
逻辑设计中输出对应 wire 型
使用 initial 或 always 语句产生激励
例化待测试模块
监控和比较输出响应
endmodule
声明仿真的单位和精度
5.2.2 时钟激励设计
/*----------------------------------------------------------------
时钟激励产生方法一: 50%占空比时钟
----------------------------------------------------------------*/
parameter ClockPeriod=10;
initial
begin
clk_i=0;
forever
#(ClockPeriod/2) clk_i=~clk_i;
end
/*----------------------------------------------------------------
时钟激励产生方法二: 50%占空比时钟
----------------------------------------------------------------*/
initial
begin
clk_i=0;
always #(ClockPeriod/2) clk_i=~clk_i;
end
/*----------------------------------------------------------------
时钟激励产生方法四:产生固定数量的时钟脉冲
----------------------------------------------------------------*/
initial
begin
clk_i=0;
repeat(6)
#(ClockPeriod/2) clk_i=~clk_i;
end
/*----------------------------------------------------------------
时钟激励产生方法五:产生非占空比为 50%的时钟
----------------------------------------------------------------*/
initial
begin
clk_i=0;
forever
begin
#((ClockPeriod/2)-2) clk_i=0;
#((ClockPeriod/2)+2) clk_i=1;
end
end
5.2.3 复位信号设计
/*----------------------------------------------------------------
复位信号产生方法一:异步复位
----------------------------------------------------------------*/
initial
begin
rst_n_i=1;
#100;
rst_n_i=0;
#100;
rst_n_i=1;
end
/*----------------------------------------------------------------
复位信号产生方法二:同步复位
----------------------------------------------------------------*/
initial
begin
rst_n_i=1;
@(negedge clk_i)
rst_n_i=0;
#100; //固定时间复位
repeat(10) @(negedge clk_i); //固定周期数复位
@(negedge clk_i)
rst_n_i=1;
end
/*----------------------------------------------------------------
复位信号产生方法三:复位任务封装
----------------------------------------------------------------*/
task reset;
input [31:0] reset_time; //复位时间可调,输入复位时间
RST_ING=0; //复位方式可调,低电平或高电平
begin
rst_n=RST_ING; //复位中
#reset_time; //复位时间
rst_n_i=~RST_ING; //撤销复位,复位结束
end
endtask
5.2.4 双向信号设计
/*----------------------------------------------------------------
双向信号描述一: inout 在 testbench 中定义为 wire 型变量
----------------------------------------------------------------*/
//为双向端口设置中间变量 inout_reg 作为 inout 的输出寄存,其中 inout 变
//量定义为 wire 型,使用输出使能控制传输方向
//inout bir_port;
wire bir_port;
reg bir_port_reg;
reg bi_port_oe;
assign bi_port=bi_port_oe ? bir_port_reg : 1'bz;
/*----------------------------------------------------------------
双向信号描述二:强制 force
----------------------------------------------------------------*/
//当双向端口作为输出口时,不需要对其进行初始化,而只需开通三态门
//当双向端口作为输入时,只需要对其初始化并关闭三态门,初始化赋值需
//使用 wire 型数据,通过 force 命令来对双向端口进行输入赋值
//assign dinout=(!en) din :16'hz; 完成双向赋值
initial
begin
force dinout=20;
#200
force dinout=dinout-1;
end
5.2.5特殊信号设计
/*----------------------------------------------------------------
特殊激励信号产生描述一:输入信号任务封装
----------------------------------------------------------------*/
task i_data;
input [7:0] dut_data;
begin
@(posedge data_en); send_data=0;
@(posedge data_en); send_data=dut_data[0];
@(posedge data_en); send_data=dut_data[1];
@(posedge data_en); send_data=dut_data[2];
@(posedge data_en); send_data=dut_data[3];
@(posedge data_en); send_data=dut_data[4];
@(posedge data_en); send_data=dut_data[5];
@(posedge data_en); send_data=dut_data[6];
@(posedge data_en); send_data=dut_data[7];
@(posedge data_en); send_data=1;
#100;
end
endtask
//调用方法: i_data(8'hXX);
/*----------------------------------------------------------------
特殊激励信号产生描述二:多输入信号任务封装
----------------------------------------------------------------*/
task more_input;
input [7:0] a;
input [7:0] b;
input [31:0] times;
output [8:0] c;
begin
repeat(times) //等待 times 个时钟上升沿
@(posedge clk_i)
c=a+b; //时钟上升沿 a, b 相加
end
endtask
//调用方法: more_input(x,y,t,z); //按声明顺序
/*----------------------------------------------------------------
特殊激励信号产生描述三:输入信号产生,一次 SRAM 写信号产生
----------------------------------------------------------------*/
initial
begin
cs_n=1; //片选无效
wr_n=1; //写使能无效
rd_n=1; //读使能无效
addr=8'hxx; //地址无效
data=8'hzz; //数据无效
#100;
cs_n=0; //片选有效
wr_n=0; //写使能有效
addr=8'hF1; //写入地址
data=8'h2C; //写入数据
#100;
cs_n=1;
wr_n=1;
#10;
addr=8'hxx;
data=8'hzz;
end
/*----------------------------------------------------------------
Testbench 中@与 wait
----------------------------------------------------------------*/
//@使用沿触发
//wait 语句都是使用电平触发
initial
begin
start=1'b1;
wait(en=1'b1);
#10;
start=1'b0;
end
5.2.6repeat ,wait函数
//==========================================
//== repeat重复执行
//==========================================
initial begin
start = 1;
repeat(5) @(posedge clk) //等待5个时钟上升沿
start = 0;
end
initial begin
repeat(10)begin
...//执行10次
end
end
//===========================================
//== wait为电平触发
//==========================================
initial begin
start = 1;
wait(en); //等待en==1
start = 0;
end
5.2.7 随机数的产生
$random //产生随机数
$random % n //产生范围 {-n,n} 的随机数
{$random} % n //产生范围 { 0,n} 的随机数
5.2.8 文本输入输出
reg [a:0] data_mem [0:b]; //定义位宽为(a+1)深度为(b+1)的存储器
$readmemb/$readmemh("<读入文件名>",<存储器名>);
$readmemb/$readmemh("<读入文件名>",<存储器名>,<起始地址>);
$readmemb/$readmemh("<读入文件名>",<存储器名>,<起始地址>,<结束地址>);
$readmemb
/*------------------------------------------------------------------------*\
读取二进制数据,读取文件内容只能包含:空白位置,注释行,二进制数
数据中不能包含位宽说明和格式说明,每个数字必须是二进制数字。
\*------------------------------------------------------------------------*/
$readmemh
/*------------------------------------------------------------------------*\
读取十六进制数据,读取文件内容只能包含:空白位置,注释行,十六进制数
数据中不能包含位宽说明和格式说明,每个数字必须是十六进制数字.
\*------------------------------------------------------------------------*/
//==========================================================================
//== 输出txt文件
//==========================================================================
integer fp_write; //定义
initial begin
begin
fp_write = $fopen("output.txt"); //打开输出文件
begin
$fwrite(fp_write, "\n%h", output_data); //写入数据16进制
#(`clk_period);
end
end
$fclose(fp_write); //关闭文件,不可少
end
5.2.9 文本输入输出
reg [a:0] data_mem [0:b]; //定义位宽为(a+1)深度为(b+1)的存储器
$readmemb/$readmemh("<读入文件名>",<存储器名>);
$readmemb/$readmemh("<读入文件名>",<存储器名>,<起始地址>);
$readmemb/$readmemh("<读入文件名>",<存储器名>,<起始地址>,<结束地址>);
$readmemb
/*------------------------------------------------------------------------*\
读取二进制数据,读取文件内容只能包含:空白位置,注释行,二进制数
数据中不能包含位宽说明和格式说明,每个数字必须是二进制数字。
\*------------------------------------------------------------------------*/
$readmemh
/*------------------------------------------------------------------------*\
读取十六进制数据,读取文件内容只能包含:空白位置,注释行,十六进制数
数据中不能包含位宽说明和格式说明,每个数字必须是十六进制数字.
\*------------------------------------------------------------------------*/
//==========================================================================
//== 输出txt文件
//==========================================================================
integer fp_write; //定义
initial begin
begin
fp_write = $fopen("output.txt"); //打开输出文件
begin
$fwrite(fp_write, "\n%h", output_data); //写入数据16进制
#(`clk_period);
end
end
$fclose(fp_write); //关闭文件,不可少
end
5.2.10 打印信息
$monitor //仿真打印输出,打印出仿真过程中的变量,使其终端显示
/*------------------------------------------------------------------------*\
$monitor($time,,,"clk=%d reset=%d out=%d",clk,reset,out);
\*------------------------------------------------------------------------*/
$display //终端打印字符串,显示仿真结果等
/*------------------------------------------------------------------------*\
$display(” Simulation start ! ");
$display(” At time %t,input is %b%b%b,output is %b",$time,a,b,en,z);
\*------------------------------------------------------------------------*/
$time //返回 64 位整型时间
$stime //返回 32 位整型时间
$realtime //实行实时模拟时间
5.2.11 testbench总体代码结构
`timescale 1ns/1ps //时间精度
`define clk_perilod 20 //时钟周期可变
module test_file_tb;
//==================<端口>==================================================
reg clk ; //时钟,50Mhz
reg rst_n ; //复位,低电平有效
reg [XX:0] in ; //
wire [XX:0] out ; //
//--------------------------------------------------------------------------
//-- 模块例化
//--------------------------------------------------------------------------
my_design u_my_design
(
.clk (clk ),
.rst_n (rst_n ),
.in (in ),
.out (out )
);
//----------------------------------------------------------------------
//-- 时钟信号和复位信号
//----------------------------------------------------------------------
initial begin
clk = 0;
forever
#(`Clock/2) clk = ~clk;
end
initial begin
rst_n = 0; #(`Clock*20+1);
rst_n = 1;
end
//----------------------------------------------------------------------
//-- 设计输入信号
//----------------------------------------------------------------------
initial begin
in = 0;
#(`Clock*20+2); //初始化完成
$stop;
end
endmodule在这里插入代码片
六、管脚分配
6.1 管脚分配
引脚分配完成后, 直接关闭引脚分配窗口, 软件会在工程所在位置生成一个.qsf 文件用来存放引脚信息。 当然我们也可以生成一个 TCL 文件,这样下次在使用的时候就可以直接运行 TCL 文件自动分配引脚。
6.2 清除管脚分配


再次打开Pin Planner,我们可以看到引脚绑定信息已经被清除了。
6.3 生成TCL文件
生成TCL文件
点击状态栏的 File,然后点击 Export。
然后弹出下面的界面, 这里我们将生成的 TCL 文件放置到 doc 文件夹下,选择保存类型*.tcl。然后点击“ Export”。
此时 TCL 文件就已经生成。
6.4 添加TCL文件
6.4.1 18.0之后版本



6.4.2 18.0之前版本
方法一:使用TCL绑定管脚
a.回到工程界面,单击View->Utility Windows->Tcl Console,然后复制导出的tcl文件中的所有内容,然后粘贴到Tcl Console窗口,完成引脚绑定。
方法二:使用 .cvs文件绑定管脚
a. 在pin planer界面,导出tcl文件的同时,也导出 .cvs文件;
b. 在工程界面,点击Assignments->Import Assignments,然后点击右侧的三个点,找到我们第一次导出的.csv文件,选中后选择打开,然后点击OK;
七、编译工程
分配完引脚之后, 需要对整个工程进行一次全编译,
八、下载工程
编译完成后,就可以下载程序,
首先将 USB Blaster 下载器一端连接电脑, 另一端与JTAG 接口相连接;
然后连接电源线,并打开电源开关。


点击【 Hardware Setup...】按钮,选择“ USB-Blaster”,




九、固化
如果我们想要程序断电不丢失的话,就必须将程序保存在片外 Flash 中, Flash 的引脚是和 FPGA 固定的引脚相连接, FPGA 会在上电后自动读取 Flash 中存储的程序,这个过程不需要我们编写驱动代码和人为干预,只需要通过 JTAG 下载jic 文件即可。
需要注意的是, jic 文件不是软件自动生成的, 而是需要我们手动的将 sof 文件转换成 jic 文件。
9.1 jic文件生成


①文件类型,jic;
②flash型号;
③输出文件名;
④⑤选择fpga型号;


9.2 固化
回到下载界面,删除sof文件;

加载jic文件;

在 Program/Configure 选项打勾,开始下载;

9.3 擦除
如果需要擦除 Flash 中的程序的话, 可以通过勾选【 Erase】下面的方框来擦除程序。
需要注意的是,如果已经勾选了【 Program/Configure】方框,是无法勾选【 Erase】方框的,所以先取消勾选
【 Program/Configure】,然后再勾选【 Erase】,如下图所示。
