前言:
本专栏旨在记录高频笔面试手撕代码题,以备数字前端秋招,本专栏所有文章提供原理分析、代码及波形,所有代码均经过本人验证。
目录如下:
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,要求输出一个clk_out,满足clk_out的频率是clk的四倍。
解决思路
要注意的是,这里实现的是倍频,而不是分频,如果是分频的话很好实现,在笔者前期文章中对任意奇数分频、偶数分频、小数分频都做了具体介绍,但这里的倍频实现起来则需要一些不同于分频的技巧。
这里实现的技巧就是利用组合逻辑的延时来实现倍频。
二倍频
在实现四倍频之前,我们先来实现二倍频。利用一小段组合逻辑clk_out = Q^clk来做延迟,然后Q的改变通过检测clk_out的上升沿来实现。实现代码如下:
代码
module double_f(input clk ,input rstn ,output clk_out
);reg Q;
reg [4:0] count;
always @(posedge clk_out or negedge rstn)beginif(!rstn)beginQ <= 0;count <= 1'd0;endelse beginQ <= ~Q;count <= count + 1'b1;end
endassign clk_out = Q ^ clk;endmodule
波形
通过计数器count可以看见,计数器每在clk_out的上升沿,计数器加1,从波形中我们可以看到计数器在每个clk的周期里,都加了两次1,也就是说clk_out的频率是clk频率的两倍,由此我们就实现了二倍频,只不过是占空比极小的二倍频。
实现原理
初始化Q为0,在clk为低时, Q=0,clk=0所以,clk_out=Q^clk=0。在clk上升沿到来的瞬间,clk=1,Q=0,所以得到clk_out=Q^clk=1。从而clk_out的时钟上升沿到来,clk_out的时钟上升沿到来后,触发always语句块后的敏感事件列表,使得Q = ~Q;所以Q=1。右因为Q =1,clk=1,所以clk_out = Q ^clk=0,相当于产生了一个极小的脉冲。
打开modelsim中仿真时间片的模式后,可以看见,clk上升沿时刻的数据变化和我们所描述分析的一致。下降沿同理。
四倍频
在理解了二倍频的实现原理(利用组合逻辑延时,配合always敏感事件列表创造极短脉冲)后,四倍频的实现也是同样的道理。
只不过再二倍频的基础上, 再次二倍时,使用的参考时钟为第一次二倍频得到的时钟,譬如第一次二倍频产生的二倍时钟令其为clk_out_temp1,那么第二次做二倍频的时候,以clk_out_temp1为参考时钟,令clk_out_temp2 = Q1 ^ clk_out_temp1;
代码
module four_f(
input clk ,
input rstn ,
output clk_out
);wire clk_out_temp1,clk_out_temp2;
reg Q,Q1;
reg [4:0] count;
always@(posedge clk_out_temp1 or negedge rstn)beginif(!rstn)beginQ <= 0;end else beginQ <= ~Q;end
endassign clk_out_temp1 = Q^clk;always@(posedge clk_out_temp2 or negedge rstn)beginif(!rstn)beginQ1 <= 0;end else beginQ1 <= ~Q1;end
endassign clk_out_temp2 = Q1^clk_out_temp1;
assign clk_out = clk_out_temp1 ^ clk_out_temp2;always @(posedge clk_out or negedge rstn)beginif(!rstn)begincount <= 1'd0;endelse begincount <= count + 1'b1;end
endendmodule
testbench
module test_four();reg rstn,clk;
wire clk_out;initial beginrstn <= 1;clk <= 0;#5rstn <= 0;#20rstn <= 1;
end always #5 clk = ~clk;four_f u_four_f(
.clk (clk) ,
.rstn (rstn) ,
.clk_out(clk_out)
);endmodule
波形
如何判断我们代码是否实现了二倍频呢?利用一个计数器,每在最终生成的clk_out时钟上升沿来进行计数器加1。
always @(posedge clk_out or negedge rstn)beginif(!rstn)begincount <= 1'd0;endelse begincount <= count + 1'b1;end
end
从波形图可以看到,每一个clk时钟周期里,count计数器都加了4,也就是经历了4个clk_out的时钟上升沿,也即,每一个clk周期,对应四个clk_out周期,也就是实现了我们所说的时钟四倍频,只不过四倍频得到的时钟的占空比非常小(波形图上展示为尖峰脉冲)。
更多手撕代码题可以前往 数字IC手撕代码--题库