关于UART通信总线的基本概念相比大家都已熟知,这里就不再赘述了。本文旨在设计一个支持多种波特率、数据位宽以及校验位的通用UART接收和发送模块,接下来直接给出具体的代码。
通用UART收发模块Verilog代码
module Uart_Tx#(parameter BAUD_RATE = 115200 ,//Baud Rate:9600,19200,38400,57600,115200,230400; default:115200
CLK_FREQY = 50_000_000 ,//System clk; default:50MHz
STOP_BIT = 1 ,//Stop Bit Width:1 bit,2 bit; default:1 bit
CHECK_TYP = "NONE" ,//Parity Check:0-NONE,1-ODD,2-EVEN; default:0
DATA_WIDE = 8 //Data Wide:4,5,6,7,8 bit; default:8bit
)(
input clk ,
input rst_n ,
input [DATA_WIDE - 1 : 0] din ,
input din_vld ,
output ready ,
output uart_txd
);
//function declare
function integer clog2b(input integer data);begin
data = data - 1;
for(clog2b = 0;data > 0;clog2b = clog2b + 1)begin
data = data >> 1;
end
end
endfunction
localparam BIT_PERIOD = CLK_FREQY / BAUD_RATE ,//The number of clock cycles to Tx one bit
CHK_TYPE = (CHECK_TYP == "NONE") ? 0 :
(CHECK_TYP == "ODD" ) ? 1 :
(CHECK_TYP == "EVEN") ? 2 : 0 ,
CNT0_W = clog2b(BIT_PERIOD) ,//Count BIT_PERIOD - 1 clock cycles
CNT1_W = clog2b(16) ;//DATA_WIDE bit to Tx
localparam IDLE = 6'b00_0001,
START = 6'b00_0010,//Tx Start bit
DATA = 6'b00_0100,//Tx Data bit
CHECK = 6'b00_1000,//Tx Check bit
STOP = 6'b01_0000,//Tx Stop bit
DONE = 6'b10_0000;//Tx Done
//Signal declare
reg [5 : 0] state_c ;
reg [5 : 0] state_n ;
wire idle2start ;
wire start2data ;
wire data2stop ;
wire data2check ;
wire check2stop ;
wire stop2done ;
reg [CNT0_W - 1 : 0] cnt0 ;//0 ~ BIT_PERIOD - 1
wire add_cnt0 ;
wire end_cnt0 ;
reg [CNT1_W - 1 : 0] cnt1 ;//0 ~ xx - 1
wire add_cnt1 ;
wire end_cnt1 ;
reg [CNT1_W - 1 : 0] xx ;
reg [DATA_WIDE - 1 : 0] tx_data ;//Latch din
reg tx_bit ;//Tx bit
//FSM
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
state_c <= IDLE;
end
else begin
state_c <= state_n;
end
end
always @(*)begin
case(state_c)
IDLE :state_n = idle2start ? START : state_c;
START:state_n = start2data ? DATA : state_c;
DATA :state_n = data2stop ? STOP : (data2check ? CHECK : state_c);
CHECK:state_n = check2stop ? STOP : state_c;
STOP :state_n = stop2done ? DONE : state_c;
DONE :state_n = IDLE;
default:state_n = IDLE;
endcase
end
assign idle2start = state_c == IDLE && (din_vld);
assign start2data = state_c == START && (end_cnt1);//Start bit(bit0) Tx done
assign data2stop = state_c == DATA && (CHK_TYPE == 0 && end_cnt1);
assign data2check = state_c == DATA && (CHK_TYPE != 0 && end_cnt1);
assign check2stop = state_c == CHECK && (end_cnt1);
assign stop2done = state_c == STOP && (end_cnt1);
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
cnt0 <= 0;
end
else if(add_cnt0)begin
if(end_cnt0)
cnt0 <= 0;
else
cnt0 <= cnt0 + 1;
end
end
assign add_cnt0 = state_c != IDLE;
assign end_cnt0 = add_cnt0 && cnt0 == BIT_PERIOD - 1;
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
cnt1 <= 0;
end
else if(add_cnt1)begin
if(end_cnt1)
cnt1 <= 0;
else
cnt1 <= cnt1 + 1;
end
end
assign add_cnt1 = end_cnt0;
assign end_cnt1 = add_cnt1 && cnt1 == xx - 1;
always @(*)begin
if(state_c == START)
xx = 1;
else if(state_c == DATA)
xx = DATA_WIDE;
else if(state_c == CHECK)
xx = 1;
else //if(state_c == STOP)
xx = STOP_BIT;
end
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
tx_data <= 'd0;
end
else if(din_vld)begin
tx_data <= din;
end
end
//Tx bit
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
tx_bit <= 1'b1;
end
else begin
case(state_c)
START:tx_bit <= 1'b0;
DATA :tx_bit <= tx_data[cnt1];
CHECK:tx_bit <= CHK_TYPE == 2 ? (^tx_data) : (~^tx_data);
STOP :tx_bit <= 1'b1;
default:tx_bit <= 1'b1;
endcase
end
end
//Output
assign uart_txd = tx_bit;
assign ready = state_c == IDLE;
endmodule
module Uart_Rx#(parameter BAUD_RATE = 115200 ,//Baud Rate:9600,19200,38400,57600,115200,230400; default:115200
CLK_FREQY = 50_000_000 ,//System clk; default:50MHz
STOP_BIT = 1 ,//Stop Bit Width:1 bit,2 bit; default:1 bit
CHECK_TYP = "NONE" ,//Parity Check:0-NONE,1-ODD,2-EVEN; default:0
DATA_WIDE = 8 //Data Wide:4,5,6,7,8 bit; default:8bit
)(
input clk ,
input rst_n ,
input uart_rxd ,
output [DATA_WIDE - 1 : 0] rx_dout ,
output rx_dout_vld
);
//function declare
function integer clog2b(input integer data);begin
data = data - 1;
for(clog2b = 0;data > 0;clog2b = clog2b + 1)begin
data = data >> 1;
end
end
endfunction
localparam BIT_PERIOD = CLK_FREQY / BAUD_RATE ,//The number of clock cycles to Rx one bit
RX_WIDE = (CHECK_TYP == "NONE") ? (DATA_WIDE + 1 + STOP_BIT) : (DATA_WIDE + 1 + 1 + STOP_BIT) ,//Start bit + Data bit (+ Check bit) + Stop bit
CHK_TYPE = (CHECK_TYP == "NONE") ? 0 : (CHECK_TYP == "ODD" ) ? 1 : (CHECK_TYP == "EVEN") ? 2 : 0 ,
CNT0_W = clog2b(BIT_PERIOD / 16) ,//16 times oversampling
CNT1_W = clog2b(16) ,//16
CNT2_W = clog2b(RX_WIDE) ;//RX_WIDE bit to Rx
localparam IDLE = 6'b00_0001,
START = 6'b00_0010,//Rx Start bit
DATA = 6'b00_0100,//Rx Data bit
CHECK = 6'b00_1000,//Rx Check bit
STOP = 6'b01_0000,//Rx Stop bit
DONE = 6'b10_0000;//Rx Done
//signale declare
reg [5 : 0] state_c ;
reg [5 : 0] state_n ;
wire idle2start ;
wire start2data ;
wire start2idle ;
wire data2stop ;
wire data2check ;
wire check2stop ;
wire stop2done ;
reg [CNT0_W - 1 : 0] cnt0 ;//0 ~ BIT_PERIOD / 16 - 1
wire add_cnt0 ;
wire end_cnt0 ;
reg [CNT1_W - 1 : 0] cnt1 ;//0 ~ 16 - 1
wire add_cnt1 ;
wire end_cnt1 ;
reg [CNT2_W - 1 : 0] cnt2 ;//0 ~ xx - 1
wire add_cnt2 ;
wire end_cnt2 ;
reg [CNT2_W - 1 : 0] xx ;
reg [3 : 0] oversample_acc ;//Oversampling accumulator
reg [RX_WIDE - 1 : 0] rx_data ;
reg uart_rxd_sync ;//Sync uart_rxd input
reg [1 : 0] uart_rxd_reg ;//Register uart_rxd_sync 2 clk cycle
wire uart_rxd_nedge ;//uart_rxd_sync negedge
reg [DATA_WIDE - 1 : 0] data ;//Output rx_dout register
reg data_vld ;
//FSM
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
state_c <= IDLE;
end
else begin
state_c <= state_n;
end
end
always @(*)begin
case(state_c)
IDLE :state_n = idle2start ? START : state_c;
START:state_n = start2data ? DATA : (start2idle ? IDLE : state_c);
DATA :state_n = data2stop ? STOP : (data2check ? CHECK : state_c);
CHECK:state_n = check2stop ? STOP : state_c;
STOP :state_n = stop2done ? DONE : state_c;
DONE :state_n = IDLE;
default:state_n = IDLE;
endcase
end
assign idle2start = state_c == IDLE && (uart_rxd_nedge);
assign start2data = state_c == START && (end_cnt2 && oversample_acc[3] == 1'b0); //Start bit(bit0) is low,contiue
assign start2idle = state_c == START && (end_cnt2 && oversample_acc[3] == 1'b1); //Start bit(bit0) is not low
assign data2stop = state_c == DATA && (CHK_TYPE == 0 && end_cnt2);
assign data2check = state_c == DATA && (CHK_TYPE != 0 && end_cnt2);
assign check2stop = state_c == CHECK && (end_cnt2);
assign stop2done = state_c == STOP && (end_cnt2);
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
cnt0 <= 0;
end
else if(add_cnt0)begin
if(end_cnt0)
cnt0 <= 0;
else
cnt0 <= cnt0 + 1;
end
end
assign add_cnt0 = state_c != IDLE;
assign end_cnt0 = add_cnt0 && cnt0 == (BIT_PERIOD >> 4) - 1;
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
cnt1 <= 0;
end
else if(add_cnt1)begin
if(end_cnt1)
cnt1 <= 0;
else
cnt1 <= cnt1 + 1;
end
end
assign add_cnt1 = end_cnt0;
assign end_cnt1 = add_cnt1 && cnt1 == 16 - 1;
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
cnt2 <= 0;
end
else if(add_cnt2)begin
if(end_cnt2)
cnt2 <= 0;
else
cnt2 <= cnt2 + 1;
end
end
assign add_cnt2 = end_cnt1;
assign end_cnt2 = add_cnt2 && cnt2 == xx - 1;
always @(*)begin
if(state_c == START)
xx = 1;
else if(state_c == DATA)
xx = DATA_WIDE;
else if(state_c == CHECK)
xx = 1;
else //if(state_c == STOP)
xx = STOP_BIT;
end
//Sync Register
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
uart_rxd_sync <= 1'b1;
uart_rxd_reg <= 2'b11;
end
else begin
uart_rxd_sync <= uart_rxd;
uart_rxd_reg <= {uart_rxd_reg[0],uart_rxd_sync};
end
end
assign uart_rxd_nedge = ~uart_rxd_reg[0] & uart_rxd_reg[1];
//Over sample
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
oversample_acc <= 'd0;
end
else if(idle2start || end_cnt1)begin //High priority
oversample_acc <= 'd0;
end
else if(end_cnt0)begin
case(cnt1)
3,4,5,6,7,8,9,10,11,12:oversample_acc <= oversample_acc + uart_rxd_reg[1];
default :oversample_acc <= oversample_acc;
endcase
end
end
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
rx_data <= 'd0;
end
else if(end_cnt1)begin
rx_data <= {oversample_acc[3],rx_data[RX_WIDE - 1 : 1]};
end
end
//Check the result is correct or not
generate
case(CHK_TYPE)
1:begin //Ji Parity
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
data_vld <= 1'b0;
end
else begin
data_vld <= state_c == DONE && (^rx_data[1 +: DATA_WIDE + 1]);
end
end
end
2:begin //Ou Parity
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
data_vld <= 1'b0;
end
else begin
data_vld <= state_c == DONE && (~^rx_data[1 +: DATA_WIDE + 1]);
end
end
end
default:begin //No Parity
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
data_vld <= 1'b0;
end
else begin
data_vld <= state_c == DONE;
end
end
end
endcase
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
data <= 'd0;
end
else if(state_c == DONE)begin
data <= rx_data[1 +: DATA_WIDE];
end
end
endgenerate
//Output
assign rx_dout = data ;
assign rx_dout_vld = data_vld;
endmodule