在UART(一)中主要解决的是单字节的发送和接受,但是单字节的应用场景相对较小。在大多数场景下,串口都是用于连续多字节的收发工作。 因此,本篇文章将基于已有的单字节串口收发模块,结合上一章学习的状态机,完成多字节串口发送模块和多字节串口接收模块的设计,并通过仿真和板级测试对设计进行验证,完成多字节串口收发的验证。
一、多字节发送(Tx_bytes)
1.1Verilog 代码:
例化UART(一)的单字节发送模块
`timescale 1ns / 1ps
//
// Description: 串口多字节发送
//
module uart_bytes_tx#(
parameter DATA_WIDTH = 32
)(
Clk ,
Rst_n ,
data_bytes ,
send_en ,
Baud_Set ,
uart_tx ,
Tx_Done ,
uart_state
);
input Clk ; //模块全局时钟输入,50M
input Rst_n ; //复位信号输入,低有效
input [DATA_WIDTH - 1:0] data_bytes ; //待传输 多字节 数据
input send_en ; //发送使能
input [2:0] Baud_Set ; //波特率设置
output uart_tx ; //串口输出信号
output reg Tx_Done ; //数据发送完成标志
output uart_state ; //发送数据状态
reg [3:0] state,n_state ;
reg [7:0] cnt ;
reg [DATA_WIDTH - 1:0] r_data_bytes ;
reg [7:0] data_byte ;
reg send_byte_en ;
wire byte_tx_done ;
wire uart_tx ;
wire uart_state ;
//********** 单字节数据发送模块例化 *******\\
uart_tx_byte uart_tx_byte_u0(
.Clk (Clk ),
.Rst_n (Rst_n ),
.data_byte (data_byte ),
.send_en (send_byte_en),
.Baud_Set (Baud_Set ),
.uart_tx (uart_tx ),
.Tx_Done (byte_tx_done),
.uart_state (uart_state )
);
localparam S0 = 4'b0001, //等待发送使能(初始态)
S1 = 4'b0010, //准备数据
S2 = 4'b0100, //等待单字节数据发送完成
S3 = 4'b1000; //检查所有数据是否发送完成
//************** 状态机 ***************\\
always @(posedge Clk or negedge Rst_n) begin
if(!Rst_n)
state <= S0;
else
state <= n_state;
end
always @(*) begin
case (state)
S0: if(send_en)
n_state <= S1;
else
n_state <= S0;
S1: n_state <= S2;
S2: if(byte_tx_done)
n_state <= S3;
else
n_state <= S2;
S3: if(cnt >= DATA_WIDTH - 8)
n_state <= S0;
else
n_state <= S1;
default:n_state <= S0;
endcase
end
always @(posedge Clk or negedge Rst_n) begin
if(!Rst_n)
r_data_bytes <= 'd0;
else if(state == S0 && send_en)
r_data_bytes <= data_bytes;
else if(state == S1)
r_data_bytes <= r_data_bytes << 8;
else
r_data_bytes <= r_data_bytes;
end
always @(posedge Clk or negedge Rst_n) begin
if(!Rst_n)
send_byte_en <= 'd0;
else if(state == S1)
send_byte_en <= 'd1;
else
send_byte_en <= 'd0;
end
always @(posedge Clk or negedge Rst_n) begin
if(!Rst_n)
data_byte <= 'd0;
else if(state == S1)
data_byte <= r_data_bytes[DATA_WIDTH - 1 : DATA_WIDTH -8];
else
data_byte <= data_byte;
end
always @(posedge Clk or negedge Rst_n) begin
if(!Rst_n)
cnt <= 'd0;
else if(state == S3 && cnt >= DATA_WIDTH - 8)
cnt <= 'd0;
else if(state == S3)
cnt <= cnt + 'd8;
else
cnt <= cnt;
end
always @(posedge Clk or negedge Rst_n) begin
if(!Rst_n)
Tx_Done <= 'd0;
else if(state == S3 && cnt >= DATA_WIDTH - 8)
Tx_Done <= 'd1;
else
Tx_Done <= 'd0;
end
endmodule
1.2 tb文件
`timescale 1ns / 1ps
module tb_byte_tx();
parameter DATA_WIDTH = 32;
reg Clk ; //模块全局时钟输入,50M
reg Rst_n ; //复位信号输入,低有效
reg [DATA_WIDTH - 1:0] data_bytes ; //待传输 多字节 数据
reg send_en ; //发送使能
wire uart_tx ; //串口输出信号
wire Tx_Done ; //1byte数据发送完成标志
wire uart_state ; //发送数据状态
uart_bytes_tx#(
.DATA_WIDTH (DATA_WIDTH )
)
uart_bytes_tx_u0
(
.Clk (Clk ),
.Rst_n (Rst_n ),
.data_bytes (data_bytes ),
.send_en (send_en ),
.Baud_Set ('d4 ),
.uart_tx (uart_tx ),
.Tx_Done (Tx_Done ),
.uart_state (uart_state )
);
initial Clk = 0;
always #10 Clk = ~Clk;
initial begin
Rst_n = 0;
data_bytes = 0;
send_en = 0;
#201;
Rst_n = 1;
#2000;
data_bytes = 32'h01234567;
send_en = 1;
#20;
send_en = 0;
#20;
@(posedge Tx_Done);
#1;
data_bytes = 32'h12345678;
send_en = 1;
#20;
send_en = 0;
#20;
@(posedge Tx_Done);
#1;
data_bytes = 32'h23456789;
send_en = 1;
#20;
send_en = 0;
#20;
@(posedge Tx_Done);
#1;
#2000;
$stop;
end
endmodule
仿真图
二、多字节接收 (Rx_bytes)
2.1Verilog 代码
`timescale 1ns / 1ps
//
// Description: 多字节接收
//
module uart_bytes_rx#(
parameter DATA_WIDTH = 32
)(
Clk ,
Rst_n ,
uart_rx ,
Baud_Set ,
Rx_done ,
timeout_flag ,
rx_data_bytes
);
input Clk ;
input Rst_n ;
input uart_rx ;
input[2:0] Baud_Set ;
output reg Rx_done ;
output reg timeout_flag ;
output reg [DATA_WIDTH - 1 : 0] rx_data_bytes ;
reg [DATA_WIDTH - 1 : 0] r_r_data_bytes ;
wire [19:0] TIMEOUT ;
wire [7:0] data_byte ;
wire byte_rx_done ;
reg to_state ;
reg[31:0] timeout_cnt ;
reg[1:0] state ;
reg[8:0] cnt ;
//根据波特率自动设置超时时间
assign TIMEOUT = (Baud_Set == 3'd0) ? 20'd182291:
(Baud_Set == 3'd1) ? 20'd91145 :
(Baud_Set == 3'd2) ? 20'd45572 :
(Baud_Set == 3'd3) ? 20'd30381 :
20'd15190 ;
localparam S0 = 0; //等待单字节接收完成信号
localparam S1 = 1; //判断接收是否超时
localparam S2 = 2; //检查所有数据是否接收完成
//**************** 单字节接收模块例化 ******************\\
uart_byte_rx uart_byte_rx_u0(
.Clk (Clk ),
.Reset_n (Rst_n ),
.Baud_Set (Baud_Set ),
.uart_rx (uart_rx ),
.Data (data_byte ),
.Rx_Done (byte_rx_done )
);
//****************超时标志 ******************\\
always @(posedge Clk or negedge Rst_n) begin
if(!Rst_n)
timeout_flag <= 'd0;
else if(timeout_cnt >= TIMEOUT)
timeout_flag <= 'd1;
else if(state == S0)
timeout_flag <= 'd0;
else
timeout_flag <= timeout_flag;
end
always @(posedge Clk or negedge Rst_n) begin
if(!Rst_n)
to_state <= 'd0;
else if(!uart_rx)
to_state <= 'd1;
else if(byte_rx_done)
to_state <= 'd0;
else
to_state <= to_state;
end
always @(posedge Clk or negedge Rst_n) begin
if(!Rst_n)
timeout_cnt <= 'd0;
else if(to_state)begin
if(byte_rx_done)
timeout_cnt <= 'd0;
else if(timeout_cnt >= TIMEOUT)
timeout_cnt <= TIMEOUT;
else
timeout_cnt <= timeout_cnt + 1;
end
else
timeout_cnt <= timeout_cnt;
end
//**************** 状态机 ******************\\
always @(posedge Clk or negedge Rst_n) begin
if(!Rst_n)begin
state <= S0;
r_r_data_bytes <= 0 ;
cnt <= 0 ;
rx_data_bytes <= 0 ;
end
else begin
case(state)
S0:
begin
Rx_done <= 0;
r_r_data_bytes <= 0;
if(DATA_WIDTH == 8)begin
rx_data_bytes <= data_byte;
Rx_done <= byte_rx_done;
end
else if(byte_rx_done)begin
state <= S1;
cnt <= cnt + 9'd8;
r_r_data_bytes <= {r_r_data_bytes[DATA_WIDTH - 1 - 8 : 0], data_byte};
end
end
S1:
if(timeout_flag)begin
state <= S0;
Rx_done <= 1 ;
end
else if(byte_rx_done)begin
state <= S2;
cnt <= cnt + 9'd8;
r_r_data_bytes <= {r_r_data_bytes[DATA_WIDTH - 1 - 8 : 0], data_byte};
end
S2:
if(cnt >= DATA_WIDTH)begin
state <= S0;
cnt <= 0;
rx_data_bytes <= r_r_data_bytes;
Rx_done <= 1;
end
else begin
state <= S1;
Rx_done <= 0;
end
default:state <= S0;
endcase
end
end
endmodule
2.2 TB文件
`timescale 1ns / 1ps
module uart_rx_bytes_tb();
parameter DATA_WIDTH = 32;
reg Clk ;
reg Rst_n ;
reg [DATA_WIDTH - 1 : 0] data ;
wire [DATA_WIDTH - 1 : 0] rx_data ;
reg send_en ;
wire uart_tx ;
wire uart_rx ;
wire Tx_Done ;
wire uart_state;
wire Rx_Done ;
wire timeout_flag;
assign uart_rx = uart_tx;
uart_bytes_tx#(
.DATA_WIDTH (DATA_WIDTH)
)
uart_bytes_tx_u0
(
.Clk (Clk ),
.Rst_n (Rst_n ),
.data_bytes (data ),
.send_en (send_en),
.Baud_Set ('d4 ),
.uart_tx (uart_tx),
.Tx_Done (Tx_Done),
.uart_state (uart_state)
);
uart_bytes_rx#(
.DATA_WIDTH (DATA_WIDTH)
)
uart_bytes_rx_u0
(
.Clk (Clk ),
.Rst_n (Rst_n ),
.uart_rx (uart_tx),
.Baud_Set ('d4 ),
.Rx_done (Rx_Done),
.timeout_flag (timeout_flag),
.rx_data_bytes (rx_data)
);
initial Clk = 1;
always #10 Clk = !Clk;
initial begin
Rst_n = 0;
data = 0;
send_en = 0;
#201;
Rst_n = 1;
#2000;
data = 32'h12345678;
send_en = 1;
#20;
send_en = 0;
#20;
@(posedge Tx_Done);
#1;
#1000000;
data = 32'h87654321;
send_en = 1;
#20;
send_en = 0;
#20;
@(posedge Tx_Done);
#1;
#1000000;
data = 32'h24680135;
send_en = 1;
#20;
send_en = 0;
#20;
@(posedge Tx_Done);
#1;
#400000;
$stop;
end
endmodule
仿真图![](https://i-blog.csdnimg.cn/direct/c956187bcec3497ebeb422505cea2b84.png)
三、多字节串口收发回环 (loopback)
3.1Verilog 代码
`timescale 1ns / 1ps
//
// Description: 串口回环
//
module uart_loopback#(
parameter DATA_WIDTH = 32
)
(
Clk ,
Rst_n ,
uart_rx ,
led ,
uart_tx
);
input Clk ;
input Rst_n ;
input uart_rx ;
output [2:0] led ;
output uart_tx ;
wire [DATA_WIDTH-1:0] data ;
wire Rx_Done ;
wire [7:0] data_byte ;
uart_bytes_rx#(
.DATA_WIDTH (DATA_WIDTH)
)
uart_bytes_rx_u0
(
.Clk (Clk ),
.Rst_n (Rst_n ),
.uart_rx (uart_rx),
.Baud_Set ('d4 ),
.Rx_done (Rx_Done),
.timeout_flag (led[0]),
.rx_data_bytes (data )
);
uart_bytes_tx#(
.DATA_WIDTH (DATA_WIDTH)
)
uart_bytes_tx_u0
(
.Clk (Clk ),
.Rst_n (Rst_n ),
.data_bytes (data ),
.send_en (Rx_Done),
.Baud_Set ('d4 ),
.uart_tx (uart_tx),
.Tx_Done (led[1]),
.uart_state (led[2])
);
endmodule
3.2 TB文件(例化多字节发送数据来发模拟数据)
`timescale 1ns / 1ps
module uart_loopback_tb();
parameter DATA_WIDTH = 256;
reg Clk ;
reg Rst_n ;
wire uart_rx ;
wire [2:0] led ;
wire uart_tx ;
reg [DATA_WIDTH-1:0] data ;
reg send_en ;
wire TX_Done ;
wire uart_state ;
wire uart_tx_u0 ;
assign uart_rx = uart_tx_u0;
uart_bytes_tx#(
.DATA_WIDTH (DATA_WIDTH)
)
uart_bytes_tx_u0
(
.Clk (Clk ),
.Rst_n (Rst_n ),
.data_bytes (data ),
.send_en (send_en),
.Baud_Set ('d4 ),
.uart_tx (uart_tx_u0),
.Tx_Done (TX_Done),
.uart_state (uart_state)
);
uart_loopback #(
.DATA_WIDTH (DATA_WIDTH)
)
uart_loopback_u0
(
.Clk (Clk ),
.Rst_n (Rst_n ),
.uart_rx (uart_rx ),
.led (led ),
.uart_tx (uart_tx )
);
initial Clk = 1;
always #10 Clk = !Clk;
initial begin
Rst_n = 0;
data = 0;
send_en = 0;
#201;
Rst_n = 1;
#2000;
data = 256'h890abcdef12312345abcdef674567890cba0987654fed365432121fedcba0987;
send_en = 1;
#20;
send_en = 0;
#20;
@(posedge TX_Done);
// #1;
#1000000;
data = 256'hcba0987654fed365432121fedcba0987ba09876fe321dc54b321dc6fe54a0978;
send_en = 1;
#20;
send_en = 0;
#20;
@(posedge TX_Done);
// #1;
#1000000;
data = 256'h890abcdef123123454fed365432121fedcba09875abcdef674567890cba09876;
send_en = 1;
#20;
send_en = 0;
#20;
@(posedge TX_Done);
#1;
#4000000;
$stop;
end
endmodule
仿真图
上板仿真
可以成功实现