P0-logisim简单部件与有限状态机
阅读本文,您的收获可能有:从课下测试部分,了解一些用logisim画电路实现功能的部分可能出现的bug以及修复方法;从课上测试部分,了解一下课上测试的问题类型以及可能出现的bug,知道怎么准备课上测试。笔者认为本篇文章对您最有帮助的地方可能在于细节上的提醒。更优质的文章请参考吴佬的博客:roife.github.io
。坑点没有专门总结在一起,所以只能麻烦读者通读一遍自行聚集了。课下测试部分:
P0课下测试部分的题目是CRC校检,4-bit ALU,GRF,正则表达式匹配。
CRC校检:
这个题比较简单,在这个题之前有一节讲解搭建CRC校检电路的教程,该教程的意义不只是告诉我们CRC咋搞,它更重要的意义在于:告诉我们设计出符合题目要求电路的基本步骤:找输入输出(有哪些?多少位?)、分析输入与输出的关系(暴力真值表 or 找到计算规律,比如模2除法)、分块搭建(和程序设计里写函数并调用类似),测试(平凡数据、极限数据、非法数据、搭测试电路自动化测试)。注意,电路是解决问题的一种工具,而不是解决问题的方案,通俗一点来说就是给你一把枪并不代表你能打中人。所以,在搭建电路之前,一定要把问题的模型抽象出来,算法想清楚(最好写一下),这样就相当于保证了你掌握了射击技术,这时拿到枪,才有更高的概率打中人。
经验教训:在看CRC前面的教程的时候,以为只能是8位数,所以在模2除法之前没有对8位的原数据帧进行补0操作,导致错误(题目其实已经提示要补成11位了但我只是认为只要结果是11位就好);模2除法开始没有看懂余数的生成规则。
4-bit-ALU:
这个题算是一个水题吧,只让实现四位的加减法,与,或运算,考虑到logisim中有现成的加减法器,所以这个题很容易过去。唯一可能注意的地方,就是模块的appearance,如果不对应的话必定会错得很离谱。
关于ALU,说说logisim自带的比较器吧,这个比较器默认是有符号数比较,所以使用时一定要注意是否需要修改其工作状态。
实现GRF:
(部编版语文教材《童趣》插图)
余忆童稚时,能张目对日,明察秋毫…………不好意思走错片场了。。。
这个题不算难,但却足够繁琐。从入手到AC,我大约用了三个小时。虽然最后是交了第二次就过了(第一次忘了把自己测试用的时钟去掉了,第二次把时钟改回普通输入之后就对了),但是期间是踩了很多的坑。下面把此题的坑点总结一下。
第一,时钟的给予者没有搞对。根据题中给出的测试电路,我们发现测试用的时钟是测试电路给出来的,所以我们只需要用一个普通的输入去接受这个时钟信号就可以了,而不必自己加时钟。
第二,DMX的使用。之前就知道DMX似乎有一个Three state的选项,但是不知道有什么用处,直到今天在某位大佬的评论中才明白:如果Three state勾选为yes,那么DMX输出端没有被选中的路径会保持原来的值不变,这样就实现了本题的重要需求:根据A3的值不仅要将数据写入到相应的寄存器中,同时还保持其它寄存器不变。 有些同学是最开始没想到这一点,所以提交出错,而我是想到了这一点,苦于不知道如何实现这个功能而去评论区找灵感,才发现这个简单方法,并知道了Decoder同样拥有三态选项。多读官方帮助文档,会有很多好处的!
第三,别一用tunnel就兴奋,这玩意儿确实省事儿,但是你不能用着用着忘了自己要干啥,以至于脑子抽筋把32位输入用spliter拆开了,题目中本来就要求32位输出,别忘了任务!
第四,还是模块的appearance,这种比较复杂的,一定要看测试电路怎么设计的,然后在appearance中逐个检查看看接口是不是对应的!!!
正则表达式匹配:
吸取了CRC的经验,先认真读了题,了解了正则表达式的生成规则,以及检验规则:在输入到某一个字符x的时候,发现从这个字符前面的某个字符y开始,一直到x所组成的字符串如果符合要求,就输出1。本题要求同步复位,是一个容易错的地方:一是可能会忘记按同步复位连,二是即使想同步复位但连错了。我属于后者。不知为什么我这个题用到了15个状态,要不是有analyse circuit我就死了
//10月16日补充:确实是我状态想多了,其实我那个本质上是个Moore型状态机,改成Mealy型之后瞬间15->7,然后经过室友的优化,合并了状态,最后只用3个状态就结束了(主要是对b的处理以及Mealy型本身省状态的优势)
经验教训:1.有限状态机搭建关键操作:
只有状态存储电路中有寄存器,也就是说,在搭建状态转移电路时,可以使用analyse circuit,注意先把输入数据全部用spliter分开 (这条纯属废话,只是提醒我这个好久没搭建过状态机的人)
填写真值表的时候,操作规模主要取决于输入的个数,如果输入为n个,输出为m个,则需要操作的次数为m*2^n,所以一般不超过7个输入都可以接受,不要太害怕
状态少可以用独热编码,状态多就必然得用二进制编码或者其他更优秀的编码
看清楚是搭建Moore型还是Mealy型状态机,这两种状态机的输出不同,Mealy型状态机设计输出的时候需要传递寄存器保存的状态以及当前输入
如果在搭建完毕状态转移电路之后发现自己类型搭建错误了,不要着急,这两种类型的状态机的状态转移部分基本是一样的,所以可能不需要修改状态转移电路
有限状态机中的同步复位:一种实现方法:二选一多路选择器法。利用复位信号reset选择是输出算出的状态(即状态转移电路刚算出来的下一次的状态)还是初始置零状态。
看图便于理解:
(某巨佬的图)
下面是我对于该同步复位方法的应用(看中间)
Warning:错误的“同步复位”:首先如果仅仅将CLR与寄存器的reset相连肯定不能实现同步的操作,比如在时钟处于某个电平时,如果CLR置1,那寄存器状态会立马改变,不能按照题目要求的在时钟上升沿才实现复位操作。 其次,如果上面直接连reset行不通,很容易想到直接在CLR后面加一个寄存器,然后同步清除状态寄存器的值,但是这个方法也有问题,虽然在CLR为1时复位寄存器没有问题,但是由于CLR置0之后需要一个上升沿才能到reset,这个上升沿状态寄存器是不能存储状态的,所以后面要延迟了一个周期,所以这种方法也不是同步的,这样的话输出就有可能出错。
2.模块外观的重要性:模块和要求的有一点不一样,就会running time error输出xxxxx。看测试电路是怎么连接的,别输入的顺序连接反了,不然也是running time error
备战P0心情:今天尝试重做斐波那契数列,结果从七点搭到十一点,愣是没有搭出来,心情爆炸,感觉自己会死在P0上,希望周四不要凉凉。学会儿竞赛换换脑子。
课上测试部分:
第一题:题面大概是有一个售货机,你只能往里面不投币,投一元,投两元,投五元,只要大于等于5元就要使得出货信号dispense为1,然后找钱信号1,2,4相应的给出。题目要求异步复位,要求一旦出货之后,就需要让售货机下一个状态无条件变成初始的没有接收到钱的状态,并且忽略刚变回初始状态的时候的投币(应该是这样,题面有点记不清楚了),要求使用moore型状态机实现。
本题有很多同学最开始搭成了mealy型状态机,但是这样似乎是过不了的,不妨举一个例子:假设现在已经投入了4元,即now_state=4,即状态存储电路里面现在存储的是4,我们现在输入1元,那么由于mealy型状态机的输出与输入和当前状态有关系,故输出变为dispense=1,考虑这时候的状态转移电路,是一个state=5与一个1元输入进行运算,得到state=0,即这时候state=0到了存储电路的左边,但目前state里面还是4,等到下一个时钟上升沿来临的时候,我们给一个输入,比如输入为5元,我们希望这个输入被忽略掉,并且输出dispense=0,但是事实上,dispense是state=0与输入1共同决定的,实际上此时却是输出了dispense=1,与题目要求不符,所以mealy型就炸了。下面给出mealy型的图以便于思考这个错误例子。
关于正解,其实就是按照moore型直接搭建即可。状态有0-9这十个,输入有四种,由状态和输入用真值表画出来状态转移电路,然后利用现在的状态列出输出的四个值的真值表(注意理解moore型状态机),状态转移部分记得把reset直接连到寄存器的reset上面(异步复位),自备时钟(看测试电路可以知道需要自备时钟,其实题目中也说了),最后在主电路里把各个模块拼接起来,检查一下模块名字,appearance,就可以了。这个题的提问有问到如何进行单步调试(ctrl T调时钟),如何查看单步调试的时候子电路的状态(右键单击某个子电路,点view xxxx),以及logisim自带加法器对于有符号和无符号运算的区别(感觉最后一个问题好迷啊,我记得那个加法器不能选择有符号无符号的,我就回答了Logisim自带加法器默认为无符号运算……然后我人就没了,助教让我再看看。。。。后来我发现这好像和符号没关系,比如1001+0111,结果是0000,理解成有符号的话,就是进位进没了,理解为无符号,就是0111-0111=0000,跟符号没关系,看来是我理论课太摸了)
第二题:大概的意思是通过理解下图所示的逻辑左移的电路,去设计一个算数右移电路。
(方框中自己脑补DMX)
先讲一下这个图中的移位器怎么实现的。看第一组(最左边两个)spliter,那个错开一位连接是容易理解的,移位嘛。由于是逻辑左移,所以最低位的数据会往高位处移动,以至于低位会没有数据,没有数据我们需要补上0。右边三组spliter类似。通过观察,我们发现这几组分别错开了1,2,4,8位,然后又注意到下面的4位选择信号分别控制四组DMX的输出,我们大概明白了,这个移位是用位的权重来看到底需要移多少位的。假设大家已经脑补了DMX,我们不妨用一组数据试试,比如选择信号为1001,那么,最左边和最右边的DMX都是选择的下路输出,上路呢,根据DMX的特性,我们知道输出是0.由于是从下路输出的,所以这组数据通过第一组spliter发生了1位的位移,之后再和0做按位或运算(就是得到自己本身)。这样就把1位移位传给了下一组spliter。而下一个DMX选择的是上路(因为1001,这一位是0),所以数据没有被移位,就传到了或门,和0进行按位或,得到本身,即相当于1001的第二位0使得未发生移位运算。第三组类似。第四组类似地,由于被选中了下路,所以数据进入spliter发生了8位位移,正好对应了1001最高位1,综上,数据移动了9位(1001)。
明白了上面给出的移位原理,我们就容易搭建移位电路了,不再过多解释。唯一需要解释的一点是,算数右移,意味着我们不是补0,而是补符号位,即负数补1,正数补0。这样的话,我们就不能给spliter接常数0了,而是应该接常数1逻辑与符号位,这样我们就可以补符号位了。另外,我们不用把数据转成补码,直接做即可。虽然本题不让用Logisim自带的shifter,但是你可以先用那个东西试一试,看看算术移位是什么意思,然后再去搭建,就不会有那么多问题了。
如果没看懂上面的讲解,还有一个暴力打表法:搭建一个只能将这个8位数据移动一位的电路,然后在主电路里堆8个,把每一步移位的结果都接到一个8位的MUX上,用选择信号直接选择答案(打表法)。很暴力,但是,助教问答的时候凉了不怪我哦。
第三题:
考察浮点数相关的知识,理论课上有讲过浮点数的表示,当时虽然听课的时候没有摸,也问了老师相关的问题,但是我对于这个题里面的补码=expontent-01111 有点不太理解。这个题我场上没做出来,加时也没用。题面在宿舍里,回去之后再补上题面,并且说说自己的想法。希望大佬们会的能给我讲讲为啥这个补码要这样算(我记得和课上讲的不太一样,虽然我们可以无视这一点,直接拿这个公式去做)。