低级建模有讲求资源的分配,目的是使用“图形”来提高建模的解读性。
图上是低级建模最基本的建模框图,估计大家在实验一和实验二已经眼熟过。功能模块(低级功能模块)是一个水平的长方形,而控制模块(低级控制模块)是矩形。组合模块,可以是任意的形状(随意正方形)。注意功能模块和控制模块都包含“模块名”和“.v文件名”,相反组合模块只含“.v文件名”。
每一个“低级建模资源"的任务如自身命名一样。功能模块的例子有“flash_module.v”,控制模块的例子有“run_module.v”,和 “mix_module.v”是组合模块。
一个完整的建模都是由图上的基本资源“组合再组合”。当然低级建模有一个问题,就是建模量很大,但是这个问题是见仁见智,因为不同的人都有不同的组合习惯。除此之外,低级建模有一个必须遵守的准则就是“一个功能模块(控制模块)仅有一个功能”
。为什么这个准则那么重要呢?
因为低级建模是利用“图形”来表达一个完整的模块。其中,模块与模块之间是以“信号”来表达关系。
假设一个例子:一个老板对一个员工命令。老板(控制模块),和员工(功能模块)。如果从另一种角度去思考 ,“老板发号”,“员工干活”。“连线”表示了他们是“宾主关系”。
又或者另一个例子:一个老员工对一个新手员工发号。在现实中,老员工是来打工的(功能模块),新手也是来打工的(功能模块)。但是老员工有时也需要新手来支持。如上框图,那么从“连接”上推断,结果即可“一目了然”,亦即“老手新手关系”
上面两个例子,表示了我非常提倡“一个功能模块(控制模块)仅有一个功能”。因为如此,会大大的提升解读性,初次之外也使得建模更容易更多样化。
单单两个例子是无法说明白“低级建模”的好处,我们以一个简单实验,从设计中理解。
图上是一个简单的按键消抖模块。设计的方法主要是由“电平检查模块”和“10ms延迟模块”组合合成。
设计的思路如下:
1)一但检测到按键资源按下(高电平到低电平变化),“电平检查模块” 就会拉高
H2L_Sig电平,然后拉低。
2)“10ms延迟模块”,检测到H2L_Sig 高电平,就会利用10ms过滤H2L_Sig,拉高
输出
3)当按键被释放“电平检测模块”,会拉高L2H_Sig 电平,然后拉低。
4)“10ms延迟模块”,检查到L2H_Sig就会利用10ms过滤H2L_Sig,然后拉低输出。
detect_module.v 是电平检测的功能模块。14行定义了100us的常量,而第18~30行是用于延迟100us。因为电平检测模块是非常敏感,在复位的一瞬间,电平容易处于不稳定的状态。isEn = 1 寄存器是表示100us的延迟已经完成(28行)。
第34~37行,声明了四个寄存器。H2L_F1,H2L_F2,是针对检测电平由高变低。相反的L2H_F1,L2H_F2,则是针对检测电平由低变高。在41~46行,对各个寄存器初始化了,由于H2L_FX是为了检测由高变低的电平,所以初始化位逻辑1。L2H_FX 是为了检测由低变高的电平,初值被设置为逻辑0。
//初始化
H2L_F1 <= 1'b1;
H2L_F2 <= 1'b1;
//每一个时间的操作
H2L_F1 <= Pin_In;
H2L_F2 <= H2L_F1;
//每一个时间的布尔运算输出
Pin_Out = H2L_F2 & !H2L_F1
上面代码是用来检测电平由高变低。H2L_F1和H2L_F2的初值都是逻辑1。假设第一个时间Pin_In 为低电平,H2L_F1就会被赋值位逻辑0,而H2L_F1则是被赋值为H2L_F1上一个时间的值(也就是H2L_F1的初值)。
我们知道在第一个时间,H2L_F1为逻辑0, H2L_F2位逻辑1。由于对H2L_F1的取反操作,H2L_F1与H2L_F2“求与”运算,所以这个表达式的输出是逻辑1。
再假设第二个时间Pin_In保持为低电平,H2L_F1同样会被赋值为逻辑0, 而H2L_F2则是被赋值为H2L_F1上一个时间的值,亦即逻辑0 (第一个时间的值)。再经过布尔表达式的运算, 在第二个时间,H2L_F1 是逻辑0, H2L_F2是逻辑0, 所以输出的结果是逻辑0.
时间 | H2L_F1 | H2L_F2 | Pin_Out = H2L_F2 ( !H2L_F1 ) |
Initial | 1 | 1 | 0 |
T1 | 0 | 1 | 1 |
T2 | 0 | 0 | 0 |
第48~53行,正是执行如上的操作,无论是检测电平由高变低或者由低变高,思路基本都是一样。而最后的58~59行,是关于“电平检测”的布尔表达式。但是有一点不同的是,Pin_Out的输出,是发生在100us之后,因为100us之前被 isEn寄存器所限制(原因之前已经说了)。换一句话说,电平检测的有效是100us的延迟之后。
delay_module.v 是10ms延迟的功能模块。模块采用“仿顺序操作”的编写方式(第四章)。第16~42行是“延迟”的写法,第18~28行是1ms的定时器,而第32~40行是计数器。无论是定时器或者计数器都是由 isCount 标志寄存器使能。
第44~71行是“仿顺序操作”(第四章),第46行 i 寄存器用来控制执行的步骤。开始的时候i会根据“H2L_Sig”或者“L2H_Sig”进入不同的步骤(58~60行)。从62~64,或者66~68行,都是延迟10ms的操作,不同的地方就只有 rPin_Out 寄存器的赋值(63行与67行)。
我们先简单的了解一下地44~71行的设计思路:
1)如果H2L_Sig 信号有反应,就进入步骤1,
2)在进入步骤1之后,由于达不到if条件, isCount使能。定时器,计数器呀开始执行。
3)在经过10ms之后,isCount不使能,定时器,计数器停止执行。rPin_Out为逻辑1
。返回步骤0。
又或者:
1)如果L2H_Sig 信号有反应,就进入步骤2,
2)在进入步骤2之后,由于达不到if条件, isCount使能。定时器,计数器呀开始执行。
3)在经过10ms之后,isCount不使能,定时器,计数器停止执行。rPin_Out为逻辑0
。返回步骤0。
Debounce_module.v 是组合模块。模块的组合上上图。信号连线的地方已经很清楚的注释。
完成框图:
实验三是“功能模块对功能模块”的组合建模。建模方法遵守了“一个模块一个功能”的准则,在加上“图形”和“连线”的描述,以一种最直接的方式,很好的提升“完成模块”的“解读性”和设计的“可能性”。
当然“低级建模”的好处不止在这里而已,随着建模的工程度增加,就会越发的凸显“低级建模”的优势。
黑金版的FPGA型号:CYCLONE II EP2C8Q208C8
实验三所包含的.v文件。
编译成功后的层次关系。
实验三所使用的按键资源是KEY1。
扩展板 | 核心板 |
核心板和扩展板的链接关系。实验三使用的扩展板资源是KEY1, 亦即PIN3。
实验3使用的LED资源是LED1,亦即PIN69。
引脚配置如上。关于CLK和RSTn的引脚配置就不再重复了。
完成后扩展图。
实验四和实验三的区别,就是输出。实验三的debounce_modulve.v 当检测到由高变低的电平变化时就拉高输出,然而当检查到由低变高的电平变化时拉低输出。实验四的debounce_module.v 当检测到由高变低的电平变化时,拉高输出一个时钟的时间,然后拉低输出。当检测到由低变高的电平变化时,只有消抖动作,输出没有任何影响。
大部分的源码和实验三一样,不同的地方就在于delay_module.v的“仿顺序操作”代码段。从上面的代码可以看到,当检测到由高变低的电平变化的时候(第59行),步骤i就进入,步骤1。经延迟10ms过后,拉高输出,然后进入步骤2。步骤2会拉高拉低电平,然后回到步骤0。同样的,在步骤0当检测到由低变高的电平变化是,就会进入步骤3,经延迟10ms过后,就会返回步骤0。
文章评论(0条评论)
登录后参与讨论