本次的三道题目主要考插入了组合逻辑中的乘法、moore状态机和状态机。
分享一下自己的做法,欢迎大家批评指正。
向量乘法
本题的要求是将ver_a
和ver_b
两个32位向量数进行逐位相乘,再把每一位的结果加起来作为输出。
可以把这道题分为两个部分:
- 逐位相乘
- 相加
首先,对于逐位相乘。1位二进制数的乘法和与操作等价,故可以把ver_a
和ver_b
进行与操作,这样便捷地实现了逐位相乘的效果。
其次,对于诸位相加,我采用的方法是使用for
循环实现,在verilog
中for
循环只能在always
块(或initial
块)中使用。而本题是一道组合逻辑的题目,故采用always @(*)
,实现组合逻辑。
由于位数不是特别多,本题的另一种方法是
Ctrl cv
大法,万一以后有更大规模的数据,全面准备吧()
代码框架为:
1 | assign ver=ver_a&ver_b; |
筛子涂色问题
这道题是一道moore
型状态机问题,根据当前状态的输入(涂色),进行状态转移(涂色后目前的颜色是什么)。但是需要注意的是,尽管是moore
型状态机,但是错误信息需要在当前周期就进行输出。
我的思路是这样的,一共四个状态:初始状态S0、红S1、蓝S2、绿S3。再使用一个寄存器保存了当前颜色出现了多少次。
如果红和绿相遇了,或者当前的颜色出现到第三次,按照题意,清除上一次的输入,故只需要将输出置1,并不需要进行状态转移。
展示一个状态的伪代码:
1 | //... |
个人觉得,相比更面向硬件的logisim
,verilog
很好的一点就是状态转移的一大部分用一个else
就可以。
随着见到了越来越多的检查型的状态机题目,不少是一个不符合就需要扔到初始状态或者垃圾桶状态,logisim
中的线路连接很烦,但是verilog
只需要一个else
异步复位问题
另外这一题值得一说的就是异步复位问题,在之前的课下中,我们所见过的复位都是高电平有效,而这道题目的异步复位信号是低电平有效。
先来看下这段代码:
1 | always @(rst_n) begin |
这段代码是不能实现异步复位操作的,为何:
always
模块是响应信号变化,如果rst_n
一直是低电平,则只有最初的由高电平转为低电平的那个下降沿会被always
响应。如果后续一直是低电平,则无法响应- 如果想要实现操作,则应该在下面一个
always
块中加上if(rst_n==0)
,但是这样此拆分成两个always块就显得有些多次一举了
- 如果想要实现操作,则应该在下面一个
- 这样写还有一个规范问题,那就是reset会无可避免地修改状态量,在两个always模块中都对变量进行赋值,这是不符合规范的,应该避免
正确的异步复位操作可以是这样的:
1 | always @(posedge clk or negedge rst_n) begin |
如果rst_n一直是低电平,则会在时钟沿被识别进行同步复位。
如果rst_n是由高电平变为低电平,则必然有一个下降沿过程,可以用negedge进行识别信号
数字上升下降匹配
这道题有两种做法,一种是设计具有6个状态的状态机,在读入时进行状态转移和匹配;另一种的做法更为普遍,是识别数据的上升下降,如果匹配了上升下降+数量匹配,则输出。
状态机
输入只有1-5,故可以设计一个6状态状态机,记录当前序列到哪里了。
如果连续输入的数是奇数个(分析见下),则顺利转移到下一个状态,否则报错。
这里状态机的书写并不难,就略去状态转移图和代码了。
上升下降匹配
这是一种更为普遍的做法。本体的数据输入只有1-5,还可以用状态机解决,但是如果大胆一点,把输入扩展到1-100,手写101种状态的状态机显然是不太显示,这时候这种方法依然能解决,具有较强的适应性。
分析题目:
- 对于数据,上升下降必须是相邻的,n的下一个输出只能是n-1或n+1,并且上升下降性质必须不变
- 同一个数字在上升下降中分别出现奇数次
- 这道题需要解决的就是
12233221
并不匹配,因为两个2完成了“贪婪匹配”,3并不包在序列中,像一个俄罗斯方块一样,只要同一个数字相邻出现偶数次就相消了:12223321
和123321
相比,如果是正确的数字,重复的数字必须在相邻处完成匹配。 - 实际上这个问题相当好解决,只需要检查一侧的正确性,如果一个数,重复出现了奇数次,相当于只出现了一次;重复出现了偶数次,则相当于没出现。
- 用一个寄存器存下当前数字相邻出现了几次,在下一个更大数/更小数来临时,则可以判断是否是正确的序列
- 这道题需要解决的就是
给出部分伪代码:
1 | if(IsDown) begin |
同学你好,看了你关于异步复位部分的讲解,我有很多收获。之前我对于异步复位的理解仅限于代码实现方式上(只会写不会解释),通过这篇详细的总结,我完全理解了异步复位的各种细节。
但是,我还有一个问题,这样的实现方式对下降沿(上升沿)敏感,但要是rst_n一开始就是0这种情况如何正确对状态置初值呢?或者说如果rst_n一直保持0,如何正确实现清零操作?