8个用户LED被74HC595芯片驱动且高电平点亮LED,如下图所示。74HC595芯片由8位移位寄存器和8位锁存器组成,通过控制CLK、LATCH、DIN三个信号即可控制8个LED的状态,因此,采用该芯片可节省FPGA用户管脚的使用。
74HC595的驱动时序图如下所示,串行输入数据在移位时钟的上升沿被读进移位寄存器;移位寄存器的值在锁存时钟的上升沿被锁存输出。
需要考虑时钟和信号之间的建立时间和保持时间,如下图所示,由于芯片电压为3.3V且室温在25℃~85℃之间,故输入数据对移位时钟上升沿的最小建立时间和保持时间分别为50ns和5ns,而移位时钟上升沿对锁存时钟上升沿的最小建立时间为70ns。
此外,移位时钟的最大频率为10MHz,如下图所示。由于AN88开发板的晶振时钟频率为24MHz,可以通过计数器分频产生6MHz(即166.67ns)的移位时钟,则输入数据的建立时间和保持时间都为83.33ns,移位时钟上升沿对锁存时钟上升沿的建立时间为83.33ns,很显然满足74HC595芯片的时序要求。
本设计通过驱动74HC595芯片实现流水灯功能,设计框图如下所示,由两个子模块组成,其中led_ctrl模块实现流水灯功能,driver_74hc595模块驱动74HC595芯片。
打开Anlogic TD软件。选择菜单“Project”—>“New Project…”新建工程,如下所示。
在弹出的对话框中输入工程名run_water_led、选择工程路径以及选择FPGA器件EG4S20NG88,如下所示:
建立工程后,选择菜单“Source”—>“New Source…”新建源文件,如下所示。
在弹出的对话框选择文件类型、输入文件名run_water_led以及文件保存路径,如下所示。
用上述方法新建源文件led_ctrl.v和driver_74hc595.v。
led_ctrl模块的RTL设计:(1)由于输入时钟clk为24MHz,则计数12_000_000个时钟周期可得到0.5s的时间片段;(2)每完成0.5s的计数,则对LED值进行一次循环移位处理,如下图所示。
module led_ctrl
(
input wire clk , // 24MHz
input wire rst_n ,
output reg [7:0] led_data ,
output reg led_data_valid
);
//----------------------------------------------------------------------
localparam C_CNT_VALUE = 28'd12_000_000; // 0.5s
reg [27:0] cnt;
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
cnt <= 28'b0;
else
begin
if(cnt < C_CNT_VALUE - 1'b1)
cnt <= cnt + 1'b1;
else
cnt <= 28'b0;
end
end
wire cnt_done_flag;
assign cnt_done_flag = (cnt == C_CNT_VALUE - 1'b1) ? 1'b1 : 1'b0;
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
begin
led_data <= 8'b0000_0001;
led_data_valid <= 1'b0;
end
else
begin
if(cnt_done_flag == 1'b1)
begin
led_data <= {led_data[6:0],led_data[7]};
led_data_valid <= 1'b1;
end
else
begin
led_data <= led_data;
led_data_valid <= 1'b0;
end
end
end
endmodule
driver_74hc595模块的RTL设计。
module driver_74hc595
(
input wire clk , // 24MHz
input wire rst_n ,
input wire [7:0] led_data ,
input wire led_data_valid ,
output reg shift_clock ,
output reg latch_clock ,
output reg serial_data
);
//----------------------------------------------------------------------
localparam C_CNT = 3'd2; // 83.333ns
localparam C_CNT_NUM = 5'd17;
localparam S_IDLE = 1'b0;
localparam S_SEND_DATA = 1'b1;
reg [1:0] state;
reg [7:0] led_data_tmp;
reg [2:0] cnt;
reg [4:0] cnt_num;
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
state <= S_IDLE;
else
begin
case(state)
S_IDLE :
begin
if(led_data_valid == 1'b1)
state <= S_SEND_DATA;
else
state <= S_IDLE;
end
S_SEND_DATA :
begin
if((cnt == C_CNT - 1'b1)&&(cnt_num == C_CNT_NUM - 1'b1))
state <= S_IDLE;
else
state <= S_SEND_DATA;
end
endcase
end
end
always @(posedge clk)
begin
if((state == S_IDLE)&&(led_data_valid == 1'b1))
led_data_tmp <= led_data;
else
led_data_tmp <= led_data_tmp;
end
always @(posedge clk)
begin
if(state == S_SEND_DATA)
begin
if(cnt < C_CNT - 1'b1)
cnt <= cnt + 1'b1;
else
cnt <= 3'b0;
end
else
cnt <= 3'b0;
end
always @(posedge clk)
begin
if(state == S_SEND_DATA)
begin
if(cnt == C_CNT - 1'b1)
cnt_num <= cnt_num + 1'b1;
else
cnt_num <= cnt_num;
end
else
cnt_num <= 5'b0;
end
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
shift_clock <= 1'b0;
else
begin
if(state == S_SEND_DATA)
begin
shift_clock <= cnt_num[0];
end
else
shift_clock <= 1'b0;
end
end
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
latch_clock <= 1'b0;
else
begin
if((state == S_SEND_DATA)&&(cnt_num == C_CNT_NUM - 1'b1))
latch_clock <= 1'b1;
else
latch_clock <= 1'b0;
end
end
always @(posedge clk)
begin
if(state == S_SEND_DATA)
serial_data <= led_data_tmp >> cnt_num[4:1];
else
serial_data <= 1'b0;
end
endmodule
run_water_led模块作为顶层模块,用于例化led_ctrl和driver_74hc595。
run_water_led
(
input wire clk_24m ,
output wire shift_clock ,
output wire latch_clock ,
output wire serial_data
);
//----------------------------------------------------------------------
reg [3:0] delay_cnt = 4'd0;
always@(posedge clk)
begin
if(delay_cnt < 4'hf)
delay_cnt <= delay_cnt + 1'b1;
else
delay_cnt <= delay_cnt;
end
wire rst_n;
assign rst_n = (delay_cnt == 4'hf)? 1'b1 : 1'b0;
//----------------------------------------------------------------------
wire [7:0] led_data;
wire led_data_valid;
led_ctrl u_led_ctrl
(
.clk (clk_24m ),
.rst_n (rst_n ),
.led_data (led_data ),
.led_data_valid (led_data_valid )
);
//----------------------------------------------------------------------
driver_74hc595 u_driver_74hc595
(
.clk (clk_24m ),
.rst_n (rst_n ),
.led_data (led_data ),
.led_data_valid (led_data_valid ),
.shift_clock (shift_clock ),
.latch_clock (latch_clock ),
.serial_data (serial_data )
);
endmodule
run_water_led模块作为顶层模块,用于例化led_ctrl和driver_74hc595。
run_water_led
(
input wire clk_24m ,
output wire shift_clock ,
output wire latch_clock ,
output wire serial_data
);
//----------------------------------------------------------------------
reg [3:0] delay_cnt = 4'd0;
always@(posedge clk)
begin
if(delay_cnt < 4'hf)
delay_cnt <= delay_cnt + 1'b1;
else
delay_cnt <= delay_cnt;
end
wire rst_n;
assign rst_n = (delay_cnt == 4'hf)? 1'b1 : 1'b0;
//----------------------------------------------------------------------
wire [7:0] led_data;
wire led_data_valid;
led_ctrl u_led_ctrl
(
.clk (clk_24m ),
.rst_n (rst_n ),
.led_data (led_data ),
.led_data_valid (led_data_valid )
);
//----------------------------------------------------------------------
driver_74hc595 u_driver_74hc595
(
.clk (clk_24m ),
.rst_n (rst_n ),
.led_data (led_data ),
.led_data_valid (led_data_valid ),
.shift_clock (shift_clock ),
.latch_clock (latch_clock ),
.serial_data (serial_data )
);
endmodule
完成RTL设计之后,还需要对其功能进行仿真,以确保满足设计需求。仿真结果如下所示,很显然符合预期。
接下来进行管脚约束和时序约束,在工程目录下新建文件夹sdc,且在文件夹下新建文件run_water_led.adc(用于管脚约束)和run_water_led.sdc(用于时序约束)。
将run_water_led.adc添加到工程,如下所示。
在run_water_led.adc中编写管脚约束:set_pin_assignment { clk_24m } { LOCATION = P13; }
set_pin_assignment { shift_clock } { LOCATION = P59; }
set_pin_assignment { serial_data } { LOCATION = P45; }
set_pin_assignment { latch_clock } { LOCATION = P57; }
将run_water_led.sdc添加到工程,如下所示。
在run_water_led.sdc中编写时序约束:
create_clock -name clk_24m -period 41.667 [get_ports {clk_24m}]
双击HDL2Bit Flow即可对设计进行综合、实现以及生成bit文件,如下图所示。
用下载器连接AN88开发板和电脑,并给AN88开发板上电。选择菜单“Tools”—>“Download”打开目标文件烧录界面,通过点击“Add”添加run_water_led.bit,然后点击“Run”开始烧录,如下所示。
完成烧录后,在AN88开发板上可以看到8个LED像流水一样流动,每隔0.5秒流动一次。