IC/FPGA校招笔试题分析(二)任意切换的时钟分频电路

举报
李锐博恩 发表于 2021/07/15 03:22:46 2021/07/15
【摘要】 今天的笔试题是某芸科技的现场笔试题,数字前端的笔试题,要求很简单,就是现场写出代码实现: 任意切换1-8分频,且无论奇分频还是偶分频,占空比均为50%,我至今仍然认为,在那种紧张且时间有限的情况下(本科大约预留15分钟),真的能设计出这种可任意切换的分频电路(之前有所准备的话可以),反正我是没写出来,笔试归来,我花了多个小时的时间写了一个且仿真了下。 个人认为,这个电路的...

今天的笔试题是某芸科技的现场笔试题,数字前端的笔试题,要求很简单,就是现场写出代码实现:

任意切换1-8分频,且无论奇分频还是偶分频,占空比均为50%,我至今仍然认为,在那种紧张且时间有限的情况下(本科大约预留15分钟),真的能设计出这种可任意切换的分频电路(之前有所准备的话可以),反正我是没写出来,笔试归来,我花了多个小时的时间写了一个且仿真了下。

个人认为,这个电路的设计的步骤分为两部分,

第一:

确定整体架构,包括输入输出:如何任意切换?我写了一个分频模块,其中一个输入为div,你输入1到8,可实现1到8的分频。

第二:

确定分频实现电路,分频大家都会写,实现过程可分为奇分频的实现以及偶分频的实现,这两种的实现方法是不一样的,偶分频简单,直接计数即可,奇分频需要三个步骤,第一个步骤就是计数,第二个步骤就是下降沿采样,第三个步骤就是相与或相或,具体实现方法后面再说。


思路简述:

上面说了这个电路的设计分为两部分,第一部分确定整体架构,其中最重要的是输入输出:


  
  1. input clk,
  2. input [3:0] div,
  3. input rst_n,
  4. output clk_out

确定好输入输出之后,我们想,我们是否需要一个使能信号,当输入分频述div后,我们让某一种分频模式使能,我们有8中分频模式,总不能傻傻地设置8个使能变量吧,en1,en2,...,en8;

当然这样很不好看,而且不利于后面程序编写,我的方式是:

	reg [7:0] fre_en;
 

每次只能使能一位,fre_en[i]使能代表i+1分频模式有效。

通过div的值来确认某种模式使能,其实现如下:


  
  1. localparam DIV1 = 1, DIV2 = 2, DIV3 = 3, DIV4 = 4;
  2. localparam DIV5 = 5, DIV6 = 6, DIV7 = 7, DIV8 = 8;
  3. reg [7:0] fre_en;
  4. always@(posedge clk or negedge rst_n) begin
  5. if(~rst_n) begin
  6. fre_en <= 0;
  7. end
  8. else begin
  9. case(div)
  10. DIV1: fre_en <= 8'b0000_0001;
  11. DIV2: fre_en <= 8'b0000_0010;
  12. DIV3: fre_en <= 8'b0000_0100;
  13. DIV4: fre_en <= 8'b0000_1000;
  14. DIV5: fre_en <= 8'b0001_0000;
  15. DIV6: fre_en <= 8'b0010_0000;
  16. DIV7: fre_en <= 8'b0100_0000;
  17. DIV8: fre_en <= 8'b1000_0000;
  18. endcase
  19. end
  20. end

我们说了,分频分为奇分频和偶分频,二者都有计数的过程,但是这里想做出一点改变,因为1分频等于没分频,2分频直接对输出时钟取反即可,其他分频就和计数有关了。

我们写一个计数器,用于其他分频模式,8分频需要计数次数最多,我们设计数器位数为4位,其实3位就够了。

根据模式使能确定计数次数。


  
  1. //计数模块
  2. reg [3:0] fre_cnt;
  3. always @(posedge clk or negedge rst_n) begin
  4. if(~rst_n) begin
  5. fre_cnt <= 0;
  6. end
  7. else begin
  8. case(1'b1)
  9. fre_en[0]: begin
  10. ;
  11. end
  12. fre_en[1]: begin
  13. ;
  14. end
  15. fre_en[2]: begin //3分频计数
  16. if(fre_cnt < 2) fre_cnt <= fre_cnt + 1;
  17. else fre_cnt <= 0;
  18. end
  19. fre_en[3]: begin
  20. if(fre_cnt < 3) fre_cnt <= fre_cnt + 1;
  21. else fre_cnt <= 0;
  22. end
  23. fre_en[4]: begin
  24. if(fre_cnt < 4) fre_cnt <= fre_cnt + 1;
  25. else fre_cnt <= 0;
  26. end
  27. fre_en[5]: begin
  28. if(fre_cnt < 5) fre_cnt <= fre_cnt + 1;
  29. else fre_cnt <= 0;
  30. end
  31. fre_en[6]: begin
  32. if(fre_cnt < 6) fre_cnt <= fre_cnt + 1;
  33. else fre_cnt <= 0;
  34. end
  35. fre_en[7]: begin
  36. if(fre_cnt < 7) fre_cnt <= fre_cnt + 1;
  37. else fre_cnt <= 0;
  38. end
  39. endcase
  40. end
  41. end

有了计数模块,就可以设计分频实现模块了,根据分频使能来确定分频模式,不同的分频模式对应不同的分频实现方式。


  
  1. reg clk_out_r;
  2. reg clk_even;
  3. always @(posedge clk or negedge rst_n) begin
  4. if(~rst_n) begin
  5. clk_even <= 0;
  6. clk_out_r <= 0;
  7. end
  8. else begin
  9. case(1'b1)
  10. fre_en[0]: begin //1 分频
  11. ;
  12. end
  13. fre_en[1]: begin //2 分频
  14. clk_even <= ~clk_even;
  15. end
  16. fre_en[2]: begin //3 分频
  17. if(fre_cnt == 1) clk_out_r <= ~clk_out_r;
  18. else if(fre_cnt == 2) clk_out_r <= ~clk_out_r;
  19. else clk_out_r <= clk_out_r;
  20. end
  21. fre_en[3]: begin //4 分频
  22. if(fre_cnt == 1) clk_even <= ~clk_even;
  23. else if(fre_cnt ==3) clk_even <= ~clk_even;
  24. else clk_even <= clk_even;
  25. end
  26. fre_en[4]: begin //5分频
  27. if(fre_cnt == 2) clk_out_r <= ~clk_out_r;
  28. else if(fre_cnt == 4) clk_out_r <= ~clk_out_r;
  29. else clk_out_r <= clk_out_r;
  30. end
  31. fre_en[5]: begin // 6分频
  32. if(fre_cnt == 2) clk_even <= ~clk_even;
  33. else if(fre_cnt == 5) clk_even <= ~clk_even;
  34. else clk_even <= clk_even;
  35. end
  36. fre_en[6]: begin //7 分频
  37. if(fre_cnt == 3) clk_out_r <= ~clk_out_r;
  38. else if(fre_cnt == 6) clk_out_r <= ~clk_out_r;
  39. else clk_out_r <= clk_out_r;
  40. end
  41. fre_en[7]: begin //8 分频
  42. if(fre_cnt == 3) clk_even <= ~clk_even;
  43. else if(fre_cnt == 7) clk_even <= ~clk_even;
  44. else clk_even <= clk_even;
  45. end
  46. endcase
  47. end
  48. end

解释下上面的几个reg变量以及wire变量的意义,由于考虑到奇数分频最后的输出clk_out需要一段组合逻辑,所以需要使能wire类型。

这就导致这个分频实现模块里不能直接使用clk_out作为左值。

用clk_even用作偶分频输出clk_out的缓冲;

至于clk_out_r,以及clk_out_rr是为了奇分频而声明的。

这里提一下奇分频的实现原理:(之前写过Verilog HDL 训练】第 11 天(分频电路)

假如N为奇数,那么进行N分频,需要先设计一个占空比为(N-1)/2 : N的分频输出clk_out_r,之后用时钟下降沿采样clk_out_r得到clk_out_rr;最后将clk_out_r或上clk_out_rr即可。

具体可参考上面引用的博文。

至于偶分频,则需要计数一半反转一次,计数结束反转一次即可。

好了,下面继续解决奇分频的下降沿采样问题:


  
  1. //下降沿采样模块
  2. reg clk_out_rr;
  3. always @(negedge clk or negedge rst_n) begin
  4. if(~rst_n) begin
  5. clk_out_rr <= 0;
  6. end
  7. else begin
  8. case(1'b1)
  9. fre_en[0]: ;
  10. fre_en[1]: ;
  11. fre_en[2]: begin
  12. clk_out_rr <= clk_out_r;
  13. end
  14. fre_en[3]: ;
  15. fre_en[4]: begin
  16. clk_out_rr <= clk_out_r;
  17. end
  18. fre_en[5]: ;
  19. fre_en[6]: begin
  20. clk_out_rr <= clk_out_r;
  21. end
  22. fre_en[7]: ;
  23. endcase
  24. end
  25. end

最后输出分频时钟:


  
  1. //产生分频时钟
  2. assign clk_out = ( fre_en[0] | fre_en[1] | fre_en[3] | fre_en[5] | fre_en[7] )? clk_even : clk_out_r | clk_out_rr;

由于一次输入只能有一个使能位有效,所以上述assign自然明白了吧。

大概就是这么多了,给出一个简单的仿真,文章的最后会给出完整的代码以及仿真代码。

先给出一个仿真,假如输入div为5,则进行5分频,则:

假如输入div为6则进行6分频:

先5分频在1分频:

完美切换。

最后的最后给出完整版程序以及测试。

 


完整版程序:


  
  1. //任意切换1——8分频电路设计(某芸科技),无论是奇分频还是偶分频,占空比均为50%
  2. module Fre_Div(
  3. input clk,
  4. input [3:0] div,
  5. input rst_n,
  6. output clk_out
  7. );
  8. localparam DIV1 = 1, DIV2 = 2, DIV3 = 3, DIV4 = 4;
  9. localparam DIV5 = 5, DIV6 = 6, DIV7 = 7, DIV8 = 8;
  10. reg [7:0] fre_en;
  11. always@(posedge clk or negedge rst_n) begin
  12. if(~rst_n) begin
  13. fre_en <= 0;
  14. end
  15. else begin
  16. case(div)
  17. DIV1: fre_en <= 8'b0000_0001;
  18. DIV2: fre_en <= 8'b0000_0010;
  19. DIV3: fre_en <= 8'b0000_0100;
  20. DIV4: fre_en <= 8'b0000_1000;
  21. DIV5: fre_en <= 8'b0001_0000;
  22. DIV6: fre_en <= 8'b0010_0000;
  23. DIV7: fre_en <= 8'b0100_0000;
  24. DIV8: fre_en <= 8'b1000_0000;
  25. endcase
  26. end
  27. end
  28. reg clk_out_r;
  29. reg clk_even;
  30. always @(posedge clk or negedge rst_n) begin
  31. if(~rst_n) begin
  32. clk_even <= 0;
  33. clk_out_r <= 0;
  34. end
  35. else begin
  36. case(1'b1)
  37. fre_en[0]: begin //1 分频
  38. ;
  39. end
  40. fre_en[1]: begin //2 分频
  41. clk_even <= ~clk_even;
  42. end
  43. fre_en[2]: begin //3 分频
  44. if(fre_cnt == 1) clk_out_r <= ~clk_out_r;
  45. else if(fre_cnt == 2) clk_out_r <= ~clk_out_r;
  46. else clk_out_r <= clk_out_r;
  47. end
  48. fre_en[3]: begin //4 分频
  49. if(fre_cnt == 1) clk_even <= ~clk_even;
  50. else if(fre_cnt ==3) clk_even <= ~clk_even;
  51. else clk_even <= clk_even;
  52. end
  53. fre_en[4]: begin //5分频
  54. if(fre_cnt == 2) clk_out_r <= ~clk_out_r;
  55. else if(fre_cnt == 4) clk_out_r <= ~clk_out_r;
  56. else clk_out_r <= clk_out_r;
  57. end
  58. fre_en[5]: begin // 6分频
  59. if(fre_cnt == 2) clk_even <= ~clk_even;
  60. else if(fre_cnt == 5) clk_even <= ~clk_even;
  61. else clk_even <= clk_even;
  62. end
  63. fre_en[6]: begin //7 分频
  64. if(fre_cnt == 3) clk_out_r <= ~clk_out_r;
  65. else if(fre_cnt == 6) clk_out_r <= ~clk_out_r;
  66. else clk_out_r <= clk_out_r;
  67. end
  68. fre_en[7]: begin //8 分频
  69. if(fre_cnt == 3) clk_even <= ~clk_even;
  70. else if(fre_cnt == 7) clk_even <= ~clk_even;
  71. else clk_even <= clk_even;
  72. end
  73. endcase
  74. end
  75. end
  76. //下降沿采样模块
  77. reg clk_out_rr;
  78. always @(negedge clk or negedge rst_n) begin
  79. if(~rst_n) begin
  80. clk_out_rr <= 0;
  81. end
  82. else begin
  83. case(1'b1)
  84. fre_en[0]: ;
  85. fre_en[1]: ;
  86. fre_en[2]: begin
  87. clk_out_rr <= clk_out_r;
  88. end
  89. fre_en[3]: ;
  90. fre_en[4]: begin
  91. clk_out_rr <= clk_out_r;
  92. end
  93. fre_en[5]: ;
  94. fre_en[6]: begin
  95. clk_out_rr <= clk_out_r;
  96. end
  97. fre_en[7]: ;
  98. endcase
  99. end
  100. end
  101. //产生分频时钟
  102. assign clk_out = fre_en[0] ? clk :( ( fre_en[1] | fre_en[3] | fre_en[5] | fre_en[7] )? clk_even : clk_out_r | clk_out_rr );
  103. //计数模块
  104. reg [3:0] fre_cnt;
  105. always @(posedge clk or negedge rst_n) begin
  106. if(~rst_n) begin
  107. fre_cnt <= 0;
  108. end
  109. else begin
  110. case(1'b1)
  111. fre_en[0]: begin
  112. ;
  113. end
  114. fre_en[1]: begin
  115. ;
  116. end
  117. fre_en[2]: begin //3分频计数
  118. if(fre_cnt < 2) fre_cnt <= fre_cnt + 1;
  119. else fre_cnt <= 0;
  120. end
  121. fre_en[3]: begin
  122. if(fre_cnt < 3) fre_cnt <= fre_cnt + 1;
  123. else fre_cnt <= 0;
  124. end
  125. fre_en[4]: begin
  126. if(fre_cnt < 4) fre_cnt <= fre_cnt + 1;
  127. else fre_cnt <= 0;
  128. end
  129. fre_en[5]: begin
  130. if(fre_cnt < 5) fre_cnt <= fre_cnt + 1;
  131. else fre_cnt <= 0;
  132. end
  133. fre_en[6]: begin
  134. if(fre_cnt < 6) fre_cnt <= fre_cnt + 1;
  135. else fre_cnt <= 0;
  136. end
  137. fre_en[7]: begin
  138. if(fre_cnt < 7) fre_cnt <= fre_cnt + 1;
  139. else fre_cnt <= 0;
  140. end
  141. endcase
  142. end
  143. end
  144. endmodule

测试:


  
  1. module Sim_Freq_Div(
  2. );
  3. reg clk;
  4. reg rst_n;
  5. reg [3:0] div;
  6. wire clk_out;
  7. initial begin
  8. clk = 0;
  9. forever
  10. #2 clk = ~clk;
  11. end
  12. initial begin
  13. rst_n = 0;
  14. div = 5;
  15. #15
  16. rst_n = 1;
  17. #60
  18. div = 1;
  19. end
  20. Fre_Div fre_div_tb(
  21. .clk(clk),
  22. .rst_n(rst_n),
  23. .div(div),
  24. .clk_out(clk_out)
  25. );
  26. endmodule

 

 

 

 

 

文章来源: reborn.blog.csdn.net,作者:李锐博恩,版权归原作者所有,如需转载,请联系作者。

原文链接:reborn.blog.csdn.net/article/details/97553078

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。