本文是参考https://blog.csdn.net/vivid117/article/details/102021707这篇文章后结合自己的理解后修改的。
可以用两种方式来实现串并转换,一种是移位寄存器,另一种是计数器,这里只用移位寄存器,计数器的可以参考上面的博客
用verilog语言描述为:data<={data[6:0],data_in}
data为8位输出寄存器,移位拼接符内的变量位置不同那么移位方向不同。
本次实现串并转换与上面提到的博客中略有不同,增加了一个输出寄存器,这样可以锁存输出,防止在移位过程中数据变化。
`timescale 1 ns/ 1 ns
module serial2parallel(
input clk,
input rst_n,
input en,
input data_i,
output reg [7:0] data_o
);
reg [7:0] data;//输出寄存器
reg [2:0] count;//计数器,控制并行数据更新,8位数据
//初始化
always@(posedge clk or negedge rst_n)
begin
if(rst_n==1'b0)
count<=3'b0;
else if(en==1)
count<=count+3'b1;
end
//移位
always@(posedge clk or negedge rst_n)
begin
if(rst_n==1'b0)
data<=8'b0;
else if(en==1'b1)
data<={data[6:0],data_i};
end
//数据输出
always@(posedge clk or negedge rst_n)
begin
if(rst_n==1'b0)
data_o<=8'b0;
else if(rst_n==1'b1&&en==1'b1&&count==3'b111)
data_o<=data;
end
endmodule
对应的testbench如下:
`timescale 1 ns/ 1 ns
module serial2parallel_tb();
reg clk;
reg data_i;
reg en;
reg rst_n;
// wires
wire [7:0] data_o;
initial
begin
clk=1'b0;
rst_n=1'b0;
data_i=1'b0;
en=1'b0;
#10 rst_n=1'b1;
#5 en = 1'b1;
end
always #2 clk=~clk;
always #10 data_i = data_i+1'b1;//这里data_i的翻转时间不能设置的太小
serial2parallel s2p (
.clk(clk),
.data_i(data_i),
.data_o(data_o),
.en(en),
.rst_n(rst_n)
);
endmodule
在使用这个testbench的时候,data_i的翻转时间不能设置太小,一开始设置了每两个时钟翻转一次,结果在仿真时data_o只出一两组数据,但是data中的数据是正常的,这里不知道是什么原因。
并转串依旧使用移位操作完成,这里设置了一个使能信号,当使能信号使能后才能接收并行数据。因为在实际应用中,如果并口一直在接收数据,那么串口将始终发送新数据的最高位。比如,当第一个时钟来临时,并口发送了0100_0001这8位数据,那么串口会立即输出这8位数据的最高位0,如果第二个时钟来临时,并口发送了0000_0001这8位数据,那么串口此时会立即输出这8位数据的最高位0,而不是上一组数据的次高位。如果加入使能信号,就可以在第一组数据串口没有发送完成之前不接收并口的数据。
`timescale 1ns/1ns
module parallel2serial(
input clk,
input rst_n,
input en,
input [7:0] data_i,
output data_o
);
reg [7:0] data_buf;
always@(posedge clk or negedge rst_n)
begin
if(rst_n==0)
data_buf<=0;
else if(en==1'b1)
data_buf<=data_i;
else
data_buf<=data_buf<<1;
end
assign data_o=data_buf[7];
endmodule
Testbench如下:
`timescale 1 ns/1 ns
module parallel2serial_tb();
reg clk;
reg rst_n;
reg en;
reg [7:0] data_i;
wire data_o;
initial
begin
clk=1'b0;
en=1'b0;
rst_n=1'b0;
data_i=8'b01010101;
#2 rst_n=1'b1;
#4 en=1'b1;
#8 en=1'b0;//第一次采集数据,采集到01010101,将在之后的7个时钟内由data_o输出
#10 data_i=8'b10110010;
#102 en=1'b1;
#106 en=1'b0;//第二次采集数据,采集到10110010,将在之后的7个时钟内data_o输出
end
always #2 clk=~clk;
parallel2serial p2s(
.clk (clk),
.rst_n (rst_n),
.en (en),
.data_i (data_i),
.data_o (data_o)
);
endmodule
testbench里设计了两组数据,每次当en使能时,则data_o会立即输出串口数据的最高位(这是因为assign使data_o和data_buf直连了),直到en不再使能,当en不再使能后的第二个时钟来临,data_o会输出次高位,依次类推,所以总共需要en不使能后的7个时钟才能全部输出数据。