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

声明:本文转载于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 语言是很强大。笔者想更了解它 ......

 

 

 

 

 

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值