设计规划
依次点亮4个LED灯,实现流水灯的效果,两灯之间点亮间隔为0.5s,LED灯一次点亮持续时间0.5s。
LED灯低电平点亮,因此流水灯应该是1110-1101-1011-0111-1110-1101-...
首先是时钟信号、复位信号,由于需要计时,我们还要产生一个计数器cnt。然后还需要产生一个cnt_flag脉冲标志信号作为流水切换的标志,每当计数器计数到24_999_998时拉高并只产生一个时钟的高电平(高电平出现LED状态就开始切换)。最重要的是,流水灯的实现是通过左移操作,无法直接通过led_out的左移实现,因此需要定义一个led_out_reg(led_out从1110左移一次是1100,会有两个灯点亮,而led_out_reg是LED的位反0001,左移一次是0010,取反后led_out是1101,就能实现流水的需求)。
编写代码
module water_led #( parameter CNT_MAX =25'd24_999_999) ( input wire sys_clk , input wire sys_rst_n , output wire [3:0] led_out );//reg definereg [24:0] cnt ; reg cnt_flag ; reg [3:0] led_out_reg ;//cnt:计数器计数500msalways@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n ==1'b0) cnt <=25'b0;elseif(cnt == CNT_MAX) cnt <=25'b0;elsecnt <= cnt +1'b1;//cnt_flag:计数器计数满500ms标志信号always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n ==1'b0) cnt_flag <=1'b0;elseif(cnt == CNT_MAX -1) cnt_flag <=1'b1;elsecnt_flag <=1'b0;//led_out_reg:led循环流水always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n ==1'b0) led_out_reg <=4'b0001;elseif(led_out_reg ==4'b1000&& cnt_flag ==1'b1) led_out_reg <=4'b0001;elseif(cnt_flag ==1'b1) led_out_reg <= led_out_reg < <1'b1;//左移assign led_out = ~led_out_reg; endmodule三个中间信号的定义:cnt,cnt_flag,led_out_reg。
1、cnt:计数器变化的条件是时钟上升和复位有效(复位下降),复位信号有效时cnt变为低电平;计满时清零;其他时刻+1。
2、cnt_flag:计数器计满的脉冲标志信号,变化条件和cnt一样,复位有效时变为低电平;计满前一个时钟拉高;其他时刻都为0,这样就能成为一个脉冲信号,并在计满前拉高,标志led要左移。
3、led_out_reg:暂存led灯状态,可以直接对这个信号进行操作来控制LED灯。复位和初始时是最右边的灯亮,反推出led_out_reg=0001;当最左边的灯亮且计满标志信号拉高时,令最右边的灯亮led_out_reg=0001;当计满标志信号拉高时,led_out_reg左移。而控制LED电平的输出信号led_out是led_out_reg的按位取反。
编写testbench
`timescale1ns/1ns moduletb_water_led();//wire definewire [3:0] led_out ;//reg definereg sys_clk ; reg sys_rst_n ;//初始化系统时钟、全局复位initial begin sys_clk =1'b1; sys_rst_n <=1'b0;#20sys_rst_n <=1'b1; end//sys_clk:模拟系统时钟,每10ns电平翻转一次,周期为20ns,频率为50MHzalways#10 sys_clk = ~sys_clk;//-------------------- water_led_inst --------------------water_led#(.CNT_MAX (