前言:
本专栏旨在记录高频笔面试手撕代码题,以备数字前端秋招,本专栏所有文章提供原理分析、代码及波形,所有代码均经过本人验证。
目录如下:
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手撕代码-导览目录
目录
数据位宽转换器
1.由宽到窄的数据转换
代码
testbench
波形图
2.由窄到宽的数据转换
代码
testbench
波形图
总结
数据位宽转换器
数据位宽转换器,一般常用于模块接口处,比如一个电路模块的输出数据位宽大于另一个模块的输入数据位宽,此时就需要进行数据位宽转换。比如SATA控制器中,内部数据位宽为32bit,但外部物理收发器PHY的接口通常为16bit,或者8bit,在不使用FIFO进行缓存的情况下,可以使用数据位宽转换器,通过时钟倍频在实现数据位宽的转换。
1.由宽到窄的数据转换
假设数据从模块A传入到模块B,模块A的输出数据为32位,模块B的输入数据位宽为16位,那么如何能把数据从A传入B而不损失数据呢。
答:通过时钟的分频与倍频实现。
我们假设一个原时钟clk2x,通过二分频,把原时钟变为时钟clk1x,那么clk2x的时钟频率就是clk1x的两倍。在clk2x的上升沿,我们读入32bit数据,在clk2x的上升沿我们输出16bit数据,由于clk2x的频率是clk1x的两倍,每读入一次32bit数据,就会输出两次16bit数据,从而实现了数据位宽的转换,如图:
代码
(代码内写的clk1频率是clk2的两倍,结尾附波形,和上图有些出入)
module wide_to_narrow(
input rstn ,
input clk1 ,
input [31:0] data_in ,
output [15:0] data_out
);
reg [31:0] data_sync;
reg clk2;
always @(posedge clk1)begin
if(!rstn)begin
clk2 <= 1'b0;
end
else begin
clk2 <= ~clk2; //div_2
end
end
always @(posedge clk2)begin
data_sync <= data_in;
end
assign data_out = clk2 ? data_sync[31:16] : data_sync[15:0];
endmodule
testbench
module wide_to_narrow_tb();
reg clk1,rstn;
reg [31:0] data_in;
wire [15:0] data_out;
always #5 clk1 = ~clk1;
initial begin
clk1 <= 1'b0;
rstn <= 1'b0;
#10
rstn <= 1'b1;
#5
data_in <= 32'h0000_1111;
#20
data_in <= 32'h2222_3333;
#20
data_in <= 32'h4444_5555;
#20
data_in <= 32'h6666_7777;
#20
data_in <= 32'h8888_9999;
#20
$stop();
end
wide_to_narrow u_wide_to_narrow(
.clk1 (clk1) ,
.rstn (rstn) ,
.data_in (data_in) ,
.data_out (data_out)
);
endmodule
波形图
2.由窄到宽的数据转换
由窄到宽的数据位宽转换原理和由宽到窄的是一样的。把频率高一点的时钟用来采样16bit数据,频率低一点的时钟用来输出32bit数据。
代码
module narrow_to_wide(
input rstn ,
input clk1 ,
input [15:0] data_in ,
output reg [31:0] data_out
);
reg [31:0] data_sync;
reg [15:0] data_temp;
reg clk2;
always @(posedge clk1)begin
if(!rstn)begin
clk2 <= 1'b0;
end
else begin
clk2 <= ~clk2; //div_2
end
end
always @(posedge clk1)begin
data_temp <= data_in;
data_sync <= {data_temp,data_in};
end
always @(posedge clk2)begin
data_out <= data_sync;
end
endmodule
testbench
module narrow_to_wide_tb();
reg clk1,rstn;
reg [15:0] data_in;
wire [31:0] data_out;
always #5 clk1 = ~clk1;
initial begin
clk1 <= 1'b0;
rstn <= 1'b0;
#10
rstn <= 1'b1;
#5
data_in <= 16'h1111;
#10
data_in <= 16'h2222;
#10
data_in <= 16'h3333;
#10
data_in <= 16'h4444;
#10
data_in <= 16'h5555;
#10
data_in <= 16'h6666;
#20
$stop();
end
narrow_to_wide u_narrow_to_wide(
.clk1 (clk1) ,
.rstn (rstn) ,
.data_in (data_in) ,
.data_out (data_out)
);
endmodule
波形图
总结
位宽转换的核心就在于生成一个与原时钟成比例关系的时钟,这样时钟频率的比例关系就可以和数据的位宽扯上联系。