串转并的使用环境非常多,在接口处用到最多,在某些模块的输入仅允许串行输入时,而总线上的数时并行的,那就需要通过串并转换,把并行的数据转换成串行的数据,包括在输出的时候,需要把串行的数据转换成并行的数据,才能送到总线中,使用方法都是用一个计数器来计数实现 。
串转并
常规转换
用一个计数器count,每输入8个数,就输出一次,每周期dout_temp左移一位,然后再将输入的1bit串行据存入dout_temp的最低位。
module serial_to_parallel(
input clk,
input rst_n,
input din_serial,
input din_valid,
output reg [7:0]dout_parallel,
output reg dout_valid
);
reg[7:0]din_tmp;
reg[3:0]cnt;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt <= 0;
else if(din_valid)
cnt <= (cnt == 4'd8)?0:cnt+1'b1;
else
cnt <= 0;
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
din_tmp <= 8'b0;
else if(din_valid && cnt <= 4'd7)
din_tmp <= {din_tmp[6:0],din_serial};
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dout_valid <= 1'b0;
dout_parallel <= 8'b0;
end
else if(cnt == 4'd8)begin
dout_valid <= 1'b1;
dout_parallel <= din_tmp;
end
else begin
dout_valid <= 1'b0;
end
end
endmodule
Testbench:
module serial_to_parallel_tb();
reg clk,rst_n;
reg din_serial,din_valid;
wire dout_valid;
wire[7:0]dout_parallel;
always #5 clk = ~clk;
initial begin
clk <= 1'b0;
rst_n <= 1'b0;
#15
rst_n <= 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; #20
din_valid <= 1'b0;
#50
$stop;
end
serial_to_parallel u0(
.clk (clk) ,
.rst_n (rst_n) ,
.din_serial (din_serial) ,
.dout_parallel (dout_parallel) ,
.din_valid (din_valid) ,
.dout_valid (dout_valid)
);
endmodule
仿真结果:
结合握手机制的串转并
实现串并转换电路,输入端输入单bit数据,每当本模块接收到6个输入数据后,输出端输出拼接后的6bit数据。本模块输入端与上游的采用valid-ready双向握手机制,输出端与下游采用valid-only握手机制。数据拼接时先接收到的数据放到data_b的低位。
电路的接口如下图所示。valid_a用来指示数据输入data_a的有效性,valid_b用来指示数据输出data_b的有效性;ready_a用来指示本模块是否准备好接收上游数据,本模块中一直拉高;clk是时钟信号;rst_n是异步复位信号。
代码:
module s_to_p(
input clk,
input rst_n,
input valid_a,
input data_a,
output reg ready_a,
output reg valid_b,
output reg [5:0]data_b
);
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
ready_a <= 1'b0;
else
ready_a <= 1'b1;
end
re[2:0]cnt;
always@(posedge clk or negeedge rst_n)begin
if(!rst_n)
cnt <= 3'd0;
else if(valid_a && ready_a)
cnt <= cnt == 3'd5 ? 3'd0 : cnt + 1'b1;
else
cnt <= cnt;
end
reg [5:0]data_r;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
data_r <= 6'd0;
esle if(valid_a && ready_a)
data_r <= {data_a,data_r[5:1]};
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
valid_b <= 1'b0;
data_b <= 6'd0;
end
else if(cnt == 3'd5)begin
data_b <= {data_a,data_r[5:1]};
valid_b <= 1'b1;
end
else
valid_b <= 1'b0;
end
endmodule
并转串
并转串的原理和串转并差不多,都是用一个计数器来技术,根据串行和并行之间的关系来决定计数器计数的上下限。
在输入数据有效的时候din_valid为高时,把输入的数据寄存一下,寄存到din_parallel_temp中,然后再用一个计数器去检测输出,当计数器处在1~8的范围内时,让输出的串行数据等于输入的并行数据的最高位,然后每周期让并行数据左移一bit。这样,八周期后,就实现了并转串。
代码:
module parallel_to_serial(
input clk,
input rst_n,
input [7:0] din_parallel,
input din_valid,
output reg dout_serial,
output reg dout_valid
);
reg[7:0]din_parallel_tmp;
reg [3:0]cnt;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt <= 0;
else if(din_valid)
cnt <= cnt + 1'b1;
else
cnt <= 0;
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
din_parallel_tmp <= 8'b0;
dout_serial <= 1'b0;
dout_valid <= 1'b0;
end
else if(din_valid && cnt == 0)begin
din_parallel_tmp <= din_parallel;
end
else if((cnt >= 4'd1) && (cnt <= 4'd8))begin
dout_serial <= din_parallel_tmp[7];
din_parallel_tmp <= din_parallel_tmp << 1;
dout_valid <= 1'b1;
end
else begin
dout_serial <= 1'b0;
dout_valid <= 1'b0;
end
end
endmodule
Testbench:
module parallel_to_serial_tb();
reg clk, rst_n;
reg [7:0] din_parallel;
reg din_valid;
wire dout_valid,dout_serial;
always #5 clk = ~clk;
initial begin
clk <= 1'b0;
rst_n <= 1'b0;
#15
rst_n <= 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) ,
.rst_n (rst_n) ,
.din_parallel (din_parallel) ,
.din_valid (din_valid) ,
.dout_serial (dout_serial) ,
.dout_valid (dout_valid)
);
endmodule
仿真结果: