数字逻辑大作业:FPGA数字钟实验

题目

使用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]

数字钟说明书

在这里插入图片描述

  • 23
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值