FPGA学习
五、跑马灯
5.1 设计输入
5.1.1 写法1
module led_run(Clk,Reset_n,Led
);input Clk;input Reset_n;output reg[7:0]Led;reg [24:0]count;always@(posedge Clk or negedge Reset_n)if(!Reset_n)count<=0;//写法1: else if(count==25000000-1)//判断的时候可不加上位宽 25'd24999999//写法2:为了提升仿真开发效率,将LED间隔500uselse if(count==25000-1)count<=0;else count<=count+1'd1;always@(posedge Clk or negedge Reset_n)if(!Reset_n)Led<=8'b0000_0001;// else if(count==25000000-1)beginelse if(count==25000-1) beginif(Led==8'b1000_0000)Led <=8'b0000_0001;else Led <= Led<<1;endendmodule
5.1.2 写法2
Led<={Led[6:0],Led[7]}-------循环移位写法
module led_run1(Clk,Reset_n,Led
);input Clk;input Reset_n;output reg[7:0]Led;reg [24:0]count;always@(posedge Clk or negedge Reset_n)if(!Reset_n)count<=0;//写法1: else if(count==25000000-1)//判断的时候可不加上位宽 25'd24999999//写法2:为了提升仿真开发效率,将LED间隔500uselse if(count==25000-1)count<=0;else count<=count+1'd1;always@(posedge Clk or negedge Reset_n)if(!Reset_n)Led<=8'b0000_0001;// else if(count==25000000-1)beginelse if(count==25000-1) beginLed<={Led[6:0],Led[7]};//循环移位写法endendmodule
5.1.3 写法3
我们需要在led_run2.v文件中调用3-8译码器驱动LED,导入3-8译码器,在定义一个计数器,count2作为3位的数,0~7循环变化,下面我们导入原来写好的3-8译码器工程,
这里涉及到语法例化写好的模块,例化的过程和在测试平台tb文件中的过程一样,
写法3的完整代码如下:
module led_run2(Clk,Reset_n,Led
);input Clk;input Reset_n;// output reg[7:0]Led;output [7:0]Led;reg [24:0]count;always@(posedge Clk or negedge Reset_n)if(!Reset_n)count<=0;//写法1: else if(count==25000000-1)//判断的时候可不加上位宽 25'd24999999//写法2:为了提升仿真开发效率,将LED间隔500uselse if(count==25000-1)count<=0;else count<=count+1'd1;reg[2:0]count2;always@(posedge Clk or negedge Reset_n)if(!Reset_n)count2<=0;else if(count==25000-1)count2<=count2+1'd1;//count2只有3位,当他挤满以后溢出会变为0decoder_38 decoder_38_label(.a(count2[2]),//count2[2]连接到了38译码器的a端口.b(count2[1]),.c(count2[0]),.out(Led)); endmodule
5.1.4 写法4
写法4只是在前面几种写法的基础上进行小优化,在FPGA开发中,parameter 是一种常量,用于模块的参数化设计。
parameter CNT = 25'd25000000;always@(posedge Clk or negedge Reset_n)if(!Reset_n)count<=0;else if(count==CNT-1)count<=0;else count<=count+1'd1;
5.2 分析综合
分析设计输入中代码是否有误
5.3 仿真模拟
5.3.1 仿真模拟写法1
`timescale 1ns/1nsmodule led_run_tb;//测试平台reg s_Clk;reg s_Reset;wire [7:0]s_Led;led_run2 led_run_label(.Clk(s_Clk),.Reset_n(s_Reset),.Led(s_Led));initial s_Clk=1;always#10 s_Clk=!s_Clk;initial begins_Reset = 0;#201;//延迟201ns用来错开时钟边沿s_Reset = 1;#40000000;//$stop;endendmodule
功能仿真时序图如下
5.3.1 仿真模拟写法2
前面的写法中,为了提升仿真开发效率,将LED间隔500us;如果按照正常要求仿真间隔500ms,仿真软件会运行的很慢,但是我们硬件开发的时候就是要求500ms,下面提供一种语法,可以满足仿真时候的时间要求,又不影响实际开发板的运行结果。
仿真文件不参与布局布线,不影响模块参数(开发板硬件要求的实验参数)。
只需要修改tb文件,设计输入文件中的CNT常量依旧为25000000:
`timescale 1ns/1nsmodule led_run_tb;//测试平台reg s_Clk;reg s_Reset;wire [7:0]s_Led;led_run2 led_run_label(.Clk(s_Clk),.Reset_n(s_Reset),.Led(s_Led));defparam led_run_label.CNT = 2500;initial s_Clk=1;always#10 s_Clk=!s_Clk;initial begins_Reset = 0;#201;//延迟201ns用来错开时钟边沿s_Reset = 1;#40000000;//$stop;endendmodule
或者下述写法,只需要修改tb文件,设计输入文件中的CNT常量依旧为25000000::
`timescale 1ns/1nsmodule led_run_tb;//测试平台reg s_Clk;reg s_Reset;wire [7:0]s_Led;
//====================================================================================== led_run2 //模块放进来先不给标签名,首先重新定义CNT#(.CNT(25000))led_run_label(.Clk(s_Clk),.Reset_n(s_Reset),.Led(s_Led));//======================================================================================initial s_Clk=1;always#10 s_Clk=!s_Clk;initial begins_Reset = 0;#201;//延迟201ns用来错开时钟边沿s_Reset = 1;#40000000;//$stop;endendmodule