FPGA入门文章目录FPGA入门前言一、实操历史 decoder_3_8 译码器模块添加至新项目二、顶层模块 led_run3 代码逐行详解1、第一个计数器延时计数器2、第二个计数器LED 选择计数器控制亮哪个灯3、例化 3-8 译码器4、一句话总结三、parameter MCNT 参数精讲四、Testbench 仿真文件逐行精讲总结前言做 FPGA 项目经常复用之前调试好的功能模块重复手写代码费时还容易出错。本文先手把手演示Vivado 导入已有.v 模块到新项目实操再基于导入的 3-8 译码器搭建流水灯顶层工程逐行拆解顶层代码逻辑、parameter 参数调试优势最后配套讲解 Testbench 仿真代码包含两种参数重载写法新手跟着实操即可跑通流水灯工程一、实操历史 decoder_3_8 译码器模块添加至新项目新项目打开添加源文件窗口新建工程后在 Sources 栏右键选择Add Sources弹出添加页面点击【Add Files】按钮进入本地文件选择弹窗。定位历史工程根目录在文件浏览框中找到存放旧工程的文件夹示例路径D:/FPGA/class/选中decoder_3_8旧工程文件夹找到源文件目录选中.v源码进入decoder_3_8工程目录打开decoder_3_8.srcs → sources_1 → new路径选中里面的decoder_3_8.v译码器源文件点击【OK】添加。关键配置勾选复制源码到本工程回到添加源文件界面勾选Copy sources into project选项该选项作用将原工程的.v文件复制一份到当前新项目本地目录后续修改旧工程源码不会干扰本项目是工程开发规范操作勾选后依次点击Next→Finish模块导入完成。补充导入后在当前工程 Sources 列表即可看到 decoder_3_8 模块可直接在顶层代码例化调用。二、顶层模块 led_run3 代码逐行详解先看整体功能50MHz 时钟输入 → 延时计数器 → 3 位循环计数器 → 3-8 译码器 → 8 路 LED 流水灯timescale1ns/1ps// 仿真时间单位1纳秒精度1皮秒不用深究固定写法moduleled_run3(input Clk,// 系统时钟输入开发板一般是50MHzinput Reset_n,// 复位按键输入低电平有效按下0松开1output[7:0]Led// 8路LED输出8个灯);1、第一个计数器延时计数器reg[24:0]counter;// 定义25位的寄存器变量counter最大能数到2^25-1// 时序逻辑时钟上升沿 或 复位下降沿 触发always(posedge Clk or negedge Reset_n)if(!Reset_n)// 低电平复位按下复位键counter清零counter0;elseif(counter满值)// 计数器数到最大值counter0;// 清零重新开始计数elsecountercounter1d1;// 正常情况每个时钟1FPGA 的时钟每秒跳 5000 万次如果直接让 LED 跟着时钟闪人眼完全看不见这个计数器的作用就是数数延时数到固定数值才停一下给 LED 切换留时间。2、第二个计数器LED 选择计数器控制亮哪个灯reg[2:0]counter2;// 3位寄存器只能数0~7刚好对应8个LED// 同样时钟复位触发always(posedge Clk or negedge Reset_n)if(!Reset_n)counter20;// 复位清零elseif(counter满值)// 重点只有第一个计数器数满了counter2counter21d1;// counter2才1counter2 是3 位数值范围0、1、2、3、4、5、6、7循环往复它不跟着时钟疯狂加 1只有等第一个计数器数完一轮延时结束才加 1它的数值就是告诉译码器现在亮第几个 LED。3、例化 3-8 译码器decoder_3_8decoder_3_8_inst0(// 调用我们之前导入的3-8译码器模块.A0(counter2[0]),// 3位输入接counter2的0/1/2位.A1(counter2[1]),.A2(counter2[2]),.Y0(Led[0]),// 8位输出直接接8个LED.Y1(Led[1]),.Y2(Led[2]),.Y3(Led[3]),.Y4(Led[4]),.Y5(Led[5]),.Y6(Led[6]),.Y7(Led[7]));3-8 译码器的功能输入 3 位数0~7输出只有 1 路是高电平对应点亮 1 个 LEDcounter20 → 亮 LED0counter21 → 亮 LED1…counter27 → 亮 LED7循环起来就是流水灯4、一句话总结时钟驱动延时计数器数数 → 数满一轮 → 3 位计数器 1 → 译码器点亮对应 LED → 循环往复 流水灯。完整代码timescale1ns/1ps moduleled_run3(Clk,Reset_n,Led);input Clk;input Reset_n;output[7:0]Led;reg[24:0]counter;parameter MCNT25000000-1;always(posedge Clk or negedge Reset_n)if(!Reset_n)counter0;elseif(counterMCNT)counter0;elsecountercounter1d1;reg[2:0]counter2;always(posedge Clk or negedge Reset_n)if(!Reset_n)counter20;elseif(counterMCNT)counter2counter21d1;decoder_3_8decoder_3_8_inst0(.A0(counter2[0]),.A1(counter2[1]),.A2(counter2[2]),.Y0(Led[0]),.Y1(Led[1]),.Y2(Led[2]),.Y3(Led[3]),.Y4(Led[4]),.Y5(Led[5]),.Y6(Led[6]),.Y7(Led[7]));endmodule三、parameter MCNT 参数精讲现在我们回头看这个参数parameter MCNT25000000-1;// 延时计数器的满值语法作用parameter 是可配置的常量把计数器的最大值定义成一个名字MCNT。为什么要用它核心好处上板调试开发板用 50MHz 时钟需要延时很久所以MCNT2500万LED 每秒闪 1 次肉眼可见仿真调试如果仿真也用 2500 万电脑要跑几十分钟才能看到灯闪所以我们可以直接改这个参数改成很小的数比如 25000仿真几秒钟就出结果一改全改只改参数值不用动任何逻辑代码零出错。四、Testbench 仿真文件逐行精讲timescale1ns/1ns// 仿真单位1ns精度1ns仿真常用moduleled_run_tb();// 仿真模块无输入输出reg Clk;// 激励信号给主模块的时钟用reg定义reg Reset_n;// 激励信号给主模块的复位用reg定义wire[7:0]Led;// 接收主模块的LED输出用wire定义// 例化主模块把仿真信号和主模块连接led_run3 #(.MCNT(25000-1)// 【方法1】实例化时直接修改参数)led_run3_inst0(.Clk(Clk),.Reset_n(Reset_n),.Led(Led));// defparam led_run3_inst0.MCNT 250000 - 1; // 【屏蔽的方法2】修改参数initial Clk1;// 初始化时钟初始值为1always #10Clk!Clk;// 每10ns翻转一次时钟 → 周期20ns → 50MHz时钟initial begin// 复位激励Reset_n0;// 先复位低电平有效#201;// 保持201nsReset_n1;// 释放复位开始运行#40000000;// 仿真运行时间$stop;// 停止仿真end endmodule核心信号定义规则仿真要主动发给主模块的信号时钟、复位→ 用 reg仿真接收主模块输出的信号LED→ 用 wire。仿真时我们需要把MCNT改小让仿真跑的快Verilog 有两种标准写法方法 1例化时直接传参led_run3 #(.MCNT(25000-1)// 直接把主模块的MCNT覆盖成25000)led_run3_inst0(...);优点简洁直观最常用。方法 2defparam 行内参数修改defparam led_run3_inst0.MCNT250000-1;defparam 定义参数led_run3_inst0你例化的模块名字作用单独修改实例化模块的参数适用场景模块例化后想单独改参数时用两种方法功能完全一样用哪个都行defparam 单独指定某一个实例的参数独立于例化语句之外部分老项目、分层嵌套模块大量使用该语法兼容性强日常调试备用。时钟生成initial Clk1;always #10Clk!Clk;// #10 等待10ns每 10ns 翻转一次电平一个完整周期 20ns对应开发板的50MHz 系统时钟仿真和上板时钟完全一致。复位激励initial begin Reset_n0;// 先拉低复位#201;// 保持复位Reset_n1;// 松开复位电路开始工作end时钟always #10 Clk!Clk10ns 电平翻转时钟周期 20ns精准模拟开发板 50MHz 时钟复位先拉低 Reset_n0 保持 201ns 完成上电复位随后拉高释放复位模块正式开始工作仿真末尾$stop终止仿真。总结开发规范历史调试完毕的模块优先导入复用避免重复编码导入务必勾选 Copy 复制源码流水灯逻辑分频计数器控延时 3 位计数器选灯 38 译码器驱动 LEDparameter 是跨场景调试利器上板大数、仿真小数一套源码两用参数重载两种方式实例 #传参、defparam 修改掌握后灵活适配各类仿真需求。