Verilog 串口收发:
亲测可用最快波特率可以达到1_500_000HZ;
module data_rx #(parameter clk_fre=50_000_000, baud_rate=9600,allbits=8,done_width=4)
(
input clk, rstn,
input data_in,
output [allbits-2:0]data_out,
output done
);
//bits为总共要接收的数据位数(起始位+数据位(数据+校验))
//done_width为done信号的宽度,最多为15个输入时钟宽度;
localparam baud_cnt=clk_fre/baud_rate;
localparam bits=allbits-1;
reg buff1,buff2,flag_begin;
wire en;
reg r_over;
always @(posedge clk or negedge rstn)
begin
if(!rstn)
begin
buff1<=1'b1;
buff2<=1'b1;
end
else if(!flag_begin)
begin
buff1<=data_in;
buff2<=buff1;
end
else
begin
buff1<=1'b1;
buff2<=1'b1;
end
end
assign en=buff2&~buff1;
always @(posedge clk or negedge rstn)
begin
if(!rstn)
begin
flag_begin<=1'b0;
end
else if (en)
begin
flag_begin<=1'b1;
end
else if (r_over)
begin
flag_begin<=1'b0;
end
else
begin
flag_begin<=flag_begin;
end
end
reg [13:0]cnt;
//产生一个波特率时钟
always @(posedge clk or negedge rstn)
begin
if(!rstn)
begin
cnt<=baud_cnt-1;
end
else if (flag_begin)
begin
cnt<=cnt==baud_cnt-1 ? 0:cnt+1;
end
else
begin
cnt<=baud_cnt-1;
end
end
reg baud_clk;
always @(posedge clk or negedge rstn )
begin
if (!rstn)
begin
baud_clk<=1'b0;
end
else if (flag_begin)
begin
baud_clk<=cnt==0?1'b1:(cnt==baud_cnt/2?1'b0:baud_clk);
end
else
begin
baud_clk<=1'b0;
end
end
reg[3:0] bit_cnts;
reg[bits:0] out_buff;
reg temp1,temp2;
always @(posedge clk or negedge rstn)
begin
if (!rstn)
begin
temp1<=1'b0;
temp2<=1'b0;
end
else
begin
temp1<=baud_clk;
temp2<=temp1;
end
end
//baud_clk的上升沿位数加一;
always @(posedge clk or negedge rstn)
begin
if (!rstn)
begin
bit_cnts<=4'b1111;
end
else if (flag_begin)
begin
bit_cnts<=~temp2&temp1? bit_cnts+4'd1:bit_cnts;
end
else
begin
bit_cnts<=4'b1111;
end
end
//baud_clk为下降沿时将数据存入寄存器
always @(posedge clk or negedge rstn)
begin
if (!rstn)
begin
out_buff<=0;
end
else if(flag_begin&(~temp1&temp2))
begin
out_buff[bit_cnts]<=data_in;
end
end
always@(posedge clk or negedge rstn)
if(!rstn)
begin
r_over<=1'b0;
end
else if (cnt==baud_cnt-2&bit_cnts==bits)
begin
r_over<=1'b1;
end
else
begin
r_over<=1'b0;
end
reg [3:0] done_cnt;
reg done_flag;
reg st;
always @(posedge clk or negedge rstn)
begin
if(!rstn)
begin
done_cnt<=4'd0;
done_flag<=1'b0;
st<=1'b0;
end
else
begin
case(st)
1'b0: begin
done_cnt<=4'd0;
done_flag<=1'b0;
st<=r_over?1'b1:1'b00;
end
1'b1: begin
done_flag<=1'b1;
done_cnt<=done_cnt+1;
st<= done_cnt==done_width?1'b0:1'b1;
end
endcase
end
end
assign done=done_flag;
assign data_out=out_buff[bits:1];
endmodule
module data_tx #(parameter clk_fre=50_000_000,baud_rate=9600,allbits=10,done_width=6)
(
input clk,en,rstn,
input [bits-2:0]data_bit,
output d_out,done
);
//bits=一位起始位(一位低电平)+data_bit+一位停止位(一位高电平)
//data_bit=校验位+数据
localparam baud_cnts=clk_fre/baud_rate;
localparam bits=allbits-1;
wire [bits:0] data;
assign data={1'b1,data_bit,1'b0};
reg buff1,buff2;
reg flag_send,send_over;
always @(posedge clk or negedge rstn)
begin
if (!rstn)
begin
buff1<=1'b0;
buff2<=1'b0;
end
else
begin
buff1<=en;
buff2<=buff1;
end
end
always @(posedge clk or negedge rstn)
begin
if(!rstn)
begin
flag_send<=1'b0;
end
else if (buff2&~buff1)
begin
flag_send<=1'b1;
end
else if (send_over)
begin
flag_send<=1'b0;
end
else
begin
flag_send<=flag_send;
end
end
//产生波特率时钟
reg [13:0]cnt;
always @(posedge clk or negedge rstn)
begin
if (!rstn)
begin
cnt<=baud_cnts-1;
end
else if(flag_send)
begin
cnt<=cnt==baud_cnts-1? 0:cnt+10'd1;
end
else
begin
cnt<=baud_cnts-1;
end
end
reg baud_clk;
always @(posedge clk or negedge rstn)
begin
if (!rstn)
begin
baud_clk<=1'b0;
end
else if (flag_send)
begin
baud_clk<=cnt==0? 1'b1:(cnt==baud_cnts/2 ? 1'b0 : baud_clk);
end
else
begin
baud_clk<=1'b0;
end
end
//在波特时钟下降沿发送新数据
reg temp1,temp2;
always @(posedge clk or negedge rstn)
begin
if (!rstn)
begin
temp1<=1'b0;
temp2<=1'b0;
end
else if (flag_send)
begin
temp1<=baud_clk;
temp2<=temp1;
end
else
begin
temp1<=1'b0;
temp2<=1'b0;
end
end
reg [3:0]bits_cnt;
reg out_temp;
always @(posedge clk or negedge rstn)
begin
if (!rstn)
begin
out_temp<=1'b1;
bits_cnt<=4'd0;
end
else if (flag_send&(temp1&~temp2))
begin
bits_cnt<=bits_cnt+4'd1;
end
else if (flag_send&(temp2&~temp1))
begin
out_temp<=data[bits_cnt-1];
end
else if(~flag_send)
begin
out_temp<=1'b1;
bits_cnt<=4'd0;
end
end
always @(posedge clk or negedge rstn)
begin
if(!rstn)
begin
send_over<=1'b0;
end
else if (bits_cnt==bits+1&cnt==baud_cnts/2-1)
begin
send_over<=1'b1;
end
else
begin
send_over<=1'b0;
end
end
reg [3:0]done_cnt;
reg st,done_flag;
//产生一定宽度的DONE信号
always @(posedge clk or negedge rstn)
begin
if (!rstn)
begin
st<=1'b0;
done_flag<=1'b0;
done_cnt<=4'd0;
end
else
begin
case(st)
1'b0:begin
done_flag<=1'b0;
done_cnt<=4'd0;
st<= send_over?1'b1:1'b0;
end
1'b1:begin
done_flag<=1'b1;
done_cnt<=done_cnt+1;
st<= done_cnt==done_width?1'b0:1'b1;
end
endcase
end
end
assign done=done_flag;
assign d_out=out_temp;
endmodule
module AT_test(
input clk,rstn,D_rx,
output D_tx
);
wire [7:0]rx_data;
wire rx_done;
data_rx #(.clk_fre(50_000_000),.baud_rate(1500000),.allbits(9),.done_width(6))
rx0 (
.clk(clk), .rstn(rstn),.data_in(D_rx),
.data_out(rx_data),.done(rx_done)
);
data_tx #(.clk_fre(50_000_000),.baud_rate(1500000),.allbits(10),.done_width(6))
tx0 (
.clk(clk),.en(rx_done),.rstn(rstn),.data_bit(rx_data),
.d_out(D_tx),.done()
);
endmodule
如有错误之处欢迎在评论区一起共同探讨;
#Verilog代码#
查看15道真题和解析