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

2010-12-17 13:18 1947 5 5 分类: FPGA/CPLD

声明:本文为原创作品,版权归akuei2及黑金动力社区(http://www.heijin.org)共同所有,如需转载,请注明出处http://www.cnblogs.com/kingst/

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芯片 加入“可驱动”,“可配置”,“可输出”即可。

 

上图是组合模块 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位位宽,然而位的分配如下:

 

接下来就是“可配置”的方案了。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

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

 

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

 

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

 

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

 

步骤0(56~58行)关闭 DS1302芯片的写保护。步骤1(60~62行)是初始化DS1302芯片的“时钟值”。步骤2(64~66行)是初始化DS1302芯片的“分钟值”。步骤3(68~70行)是初始化DS1302芯片的“秒钟值”,同时也是启动DS1302芯片开始计数。所以说,初始化状态的“时间”是 24'h00_00_00。

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

 

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

 

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

 

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

 

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

 

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

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

 

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

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

 

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

,返回步骤9。 

 

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

 

步骤15是递减操作。在137行if条件会先判断 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)。i 寄存器会指向返回Go的值,亦即步骤9。

 

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

 

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

 

在步骤10(105~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的值。

 

在步骤9(98~103行)没有 Config_Sig[1],在步骤11(113~118行)没有 Congfig_Sig[0]。

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

 

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

 

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

 

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

 

 

 

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

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

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

 

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

 

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

 

在148行 RTC_Start_Sig 由 isStart 命令寄存器驱动。在149行Time_Write_Data由 rData寄存器驱动。在150行RTC_Sig由 Hour,Min,Sec寄存器驱动。

 

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

rtc_interface.v

 

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

实验二十一说明:

 

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

 

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

 

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

 

完成后的扩展图:

 

实验二十一结论:

 

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

总结:

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

 

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

 

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

 

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

 

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

 

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

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

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

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

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

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

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

 

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

 

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

 

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

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

 

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

 

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

 

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

 

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

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
5
关闭 站长推荐上一条 /3 下一条