当前位置:   article > 正文

数字IC手撕代码-平头哥技术终面手撕真题

数字ic手撕代码

 前言:

        本专栏旨在记录高频笔面试手撕代码题,以备数字前端秋招,本专栏所有文章提供原理分析、代码及波形,所有代码均经过本人验证。

目录如下:

1.数字IC手撕代码-分频器(任意偶数分频)

2.数字IC手撕代码-分频器(任意奇数分频)

3.数字IC手撕代码-分频器(任意小数分频)

4.数字IC手撕代码-异步复位同步释放

5.数字IC手撕代码-边沿检测(上升沿、下降沿、双边沿)

6.数字IC手撕代码-序列检测(状态机写法)

7.数字IC手撕代码-序列检测(移位寄存器写法)

8.数字IC手撕代码-半加器、全加器

9.数字IC手撕代码-串转并、并转串

10.数字IC手撕代码-数据位宽转换器(宽-窄,窄-宽转换)

11.数字IC手撕代码-有限状态机FSM-饮料机

12.数字IC手撕代码-握手信号(READY-VALID)

13.数字IC手撕代码-流水握手(利用握手解决流水线断流、反压问题)

14.数字IC手撕代码-泰凌微笔试真题

15.数字IC手撕代码-平头哥技术终面手撕真题

16.数字IC手撕代码-兆易创新笔试真题

17.数字IC手撕代码-乐鑫科技笔试真题(4倍频)

18.数字IC手撕代码-双端口RAM(dual-port-RAM)

        ...持续更新

 更多手撕代码题可以前往 数字IC手撕代码--题库


目录

题目描述

原理分析

状态机

状态转移

三段式

代码

testbench

波形


题目描述

平头哥的手撕代码很有意思,平头哥技术终面手撕真题,题目描述如下:

        输入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-饮料机


三段式

下面直接上代码:

状态机三段式写法,第一段:状态转移 

  1. // FSM state
  2. reg [3:0] next_state ;
  3. reg [3:0] current_state ;
  4. //(1) state transfer
  5. always @(posedge clk)begin
  6. if(!rstn)begin
  7. current_state <= IDLE;
  8. end
  9. else begin
  10. current_state <= next_state;
  11. end
  12. end

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

  1. //(2) determine the next state according to the current state
  2. always @(*)begin
  3. next_state = current_state;
  4. case(current_state)
  5. IDLE:
  6. case(data_in)
  7. 1'b0: next_state = RES_0;
  8. 1'b1: next_state = RES_1;
  9. endcase
  10. RES_0:
  11. case(data_in)
  12. 1'b0: next_state = RES_0;
  13. 1'b1: next_state = RES_1;
  14. endcase
  15. RES_1:
  16. case(data_in)
  17. 1'b0: next_state = RES_2;
  18. 1'b1: next_state = RES_0;
  19. endcase
  20. RES_2:
  21. case(data_in)
  22. 1'b0: next_state = RES_1;
  23. 1'b1: next_state = RES_2;
  24. endcase
  25. default: next_state = IDLE;
  26. endcase
  27. end

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


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

  1. //(3)determine the output according to the current state and input
  2. always @(posedge clk)begin
  3. if(!rstn)begin
  4. data_out <= 1'b0;
  5. end
  6. else if((current_state==IDLE && data_in==1'b0)||(current_state==RES_0 && data_in==1'b0)||(current_state==RES_1 && data_in==1'b1))
  7. data_out <= 1'b1;
  8. else
  9. data_out <= 1'b0;
  10. end

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

代码

  1. module T_head#(
  2. parameter IDLE=4'b0001,RES_0=4'b0010,RES_1=4'b0100,RES_2=4'b1000
  3. )(
  4. input clk ,
  5. input rstn ,
  6. input data_in ,
  7. output reg data_out
  8. );
  9. // FSM state
  10. reg [3:0] next_state ;
  11. reg [3:0] current_state ;
  12. //(1) state transfer
  13. always @(posedge clk)begin
  14. if(!rstn)begin
  15. current_state <= IDLE;
  16. end
  17. else begin
  18. current_state <= next_state;
  19. end
  20. end
  21. //(2) determine the next state according to the current state
  22. always @(*)begin
  23. next_state = current_state;
  24. case(current_state)
  25. IDLE:
  26. case(data_in)
  27. 1'b0: next_state = RES_0;
  28. 1'b1: next_state = RES_1;
  29. endcase
  30. RES_0:
  31. case(data_in)
  32. 1'b0: next_state = RES_0;
  33. 1'b1: next_state = RES_1;
  34. endcase
  35. RES_1:
  36. case(data_in)
  37. 1'b0: next_state = RES_2;
  38. 1'b1: next_state = RES_0;
  39. endcase
  40. RES_2:
  41. case(data_in)
  42. 1'b0: next_state = RES_1;
  43. 1'b1: next_state = RES_2;
  44. endcase
  45. default: next_state = IDLE;
  46. endcase
  47. end
  48. //(3)determine the output according to the current state and input
  49. always @(posedge clk)begin
  50. if(!rstn)begin
  51. data_out <= 1'b0;
  52. end
  53. else if((current_state==IDLE && data_in==1'b0)||(current_state==RES_0 && data_in==1'b0)||(current_state==RES_1 && data_in==1'b1))
  54. data_out <= 1'b1;
  55. else
  56. data_out <= 1'b0;
  57. end
  58. endmodule

testbench

  1. module T_head_tb();
  2. reg clk,rstn;
  3. always #5 clk = ~clk;
  4. reg data_in;
  5. wire data_out;
  6. initial begin
  7. clk <= 1'b0;
  8. rstn <= 1'b0;
  9. #15
  10. rstn <= 1'b1;
  11. #20
  12. data_in <= 1'b1;
  13. #10
  14. data_in <= 1'b0;
  15. #20
  16. data_in <= 1'b1;
  17. #50
  18. $stop();
  19. end
  20. T_head u_T_head(
  21. .clk (clk) ,
  22. .rstn (rstn) ,
  23. .data_in (data_in) ,
  24. .data_out (data_out)
  25. );
  26. endmodule

波形

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


 更多手撕代码题可以前往 数字IC手撕代码--题库

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/566937
推荐阅读
相关标签
  

闽ICP备14008679号