熟悉Quartus II的使用方法和步骤,掌握使用Quartus II来进行FPGA编程和仿真的方法和技巧。熟悉FPGA硬件的调试方法,掌握初步的FPGA 和周边硬件的调试和故障诊断的能力。掌握用 VHDL 语言进行数字系统设计的基本方法和流程,加深对EDA课程内容的理解,提高工程设计实践能力。用VHDL实现一个数字钟的功能:实现小时,分钟,秒的计数和动态显示和整点报时等功能。
设计题目:数字钟
用VHDL实现一个数字钟的功能,功能如下:
用VHDL实现一个数字钟的功能。要求数字电子钟能够实现时、分、秒计时功能;能进行正常的时、分、秒计时功能,二十四小时制计时。能由数码管显示0~24h,0~60min,0~60s。以及校准时,分和秒的功能;校准时分秒清零的功能;整点报时的功能等。
EDA技术在硬件实现方面融合了大规模集成电路制造技术,IC 版图设计技术、ASIC测试与封装技术、FPGA /CPLD 编程下载技术、自动检测技术等;EDA技术为现代电子理论和设计的表达与实现提供了可能性。在现代技术的所有领域中,纵观许多得以飞速发展的科学技术,多为计算机辅助设计,而非自动化设计。显然,最早进入设计自动化的技术领域之一是电子技术,这就是为什么电子技术始终处于所有科学技术发展最前列的原因之一。不难理解,EDA技术已不是某一学科的分支,或某种新的技能技术,应该是一门综合性学科。它融合多学科于-体,打破了软件和硬件间的壁垒,是计算机的软件技术与硬件实现、设计效率和产品性能各二为一,它代表了电子设计技术合应用激活速的发展方向。电子时钟以成为人们常生活中数字电子钟一般由振荡器,分频器,译码器,显示器等部分组成。电子时钟的应用非常广泛,应用于人家庭或车站、剧场、办公室等公共场所,给人们的生活,学习,工作,娱乐带来极大的便利,尽管目前市场上以有现成电子时钟集成电路芯片,价格便宜这些都是数字电路中最基本的,应用最广的电路。数字电子钟的基本逻辑功能框图如下:它是一一个将“时”,“分”,“秒”显示于人的视觉器官的计时装置。他的计时装置的周期为24小时,显示满刻度为23时59分59秒,另外应有校时功能。
而我所设计的数字钟是采用自顶向下的模块化设计方法,将整个电路分为按键消抖模块、分频模块、控制模块、时钟模块、闪烁模块、译码显示模块等等,通过查阅资料、构思模块、等方法,先设计每个模块的功能,并在设计时对每个模块所需要的功能进行下载验证后,将各个模块相互连接在一起。最后分配好管脚,完成设计的。
本次的数字时钟设计我用VHDL语言开发,开发平台为黑金AX301(Cyclone IV E:EP4CE6F17C8芯片),Cyclone IV E:EP4CE6F17C8芯片是 256 脚的 FBGA 封装。整个开发板的配置实用,有两路黑金标准的 40 针 2.54 标准的扩展口,一共有 34*2=68 个IO,另外也引出了 5V 电源,3.3V 电源,还有多路 GND,具有很强的可支配性。而本次数字时钟的开发工具为quarter II 13.0。Quartus II是Altera公司的综合性CPLD/FPGA开发软件,原理图、VHDL、VerilogHDL以及AHDL(Altera Hardware 支持Description Language)等多种设计输入形式,内嵌自有的综合器以及仿真器,可以完成从设计输入到硬件配置的完整PLD设计流程。Quartus II提供了完全集成且与电路结构无关的开发包环境,具有数字逻辑设计的全部特性,包括可利用原理图、结构框图、VerilogHDL、AHDL和VHDL完成电路描述,并将其保存为设计实体文件;芯片(电路)平面布局连线编辑;LogicLock增量设计方法,用户可建立并优化系统,然后添加对原始系统的性能影响较小或无影响的后续模块;而本次数字时钟设计主要实现的功能有:时间、日期小时和切换显示,整点报时,按键消抖等功能。我们采用自顶向下的模块化设计方法,将整个电路分为按键消抖模块、分频模块、控制模块、时钟模块、闪烁模块、译码显示模块等等,通过查阅资料、构思模块、等方法,先设计每个模块的功能,并在设计时对每个模块所需要的功能进行下载验证后,将各个模块相互连接在一起。最后分配好管脚,完成设计的。
我采用自顶向下的模块化设计方法,将整个项目分为按键消抖模块、分频模块、控制模块、时钟模块、闪烁模块、译码显示模块等等,先设计每个模块的功能,并在设计时对每个模块所需要的功能进行下载验证后,将各个模块相互连接在一起。最后分配好管脚,完成设计。
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity clock is
port(clk:in std_logic;
c_reset:in std_logic;
c_key1:in std_logic;
c_key2:in std_logic;
c_key3:in std_logic;
buzzer:out std_logic;
segment_sel:out std_logic_vector(5 downto 0);
segment_seg:out std_logic_vector(7 downto 0);
flash_led:out std_logic_vector(3 downto 0));
end clock;
architecture bhv of clock is
component second_bcd_count
port(clk_to_second:in std_logic;
reset:in std_logic;
set_min : in std_logic;
co : out std_logic;
datout : out std_logic_vector(7 downto 0));
end component;
component minute_bcd_count
port(clk_to_minute: in std_logic;
Clk reset: in std_logic;
set_hour : in std_logic;
co : out std_logic;
datout : out std_logic_vector(7 downto 0));
end component;
component hour_bcd_count
port(
clk_to_hour, reset, clk_scale, set_day : in std_logic;
co : out std_logic; --进位
datout : out std_logic_vector(7 downto 0));
end component;
component seltime
port(scan_clk: in std_logic;
flash_num: in std_logic_vector(1 downto 0);
hour: in std_logic_vector(7 downto 0);
minute: in std_logic_vector(7 downto 0); second : in std_logic_vector(7 downto 0);
sel : out std_logic_vector(5 downto 0);
seg : out std_logic_vector(7 downto 0));
end component;
component dev
port(clk_50mhz,set_sec : in std_logic;
clk_1hz, clk_5hz, clk_250hz : out std_logic);
end component;
component alert
port( clk : in std_logic;
setting: in std_logic_vector(1 downto 0);
m_in : in std_logic_vector(7 downto 0);
s_in : in std_logic_vector(7 downto 0);
speaker : out std_logic;
led : out std_logic_vector(3 downto 0));
end component;
component key_delay
port(clk, key_in : in std_logic;
key_out : out std_logic);
end component;
component controller
port(o_reset, o_key1, o_key3 : in std_logic; d_reset, a_hour, a_min, a_sec, a_scale: out std_logic; a_year, a_month, a_day : out std_logic;
f_num : out std_logic_vector(1 downto 0);
set_count : out std_logic_vector(3 downto 0)); end component;
component set_view
port(
view_day : in std_logic;
setting_count : in std_logic_vector(3 downto 0);
year_in, month_in, day_in: in std_logic_vector(7 downto 0);
hour_in, min_in, sec_in: in std_logic_vector(7 downto 0);
first_out,second_out,third_out:out std_logic_vector(7 downto 0));
end component;
component day
port(
clk_to_day, reset, set_month : in std_logic; d_in_month : in std_logic_vector(7 downto 0);
day : out std_logic_vector(7 downto 0);
co : out std_logic);
end component;
component month
port(
clk_to_month, reset, set_year : in std_logic; m_in_year : in std_logic_vector(7 downto 0);
month : out std_logic_vector(7 downto 0); month_days : out std_logic_vector(7 downto 0);
co : out std_logic);
end component;
component year
port(
clk_to_year, reset : in std_logic;
year : out std_logic_vector(7 downto 0));
end component;
signal do_reset, ad_hour, ad_min, ad_sec, ad_scale, ad_year, ad_month, ad_day: std_logic;
signal s_co, m_co, h_co, d_co, mo_co: std_logic;
signal out_reset, out_key1, out_key2, out_key3 : std_logic;
signal c_clk, scan_clk, flow_clk : std_logic;
signal s_dat, m_dat, h_dat, y_dat, mo_dat, d_dat : std_logic_vector(7 downto 0);
signal fla_num : std_logic_vector(1 downto 0);
signal s_count : std_logic_vector(3 downto 0);
signal f, s, t, in_month: std_logic_vector(7 downto 0);
begin
u1 : second_bcd_count port map(c_clk, do_reset, ad_min, s_co, s_dat);
u2 : minute_bcd_count port map(s_co, do_reset, ad_hour, m_co, m_dat);
u3 : hour_bcd_count port map(m_co, do_reset, ad_scale, ad_day, h_co, h_dat);
u4 : seltime port map(scan_clk, fla_num, f, s, t, segment_sel, segment_seg);
u5 : dev port map(clk, ad_sec, c_clk, flow_clk , scan_clk);
u6 : alert port map(flow_clk,fla_num, m_dat,s_dat, buzzer, flash_led);
u7 : key_delay port map(scan_clk, c_reset, out_reset);
u8 : key_delay port map(scan_clk, c_key1, out_key1);
u10: key_delay port map(scan_clk, c_key3, out_key3);
u11: controller port map(out_reset,out_key1, out_key3, do_reset, ad_hour, ad_min, ad_sec, ad_scale, ad_year, ad_month, ad_day, fla_num, s_count);
u13: set_view port map(c_key2, s_count, y_dat, mo_dat, d_dat, h_dat, m_dat, s_dat, f, s, t );
u14: day port map(h_co, do_reset, ad_month, in_month, d_dat, d_co );
u15: month port map(d_co, do_reset, ad_year, y_dat, mo_dat, in_month, mo_co );
u16: year port map(mo_co, do_reset, y_dat );
end bhv;
这个模块是整个数字时钟的核心,首先我们先说明各个端口,1hz时钟信号输入,复位键输入,按键输入,蜂鸣器控制,数码管位选信号输出,数码管段选信号输出,LED灯输出。然后就进行各个模块的调用声明:调用秒钟计数模块声明,调用分钟计数模块声明,调用小时计数模块声明,调用译码显示模块声明(扫描时钟输入,闪烁数码管位选择,时间数据输入,位选信号输出,段选信号输出),调用分频模块声明,因为黑金AX301的EP4CE6F17C8芯片的原始频率并不是时钟信号所需要的频率,所以要对它的原始信号进行分频。然后就是整点报时模块(激励led灯变化,输入分钟显示数据,输入秒钟显示数据,蜂鸣器控制,流水灯),调用按键延时消抖模块,按键输入信号处理中心模块(三个按键输入信号,重置信号,设置时分秒,切换进制信号,设置年月日信号,选中信号),时间日期切换控制模块,日计数模块(来自小时的进位信号, 按键输入的重置/设置月份的信号,来自月计数模块的天数信号,输出当前天数,进位信号),月计数模块(来自天数的进位信号, 按键输入的重置/设置年份的信号,来自月份的天数信号,输出当前月的天数,进位信号),年计数模块(来自天数的进位信号,来自按键的重置信号,输出当前月份)。声明完各个调用模块之后,再定义信号,然后,最后使用port map语句实现元件端口名到高层设计中的信号的映射。
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity controller is port(
o_reset, o_key1, o_key3 : in std_logic; -- RESET, KEY1, KEY3
d_reset, a_hour, a_min, a_sec,a_scale : out std_logic; a_year, a_month, a_day : out std_logic;
f_num : out std_logic_vector(1 downto 0);
set_count : out std_logic_vector(3 downto 0));
end controller;
architecture bhv of controller is
signal reset_count : std_logic_vector(3 downto 0);
signal view_count : std_logic_vector(1 downto 0);
begin
process(o_reset)
begin
if (o_reset'event and o_reset = '1') then
if (reset_count > "0111") then reset_count <= "0000";
else
reset_count <= reset_count +1;
end if;
end if;
case reset_count is
when "0000" => f_num <="00";
when "0001" => f_num <="01";
when "0010" => f_num <="10";
when "0011" => f_num <="11";
when "0100" => f_num <="01";
when "0101" => f_num <="10";
when "0110" => f_num <="11";
when others => f_num <="00";
end case;
if (o_key1 = '1' and reset_count = "0000") then d_reset<= '1';
else d_reset<= '0';
end if;
if (o_key3 = '1' and reset_count = "0000") then a_scale<= '1';
else a_scale<= '0';
end if;
if (o_key1 = '1' and reset_count = "0001") then a_hour<= '1';
else a_hour<= '0';
end if;
if (o_key1 = '1' and reset_count = "0010") then a_min<= '1';
else a_min<= '0';
end if;
if (o_key1 = '1' and reset_count = "0011") then a_sec<= '1';
else a_sec<= '0';
end if;
if (o_key1 = '1' and reset_count = "0100") then a_year<= '1';
else a_year<= '0';
end if;
if (o_key1 = '1' and reset_count = "0101") then a_month<= '1';
else a_month<= '0';
end if;
if (o_key1 = '1' and reset_count = "0110") then a_day<= '1';
else a_day<= '0';
end if;
end process;
set_count <= reset_count;
end bhv;
按键输入信号处理中心模块在整个项目中起到了用户交互操作反馈的作用。首先进行端口说明,说明RESET, KEY1, KEY3 端口传输按键输入信号,说明输出重置信号:秒时分日月年进位信号,以及数码管闪烁位数输出和进程信号输出,然后通过if-else语句和case-when语句来判断进程是否在设置状态,以及单输入(通过一个按键来控制进程,比如重置和进行小时的切换)和组合输入(对时间和日期进行设定)。
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity alert is
port( clk : in std_logic;
setting: in std_logic_vector(1 downto 0);
m_in : in std_logic_vector(7 downto 0);
s_in : in std_logic_vector(7 downto 0);
speaker : out std_logic;
led : out std_logic_vector(3 downto 0));
end alert;
architecture bhv of alert is
signal sel : std_logic_vector(1 downto 0);
signal count : std_logic_vector(2 downto 0);
begin
reg : process(clk) begin
if clk'event and clk = '1' then
if count < "111" then count <= count + 1;
else count <= "000";
end if;
end if;
end process reg;
flow : process(count, m_in, s_in) begin
if m_in = "00000000" and s_in < "00001001" then case count is
when "000" => led <="1111";
when "001" => led <="0000";
when "010" => led <="1111";
when "011" => led <="0000";
when "111" => led <="1111";
when "101" => led <="0000";
when "110" => led <="1111";
when others => led <="0000";
end case;
elsif setting > "00" then
case count is
when "000" => led <="1000";
when "001" => led <="0100";
when "010" => led <="0010";
when "011" => led <="0001";
when "111" => led <="1111";
when "101" => led <="0000";
when "110" => led <="1111";
when others => led <="0000";
end case;
else led <= "0000";
end if;
end process flow;
beep : process( m_in, s_in) begin
if m_in = "00000000" and s_in < "00000011" then
if s_in = "00000000" then speaker <= '0';
elsif s_in = "00000001" then speaker <= '1';
else
speaker <= '0';
end if;
else speaker <= '1';
end if;
end process beep;
end bhv;
整点报时模块也是我们这次项目要达到要求的功能所必须具备的模块。首先我们还是进行端口说明,包括激励led变化,输入分钟显示数据,输入秒钟显示数据,蜂鸣器控制,流水灯。然后再通过if else语句和case when语句来判断是否满足整点报时的条件,若满足则蜂鸣器作用(低电平),并且使蜂鸣器响铃间隔一个节拍。
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity key_delay is
port(clk, key_in : in std_logic;
key_out : out std_logic);
end key_delay;
architecture bhv of key_delay is
begin
process(clk, key_in)
variable count : integer range 0 to 10;
begin
if clk'event and clk = '1' then
if key_in = '0' then
if count < 10 then count := count + 1;
else count := count;
end if;
if count = 9 then key_out <= '1';
else key_out <= '0';
end if;
else count := 0;
end if;
end if;
end process;
end bhv;
抖动的时间一般为5-10ms,所以在检测出按键闭合后执行一段延时程序,5-10ms的延时,让前沿抖动消失后再一次检测按键状态。如果仍保持闭合状态电平,则认为真正有按键按下。当检测到按键释放后,也要给5-10ms的延时,待后沿抖动消失后才能转入该键的处理程序。
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity dev is
port( clk_50mhz,set_sec : in std_logic;
clk_1hz, clk_5hz, clk_250hz : out std_logic);
end;
architecture bhv of dev is
signal q1 : integer range 0 to 49999999;
signal q2 : integer range 0 to 49999;
signal q3 : integer range 0 to 9999999;
begin
process(clk_50mhz) begin
if clk_50mhz'event and clk_50mhz = '1' then
-- 1hz
if q1 < 25000000 then
if set_sec = '1' then clk_1hz <= '1'; q1 <= 0;
else clk_1hz <= '0'; q1 <= q1 + 1; end if;
elsif q1 <49999999 then clk_1hz <= '1'; q1 <= q1 + 1;
else q1 <= 0;
end if;
if q2 < 25000 then clk_250hz <= '0'; q2 <= q2 + 1;
elsif q2 < 49999 then clk_250hz <= '1'; q2 <= q2 + 1;
else q2 <= 0;
end if;
-- 5hz
if q3 < 5000000 then clk_5hz <= '0'; q3 <= q3 + 1;
elsif q3 < 9999999 then clk_5hz <= '1'; q3 <= q3 + 1;
else q3 <= 0;
end if;
end if;
end process;
end;
因为黑金AX301的EP4CE6F17C8芯片所提供的频率并不是时钟信号所需要的频率,所以要对它的原始信号进行分频。输入50MHZ的时钟源信号,分别输出1hz,100khz,100hz的时钟频率,来达到项目的要求。
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity second_bcd_count is
port(clk_to_second, reset,set_min : in std_logic;
co : out std_logic;
datout : out std_logic_vector(7 downto 0));
end second_bcd_count;
architecture bhv of second_bcd_count is
signal count_shi, count_ge : std_logic_vector(3 downto 0);
signal count_en : std_logic;
begin
co <= set_min or count_en;
process(clk_to_second, reset, set_min) begin
if reset = '1' then count_shi <= "0000"; count_ge <= "0000";
elsif clk_to_second'event and clk_to_second = '1' then
if count_shi = "0101" and count_ge = "1001" then
count_shi <= "0000"; count_ge <= "0000"; count_en <= '1';
elsif count_ge < "1001" then count_ge <= count_ge + 1; count_en <= '0';
else count_ge <= "0000";
if count_shi < "1010" then count_shi <= count_shi + 1;
else count_shi <= "0000";
end if;
end if;
end if;
datout <= count_shi & count_ge;
end process;
end bhv;
秒钟计数模块,秒计数是由六十进制的计数器构成,首先是进行端口说明,包括1HZ时钟输入,重置信号输入,设置分信号输入,以及进位输出,秒的BCD码输出。然后就是对信号进行一个说明,包括计数器,十位和个位,以及记录进位信号。其中秒模块的进位信号由设置分的信号和计数信号共同决定,有复位信号则清零计数,计数进位的信号不是立即赋值,需要等下一个时钟信号到来。
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity minute_bcd_count is
port(clk_to_minute, reset, set_hour : in std_logic;
co : out std_logic;
datout : out std_logic_vector(7 downto 0));
end minute_bcd_count;
architecture bhv of minute_bcd_count is
signal count_shi, count_ge : std_logic_vector(3 downto 0);
signal count_en : std_logic;
co <= set_hour or count_en;
process(clk_to_minute, set_hour, reset) begin
if reset = '1' then count_shi <= "0000"; count_ge <= "0000";
elsif clk_to_minute'event and clk_to_minute = '1' then
if count_shi = "0101" and count_ge = "1001" then
count_shi <= "0000"; count_ge <= "0000"; count_en <= '1';
elsif count_ge < "1001" then count_ge <= count_ge + 1; count_en <= '0';
else count_ge <= "0000";
if count_shi < "1010" then count_shi <= count_shi + 1;
else count_shi <= "0000";
end if;
end if;
end if;
end process;
datout <= count_shi & count_ge;
end bhv;
分钟计数模块,分计数是由六十进制的计数器构成,总的进位信号为set_hour和co_en的结合,当分钟计数到60时产生进位,有复位信号则清除计数,59时清零进位,计数进位的信号量不是立即赋值,需要等下一个时钟信号到来,个位是9的时候十位进1,计数溢出则清零,并产生进位。
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity hour_bcd_count24 is
port(ad_scale, clk_to_hour, reset : in std_logic;
datout : out std_logic_vector(7 downto 0));
end hour_bcd_count24;
architecture bhv of hour_bcd_count24 is
signal count_shi, count_ge : std_logic_vector(3 downto 0);
begin
process(clk_to_hour, reset) begin
if reset = '0' then count_shi <= "0000"; count_ge <= "0000";
elsif clk_to_hour'event and clk_to_hour = '1' then
if ad_scale = '0' then
if count_shi = "0010" and count_ge = "0011" then
count_shi <= "0000";
count_ge <= "0000";
elsif count_ge < "1001" then
count_ge <= count_ge + 1;
else count_ge <= "0000";
count_shi <= count_shi + 1;
end if;
else
if count_shi = "0001" and count_ge = "0001" then
count_shi <= "0000";
count_ge <= "0000";
elsif count_ge < "1001" then
count_ge <= count_ge + 1;
else
count_ge <= "0000";
count_shi <= count_shi + 1;
end if;
end if;
end if;
datout <= count_shi & count_ge;
end process;
end bhv;
时钟计数模块,时计数是由二十四进制的计数器和十二进制的计数器构成,我设计的时钟的时可以进行十二进制和二十四进制的切换,跟分钟计数模块和秒钟计数模块一样,时钟计数模块也是有复位信号则清除计数,当时钟为24进制时,24溢出清零,且个位满十进一,当时钟为12进制时,12溢出清零,也是个位满十进一。
说明:reset复位键置低电平,clk和set_min都给一个频率,使co进位信号和datout数据显示都生成对应波形。
说明:reset复位键置低电平,clk和set_hour都给一个频率,使co进位信号和datout数据显示都生成相应的波形。
说明:reset复位键置低电平,clk和set_day和clk_scale都给一个频率,使co进位信号和datout数据显示都生成相应的波形。
说明:clk_50mhz和set_sec都给一个频率,使clk_1hz和clk_5hz和clk_250hz都生成相应的波形。,其中clk_250hz为零。
说明:clk_50mhz和set_sec都给一个频率,使clk_1hz和clk_5hz和clk_250hz都生成相应的波形。,其中clk_250hz和clk_5hz都为零。
说明:clk和m_in和s_in和setting.都给一个频率,使led和speaker生成相应的波形。
说明:clk和key_in都给一个频率,使key_out生成相应的波形。其中key_out为零。
说明:day_in和hour_in和min_in和month_in和sec_in和setting和view_day以及year_in都给一个频率,使first_out,second_out,third_out都生成相应的波形。
说明:clk和d_in和reset和set_month都给一个频率,使co进位信号和day都生成相应的波形。
说明:clk和d_in和reset和set_month都给一个频率,使co进位信号和day都生成相应的波形。
11.year(年份记录)
说明:clk和reset给一个频率,使year都生成相应的波形。
说明:c_reset复位键置低电平,clk和c_key1,c_key2,c_key3都给一个频率,使得buzzer蜂鸣器置低电平,flash_led全部为高电平(1111),并生成相应的段码和位码。