很多類型開發板教學都是點 LED 跑馬燈開始的而 FPGA 也不例外,Verilog HDL 語法簡單也代表一種功能能用多種的不同方法表示,好的 coding style 還有合理的設計才能確保合成器合成出好的電路。
跑馬燈架構大致如下 :
頂層包括二個主要模組,tick_1s 模組是產生1秒鐘的 tick 訊號後送至跑馬燈模組,也就是跑馬燈一秒位移一次,而跑馬燈模組會使用, (1)移位暫存器 (2)狀態機 (3)計數器 分別實作。
這次實驗使用 Arty A7 作為開發作平台,需要特別注意的是 LED 是1亮0不亮。
頂層 RTL 如下 :
`timescale 1ns / 1ps
module MARQUEE_TOP(
input CLK_100M,
input RESET_N,
output [3:0] LED
);
wire wTick1s;
tick_1s tick_1s_inst (
.clk (CLK_100M),
.rstN (RESET_N),
.oTick1s (wTick1s)
);
marquee_shift marquee_shift_inst (
.clk (CLK_100M),
.rstN (RESET_N),
.iTick1s (wTick1s),
.ovLed (LED)
);
// marquee_counter marquee_counter_inst (
// .clk (CLK_100M),
// .rstN (RESET_N),
// .iTick1s (wTick1s),
// .ovLed (LED)
// );
// marquee_state_machine marquee_state_machine_inst (
// .clk (CLK_100M),
// .rstN (RESET_N),
// .iTick1s (wTick1s),
// .ovLed (LED)
// );
endmodule
(1) 使用移位暫存器 RTL :
module marquee_shift (
input clk,
input rstN,
input iTick1s,
output [3:0] ovLed
);
reg [3:0] rvLed_d, rvLed_q;
//shift
always @(posedge clk or negedge rstN) begin
if(!rstN)
rvLed_q <= 4'b0001;
else
rvLed_q <= rvLed_d;
end
always @(*) begin
if(iTick1s)
rvLed_d = {rvLed_q[2:0], rvLed_q[3]};
else
rvLed_d = rvLed_q;
end
assign ovLed = rvLed_q;
endmodule
(2) 使用狀態機 RTL :
module marquee_state_machine (
input clk,
input rstN,
input iTick1s,
output reg [3:0] ovLed
);
localparam [1:0] S0 = 0;
localparam [1:0] S1 = 1;
localparam [1:0] S2 = 2;
localparam [1:0] S3 = 3;
reg [1:0] cState, nState;
//state machine
wire S0toS1 = (cState == S0) && iTick1s;
wire S1toS2 = (cState == S1) && iTick1s;
wire S2toS3 = (cState == S2) && iTick1s;
wire S3toS1 = (cState == S3) && iTick1s;
always @(posedge clk or negedge rstN) begin
if(!rstN)
cState <= S0;
else
cState <= nState;
end
always @(*) begin
case (cState)
S0 : nState = S0toS1 ? S1 : cState;
S1 : nState = S1toS2 ? S2 : cState;
S2 : nState = S2toS3 ? S3 : cState;
S3 : nState = S3toS1 ? S0 : cState;
default: nState = S0;
endcase
end
//led output
always @(*) begin
case(cState)
S0 : ovLed = 4'b0001;
S1 : ovLed = 4'b0010;
S2 : ovLed = 4'b0100;
S3 : ovLed = 4'b1000;
default : ovLed = 4'b0000;
endcase
end
endmodule
(3) 使用計數器 RTL :
module marquee_counter (
input clk,
input rstN,
input iTick1s,
output reg [3:0] ovLed
);
reg [1:0] rvCnt_d, rvCnt_q;
wire AddCnt, EndCnt;
//counter
always @(posedge clk or negedge rstN) begin
if(!rstN)
rvCnt_q <= 2'h0;
else
rvCnt_q <= rvCnt_d;
end
always @(*) begin
if(iTick1s)
rvCnt_d = rvCnt_q + 1'b1;
else
rvCnt_d = rvCnt_q;
end
//led output
always @(*) begin
case(rvCnt_q)
2'd0: ovLed = 4'b0001;
2'd1: ovLed = 4'b0010;
2'd2: ovLed = 4'b0100;
2'd3: ovLed = 4'b1000;
default: ovLed = 4'b0000;
endcase
end
endmodule
xdc file 如下:
set_property PACKAGE_PIN E3 [get_ports CLK_100M]
set_property IOSTANDARD LVCMOS33 [get_ports CLK_100M]
set_property PACKAGE_PIN H5 [get_ports {LED[3]}]
set_property PACKAGE_PIN J5 [get_ports {LED[2]}]
set_property PACKAGE_PIN T9 [get_ports {LED[1]}]
set_property PACKAGE_PIN T10 [get_ports {LED[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports RESET_N]
set_property PACKAGE_PIN C2 [get_ports RESET_N]
測試結果