一、设计思路
二、顶层代码
`timescale 1ns / 1ns
module uart_tx_multibyte(
clk , //时钟
rst_n , //复位
data_n , //要发送的多字节数据
trans_go , //发送使能
uart_tx //串口发送数据
);
parameter IDLE = 3'b001; //空闲状态
parameter S1 = 3'b010; //准备发送数据状态,使能,装载数据
parameter S2 = 3'b100; //发送数据状态
//CNTBY_N字节数,CNTBY_W字节数计数器的位宽(更改这两个参数,即可实现串口任意字节发送)
parameter DATAN_W = 40; //发送数据个数
parameter CNTBY_N = 5; //发送数据字节个数
parameter CNTBY_W = 3; //发送数据字节计数器位宽,5=3'b101
parameter DATA_W = 8; //一次串行发送的数据个数
parameter STATE_W = 3; //状态机位宽
input clk;
input rst_n;
input [DATAN_W-1:0] data_n;
input trans_go;
output uart_tx;
wire uart_tx;
reg [CNTBY_W-1:0] cnt_bytes;
wire add_cnt_bytes;
wire end_cnt_bytes;
reg cnt_bytes_flag;
wire tx_done;
reg [DATA_W-1:0] data;
reg send_en;
reg [STATE_W-1:0] state_c;
reg [STATE_W-1:0] state_n;
wire IDLE2S1_start;
wire S12S2_start;
wire S22S1_start ;
wire S22IDLE_start ;
reg [DATAN_W-1:0] data_tmp;
my_uart_tx my_uart_tx(
.clk (clk), //时钟
.rst_n (rst_n), //复位
.data (data), //发送数据
.send_en (send_en), //发送使能
.baud_set (4'd4), //波特率设置,默认9600bps
.uart_tx (uart_tx), //串口发送
.tx_done (tx_done) //发送完成标志位
);
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_bytes <= 0;
else if(add_cnt_bytes)begin
if(end_cnt_bytes)
cnt_bytes <= 0;
else
cnt_bytes <= cnt_bytes + 1'b1;
end
end
assign add_cnt_bytes = cnt_bytes_flag;
assign end_cnt_bytes = add_cnt_bytes && cnt_bytes == CNTBY_N - 1;
always @(*)begin
if(tx_done)
cnt_bytes_flag = 1;
else
cnt_bytes_flag = 0;
end
//状态机
//状态转移描述
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
state_c <= IDLE;
else
state_c <= state_n;
end
//状态转移条件判断描述
always @(*)begin
case(state_c)
IDLE:begin
if(IDLE2S1_start)
state_n = S1;
else
state_n = state_c;
end
S1:begin
if(S12S2_start)
state_n = S2;
else
state_n = state_c;
end
S2:begin
if(S22S1_start)
state_n = S1;
else if(S22IDLE_start)
state_n = IDLE;
else
state_n = state_c;
end
default:
state_n = IDLE;
endcase
end
//状态转移条件描述
assign IDLE2S1_start = state_c==IDLE && trans_go;
assign S12S2_start = state_c==S1 && send_en;
assign S22S1_start = state_c==S2 && tx_done && !end_cnt_bytes;
assign S22IDLE_start = state_c==S2 && end_cnt_bytes;
//状态输出
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
send_en <= 0;
else if(state_c == S1)
send_en <= 1;
else
send_en <= 0;
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
data <= 0;
data_tmp <= 0;
end
else if(state_c == IDLE)begin
data <= 0;
data_tmp <= data_n;
end
else if(state_c == S1 && !send_en)begin
data <= data_tmp[7:0];
data_tmp <= {data_tmp[7:0],data_tmp[DATAN_W-1:8]};
end
end
endmodule
三、串口发送代码
module my_uart_tx(
clk , //时钟
rst_n , //复位
data , //发送数据
send_en , //发送使能
baud_set , //波特率设置
uart_tx , //串口发送
tx_done //发送完成标志位
);
parameter DATA_W = 8;
parameter SET_W = 3;
parameter BYTE_D = 10;
parameter BAUT_W = 17;
parameter BYTE_W = 4;
input clk;
input rst_n;
input [DATA_W-1:0] data;
input send_en;
input [SET_W-1:0] baud_set;
output uart_tx;
output tx_done;
reg uart_tx;
reg tx_done;
reg [BAUT_W-1:0] cnt_baud;
reg [BAUT_W-1:0] baud;
wire add_cnt_baud;
wire end_cnt_baud;
reg [BYTE_W-1:0] cnt_byte;
wire add_cnt_byte;
wire end_cnt_byte;
reg [BYTE_D-1:0] uart_data;
reg cnt_baud_flag;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_baud <= 0;
else if(add_cnt_baud)begin
if(end_cnt_baud)
cnt_baud <= 0;
else
cnt_baud <= cnt_baud + 1'b1;
end
end
assign add_cnt_baud = cnt_baud_flag;
assign end_cnt_baud = add_cnt_baud && cnt_baud == baud - 1;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_byte <= 0;
else if(add_cnt_byte)begin
if(end_cnt_byte)
cnt_byte <= 0;
else
cnt_byte <= cnt_byte + 1'b1;
end
end
assign add_cnt_byte = end_cnt_baud;
assign end_cnt_byte = add_cnt_byte && cnt_byte == BYTE_D - 1;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
uart_tx <= 1;
else if(cnt_baud == 0 && add_cnt_baud)
uart_tx <= uart_data[cnt_byte];
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
uart_data <= 10'b0;
else if(!cnt_baud_flag && send_en)
uart_data <= {1'b1,data,1'b0};
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_baud_flag <= 0;
else if(send_en)
cnt_baud_flag <= 1;
else if(end_cnt_byte)
cnt_baud_flag <= 0;
end
always @(*)begin
case(baud_set)
3'd0:baud = 17'd83333;//600bps
3'd1:baud = 17'd41666;//1200bps
3'd2:baud = 17'd20833;//2400bps
3'd3:baud = 17'd10416;//4800bps
3'd4:baud = 17'd5208 ;//9600bps
3'd5:baud = 17'd2604 ;//19200bps
3'd6:baud = 17'd1302 ;//38400bps
3'd7:baud = 17'd868 ;//57600bps
default:
baud = 0;
endcase
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
tx_done <= 0;
else if(end_cnt_byte)
tx_done <= 1;
else
tx_done <= 0;
end
endmodule
四、板级测试代码
由于开发板输入端口不够,所以专门写了一个每10毫秒发送一串数据的代码
module uart_tx_multibyte_test(
clk ,
rst_n ,
uart_tx
);
parameter DATAN_W = 40;
parameter CNT_D = 500000;
parameter CNT_W = 19;
input clk;
input rst_n;
output uart_tx;
reg trans_go;
reg [DATAN_W-1:0] data_n;
reg [CNT_W-1:0] cnt;
wire end_cnt;
uart_tx_multibyte uart_tx_multibyte(
.clk (clk), //时钟
.rst_n (rst_n), //复位
.data_n (data_n), //要发送的多字节数据
.trans_go (trans_go), //发送使能
.uart_tx (uart_tx) //串口发送数据
);
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt <= 0;
else if(end_cnt)
cnt <= 0;
else
cnt <= cnt + 1;
end
assign end_cnt = cnt == CNT_D - 1;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
data_n <= 40'hA987654321;
else if(add_cnt_data)begin
if(end_cnt_data)
data_n <= 40'hA987654321;
else
data_n <= data_n +1'b1;
end
end
assign add_cnt_data = end_cnt;
assign end_cnt_data = add_cnt_data && data_n == 40'hB987654321 - 1;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
trans_go <= 0;
else if(end_cnt)
trans_go <= 1;
else
trans_go <= 0;
end
endmodule
五、顶层代码仿真文件
`timescale 1ns / 1ps
module uart_tx_multibyte_tb();
parameter DATAN_W = 40; //发送数据个数
parameter CYCLE = 20;
parameter RST_TIME = 3;
reg clk;
reg rst_n;
reg [DATAN_W-1:0] data_n;
reg trans_go;
wire uart_tx;
uart_tx_multibyte uart_tx_multibyte(
clk , //时钟
rst_n , //复位
data_n , //要发送的多字节数据
trans_go , //发送使能
uart_tx //串口发送数据
);
initial begin
clk = 1;
forever begin
#(CYCLE/2);
clk = ~clk;
end
end
initial begin
rst_n = 1;
#3;
rst_n = 0;
#(RST_TIME*CYCLE);
rst_n = 1;
end
initial begin
#1;
data_n = 0;
#(10*CYCLE);
data_n = 40'hA987654321;
#5400000;
data_n = 40'h123456789A;
end
initial begin
#1;
trans_go = 0;
#(10*CYCLE);
trans_go = 1;
#(CYCLE);
trans_go = 0;
#5400000;
trans_go = 1;
#(CYCLE);
trans_go = 0;
end
initial begin
#12000000;
$stop;
end
endmodule
六、测试代码仿真文件
`timescale 1ns / 1ns
module uart_tx_multibyte_test_tb();
reg clk;
reg rst_n;
wire uart_tx;
uart_tx_multibyte_test uart_tx_multibyte_test(
clk ,
rst_n ,
uart_tx
);
parameter CYCLE = 20;
parameter RST_TIME = 3;
initial begin
clk = 1;
forever begin
#(CYCLE/2)
clk = ~clk;
end
end
initial begin
rst_n = 1;
#3;
rst_n = 0;
#(RST_TIME * CYCLE);
rst_n = 1;
end
initial begin
#15203500
$stop;
end
endmodule