FPGA 06 基础 led流水灯
模块名称: led_flow
主要功能 :每隔一段时间,输出 led[x:0] 所有电平进行循环移位,对外部电路实现控制4个led灯的流水灯效果。
实现(设计)流程: 1、内部使用一个定时器计数器,分两个部分
①计数到设置值,重新开始计数
②计数到设置值,led[x:0]循环左移或者右移,否则保持不变。
led_r <= {led_r[2:0],led_r[3]}; //使用位拼接的方式进行移位操作
- led_flow.v 文件
module led_flow(
//端口列表
clk50M ,
Rst_n ,
led
);
// 这里类似与 C 语言的函数定义,也可以使用类似于C语言的定义,如下所示:
// module led_flash(input clk50M ,input Rst_n , reg [3:0]led);
// 上述可以看到
//端口定义
input clk50M ;
input Rst_n ;
//output wire [3:0]led ; //定义的是一个【线网】类型的信号
output [3:0]led ; //定义的是一个寄存器输出的端口
//传参,修改定时器的值,方便在后续的参数传递时
//仅仅只需要修改一个参数变量即可
// 1、减少修改文件的数目
// 2、减少修改出现的错误
parameter CNT_MAX = 25'd24_999_999 ; //类似与 C 语言的 #define
reg [24:0]cnt ; //定义一个25位的寄存器,用做计数
//always 语句分析
// 总是关注 clk50M 信号的上升沿或者 Rst_n 的下降边沿,那么就会
// 执行 always 里面的语句
// always 语句,是不断在执行和处理的,
// 个人理解: always 会不断执行alway 里面到下一个always 块之前的语句
// 这里面属于一个单独的always 块,相当于分片进行设计和操作
// 此外,always 和另外一个always 是互不影响的
//第一个always块
always@(posedge clk50M or negedge Rst_n)
// posedge : 上升沿 negedge: 下降沿
if(!Rst_n) //判断 复位引脚是否位低电平,如果是,则进入if语句执行一些操作
cnt <=25'd0 ; //进入if语句,表示Rst_n是低电平,则进入复位
else if(cnt == CNT_MAX) //判断是否到达定时器的值,到了,对cnt 清零
cnt <= 25'd0 ;
else //未满足上面的条件,则表示既没有复位,计数器的值也没有达到设定的值
cnt <= cnt +1'b1 ; // cnt++
//第二个always 块,不影响其他always块的执行
/* ① flow 流水灯操作
reg [3:0]led_r ; //定义一个4位的寄存器
always@(posedge clk50M or negedge Rst_n)
if(!Rst_n)
led_r <= 4'b0001; // 4'b 表示的是四位的二进制数赋值
else if(cnt == CNT_MAX) begin //进入 else if 执行的语句
if(led_r == 4'b0000) //判断是否全部左移完成
led_r <= 4'b0001 ; //执行 赋值 0001
else
led_r <= led_r <<1 ; //否则左移一位
end //表示的是 eles if 判断执行的语句
else //定时器没到,保持当前状态
led_r <=led_r ; //对寄存器进行操作
assign led = ~ led_r ; //寄存器的值取反 赋值给 led 灯
// assign: 对应的是连线,是组合逻辑输出, 此时这里的 [3:0]led 必须保证是 wire [3:0]led 类型的才可以直接连接
// 如果是 reg [3:0]led ,则使用assign 是会出错
*/
// ②flow 流水灯操作
reg [3:0]led_r ; //定义一个4位的寄存器
always@(posedge clk50M or negedge Rst_n)
if(!Rst_n)
led_r <= 4'b0001; // 4'b 表示的是四位的二进制数赋值
else if(cnt == CNT_MAX) begin //进入 else if 执行的语句
if(led_r == 4'b0000) //判断是否全部左移完成
led_r <= 4'b0001 ; //执行 赋值 0001
else
led_r <= {led_r[2:0],led_r[3]}; //否则左移一位,使用位拼接的方式进行移位操作
end //表示的是 eles if 判断执行的语句
else //定时器没到,保持当前状态
led_r <=led_r ; //对寄存器进行操作
assign led = ~ led_r ; //寄存器的值取反 赋值给 led 灯
// assign: 对应的是物理世界中的连线,是组合逻辑输出, 此时这里的 [3:0]led 必须保证是 wire [3:0]led 类型的才可以直接连接
// 如果是 reg [3:0]led ,则使用assign 是会出错
endmodule
// 编辑完成上述步骤后,进行分析和综和进行编译 按钮 start Analysie & Synthesis
- led_flow_tb.v文件
`timescale 1ns/1ns
//建议上面直接复制粘贴,不然容易出错
`define clock_period 20 //定义一个仿真的时钟周期的时间是20ns
module led_flow_tb;
//端口模块测试的端口定义
//测试模块编写流程
reg clk50M ;
reg Rst_n ;
//output wire [3:0]led ; //定义的是一个【线网】类型的信号
wire [3:0]led ; //定义的是一个寄存器输出的端口
//测试模块函数,重定义了一个新的 测试模块名称: led_flash0
//传递参数进入
//测试模块函数
led_flow
#(
.CNT_MAX(25'd249) // 这里表示,在我们仿真的时候修改
// led_flash 模块里面的 CNT_MAX 修改为 25’d2499 进行仿真测试
// 参数传递的好处,不需要修改源文件的蚕食
// 保证实际电路的输出效果时不会出错
)
//加入测试接口和函数
led_flow0(
.clk50M(clk50M) ,
.Rst_n(Rst_n) ,
.led(led)
);
/*
//实际电路仿真, CNT_MAX 的参数不可更改 ,若 CNT_MAX参数,则实际电路仿真时会出错
//其余和逻辑仿真保持一致即可,注: 可以和上面的近对比。
led_flow
//加入测试接口和函数
led_flow0(
.clk50M(clk50M) ,
.Rst_n(Rst_n) ,
.led(led)
);
*/
//设定初始状态
initial clk50M =1 ; //初始化 clk50M = 1 定义一个
//
// always #10 clk50M = ~clk50M ; //延时10ns,时钟翻转一次,这样的话,时钟周期就是 20ns
// 也可以使用下面的方式进行操作 ,一个周期有 10ns 的低电平,10ns的高电平
// 每个电平持续时间是 10ns , 则一个总的时钟信号周期就是 20ns
always #(`clock_period/2) clk50M = ~clk50M ;
// 初始化信号,(begin end)
// 出现begin 后面必须有 end
// 开机执行一次
initial begin
Rst_n = 0 ;
#(`clock_period*20+1);
Rst_n =1 ; //延时20个周期+1,避免异步对准(齐)
#(`clock_period*25000+1); //仿真的延时时间
$stop ; //仿真停止
end
endmodule
// 编写完成测试后,设置该源代码位仿真代码
// Assigment->Settings --> EDA Tool Settings
// --> Simulation --> Compile test bench
// 添加测试的文件 (点击 Test Benches --> New)
// 点击Filename 后面跟随的(...)--> 添加自己的测试文件
// 添加完成以后,在TestBench 里面添加 测试的文件名称(不要添加.v这个后缀) 即可
// 保存--> Ok--> apply 即可
// 主界面 --> Tools --> Run Simulation --> RTL Simulation or Gate Level Simulation
// 重新仿真操作
// 仿真出错 --> 修改好文件 -> 在仿真软件 ->Library -> Recompile
// Reset --> Run All
// 出现仿真时间过长--> break 进行停止
// 电路图视图 操作: 看内部实际电路的连接方式
// Tools -> NetList Viwers -> RTL Viewer
// 逻辑仿真和模拟实际情况下的仿真 的对比(不同点)
// #(.CNT_MAX(25'd249)) 在逻辑电路可以传递参数
// 实际电路中不可以传递参数
// 最后,配置IO 口进行下载到芯片中
// 2种方式
// 方式1、选择Pin planner 配置选中对应的IO口 Assigment->Pin Planner
// 方式2、 ①编写自动化脚本文件 new -> File -> Tcl_script.tcl 文件 (针对IO口较多的情况)
// ②编写完成后, Tools -> Tcl_script --> 导入 --> run 执行编写的脚本文件
.tcl 脚本文件
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to clk50M
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to Rst_n
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to led[0]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to led[1]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to led[2]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to led[3]
set_location_assignment PIN_E1 -to clk50M
set_location_assignment PIN_E16 -to Rst_n
set_location_assignment PIN_A2 -to led[0]
set_location_assignment PIN_B3 -to led[1]
set_location_assignment PIN_A4 -to led[2]
set_location_assignment PIN_A3 -to led[3]