【连载】【FPGA黑金开发板】Verilog HDL那些事儿--GUI系统(二十五)(大结局)

来源:互联网 发布:阿里云1m带宽日访问量 编辑:程序博客网 时间:2024/04/27 15:40

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

2

6.3 实验二十四:GUI系统

终于写到这本笔记的尾声了,在6.1章和6.2章,笔者所建立的系统都是由几个接口东拼西凑组合而成,那并非“系统建模”的主要意义,而是一个概念而已。在这一章笔者用另一种概念,一种更接近“系统建模”的实例。同时间我们也探讨“接口”对“系统建模”的重要性。

 

 

这一章我们要讨论的就是简易的“GUI系统”,GUI - 顾名思义就是“图形接口”(想知道更详细的就查维基百科吧)。在这一实验,虽然笔者不是建立非常牛的“GUI系统”,但笔者焦距的是“GUI系统”建立的基本思路。

 

 

 

 

 

 

clip_image002[9]

上图是“GUI系统”的层次关系。Menu代表主目录,MenuAMenuB  MenuC 代表子目录。然而每一个目录的图像都有代表的意义:

 

 

 

 

 

 

clip_image004

向右的流水灯效果

 

 

 

 

 

 

clip_image006

延迟400ms

 

 

 

clip_image008

向左的流水灯效果

 

 

 

 

 

 

clip_image010

延迟200ms

 

 

 

clip_image012

闪耀效果

 

 

 

 

 

 

clip_image014

延迟100ms

 

 

当然,每一副图像的“箭头”也不是花瓶,图像中“箭头”表示了“目录与子目录之间切换的关系”和“同目录中不同选项切换的关系”。引一个例子来讲,“向右流水灯效果”的图像可以向右切入“延迟400ms”,然而“延迟400ms”可以向下切入“延迟200ms”。

 

 

 

GUI统”主要的功能如下:

 

 

 

在主目录 Menu 3个选项,亦即“向右的流水灯效果”,“向左的流水灯效果”和“闪耀效果”。然而每一个Menu的选项,还包含各自的子目录,每一个子目录都有3个选项,亦即“延迟400ms”,“延迟200ms”,“延迟100ms”(延迟的意义上就是效果的延迟时间)。

 

 

 

很简单吧?但是好戏在后头。

 

GUI系统”有一个经典的难题就是“目录指针”。当我们从什么目录,什么选项切换到什么目录,什么选项,该“目录指针”都要一一追踪。想到“指针”读者一定会联想到C语言的“变量指针”,“函数指针”和“结构体指针”等。

 

 

在前面笔者就强调过,Verilog HDL语言是硬件描述语言,而不是高级语言,它没有“代码的结构和特性”。但是Verilog HDL语言有一个很强大的东西,就是“位操作”,我们只要稍微的下功夫一番,就会完成“目录Flag”。

 

 

 

我们先假设 Menu有“三个选项”,我们可以这样作:

 

reg [2:0]Menu;  // 建立一个寄存器表示该目录Flag

 

 

Menu[2] = 选项AFlag  // 向右的流水灯效果的选项

 

 

Menu[1] = 选线BFlag  // 向左的流水灯效果的选项

 

 

Menu[0] = 选线CFlag  // 闪耀效果的选项

 

 

 

 

 

假设,默认的选项是“向右的流水灯效果”,那么Menu寄存器经初始化过后的赋值是

3'b100。再假设,我从当前的“向右的流水灯效果”向下切换至“向左的流水灯效果”

Menu寄存器的值表示 2'b010;

 

 

 

同样的道理,我们可以为每一个Menu选线的子目录创建一个子“目录Flag”:

 

 

reg [2:0]MenuA;  // MenuA 的目录Flag

 

 

reg [2:0]MenuB;  // MenuB 的目录Flag

reg [2:0]MenuC;  // MenuC 的目录Flag

 

 

 

 

 

MenuA[2] = 选项AFlag  // 向右流水灯效果的“400ms延迟”选项

 

 

MenuA[1] = 选线BFlag  // 向右流水灯效果的“200ms延迟”选项

MenuA[0] = 选线CFlag  // 向右流水灯效果的“100ms延迟”选项

MenuB[2] = 选项AFlag  // 向左流水灯效果的“400ms延迟”选项

 

 

MenuB[1] = 选线BFlag  // 向左流水灯效果的“200ms延迟”选项

 

 

MenuB[0] = 选线CFlag  // 向左流水灯效果的“100ms延迟”选项

 

 

MenuC[2] = 选项AFlag  // 闪耀效果的“400ms延迟”选项

 

 

MenuC[1] = 选线BFlag  // 闪耀效果的“200ms延迟”选项

MenuC[0] = 选线CFlag  // 闪耀效果的“100ms延迟”选项

 

 

为了更好的表达所有“目录Flag”的关系,我们可以建立一个信号 Menu_Sig 将所有“目录Flag”整合起来,成为“目录路径”:

 

 

 

 

output [11:0]Menu_Sig;

 

 

 

assign Menu_Sig = { Menu, MenuA, MenuB, MenuC };

Menu_Sig[11..0]

{ Menu, MenuA, MenuB, MenuC }

选项

12'b100_000_000_000

向右的流水灯效果的选项

12'b010_000_000_000

向左的流水灯效果的选项

12'b001_000_000_000

闪耀效果的选项

12'b100_100_000_000

向右的流水灯效果“延迟400ms”的选项

12'b100_010_000_000

向右的流水灯效果“延迟200ms”的选项

12'b100_001_000_000

向右的流水灯效果“延迟100ms”的选项

12'b010_000_100_000

向左的流水灯效果“延迟400ms”的选项

12'b010_000_010_000

向左的流水灯效果“延迟200ms”的选项

12'b010_000_001_000

向左的流水灯效果“延迟100ms”的选项

12'b001_000_000_100

闪耀效果“延迟400ms”的选项

12'b001_000_000_010

闪耀效果“延迟200ms”的选项

12'b001_000_000_001

闪耀效果“延迟100ms”的选项

 

 

为了更好的表达“从什么选项切换到什么选项”或者“从什么目录切换到什么子目录”,亦即笔者就建立如上的图表。假设我进入“向右的流水灯效果“延迟400ms”的选项”那么 Menu_Sig 信号的表达会是如此:

 

 

 

 

12'b100_100_000_000

 

 

从中我们知道MenuMenu_Sig[11:9])的A项被设置,我们知道“目录路径”从MenuA项开始开始。然后从中我们又知道MenuA ( Menu_Sig[8:6] ) A项被设置,那么我们可以这样结论:“目录路径是从MenuA项开始,然后到MenuAA项结束”。故,从“向右的流水灯效果的选项”切入“向右的流水灯效果“延迟400ms”的选项”。

 

这样的设计有一个好处,就是“方便理解”。

 

 

===================================================================

 

 

 

讨论完了GUI系统的目录结构,接下来我们要讨论的问题就是“可配置”。“GUI系统”主要是由“上下左右”四个信号来配置。

 

 

 

 

Config_Sig[4..0]

分配

功能

Config_Sig[4]

Enter (保留)

Config_Sig[3]

Config_Sig[2]

Config_Sig[1]

Config_Sig[0]

 

 

虽说Config_Sig有五位,但是GUI系统的目录切换真正被使用到的仅是Config_Sig[3..0]

 

Config_Sig[4] 被保留作为其他用途。 
5.8章一样 Config_Sig 中的每一位都对“高脉冲敏感”。

 

 

在这里笔者假设一个例子:“GUI系统”经初始化过后“向右流水灯效果”是默认选项。这时候笔者只有两个切换的选择:

 

 

 

 

(一)Config_Sig[2] 接收一个高脉冲,从“向右流水灯效果”选项,向下切换至“向

      左流水灯效果”选项。

 

 

 

(二)Config_Sig[0] 接收一个高脉冲,就会切入“向右流水灯效果”的子目录选项。

 

 

 

笔者再假设一个情况,如果“向右流水灯效果”的“400ms延迟”作为开始选项,那么:

 

 

 

(一)Config_Sig[2] 接收一个高脉冲,从“向右流水灯效果”的“400ms延迟”选

         项,向下切换至“向右流水灯效果”的“200ms延迟”选项。

 

 

(二)Config_Sig[1] 接收一个高脉冲,从“向右流水灯效果”的“400ms延迟”选

      项(子目录)退回从“向右流水灯效果”选项(目录)。

 

 

至于目录从哪里来又切换至那里去,读者就浏览“GUI系统的目录”吧。

 

 

 

menu_module.v

clip_image016

 

 

关于menu_module.v到底要它属于“控制模块”还是“功能模块”,笔者也曾经纠结过。但是笔者还是给它定位“功能模块”,实际上这个模块的功能也很单纯,就是根据Config_Sig 信号的配置如何,就产生怎样 Menu_Sig

 

 

 

 

menu_module.v 主要的功能就是跟踪“目录路径”而已。也就是说“GUI系统”的“目录路径”会因为Config_Sig 信号而产生变化,然而这个模块只是跟踪,然后更改 Menu_Sig。具体的功能还是直接看代码比较强。

 

 

 

 

 

clip_image018

12~16行是核心功能中所使用的寄存器。Menu是主目录Flag的寄存器,MenuAMenu A项的子目录Flag寄存器,其他的 MenuB  MenuC 都是大同小异。在这里有一点必须注意,在“GUI系统”初始化的时候MenuA项作为默认选项,所以在21~25Menu寄存器的值初始化为 3'b100

 

 

30~50行就是主目录Menu,根据Config_Sig信号产生的结果。初头会进入步骤0,亦即主目录MenuA项,在35行是向下切换的动作(Menu寄存器赋值为3'b010,进入步骤1),36行是切入子目录的动作(Menu寄存器清理,MenuA寄存器赋值3'b100,进入步骤3),亦即进入子目录后,子目录的A项作为默认。步骤12分别是 MenuB项和C项。主目录Menu项与项之间的切换都根据“GUI系统”的“目录结构”。 
步骤140行),如果41行成立的话,就会切换回MenuA项(Menu寄存器赋值为3'b100, 返回步骤0)。如果42行成立,就会切换到MenuC项(Menu寄存器赋值为,3'b001,进入步骤2)。如果43行成立,就会切入MenuB项的子目录(MenuB寄存赋值为3'b100, 进入步骤6)亦即进入子目录后,子目录的A项作为默认。

 

 

步骤248行),如果49行成立的话,就会切换回MenuB项(Menu寄存器赋值为3'b010)。如果50行成立的话,就会切入MenuC项的子目录(MenuC寄存器赋值为3'b100,进入步骤九)亦即进入子目录后,子目录的A项作为默认。

 

 

 

 

clip_image019

 

 

36行如果if条件成立的话,Menu寄存器会保存父目录的Flag,然后MenuA寄存器会设置该A项的Flag。换句话说从MenuA项切入的话,就会进入MenuA项的子目录MenuAA项(MenuAA项作为进入该目录后的默认选项)。亦即进入步骤3。步骤3~556~72行)是目录MenuA的选项。

 

 

 

步骤356行)就是MenuAA项。如果58行成立就会切换至MenuAB项(MenuA寄存器会赋值与3'b010,会进入步骤4),如果57行成立就会切出至父目录Menu,然而根据Menu的跟踪,会返回MenuA项(MenuA寄存器清理,会返回步骤0)。

 

 

 

步骤462行)是MenuAB项,如果63行成立会切出至父目录(MenuA寄存器清零,返回步骤0),亦即MenuA项。如果64行成立,就会切回MenuAA项(MenuA寄存器赋值为3'b100, 返回步骤3)。如果65行成立,会切换MenuAC项(MenuA寄存器赋值为3'b001,进入步骤5)。

 

 

 

步骤570行)是MenuAC项,如果71行成立的话就会切回MenuAB项(MenuA寄存器赋值为3'b010, 返回步骤4)。如果72行成立的话就会切出至父目录(MenuA寄存器清零,返回步骤0

 

clip_image020

 

 

MenuB项的子目录MenuB77~92行)和MenuC项的子目录MenuC97~112行),与MenuA项的子目录MenuA56~72行)的操作都是大同小异,笔者就不多罗嗦了(再这样写下去,笔者会患上焦急症候群 ... (o))。

 

 

 

120行是Menu_Sig 的输,该信号是由 Menu寄存器,MenuA寄存器,MenuB寄存器和MenuC寄存,按顺序结合驱动。

 

 

 

在这里我们可以证实一点,menu_module.v 的工作是依据Config_Sig信号对目录结构的影响来跟踪“目录路径”。然而这个“目录路径”的可视信号便是 Menu_Sig信号。

 

 

 

 

clip_image002[12]

 

 

 

上图是“GUI系统”的全图形(不要被吓到),笔者会慢慢解释的。 

 

 

 

目录模块”就如前面说所那样,它是跟踪“GUI系统”的“目录路径”,该模块只需要Config_Sig[3..0],然而随着Config_Sig[3..0]的配置,输出信号Menu_Sig,也会随着更改。

 

 

然后 Menu_Sig 信号分别驱动“页控制模块”和“LED控制模块”,我们先看左半部分:

 

 

 

 

clip_image004

 

 

 

上面的“图形”和实验二十演示(LCD接口演示实验)非常相似吧。ROM模块所拥有的空间是 8 Bits x 6144 Words,亦即这个ROM模块储存了 6 x 8 Bits x 1024 Words,也就说它包含了6 8 Bits x 1024 Words 的图像信息。

地址0~1023 是“向右流水灯效果”的图像信息。

地址1024~2047 是“向左流水灯效果”的图像信息。

地址2048~3071 是“闪耀效果”的图像信息。

地址3072~4095 是“400ms延迟”的图像信息。

地址4096~5119 是“200ms延迟”的图像信息。

地址5120~6143 是“100ms延迟”的图像信息。

 

 

页控制模块”的主要功能就是根据Menu_Sig 信号,从ROM模块读取不同的图像信息写入液晶接口。

 

 

 

假设 Menu_Sig  12'b100_000_000_000。那么,地址0~1023 向右流水灯效果”的图像信息会被写入LCD接口。

 

page_control_module.v

 

 

 

clip_image006

 

 

 

page_control_module.v 的输入输出接口。

 

 

 

clip_image007

 

 

 

16~32行这一行代码和detect_module.v 很相识,但是我们不是要检测电平的变化,而是要检测“Menu_Sig”的变化。当Menu_Sig 产生变化的时候 上一个时间的Menu_Sig  下一个时间的Menu_Sig 的值是不一样,然而F1寄存器暂存下一个时间的Menu_Sig F2寄存器则暂存 上一个时间的 Menu_Sig

 

 

 

当我们要检测 Menu_Sig 是否发生变化的时候,可以这样表达:

 

 

if( F1 != F2 )  // Menu_Sig 发生变化

 

 

 

......     // 执行语句

 

 

 

else         // Menu_Sig 没有发生变化

 

......     // 执行语句

 

clip_image008

 

 

在“液晶接口实验演示”中,我们知道 Z 寄存器是用来“表达图像的切换”。在42~57行是根据不同“Menu_Sig”的结果,切换不同的图像。也就是说 “不同的 Menu_Sig 值,都有不同 Z 值”。

 

Z

图像信息

Z

图像信息

0

“向右流水灯效果”图像信息

3

“延迟400ms”图像信息

1

“向左流水灯效果”图像信息

4

“延迟200ms”图像信息

2

“闪耀效果”图像信息

5

“延迟100ms”图像信息

 

 

 

 40行表示了“Menu_Sig产生变化,就根据Menu_Sig的值,更新Z寄存器的值”。

 

 

 

clip_image010

 

 

61~87行就是该控制模块的功能。ROM模块的空间是 0 ~ 6143 , 所以驱动用的 rAddr 寄存器的位宽是 13 位(62行)。X寄存是用来计数列填充(63行),Y寄存器是行计数(64行)。

 

 

 

初始化的时候步骤i被初始化为 170行),目的是为液晶资源写入“默认选项的图像信息”,初始化的时候由于 Z值是0, 所以“向右流水灯效果”的图像信息作为默认的角色。

 

 

 

79行的步骤0, 是用来检测“Menu_Sig是否发生变化”,Menu_Sig 发生变化步骤i就递增以示下一个步骤(80行)。

 

 

 

82行步骤1是绘图操作,该85行中的 rAddr <=  X + Y << 7 + Z << 10 )表达式,是图像信息寻址的表达式,笔者就不重复了,如果笔者有不明白的地方请复习5.7章的实验二十演示。

 

 

 

 

clip_image011

 

 

91~94行是该控制模块的输出驱动。

 

 

 

 

led_control_module.v

 

 

 

clip_image013

 

 

 

左图是LED控制模块的图形,该控制模块会根据不同的Menu_Sig 产生不同的LED效果。然而该控制模块不会像 page_control_module.v 那样,在Menu_Sig产生变化的瞬间,输出也会产生变化。每当Menu_Sig 产生变化,如果Config_Sig[4] 没有接收一个高脉冲,是LED的输出效果是不会更新的。Config_Sig[4]在位分配的意义上正是“Enter”的效果。

 

 

说简单点,如果“Enter”没有被执行,LED的效果也没有更新。

 

 

 

 

 

clip_image015

 

 

 

 

 

 

151ms的常量定义。在19~29行是1ms的定时器。33~41行是1ms的计数器。

45~58行是用来暂存上一个时间的Menu_Sig 和下一个时间的Menu_Sig,和page_control_module 16~31行是同样的道理。 

 

 

 

clip_image016

 

 

62行的 Mode寄存器是用来暂存 LED的效果值。3'b100 表示向右流水灯效果,

 

3'b010 表示向左流水灯效果,3'b001表示闪耀效果。

 

 

63行的Delay寄存器是用来暂存延迟的值。

 

 

 

70行,如果if条件成立(Menu_Sig产生变化),在72~87 Mode的寄存器和Delay寄存器的值,会根据Menu_Sig 不同的值都会产生变化。

 

 

 

举个例子 12'b001_000_000_001 表示了“闪耀效果”的“延迟100ms”的选项。那么Mode的值是3'b001  Delay的值是 100ms

 

 

 

 

clip_image017

 

 

Mode 寄存器和 Delay寄存器只是“用来暂存某值”而已。真正被用到的寄存器是 LED_Mode  rTimes。在初始化的情况下 LED_Mode 的初值是 3'b100,亦即“向右流水灯效果”,rTimes 的初值是 400。如果Enter键被按下(Config_Sig[4]接收一个高脉冲)LED_Mode 赋值与 Mode值,rTimes赋值与 Delay值。

 

 

 

 

clip_image018

 

 

108~132行是该控制模块的主要功能。在118行,会根据LED_Mode 的值产生不一样的效果。会根据不同的 rTimes值产生不一样的延迟。

 

 

 

 

gui_system.v

 

 

 

clip_image020

 

 

 

 

 

 

clip_image021

 

 

 

clip_image022

 

 

该组合模块和“图形”是大同小异,自己看着办吧。

 

 

 

 

实验二十四说明:

 

 

 

整个“GUI系统”就是 menu_module.v , page_control_module.v  lcd_interface.v 。该“GUI系统”在“显示”方面的设计比较简单,就是“一个事件一副图像”。此外“什么事件,产生什么效果”,这就是不主要了。

 

 

完成后的扩展图:

 

 

 

 

clip_image024

 

 

实验二十四结论:

 

 

看吧!这一章的实验再也不是由几个接口东平西凑成为一个系统,而是“单个系统在全部设计中占一个重要部分而已”。虽然实验二十四充其量是一个简易的“GUI系统”而已,但是这个实验中所要传达的消息也是非常的明显,就是:

 

 

 

 

接口在系统建模中扮演的角色”此外还有“系统不同的概念”。 

 

 

 

总结:

 

 

 

笔记终于写到这里,从第一章开始到第五章,所有的实验,所有的内容都是在为第六章作基础。

 

 

 

什么是系统”这个问题其实笔者也是考过许多,但是“系统”这东西涉及的东西实在是太多了,由此笔者又延伸几个问题出来“什么是系统建模?”,“系统建模应该作什么?”实验二十二和二十三就是用来回答“什么是系统建模”,实验二十四则是用来回答“系统建模应该做什么”。

 

 

 

系统建模”比起“基础建模”或者“封装(接口建模)”不是同一个等次的东西。因为“系统建模”的建模量不是一般的多,而是非常多。如果没有建模技巧,要完成“系统建模”是一件苦差事。

 

 

所以呀:

 

 

 

 

系统建模”作为“低级建模”结束的一页,是再适合不过了。就如笔者在前面说所的,“前期的建模是为后期的建模作准备”。显然“系统建模”不可能是后期建模的最后一站,在“系统建模”的后面还有更后期的建模。但是那是什么,笔者也不知道甚么...

 

 

 

笔者只知道一个事实“当读者有本事走到这里,完成·明白什么是系统建模,读者就已经了解什么是低级建模”。在笔者的眼里“系统建模”是“低级建模”的综合练习,因为要清楚的表达“系统建模的结构”,读者必须掌握好“低级建模”的所有基础。无论是“代码风格”,“模块性质”,“建模结构”等,少了一样也不行。

 

 

 

读者呀:

 

 

 

是不是更上一层的明白“低级建模”的基本概念呢?一个大东西是需要许多的小东西不停的组成和不停的组合。在组合的过程要相互尊重(明白模块之间的性质),相互协调(不同性质的模块之间的调用),相互支持(一层接一层的组合)... 

 

 

 

结束语

 

 

 

    终于把这本笔记编辑完毕了,编辑笔记的过程真是辛酸但是又是真实。编写这本笔记的锄头笔者是重新从零开始的。说实话,笔者在编辑这本笔记之前水平很低,但是当笔者掌握了“建模技巧”之后,跳跃式的进步。读者们相不相信,就见仁见智。

话说3个月的时间说长不长说短不短,悄悄好是四份之一年,但是这一段时间对于笔者来说是绝对真实而且值得的。当这本笔记完成之际,笔者仿佛又多了解了 Verilog HDL+ FPGA的世界。Verilog HDL + FPGA的世界是深不可测,如果以笔者的话来说,笔者也仅是了解到冰山一角而已。但是这一步的踏出,笔者发现了新大陆。

 

 

 

 

 

好了,笔者不再罗嗦了。笔者真心的希望读者们可以借与这本笔记重新去认识 Verilog HDL + FPGA 的世界。Verilog HDL + FPGA 的世界一点也不可怕,而且多姿多彩,只是我们在学习的路上,忽然间迷失而已,只要重新思考,重新出发,就会发现这个世界的不同。

 

 

 

 

 

可能读者们产生这样的问题:“下一站的学习旅程,我应该选择哪里?”。笔者不能断定什么,但是笔者建议一下的几个选择:

 

 

 

 

 

一、了解功能仿真和验证(你会了解系统级的硬件描述语言)。

 

 

二、了解时序分析(你会了解寄存器级的世界)。

 

 

三、了解NIOS II(你会了解软核)。

 

 

四、继续走 Verilog HDL 的道路。

 

 

 

 

 

笔者的选择是第四点,因为笔者从这本笔记了解到 Verilog HDL 语言是很强大。笔者想更了解它 ......

 

 

 

 

 

最后一点就是笔者的不请之求,笔者希望这本笔记可以帮助更多人。读者们如果有顺手之力,就把它转发到每一个学习的角落。

原创粉丝点击