zoukankan      html  css  js  c++  java
  • 【连载】【FPGA黑金开发板】Verilog HDL那些事儿GUI系统(二十五)(大结局)

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

    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 语言是很强大。笔者想更了解它 ......

    最后一点就是笔者的不请之求,笔者希望这本笔记可以帮助更多人。读者们如果有顺手之力,就把它转发到每一个学习的角落。
  • 相关阅读:
    mysql常用基本命令
    mysql8.0.13下载与安装图文教程
    k8s ingress 增加跨域配置
    Jenkins 备份恢复插件 thinBackup 使用
    k8s HA master 节点宕机修复
    nginx 跨域问题解决
    mongodb 3.4.24 主从复制
    k8s 线上安装 jenkins并结合 jenkinsfile 实现 helm 自动化部署
    k8s helm 运用与自建helm仓库chartmuseum
    centos6 源码安装 unzip
  • 原文地址:https://www.cnblogs.com/kingst/p/1864742.html
Copyright © 2011-2022 走看看