目录
一、基本流程
二、新建工程
三、文件输入
3.1、文件创建
3.2、端口设定补充
3.3、RTL程序输入
3.4、XDC约束文件建立
3.5、快捷定义
四、仿真
4.1、文件创建
4.2、激励文件编写
4.3、开始仿真
五、下载
六、仿真文件要点补充
1、端口变量
2、文件关系
3、简单案例
七、代码写入
根据使用的 FPGA 开发平台,选择对应的 FPGA 目标器件;Next;
新建工程如图
如下图所示,点击 Flow Navigator 下的 Project Manager->Add Sources 或中间 Sources 中的对话框打开设计文件导入添加对话框。
选择第二项 Add or Create Design Sources,用来添加或新建 Verilog 或 VHDL 源文件,点击Next。
如果有现有的 V/VHD 文件,可以通过 Add Files 一项添加。在这里,我们要新建文件,所以选择 Create File 一项。
在 Create Source File 中输入 File Name,点击 OK。
在弹出的 Define Module 中的 I/O Port Definition,输入设计模块所需的端口,并设置端口方向,如果端口为总线型,勾选 Bus 选项,并通过 MSB 和 LSB 确定总线宽度。
在弹出的 Define Module 中的 I/O Port Definition,输入设计模块所需的端口,并设置端口方向,完成后点 OK。
新建的设计文件(此处为 flowing_light.v)即存在于 Sources 中的 Design Sources 中。双击打开该文件,输入相应的设计代码。
Vivado中Bus用于创建、编辑和配置总线(Bus)的工具,总线即计算机系统中用于多设备之间传输的通信系统。
Bus选项有四种功能:
MSB(Most Significant Bit)和 LSB(Least Significant Bit)分别为数据最高有效位(左侧位)与最低有效位(右侧位)。
'timescale 1ns / 1ps
module flowing_light(
input clk, //时钟
input rst, //复位
output [3:0] led //管脚设定
);
reg [23 : 0] cnt_reg; //计数
reg [3 : 0] light_reg; //亮灯控制
always @ (posedge clk) //高电平触发
begin
if (!rst) //若rst为低电平
cnt_reg <= 0; //计数器复位
else
cnt_reg <= cnt_reg + 1;
//rst为高电平时计数器累加,达到最大值后越界变回0
end
//计数器,作用于分频相同,控制输出信号的变化频率
always @ (posedge clk) //高电平触发
begin
if (!rst) //rst为低电平
light_reg <= 4'h1; //复位,0001
else if (cnt_reg == 24'hffffff) //计数器达到最大值
begin
if (light_reg == 4'h8) //若为'b1000
light_reg <= 4'h1; //变为'b0001
//数位为二进制是的位数
else
light_reg <= light_reg << 1; //左移一位,下一位LED亮
end
end
//输出信号生成器
assign led = light_reg; //赋值输出信号
//连个always并行执行
endmodule
点击 Add Sources,选择第一项 Add or Create Constraints 一项,点击 Next。
点击 Create File,新建一个 XDC 文件,输入 XDC 文件名,点击 OK。点击 Finish。
点击 Create File,新建一个 XDC 文件,输入 XDC 文件名,点击 OK,点击 Finish。
双击打开新建好的 XDC 文件,并按照如下规则,输入相应的 FPGA 管脚约束信息和电平标准。
## Clock signal 125 MHz
//时间信号为125MHz
set_property -dict { PACKAGE_PIN H16 IOSTANDARD LVCMOS33 } [get_ports { clk }];
create_clock -add -name sys_clk_pin -period 8.00 -waveform {0 4} [get_ports { clk }];
##LEDs
set_property -dict { PACKAGE_PIN R14 IOSTANDARD LVCMOS33 } [get_ports { led[0] }];
set_property -dict { PACKAGE_PIN P14 IOSTANDARD LVCMOS33 } [get_ports { led[1] }];
set_property -dict { PACKAGE_PIN N16 IOSTANDARD LVCMOS33 } [get_ports { led[2] }];
set_property -dict { PACKAGE_PIN M14 IOSTANDARD LVCMOS33 } [get_ports { led[3] }];
##Buttons
set_property -dict { PACKAGE_PIN D19 IOSTANDARD LVCMOS33 } [get_ports { rst }];
其中"PACKAGE_PIN"为引脚代号,"IOSTANDARD LVCMOS33"表示标准电平为3.3V的低压差分CMOS,即逻辑高电平为3.3V,逻辑低电平为0V。标准电平为电路正常工作下单电压,通常在数字电路中标准电平用于描述逻辑高、低电平。
在PROJECT MANAGER中点击RTL ANALYSIS,在点击Open Elaborated Design,再在Schematic界面选择 I/O Ports,即可在屏幕下方弹出的Find Results 中快速设定端口管脚。
其中管脚名与IO直接由主程序定义,用户只需要对Package Pin(管脚) 以及 I/O Std 标准电平进行选择即可。但需要注意由此生成的xdc文件在格式上会与手动输入有所不同。
创建激励文件,在 Add Source 界面中选择第三项 Add or Create Simulation Source,点击 Next。选择 Create File 创建一个仿真激励文件。
确认添加完成之后点击 Finish,因为是激励文件不需要对外端口,所以直接 Port 部分直接空着,点击 ok。
`timescale 1ns / 1ps
module tb( );
reg clk;
reg rst;
wire [3 : 0] led;
flowing_light u0(
.clk(clk),
.rst(rst),
.led(led) );
parameter PERIOD = 10;
always //持续执行
begin
clk = 1'b0;
#(PERIOD/2)
clk = 1'b1;
#(PERIOD/2);
end
//时钟设置,周期为10个时间单位,占空比50%
initial //仅执行一次
begin
clk = 1'b0;
rst = 1'b0;
#100;
rst = 1'b1;
#100;
rst = 1'b0;
#100;
rst = 1'b1;
end
//初始化复位状态
endmodule
initial 块,表示在模块被实例化时只执行一次。
initial块的作用是对时钟和复位信号进行初始化,以确保模块在开始工作之前处于正确的状态。具体来说,该代码段将时钟信号 clk 初始化为低电平,并将复位信号 rst 初始化为高电平。然后,等待一段时间,将复位信号 rst 置为低电平。等待一段时间后,再次将复位信号 rst 置为高电平。这样做的目的是确保模块在开始工作时具有稳定的时钟信号和正确的复位状态。
可通过左侧 Scope 一栏中的目录结构定位到设计者想要查看的 module 内部寄存器,在 Objects对应的信号名称上右击选择 Add To Wave Window,将信号加入波形图中。 因为窗口已有信号,此操作不需要进行。 可通过选择工具栏中的如下选项来进行波形的仿真时间控制。如下工具条,分别是复位波形(即清空现有波形)、运行仿真、运行特定时长的仿真、仿真时长设置、仿真时长单位、单步运行、暂停……
在 Flow Navigator 中点击 Program and Debug 下的 Generate Bitstream 选项,工程会自动完成综合、实现、 Bit 文件生成过程。
点击 Flow Navigator 中 Open Hardware Manager 一项,进入硬件编程管理界面。
在提示的信息中,选择 Open Hardware Manager(或在 Flow Navigator 中展开 HardwareManager,点击 Open Target)。 选择Auto Connect连接到板卡。
连接成功后, 在目标芯片上右击,选择 “Program Device”。在弹出的对话框中 “Bitstream File” 一栏已经自动加载本工程生成的比特流文件,点击 “Program” 对 FPGA 芯片进行编程。下载完成后, 在板子上观察实验结果。
主程序:
`timescale 1ns / 1ps
module test1(
input rst,
input clk,
output [5:0] outout
);
reg [5:0] outout1 = 6'b0;
//应当尽量保持计算变量与输出变量的长度一致
always@(posedge clk)
outout1 = outout1 + 1;
assign outout = outout1;
endmodule
tb:
`timescale 1ns / 1ps
module tb(
);
reg clk;
reg rst;
wire [5:0] outout;
//wire [7:0] outout ;//此时最高位为Z,可以发现仿真输出由tb决定
test1 test1(//后面的名字随便写
.rst(rst),
.clk(clk),
.outout(outout)
);//主程序通过此函数与tb文件相连接,与ip核的调用相同,I或O控制有主程序来定义
initial
begin
clk =1'b0;
rst = 1'b1;
#20
rst = 1'b0;
#20
rst = 1'b1;
end
always
begin
#10
//仿真仅到1k nm 若需要后续仿真数据,可以通过改主频率来实现,反正是仿真,时钟是程序给的,超过主板的主频也行
clk = ~clk;
end
endmodule
其中调用主程序IO端口为:
test1 test1(
.rst(rst),
.clk(clk),
.outout(outout));
代码中后一个test1可以写任意合法字符串,而端口变量按以上形式写即可,可以看到这段代码与ip核的调用十分相似。
4、实际端口
在vivado中完成仿真后,就是烧录步骤。在此步骤需要注意,程序中定义的所以IO口均需要进行定义
切忌生产bitstream后不录入文件(本人没发现之后和程序斗智斗勇三天结果发现是没写进去)