verilog实现串并转换以及Modelsim仿真

本文是参考https://blog.csdn.net/vivid117/article/details/102021707这篇文章后结合自己的理解后修改的。

1.串并转换模块

可以用两种方式来实现串并转换,一种是移位寄存器,另一种是计数器,这里只用移位寄存器,计数器的可以参考上面的博客

用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中的数据是正常的,这里不知道是什么原因。

2.并转串模块

并转串依旧使用移位操作完成,这里设置了一个使能信号,当使能信号使能后才能接收并行数据。因为在实际应用中,如果并口一直在接收数据,那么串口将始终发送新数据的最高位。比如,当第一个时钟来临时,并口发送了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个时钟才能全部输出数据。

你可能感兴趣的:(verilog,串口通信)