前言:
本专栏旨在记录高频笔面试手撕代码题,以备数字前端秋招,本专栏所有文章提供原理分析、代码及波形,所有代码均经过本人验证。
目录如下:
10.数字IC手撕代码-数据位宽转换器(宽-窄,窄-宽转换)
13.数字IC手撕代码-流水握手(利用握手解决流水线断流、反压问题)
18.数字IC手撕代码-双端口RAM(dual-port-RAM)
...持续更新
为了方便可以收藏导览博客: 数字IC手撕代码-导览目录
目录
串并转换
串转并的使用环境非常多,在接口处用到最多,在某些模块的输入仅允许串行输入时,而总线上的数时并行的,那就需要通过串并转换,把并行的数据转换成串行的数据,包括在输出的时候,需要把串行的数据转换成并行的数据,才能送到总线中,使用方法都是用一个计数器来计数实现 。
串转并
用一个计数器count,每输入8个数,就输出一次,每周期dout_temp左移一位,然后再将输入的1bit串行据存入dout_temp的最低位。
代码
module serial_to_parallel(
input rstn ,
input clk ,
input din_serial ,
input din_valid ,
output reg [7:0] dout_parallel ,
output reg dout_valid
);
reg [7:0] dout_temp;
reg [3:0] count;
always @(posedge clk)begin
if(!rstn)begin
dout_temp <= 8'd0;
count <= 4'd0;
dout_valid <= 1'b0;
end
else if(din_valid && count <= 4'd7)begin
dout_temp <= {dout_temp[6:0],din_serial};
count <= count + 1'b1;
end
else begin
dout_valid <= 1'b0;
dout_temp <= 8'd0;
count <= 4'd0;
end
end
always @(posedge clk)begin
if(count == 4'd8)begin
count <= 4'd0;
dout_parallel <= dout_temp;
dout_valid <= 1'b1;
end
end
endmodule
testbench
module serial_to_parallel_tb();
reg clk,rstn;
reg din_serial,din_valid;
wire dout_valid,dout_parallel;
always #5 clk = ~clk;
initial begin
clk <= 1'b0;
rstn <= 1'b0;
#15
rstn <= 1'b1;
din_valid <= 1'b1;
din_serial <= 1'b1; #10
din_serial <= 1'b1; #10
din_serial <= 1'b1; #10
din_serial <= 1'b1; #10
din_serial <= 1'b0; #10
din_serial <= 1'b0; #10
din_serial <= 1'b0; #10
din_serial <= 1'b0; #10
din_valid <= 1'b0;
#30
din_valid <= 1'b1;
din_serial <= 1'b1; #10
din_serial <= 1'b1; #10
din_serial <= 1'b0; #10
din_serial <= 1'b0; #10
din_serial <= 1'b0; #10
din_serial <= 1'b0; #10
din_serial <= 1'b1; #10
din_serial <= 1'b1; #10
din_valid <= 1'b0;
$stop();
end
serial_to_parallel u_serial_to_parallel(
.clk (clk) ,
.rstn (rstn) ,
.din_serial (din_serial) ,
.dout_parallel (dout_parallel) ,
.din_valid (din_valid) ,
.dout_valid (dout_valid)
);
endmodule
波形
并转串
并转串的原理和串转并差不多,都是用一个计数器来技术,根据串行和并行之间的关系来决定计数器计数的上下限。
在输入数据有效的时候din_valid为高时,把输入的数据寄存一下,寄存到din_parallel_temp中,然后再用一个计数器去检测输出,当计数器处在1~8的范围内时,让输出的串行数据等于输入的并行数据的最高位,然后每周期让并行数据左移一bit。这样,八周期后,就实现了并转串。
代码
module parallel_to_serial(
input clk,
input rstn,
input [7:0] din_parallel,
input din_valid,
output reg dout_valid,
output reg dout_serial
);
reg [3:0] count;
reg [7:0] din_parallel_temp;
always @(posedge clk)begin
if(!rstn)begin
dout_valid <= 1'b0;
dout_serial <= 1'b0;
end
else if(din_valid && (count == 4'b0))begin
din_parallel_temp <= din_parallel;
end
else if((count >= 4'd1) && (count <= 4'd8))begin
dout_serial <= din_parallel_temp[7];
din_parallel_temp <= din_parallel_temp << 1;
dout_valid <= 1'b1;
end
else begin
dout_valid <= 1'b0;
dout_serial <= 1'b0;
end
end
always @(posedge clk)begin
if(!rstn)begin
count <= 4'd0;
end
else if(din_valid)begin
count <= count + 1'b1;
end
else begin
count <= 4'b0;
end
end
endmodule
testbench
module parallel_to_serial_tb();
reg clk, rstn;
reg [7:0] din_parallel;
reg din_valid;
wire dout_valid,dout_serial;
always #5 clk = ~clk;
initial begin
clk <= 1'b0;
rstn <= 1'b0;
#15
rstn <= 1'b1;
din_valid <= 1'b1;
din_parallel <= 8'b11110000;
#80
din_valid <= 1'b0;
#40
din_valid <= 1'b1;
din_parallel <= 8'b10100011;
#80
din_valid <= 1'b0;
#50
$stop();
end
parallel_to_serial u_parallel_to_serial(
.clk (clk) ,
.rstn (rstn) ,
.din_parallel (din_parallel) ,
.din_valid (din_valid) ,
.dout_serial (dout_serial) ,
.dout_valid (dout_valid)
);
endmodule
波形
由此我们就实现了串并转换,其中valid信号表示有效信号,当输入有效的时候,输入的valid信号就拉高,输出有效的时候,输出valid就拉高,这有利于我们看波形,同时也有利于模块和外部通信。