数字IC手撕代码-平头哥技术终面手撕真题
平头哥的手撕代码很有意思,平头哥技术终面手撕真题,题目描述如下:
输入clk,每个时钟输入1比特的data_in,然后进来之后的序列是每次左移,也就是如果第一个时钟data_in=1,第二个时钟datain=0,第三个时钟data_in=1;则data_temp=101;然后data_out是在data_temp可以整除3的时候,输出1,其他时刻输出0;
这道题乍一看,好像是什么移位寄存器的题,似乎不难;但实有点难度,涉及到一些数学的推导,下面我们来分析一下。
首先,要声明的一点,这道题只能用状态机写,为什么?
明确一下我们的需求,我们的需求是知道一条序列,然后要求输出data_out。

有两种做法,第一种,存储所有输入的值,然后对3取余,接着输出结果。表面可以,实则不行,因为如果要存储所有的值,在输入数据量已知的情况下,你可能提前预设reg [9999:0] data_temp来存储1w个输入的结果,那如果是100w个输入呢?那100M的时钟跑一秒,1个亿的输入呢?你全存下来,然后对3取余么?显然不行,因此第一种做法是不行的。

第二种做法,考虑所有可能,然后用状态机来进行状态转移,每输入一个数据1,状态机跳转到对应状态,再根据对应状态,选择输出data_out是否为1 。下面来分析一下所有可能的情况,进而再画出状态转移图:
输入数据是1bit的,每周期输入1bit数;
假设当前余数为0时,那么左移(乘以2)后余数是0,那么进来1余1,进来0余0;
假设当前余数为1时,那么左移(乘以2)后余数是2,那么进来1余0,进来0余2;
假设当前余数为2时,那么左移(乘以2)后余数是1,那么进来1余2,进来0余1;
整个题目用有4个状态的状态机就可描述了,3个状态分别表示对3取余后的余数,1个状态为初始状态,输入为data_in,题目要求data_temp可以整除3的时候,data_out拉高,其他时候data_out拉低。
初始状态和余数为0时的状态都是进来1余1,进来0余0,两者的区别就在于:初始状态的data_out为低(其他状态),余数为0时的data_out为高。

画出状态转移图后,这道题就变得很简单了,我们之前专栏手撕过FSM饮料机,这个同理解决即可:数字IC手撕代码-有限状态机FSM-饮料机 - 哔哩哔哩 (bilibili.com)

下面直接上代码:
状态机三段式写法,第一段:状态转移


第二段:根据当前状态和当前输入,决定下一状态

状态转移就按照状态转移图来写就行了,根据当前状态和当前输出,改变下一周期的状态为什么。

第三段:根据当前状态和输入,决定输出结果

输出判断,只有在状态为RES_0也就是余数为0,即对3整除时,data_out才会拉高。

Testbench:


波形:

波形和我们分析和思考的一致,我们在tb中将状态机的所有状态都遍历了,data_out只在current_state为4‘b0010也就是RES_0状态时才拉高,也即满足题目要求:输入一串序列,data_out仅在序列能对3整除时拉高。

ps:由于b站截图显示不全,up将完整代码传至网盘自取,总计代码量60行左右,题目代码量不大,但考虑到技术面临场分析分析及推导问题,总体难度:中等。
代码自取
链接:https://pan.ba删除中文idu.com/s/1GiT删除中文SpQek5OKxF6bomNaB_g
提取码:oyis