赞
踩
前阵子去牛客网刷题,看了一道面试题,题目要求发送数据方连续向接收方发送0~7的数据并给出valid的数据有效信号,接收方接到信号后返回一个ready(我自己定义的信号是finish)的信号,发送方收到ready后才等待五个周期才可以发送下一个数据。并且两个模块的时钟频率不同。
这篇文章是记录我自己的学习过程,希望能帮助到喜欢ic设计的伙伴们,也希望大家能够给我一些建议,毕竟这写代码从头到尾没有过参考,纯自己想自己写的,通过仿真得到想要的结果后,是挺有成就感的,在这里可能我的代码风格会和大家不一样,而且可能会有很多不足,希望过来学习讨论的伙伴们多多包涵。
废话不多说直接进入正题,首先这个题目当时看完我就有了一定的思路,因为这个是异步信号,所以对于ready和valid的采样必须要经过同步器进行同步,否则会产生亚稳态现象。第二就是接收方在受到ready(finish)信号后需要等待5个时钟周期才可以发下一个数据,也就是说这里需要用到一个counter计数器。最后就是因为发送的是BCD码,可能会出现多位同时翻转的情况,异步电路可能会出现采样到中间态的情况,所以我把BCD马先转换成格雷码,然后在发送过去再在接收方转换回BCD码,这样就可以防止数据传送的错误(但是后来发现好像没有转换的必要,因为valid打了两拍以后这个时间中间态早就趋于稳定了,但是敲出来加深一下印象也无妨,所以接下来的code会有这个转换的过程)。
先分开来说说代码的组成部分,首先是同步器的实现:
图中的finish(send模块的输入信号)是从接受方那边发来的完成数据接受的信号,这里做了两级同步器,避免亚稳态的产生。
其次就是计数器的部分:
由于是接收到finish信号或者复位信号以后才开始重置count,然后计数5个周期,而count计数到5了以后新的数据就会发送,这里出现了一个count_time,我把他定义为次数,那count_time信号是怎么被赋值的呢?他应该在复位的时候被赋值为5,当count计数5个周期后产生valid信号,那么问题来了,当valid来了以后计数器就应该停止计数,我应该怎么处理这个count_time?于是最初的我就希望计满5后count_time置0,这样一来就不会继续计数了,但是问题又来了,当finish信号来了以后应该又要重新计数5个周期然后发新的数据,可是这是count_time没有回到5的操作,这样的话实现不了握手信号的协议,于是我希望在finish被采样并且打拍后得到的finish_r1的上升沿来了以后使count_time 重新赋值为5。并且valid在看到finish_r1拉高后也变为0,然后五个周期后,新的数据发送,valid再次拉高。但是后来跑仿真发现如果接收端时钟比较慢的话会出现下面的情况:
也就是count_time 再也没有机会被赋值为5了。valid也再也不会被拉高,数据传输中断。那么既然上升沿不行那我就该用下降沿。
代码如下:
那么这个复位问题就解决了。(其实还是有bug的,主要是因为count_time 在两个always中被赋值了,当clk上升沿和finisi_r1同时到来的时候必然会有亚稳态,这个问题我们下一篇文章在做修改,目前记录的是前面学习的内容和问题的总结)
这些问题解决了,那么最后就是小问题了,先来看看vali的产生:
由于必须要五个cycle之后才发一个数据而且这时候对方应该是没有产生finish_r1的所以把他与上,同时当发送方看到finish_r1拉高的时候说明数据已经完成传输,应该拉低vali并且重置count 然后把新的数据发送出去,那么这个数据是如何更新的呢? 因为是异步的传输他不应该随着clk的信号一直更新,而是跟接收方的信号有关,否则发过去的信号就会被接收方遗漏,从而得不到顺序递增的数据。所以finish_r1在这里又被用到了,代码如下:
用finish_r1的上升沿信号作为数据的翻转时钟沿这样一来,能确保对面接收完成后才改变新数据,
最后剩下的就是B2G了:
到这里代码的功能跟作用和我的思路都讲完了,当然了这个肯定是有很多错误的,至少我自己都发现了不少,虽然跑仿真是能过去的,但语法肯定是有问题的,所以记录一下自己的学习历程。在下一篇文章会总结使用状态机修改以后实现的代码,这样子可以避免在不同always语句里面对count_time的赋值。
Verdi仿真:
(G1是发送方的波形,G2是接收模块的波形)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。