【连载】【FPGA黑金开发板】Verilog HDL那些事儿--RTC接口封装(二十二)

声明:本文转载于http://www.cnblogs.com/kingst,版权归akuei2及黑金动力社区(http://www.heijin.org)共同所有。

2

5.8 实验二十一:RTC接口

 

5.1章中,笔者说过“每一件硬件资源的封装,都有自己的考虑”。

 

 

 

 

key_interface.v 考虑了“5个拥有同样功能(滤抖)按键”。

smg_interface.v 考虑了“6位数码管的动态显示”。

beep_interface.v , ps2_interface.v 考虑了“利用FIFO,来拜托对上一层模块的束缚”。

tx_interface.v, rx_interface.v 和上述蜂鸣器接口和PS2接口考虑同样的事情。

vga_interface.v , lcd_interface.v 考虑了“利用RAM来打破了显示图像信息的极限”。

 

以上的接口都包含两个定义:“最后建模工程”和“使独立性特质”。

 

 

 

 

 

当然  RTC 接口的封装也离不开“封装的定义”,但是  RTC 接口会比较特别。 RTC 接口包含了  ds1302 的实时时钟芯片,如果只是“单纯的驱动”而已,那么 ds1302 模块已经是切切有余。

 

 

 

 

 

当我们封装RTC接口的时候,我们到底要“考虑什么呢?”。笔者的想法很简单,我们只要为 ds1302芯片 加入“可驱动”,“可配置”,“可输出”即可。

clip_image002[14]

 

 

 

 

 

 

 

 

 

上图是组合模块 rtc_interface.v,该组合模块包含了“可驱动”的ds1302模块,“可显示”和“可配置”的RTC控制模块。在4.3章中(实验十三)中建模成功的 ds1302模块,我们知道它可以支持8种命令,然而我们可以基于这些事儿之上,来决定“可配置”的设计方案。

 

 

 

 

RTC控制模块扮演着“可配置”和“可显示”的同时,它也扮演着 ds1302模块的控制操作。这也是为什么笔者在实验六中一直强调 “控制模块”的重要性。

我们先来复习 ds1302模块 可以支持的8种命令:

 

 

 

 

RTC_Start_Sig[ 7..0 ]

位命令

功能

1000_0000

关闭写保护

0100_0000

变更时寄存器

0010_0000

变更分寄存器

0001_0000

变更秒寄存器

0000_1000

开启写保护

0000_0100

读取时寄存器

0000_0010

读取分寄存器

0000_0001

读取秒寄存器

 

 

 

 

RTC控制模块除了“可驱动”以外,还有“可输出”这个工作。在“图形”中的RTC_Sig 信号,包含了24位位宽,然而位的分配如下:

 

 

 

 

 

image 

 

接下来就是“可配置”的方案了。 Config_Sig  包含 5 位位宽,然而位分配如下:  

 

Config_Sig[4..0]

位分配

意义

[4]

进入配置模式|退出配置莫斯

[3]

+1操作

[2]

-1操作

[1]

向左切换 ( 时 <=  <= 秒 )

[0]

向右切换 ( 时 =>  => 秒 )

 

 

 

 

Config_Sig 信号的每一个“位”都对“高脉冲敏感”,换句话说,如果某“位”接收“一个高脉冲”就有“一次性的操作”。

 

假设Config_Sig[4]接收一个高脉冲就“进入配置模式”。然后隔一段时间之后 Config_Sig[4] 再接收一个高脉冲就会“退出配置模式”。

(在这里笔者需要强调一点,在这里所谓的“时钟”不是物体的“时钟”,而是“时分秒”中的“时钟”。)

笔者先大致的说明一下  RTC 接口的工作原理: 在一开始的时候  RTC 接口  初始化  DS1302  芯片,将时钟,分钟,秒钟都配置为 00

 

然后 RTC接口,会从 00-00-00 开始计时。换句话说,在初始的状态 RTC_Sig 的输出是24'h00_00_00

假设  Config_Sig[4]  接收一个高脉冲, DS1302  芯片就停止计时,然而  RTC_Sig  输出当前的计时状态,这时候  RTC 接口就进入配置模式。  

 

在配置模式中“时钟”作为默认配置的“时间”。如果Config_Sig[3] 接收一个高脉冲,当前的“时钟值”就会递增。反之,如果 Config_Sig[2] 接收一个高脉冲,当前的“时钟值”就会递减。“时钟值”最大的值是24, 最小值是00

 

假设我要配置“分钟”,Config_Sig[0] 就要接收一个高脉冲,从“配置时钟”向右切换“配置分钟”。和“配置时钟”同样的原理。如果此时Config_Sig[3] 接收一个高脉冲,当前的“分钟值”就会递增,反之 Config_Sig[2] 接收一个高脉冲会使得当前的“分钟值”递减。“分钟值”最大的值是59, 最小值是00

 

 

如果接下来我要配置“秒钟”, Config_Sig[0]  就要接收一个高脉冲。如果接下来我又要配置“时钟”, Config_Sig[1]  就要接收一个高脉冲。“秒钟值”的最大值是 59 ,最小值是 00

 

 

 

在这里有一点必须注意的是:

 

 

 

在配置模式中,被“配置的时间”会在  RTC_Sig  输出。假设我在当前的配置模式中,我将“时钟值”配置为  23 ,那么  RTC_Sig  就会输出  24'h23_00_00

 

 

 

“时钟”会作为“配置时间”的默认选择。换句话说,当一进入“配置模式”,立即进入“配置时钟”。

 

 

 

“配置时钟”作为“切换”的最左边,“配置秒钟”作为“切换”的最右边。也就是说:

 

时钟 <=> 分钟 <=> 秒钟。

 

 

最后,假设我最终配置的时间是  12 - 20 - 12  ,然而我已经满足时间的配置了。这时候  RTC_Sig  的输出是  24'h12_20_12 。然后我要退出配置模式 那么 Config_Sig[4]  需要接收一个高脉冲,  RTC 接口就会从“配置模式”退出至“正常模式”。

 

 

 

当返回“正常模式”, RTC 接口会从  12-20-12  开始计时。

 

 

 

 

 

rtc_control_module.v

 

clip_image004

3~13行是 rtc_control_module.v 的输入输出定义。 

 

clip_image005

 

 

在第  18 行定义了  isConfig  的标志寄存器。 isConfig 的默认值是逻辑 0,  也就是说在初始化的状态下, RTC 接口会进入“正常模式”( 22 行)。如果  Config_Sig[4]  接收到一个高脉冲  isConfig  的值就会介于  0~1  之间切换( 23~24 ),亦即 isConfig  逻辑 0 代表“正常模式”,逻辑 1 代表“配置模式”。

 

clip_image006

 

 

27~50 行是核心部分有关的寄存器声明和寄存器初始化。 rData 值作为  Time_Write_Data 的驱动。 Hour , Min, Sec  是作为“时分秒种”的暂存器。 Temp  Comp  只作为“时间值”递增和递减操作的暂存器。 Go 寄存器是步骤 i 的返回指示。 isStart 是作为驱动 ds1302 模块的命令寄存器。所有寄存器的初始化都是清零状态。

 

clip_image007

 

 

在一开始的时候, RTC 时钟写进入初始化状态:

 

 

 

步骤 056~58 行)关闭  DS1302 芯片的写保护。步骤 160~62 行)是初始化 DS1302 芯片的“时钟值”。步骤 264~66 行)是初始化 DS1302 芯片的“分钟值”。步骤 368~70 行)是初始化 DS1302 芯片的“秒钟值”,同时也是启动 DS1302 芯片开始计数。所以说,初始化状态的“时间”是  24'h00_00_00

 

 

clip_image008

步骤4~7, 是正常模式。当进入步骤474~76),if条件就会先判断,isConfig是否被拉高?如果isConfig不被拉高,就进入下一个步骤。步骤5是执行“读时钟”的命令,步骤6是执行“读分钟”的操作,步骤7是读秒钟的操作。最后会返回步骤4。换句话说步骤4~7是正常模式的循环。但是,一旦步骤4if条件成立的话,就会进入“配置模式”。 

 

clip_image009

 

 

步骤 8 是预配置 也就是说在关闭  ds1302 芯片计时的同时,初始化  Temp 寄存器为 Hour 寄存器的值,这一点很重要,因为下一个步骤的操作需要。(在 DS1302 秒寄存器的最高位写入 1 ,亦即关闭计时)。可能你会想“在为 DS1302 的秒寄存器写入 8'b1000_0000 ,的时候,会不会破坏当前秒寄存器的值呢? ”。秒寄存器的值已经被暂存在  Sec

 

 

clip_image010

 

 

步骤 9~11 是配置模式。在 103 行笔者是否看见这样一句代码,改代码表示将 Temp 的值赋予  Hour 寄存器。读者尝试想象,如果在预配置至下(步骤 8 ),没有为 Temp 赋予 Hour 的值。那么当进入配置模式,无疑  103 行的代码会被执行, Temp 的初值为 0Hour 的值已不是被破坏了?此外 111118 行类似的代码到底有什么作用  ...

 

 

 

步骤 9 是“配置时钟”,在 99 行的 if 条件先判断 isConfig 是否为逻辑 0 ,如果是就退出配置模式,如果不是就处于“配置时钟”的状态。 103 行的代码,会作为默认一直被执行着。

 

 

 

 Config_Sig[3]  接收一个高脉冲,亦即“时钟值递增”的操作。 Temp 会暂存  Hour 的值,然后  Comp 寄存器陪暂存  Hour 的最大值,也就是 8'h23Go 寄存器指示着步骤 9 ,然后 i 寄存器被赋予  4'd12  ,该表示下一个执行步骤为 12

 

 

clip_image012

 

 

步骤 12~14 是“值递增”操作。在 123if 条件会先判断,如果 Temp 的值小于 Comp 的值(如果当前的时钟值小于最大的时钟值的话), Temp 会递增。然后会进入步骤 13

 

否则的话 Temp 的值会赋予 Comp的值(当前时钟值赋予最大的时钟值),然后返回Go指示的步骤(如果当前是“配置时钟”,就会返回“配置时钟的步骤”,这也是为什么在100行,Go会指向当前的执行步骤)。

 

 

步骤 13 是进位操作,在 127if 条件会判断 Temp 的个位(时钟的个位)是否大于 9

 

如果是就执行进位操作,然后i递增以示下一个步骤。否则i也会递增以示下一个步骤。

 

 

步骤 14 ,在 131 行的 if 条件会判断  Temp 的十位(当前“时钟值”的十位)是否大于 Comp 的十位(“时钟值”最大值的十位),如果是(亦即“当前时钟值”大于“时钟值最大值”) Temp 就赋予 Comp 的值(当前“时钟值”赋予“时钟值最大值”)。然后 i 赋予 Go 的值,以示返回“配置时钟”的步骤,亦即步骤 9 。否则,同样 i 会赋予 Go 的值

 

,返回步骤9 

 

clip_image013

 

 

在步骤 9 时,如果 Config_Sig[2]  接收一个高脉冲, Temp 就会暂存 Hour 的值( Temp 暂存当前的“时钟值”),然后 Go 寄存器指向当前步骤,亦即步骤 9i 寄存器被赋予 4'd15, 也就是说下一个步骤会进入步骤 15

 

clip_image014

 

 

步骤 15 是递减操作。在 137if 条件会先判断  Temp  的个位(当前“时钟值”的个位)大于 0 。如果是  Temp 的个位就会递减(当前“时钟值”的个位递减 1 )。然后 i 寄存器会指向 Go 的值,亦即返回步骤 9 ,返回“配置时钟”。

 

 

 

如果 137 行的 if 不成立,就会判断 138 行的 if 条件。 Temp 的个位等于 0 的同时, Temp 的十位又大于 0 (当前“时钟值”的个位等于 0, 而且当前“时钟值”的十位大于 0 ),就会将 Temp 的十位递减, Temp 的个位赋予 4'd9 (将当前“时钟值”的十位递减 1 ,当前“时钟值”的个位赋予 9 )。最后  i  寄存器会指向返回 Go 的值,亦即步骤 9

 

 

 

如果 137 行和 138 行的 if 条件不成立( 139 行),即表示 Temp 的值(当前的“时钟值”是 8'h00 )。 寄存器会指向返回 Go 的值,亦即步骤 9

 

 

clip_image015

 

 

 103 行的这段代码很重要,因为无论是递增操作,或者是递减操作。最后操作的结果( Temp 值)都要更新于 Hour 寄存器的值。

 

 

 

如果 Config_Sig[0]  接收一个高脉冲( 102 行),亦即是向右边切换,换句话说就是从“时钟配置”向右切换到“分钟配置”。此时 Temp 的值必须更行为  Min 的值。和 103 行同样的道理。当  Config_Sig[0]  就收一个高脉冲,步骤 9 会递增至步骤 10

 

 

 

在步骤 10105~111 行),无疑在 111 行的代码会被执行,如果在 102 Temp 值没有被更新为  Min  的值,不难想象得到  Min 寄存器的值与  Hour 寄存器的值是一样的,这显然是严重的 BUG

 

 

 

“分钟配置”和“时钟配置”的“递增操作”或者“递减操作”都是大同小异。  Config_Sig[3]  如果被触发,就会进入步骤 12,  亦即“递增操作的步骤”。 Config_Sig[2]  被触发就会进入步骤 15 的“递减操作”。

 

 

 

不同之处是:  Temp  暂存的再也不是  Hour  而是  Min 的值, Comp  的最大值是  8'h59 

 

Go的寄存器指向“分钟配置”的步骤。

 

 

如果  Config_Sig[0]  接收一个高脉冲, i 会递增,“分钟配置”向右切换至“秒钟配置”( 108 行), Temp 会暂存  Sec 的值。如果  Config_Sig[1]  接收一个高脉冲, i 会递减,“分钟配置”向左切换至“时钟配置”( 109 行), Temp 会暂存 Hour 的值。

 

 

 

在步骤 998~103 行)没有  Config_Sig[1] ,在步骤 11113~118 行)没有  Congfig_Sig[0]

 

这也表示 “时钟配置 <=> 分钟配置 <=> 秒钟配置”,配置模式会在这3个时间配置之间切换。换句话说“时钟配置”是“向左切换的最边”,然而“秒钟配置”是“向右切换的最边”这一个事实。  

 

clip_image016

 

 

在步骤 12~14 的递增操作和在步骤 15 的递增操作,在某种程度上,可以把它们看成为“递增函数和递减函数”。根据一些常用的设计方法,我们必定会建立一个“时钟递增”,“分钟递增”,“秒钟递增”,“时钟递减”,“分钟递减”和“秒钟递减”等的多个操作步骤。这无疑是会消耗许多的逻辑资源。

 

 

 

在某程度的根本上时钟递增”,“分钟递增”,“秒钟递增”,“时钟递减”,“分钟递减”和“秒钟递减”等步骤,都可以共用一个“递增步骤”和“递减步骤”。但是问题就在于“参数传递”和“参数返回”是有关“代码概念”的操作。我们知道 Verilog HDL 语言,是“硬件描述语言”,它没有“代码的概念” ...

 

 

 

这时候我们必须把思路往后推移。笔者还记得自己在学习“微处理器”的时候(在笔者的心目中微处理器是悲剧,和单片机是不同的东西),为了是两个值相加,必须将两个值载入“操作空间”,然后使用指令是它们相加。如果使用这个思路反映到步骤 12~14 和步骤 15 的“递增递减操作”。寄存器 Temp ,  寄存器  Comp ,  和寄存器  Go  等就有所谓的“操作空间”的意义。

 

 

clip_image017

 

 

clip_image018

 

 

clip_image019

 

 

在步骤 91011 期间,如果  99106114 行的 if 条件判断到  isConfig  被拉低的话。估计只有一件事情要发生,那就是“退出配置模式”。   

 

 

clip_image020

 

 

我们知道要进入“配置模式”  isConfig 必须是逻辑 1 ,然后在“通常模式”中,如果步骤 475if 条件检测到,才会进入“预配置模式”(步骤 8 ),执行预设置的操作。

 

当“退出配置模式”之后 i寄存器会赋值为 4'd1 ,亦即表示返回步骤1

 

clip_image021

 

 

步骤 1 ,是初始化  Hour  ,但是关键点就是在  58 行的  rData <= Hour 。同样的步骤 266 行和步骤 370 行都是同样的意义。就是把配置后的  Hour Min Sec  作为输入数据,然后调用  DS1302 模块的命令,针对 DS1302 芯片的时寄存器,分寄存器和秒寄存器执行更新。

 

 

clip_image022

 

 

最后步骤 i 的流程也会进入  4~7  之间,也就是说  从“配置模式的退出”,会进入步骤 1 执行时间值更新的操作,然后会进入“普通模式”。

 

 

clip_image023

 

 

148 RTC_Start_Sig  isStart  命令寄存器驱动。在 149Time_Write_Data rData 寄存器驱动。在 150RTC_Sig HourMinSec 寄存器驱动。

 

 

clip_image024

 

 

clip_image025

这是完成的代码,好好的浏览一番吧。

rtc_interface.v

 

clip_image002

 

 

rtc_interface.v  组合模块和“图形”是一模一样。  

 

实验二十一说明:

 

 

说实话 RTC 接口的封装,却是有一点难度。 RTC 接口的“可驱动”是由 DS1302 模块负责但是由 RTC 控制模块控制。然而“可显示”和“可配置”却是由 RTC 控制模块负责。

 

 

 

对于 RTC 接口的配置控制,我们只要知道如何调用 Config_Sig 信号。 Config_Sig  信号的“每一位”都有“配置的用意”。笔者不得不承认“可配置”的设计方面,确实有一点难度。但是困难归苦难,为了未来,就要克服。

 

 

 

读者还是把重点放在“ RTC 控制模块如何操作 ”,因为只要读者明白了“ RTC 控制模块如何操作 ”,自然而然会明白“ RTC 控制模块的设计思路 ”。

 

 

 

完成后的扩展图:

 

 

 

 

 

clip_image004

 

实验二十一结论:

 

 

这一章实验主要是讲解如何为“ DS1302 芯片 ”执行封装。  

 

总结:

第五章终于完结了。在这里笔者来个总结:

 

 

低级建模中所谓的“最后工程”是针对某个硬件“有考虑”的封装。

 

 

 

低级建模中所谓的“后期建模”是为后期建模的做好准备。如第二章 ~ 第四章的“基础建模”就是为“封装”做好准备,故“封装”可以称为“基础建模”的后期建模。然而“封装”又为什么做好准备?那就是下一章的主题  - “系统”建模。

 

 

 

说道“封装”,我们必须考虑 - 经过“封装”后的模块都有“独立性”的特质。我们知道 Verilog HDL 语言硬件描述语言,“封装”的行为会很有效的将“ Verilog HDL ”语言的特性带出来。因为经过“封装”以后的模块,都能“独立”的运行起来,这也好比“并行操作”的概念。

 

 

 

经过第五章的洗礼,读者们是不是领悟到从第一章到第五章的所有内容,任何一段信息都是在为下一章做好准备。在第二章中,笔者就提及过:“低级建模是模仿管理系统”的一种建模方法。一个大部分是由许多小部分组成。

 

 

 

如果换成另外一个角度去想象:

 

 

l  每一个简单的功能模块,可以看似一位员工。

l  每一个简单的控制模块,可以看似一位领导。

l  每一个简单的组合模块,可以看似一组小组。

l  每一个组合模块再组合起来,可以看似一个大组。

l  每一个大组为某种“目的”存在,而且有独立运行的能力,就成为部门(接口)。

l  最后由许多接口组合起来,就成为一个“系统”。

 

 

这就是“低级建模”最基本思路。

 

 

 

低级建模对于每一个模块都有区分“特质”。这好比员工是员工,领导是领导,小组是小组,大组是大组,部门是部门,各个都有自己的特征。

 

 

 

无论是什么样子管理系统,员工和员工之间,领导和员工之间,部门经理和领导之间,必须和谐共处。这好比是“低级建模”的“代码风格”吧。从实验一到实验二一,读者可能会发现到笔者所使用的“代码风格”都是清一色,笔者会很脸皮的告诉你 , “这是低级建模的固有代码结构”。

 

笔者知晓,很多读者一开始的时候,都会把“步骤i”看成是某个状态机的状态。在创建“低级建模”的初头,笔者老是觉得“典型的状态机”用法实在是太魏硕了。如果是小代码量的建模,那么“典型状态机”是没有问题。但是遇见“多级建模”或者“多状态”的时候“典型的状态机”就是“见鬼”。 
笔者索性就建立自己的“代码风格”,在“低级建模”里笔者使用“步骤”来取代“状态机”。果真这个决定是对的。这样的设定,给笔者在后期的实验带来许多方便。在网上有一位网友很可爱的为笔者“Verilog HDL建模技巧 - 低级建模之仿顺序操作·思路篇”评价:

 

 

“俺觉得,每一个有仿顺序操作结构的模块,都可以理解成为一个子状态机  ... 

 

 

 

他的理解没有一丝错误,不过是在理解上有不同的立场。这也难怪,因为当时笔者在写这一本笔记的时候,正是“低级建模”的创建初头。除了“步骤 i ”具有显性的代码风格以外,还有许多许多的代码风格  ...

 

 

 

话说远了  ...  下一章就是大团圆的一章笔记了。在进入下一章之前,请确保读者本身已经理解,掌握“低级建模是什么”这一点重点。如果还没有很好的理解“低级建模时什么”,请务必加强对“低级建模”的理解和掌握。

 

 

 

学习最重要不是要得到好成绩,也不是学习速度,而是掌握学习的重点  ......

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值