题目
使用SystemVerilog实现一个数字钟。
要求:
(1)能够显示时分秒;
(2)能够设置开始时间;
(3)使用你自己的7段数码管显示译码电路实现;
(4)可以使用动态显示方法实现;
(5)依据实现的其他附加功能,酌情加分:秒表、倒计时、闹钟…
(6)需要在Basys3 FPGA开发板上实现
解决方案
本解决方案参考@Hellsegamosken的[SystemVerilog] 基于 FPGA 的数字钟设计,并在其基础上进行修改。
clock.sv
module clock
(
input logic clk_pre,TU, TL, TC, TD,TR,
input logic T1, T2, T3, T4, T15, T16,
output logic [10:0] segments,
output logic alarm_light, down_light,ontime_light
);
// Control signals for different modes
logic mode1, mode2, mode3, mode4;
// Display variables for digits A and B
logic [3:0] disp_A0, disp_A1, disp_B0, disp_B1;
// Clock variables
logic [3:0] h0 = 4'b0000, h1 = 4'b0000, m0 = 4'b0000, m1 = 4'b0000, s0 = 4'b0000, s1 = 4'b0000;
// Alarm variables
logic [3:0] ah0 = 4'b0000, ah1 = 4'b0000, am0 = 4'b0000, am1 = 4'b0000, as0 = 4'b0000, as1 = 4'b0000;
// Countdown variables
logic [3:0] dh0 = 4'b0000, dh1 = 4'b0000, dm0 = 4'b0000, dm1 = 4'b0000, ds0 = 4'b0000, ds1 = 4'b0000;
// Stopwatch variables
logic [3:0] ch0 = 4'b0000, ch1 = 4'b0000, cm0 = 4'b0000, cm1 = 4'b0000, cs0 = 4'b0000, cs1 = 4'b0000;
integer disp_pos = 0,set_pos = 0;
logic clk_sec = 1'b0, clk_centi = 1'b0, clk_disp = 1'b0;
// Enumeration for different modes
typedef enum integer {MAIN, ALARM, COUNT, DOWN,ONTIME} ModeType;
// Enumeration for display control
typedef enum logic {LOW, HIGH} DispType;
// Enumeration for hand control
typedef enum logic {OFF, ON} HandType;
ModeType mode = MAIN;
DispType DISP = LOW;
logic hand = OFF;
integer alarm_time = 0, down_time = 0,ontime_time=0;
logic down_sig = 1'b1, alarm_sig = 1'b1, ontime_sig = 1'b1, counting = 1'b0;
// Control module
// Temporary variables to handle button debouncing
logic tmpU1, tmpU2, tmpU3, UP;
logic tmpL1, tmpL2, tmpL3, LEFT;
logic tmpR1, tmpR2, tmpR3, RIGHT;
logic tmpC1, tmpC2, tmpC3, CENTER;
logic tmpD1, tmpD2, tmpD3, RESET;
// Eliminate button debounce
always_ff @ (posedge clk_centi) begin
tmpU3 <= tmpU2;
tmpU2 <= tmpU1;
tmpU1 <= TU;
tmpL3 <= tmpL2;
tmpL2 <= tmpL1;
tmpL1 <= TL;
tmpR3 <= tmpR2;
tmpR2 <= tmpR1;
tmpR1 <= TR;
tmpC3 <= tmpC2;
tmpC2 <= tmpC1;
tmpC1 <= TC;
tmpD3 <= tmpD2;
tmpD2 <= tmpD1;
tmpD1 <= TD;
end
// Decode input signals to determine current mode and other information
always_comb begin
if (T16) hand = ON;
else hand = OFF;
if (T1) begin mode = ALARM; {mode1, mode2, mode3, mode4} = 4'b1000; end
else if (T2) begin mode = COUNT; {mode1, mode2, mode3, mode4} = 4'b0100; end
else if (T3) begin mode = DOWN; {mode1, mode2, mode3, mode4} = 4'b0010; end
else if (T4) begin mode = ONTIME; {mode1, mode2, mode3, mode4} = 4'b0000; end
else begin mode = MAIN; {mode1, mode2, mode3, mode4} = 4'b0000; end
if (T15) DISP = HIGH;
else DISP = LOW;
UP = tmpU1 & tmpU2 & tmpU3;
LEFT = tmpL1 & tmpL2 & tmpL3;
RIGHT = tmpR1 & tmpR2 & tmpR3;
CENTER = tmpC1 & tmpC2 & tmpC3;
RESET = tmpD1 & tmpD2 & tmpD3;
end
// Main logic to handle clock/stopwatch increment and time settings
integer cnt_sec = 0, cnt_centi = 0, cnt_disp = 0, cnt_flip = 0;
// logic clk_sec = 1'b0, clk_centi = 1'b0, clk_disp = 1'b0;
logic flip = 1'b1;
logic jw_d = 1'b0, jw_m = 1'b0, jw_y = 1'b0;
logic prehand = 1'b0, preleft = 1'b0,preright = 1'b0, preup = 1'b0, predown_sig = 1'b0, prealarm_sig = 1'b0, preontime_sig = 1'b0, precenter, prereset;
always_ff @ (posedge clk_pre) begin
cnt_sec <= cnt_sec + 1;
cnt_centi <= cnt_centi + 1;
cnt_disp <= cnt_disp + 1;
cnt_flip <= cnt_flip + 1;
prehand <= hand;
preleft <= LEFT;
preright <= RIGHT;
preup <= UP;
precenter <= CENTER;
prereset <= RESET;
// || !preleft && LEFT
if (!prehand && hand ) begin
cnt_flip <= 0;
flip <= 1'b1;
end
else if (cnt_flip == 50000000) begin
cnt_flip <= 0;
flip = 1'b1 ^ flip;
end
if (cnt_sec == 100000000) begin
cnt_sec <= 0;
clk_sec <= 1'b1;
end
else clk_sec <= 1'b0;
if (cnt_centi == 1000000) begin
cnt_centi <= 0;
clk_centi <= 1'b1;
end
else clk_centi <= 1'b0;
if (cnt_disp == 100000) begin
cnt_disp <= 0;
clk_disp <= 1'b1;
end
else clk_disp <= 1'b0;
// count down
if (clk_centi) begin
if (!(mode == COUNT && hand) && counting) begin
if (cs0 == 9) begin
if (cs1 == 9) begin
if (cm0 == 9) begin
if (cm1 == 5) begin
if (ch0 == 9) begin
if (ch1 == 5) begin
{cs0, cs1, cm0, cm1, ch0, ch1} = 24'd0;
end
else ch1 <= ch1 + 1;
ch0 <= 0;
end
else ch0 <= ch0 + 1;
cm1 <= 0;
end
else cm1 <= cm1 + 1;
cm0 <= 0;
end
else cm0 <= cm0 + 1;
cs1 <= 0;
end
else cs1 <= cs1 + 1;
cs0 <= 0;
end
else cs0 <= cs0 + 1;
end
end
if (clk_sec) begin
if (alarm_time != 0) alarm_time <= alarm_time - 1;
if (down_time != 0) down_time <= down_time - 1;
// Primary clock increment
if (!(mode == MAIN && hand)) begin
if (s0 == 9) begin
if (s1 == 5) begin
if (m0 == 9) begin
if (m1 == 5) begin
if (h0 == 3 && h1 == 2) begin
{s0, s1, m0, m1, h0, h1} <= 24'd0;
jw_d <= 1'b1;
end
else if (h0 == 9) begin
h0 <= 4'd0;
h1 <= h1 + 1;
end
else h0 <= h0 + 1;
m1 <= 0;
end
else m1 <= m1 + 1;
m0 <= 0;
end
else m0 <= m0 + 1;
s1 <= 0;
end
else s1 <= s1 + 1;
s0 <= 0;
end
else s0 <= s0 + 1;
end
// Countdown increment
if (!(mode == DOWN && hand)) begin
if (!(ds0 == 0 && ds1 == 0 && dm0 == 0 && dm1 == 0 && dh0 == 0 && dh1 == 0)) begin
if (ds0 == 0) begin
if (ds1 == 0) begin
if (dm0 == 0) begin
if (dm1 == 0) begin
if (dh0 == 0) begin
dh1 <= dh1 - 1;
end
else dh0 <= dh0 - 1;
dm1 <= 9;
end
else dm1 <= dm1 - 1;
dm0 <= 9;
end
else dm0 <= dm0 - 1;
ds1 <= 9;
end
else ds1 <= ds1 - 1;
ds0 <= 9;
end
else ds0 <= ds0 - 1;
end
end
end
// Action display module, cyclic display position
if (clk_disp) begin
case (disp_pos)
0: disp_pos <= 1;
1: disp_pos <= 2;
2: disp_pos <= 3;
3: disp_pos <= 0;
endcase
end
// Set Time Module
if (!preup && UP) begin
if (mode == MAIN && hand) begin //set clock
if (DISP == LOW) begin
case (set_pos)
0: begin if (s0 == 9) s0 <= 0; else s0 <= s0 + 1; end
1: begin if (s1 == 5) s1 <= 0; else s1 <= s1 + 1; end
2: begin if (m0 == 9) m0 <= 0; else m0 <= m0 + 1; end
3: begin if (m1 == 5) m1 <= 0; else m1 <= m1 + 1; end
endcase
end
else begin
case (set_pos)
0: begin if (m0 == 9) m0 <= 0; else m0 <= m0 + 1; end
1: begin if (m1 == 9) m1 <= 0; else m1 <= m1 + 1; end
2: begin if (h0 == 9 || h0 == 3 && h1 == 2) h0 <= 0; else h0 <= h0 + 1; end
3: begin if (h1 == 2) h1 <= 0; else h1 <= h1 + 1; end
endcase
end
end
if (mode == ALARM && hand) begin //set alarm
if (DISP == LOW) begin
case (set_pos)
0: begin if (as0 == 9) as0 <= 0; else as0 <= as0 + 1; end
1: begin if (as1 == 5) as1 <= 0; else as1 <= as1 + 1; end
2: begin if (am0 == 9) am0 <= 0; else am0 <= am0 + 1; end
3: begin if (am1 == 5) am1 <= 0; else am1 <= am1 + 1; end
endcase
end
else begin
case (set_pos)
0: begin if (am0 == 9) am0 <= 0; else am0 <= am0 + 1; end
1: begin if (am1 == 9) am1 <= 0; else am1 <= am1 + 1; end
2: begin if (ah0 == 9 || ah0 == 3 && ah1 == 2) ah0 <= 0; else ah0 <= ah0 + 1; end
3: begin if (ah1 == 2) ah1 <= 0; else ah1 <= ah1 + 1; end
endcase
end
end
if (mode == DOWN && hand) begin // set count_down
if (DISP == LOW) begin
case (set_pos)
0: begin if (ds0 == 9) ds0 <= 0; else ds0 <= ds0 + 1; end
1: begin if (ds1 == 5) ds1 <= 0; else ds1 <= ds1 + 1; end
2: begin if (dm0 == 9) dm0 <= 0; else dm0 <= dm0 + 1; end
3: begin if (dm1 == 5) dm1 <= 0; else dm1 <= dm1 + 1; end
endcase
end
else begin
case (set_pos)
0: begin if (dm0 == 9) dm0 <= 0; else dm0 <= dm0 + 1; end
1: begin if (dm1 == 9) dm1 <= 0; else dm1 <= dm1 + 1; end
2: begin if (dh0 == 9 || dh0 == 3 && dh1 == 2) dh0 <= 0; else dh0 <= dh0 + 1; end
3: begin if (dh1 == 2) dh1 <= 0; else dh1 <= dh1 + 1; end
endcase
end
end
end
// reset
if (!prereset && RESET) begin
case (mode)
COUNT: {cs0, cs1, cm0, cm1, ch0, ch1} <= 24'd0;
MAIN: {s0, s1, m0, m1, h0, h1} <= 24'd0;
ALARM: {as0, as1, am0, am1, ah0, ah1} <= 24'd0;
DOWN: {ds0, ds1, dm0, dm1, dh0, dh1} <= 24'd0;
endcase
end
// Start timing/Stop timing
if (!precenter && CENTER) begin
if (mode == COUNT) begin
counting <= 1'b1 ^ counting;
end
end
// Reset the timer, lights on for 5s
if (ds0 == 0 && ds1 == 0 && dm0 == 0 && dm1 == 0 && dh0 == 0 && dh1 == 0) begin
down_sig <= 1'b1;
if (!down_sig) down_time <= 5;
end
else down_sig <= 1'b0;
// Alarm goes off, lights on for 5s
if (s0 == as0 && s1 == as1 && m0 == am0 && m1 == am1 && h0 == ah0 && h1 == ah1) begin
alarm_sig <= 1'b1;
if (!alarm_sig) alarm_time <= 5;
end
else alarm_sig <= 1'b0;
// Hourly chime, lights on for 5s
if (s0 == as0 && s1 == as1 && m0 == am0 && m1 == am1) begin
ontime_sig <= 1'b1;
if (!ontime_sig) ontime_time <= 5;
end
else ontime_sig <= 1'b0;
if (clk_sec) begin
if (ontime_time>0)
ontime_time<=ontime_time-1;
end
end
// Set the modification position in hand mode
// integer set_pos = 0;
always_ff @ (posedge LEFT or posedge RIGHT) begin
if (hand == ON) begin
if(LEFT) begin
if (set_pos == 3) set_pos <= 0;
else set_pos <= set_pos + 1;
end
else if(RIGHT) begin
if (set_pos == 0) set_pos <= 3;
else set_pos <= set_pos - 1;
end
else
set_pos <= set_pos;
end
end
// Display module
// Select the appropriate display content based on the current mode
always_comb begin
case (mode)
MAIN:
if (DISP == LOW) begin disp_A0 = m0; disp_A1 = m1; disp_B0 = s0; disp_B1 = s1; end
else begin disp_A0 = h0; disp_A1 = h1; disp_B0 = m0; disp_B1 = m1; end
ONTIME:
if (DISP == LOW) begin disp_A0 = m0; disp_A1 = m1; disp_B0 = s0; disp_B1 = s1; end
else begin disp_A0 = h0; disp_A1 = h1; disp_B0 = m0; disp_B1 = m1; end
ALARM:
if (DISP == LOW) begin disp_A0 = am0; disp_A1 = am1; disp_B0 = as0; disp_B1 = as1; end
else begin disp_A0 = ah0; disp_A1 = ah1; disp_B0 = am0; disp_B1 = am1; end
COUNT:
if (DISP == LOW) begin disp_A0 = cm0; disp_A1 = cm1; disp_B0 = cs0; disp_B1 = cs1; end
else begin disp_A0 = ch0; disp_A1 = ch1; disp_B0 = cm0; disp_B1 = cm1; end
DOWN:
if (DISP == LOW) begin disp_A0 = dm0; disp_A1 = dm1; disp_B0 = ds0; disp_B1 = ds1; end
else begin disp_A0 = dh0; disp_A1 = dh1; disp_B0 = dm0; disp_B1 = dm1; end
endcase
end
// Display based on disp_pos
// integer disp_pos = 0;
logic [3:0] disp_number;
always_comb begin
case (disp_pos)
0: begin segments[10:7] = 4'b1110; disp_number = disp_B0; end
1: begin segments[10:7] = 4'b1101; disp_number = disp_B1; end
2: begin segments[10:7] = 4'b1011; disp_number = disp_A0; end
3: begin segments[10:7] = 4'b0111; disp_number = disp_A1; end
default: begin segments[10:7] = 4'b0000; disp_number = 0; end
endcase
if (hand && disp_pos == set_pos && flip) begin
segments[6:0] = 7'b1111111;
end
else begin
case (disp_number)
4'd0: segments[6:0] = 7'b0000001;
4'd1: segments[6:0] = 7'b1001111;
4'd2: segments[6:0] = 7'b0010010;
4'd3: segments[6:0] = 7'b0000110;
4'd4: segments[6:0] = 7'b1001100;
4'd5: segments[6:0] = 7'b0100100;
4'd6: segments[6:0] = 7'b0100000;
4'd7: segments[6:0] = 7'b0001111;
4'd8: segments[6:0] = 7'b0000000;
4'd9: segments[6:0] = 7'b0000100;
default: segments[6:0] = 7'b1111111;
endcase
end
end
// Control the lights for alarm and countdown timer
always_comb begin
if (alarm_time != 0) alarm_light = 1'b1;
else alarm_light = 1'b0;
if (down_time == 0) down_light = 1'b0;
else down_light = 1'b1;
if (ontime_time == 0) ontime_light = 1'b0;
else ontime_light = 1'b1;
end
endmodule
clock_con.xdc
set_property PACKAGE_PIN E19 [get_ports {alarm_light}]
set_property PACKAGE_PIN V19 [get_ports {down_light}]
set_property PACKAGE_PIN W18 [get_ports {ontime_light}]
set_property PACKAGE_PIN W5 [get_ports {clk_pre}]
set_property PACKAGE_PIN V16 [get_ports {T1}]
set_property PACKAGE_PIN W16 [get_ports {T2}]
set_property PACKAGE_PIN W17 [get_ports {T3}]
set_property PACKAGE_PIN W15 [get_ports {T4}]
set_property PACKAGE_PIN V15 [get_ports {T15}]
set_property PACKAGE_PIN W14 [get_ports {T16}]
set_property PACKAGE_PIN T18 [get_ports {TU}]
set_property PACKAGE_PIN W19 [get_ports {TL}]
set_property PACKAGE_PIN U18 [get_ports {TC}]
set_property PACKAGE_PIN U17 [get_ports {TD}]
set_property PACKAGE_PIN T17 [get_ports {TR}]
set_property PACKAGE_PIN U7 [get_ports {segments[0]}]
set_property PACKAGE_PIN V5 [get_ports {segments[1]}]
set_property PACKAGE_PIN U5 [get_ports {segments[2]}]
set_property PACKAGE_PIN V8 [get_ports {segments[3]}]
set_property PACKAGE_PIN U8 [get_ports {segments[4]}]
set_property PACKAGE_PIN W6 [get_ports {segments[5]}]
set_property PACKAGE_PIN W7 [get_ports {segments[6]}]
set_property PACKAGE_PIN V7 [get_ports {segments[9]}]
set_property PACKAGE_PIN U2 [get_ports {segments[7]}]
set_property PACKAGE_PIN U4 [get_ports {segments[8]}]
set_property PACKAGE_PIN V4 [get_ports {segments[9]}]
set_property PACKAGE_PIN W4 [get_ports {segments[10]}]
set_property IOSTANDARD LVCMOS33 [get_ports {clk_pre}]
set_property IOSTANDARD LVCMOS33 [get_ports {T1}]
set_property IOSTANDARD LVCMOS33 [get_ports {T2}]
set_property IOSTANDARD LVCMOS33 [get_ports {T3}]
set_property IOSTANDARD LVCMOS33 [get_ports {T4}]
set_property IOSTANDARD LVCMOS33 [get_ports {T15}]
set_property IOSTANDARD LVCMOS33 [get_ports {T16}]
set_property IOSTANDARD LVCMOS33 [get_ports {TU}]
set_property IOSTANDARD LVCMOS33 [get_ports {TL}]
set_property IOSTANDARD LVCMOS33 [get_ports {TC}]
set_property IOSTANDARD LVCMOS33 [get_ports {TD}]
set_property IOSTANDARD LVCMOS33 [get_ports {TR}]
set_property IOSTANDARD LVCMOS33 [get_ports {alarm_light}]
set_property IOSTANDARD LVCMOS33 [get_ports {down_light}]
set_property IOSTANDARD LVCMOS33 [get_ports {ontime_light}]
set_property IOSTANDARD LVCMOS33 [get_ports {segments[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {segments[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {segments[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {segments[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {segments[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {segments[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {segments[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {segments[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {segments[8]}]
set_property IOSTANDARD LVCMOS33 [get_ports {segments[9]}]
set_property IOSTANDARD LVCMOS33 [get_ports {segments[10]}]
test.tcl
此文件是为了解决Vivido中的管脚约束错误,具体使用方法参考VIVADO中关于管脚约束错误
set_property SEVERITY {Warning} [get_drc_checks NSTD-1]
set_property SEVERITY {Warning} [get_drc_checks UCIO-1]
set_property SEVERITY {Warning} [get_drc_checks RTSTAT-1]