UART(Universal Asynchronous Receiver/Transmitter),是一種非同步的傳輸協定,非同步傳輸的意思是,不管是接收端還是傳送端都有自己傳輸資料的速度(鮑率(Baud Rate)),傳輸時兩邊必須以同樣的鮑率來收發資料才不會出錯。
UART 它的好處是線路簡單,僅兩條線路(RX&TX),但缺點是只能一對一連接,以及速度不是很快,一般而言最高為 115.2kbps(常規鮑率為 9600)
從圖片中可以看到,Uart 的閒置狀態(IDLE)是高電平的,而起始位元則是 1 bit 的低電平,再來是 8 bit 資料的傳輸,而且是由 LSB 開始傳輸的,最後則是 1 bit 的高電平當作是結束位元。
例如現在要傳輸一個 0x55 (0101_0101) 的資料:
我們剛剛看完了 timing diagram 後發現,Uart 大致可以切成四個狀態,分別為 IDLE(閒置狀態)、START(起始位元)、SHIFT(8bit的輸出移位)以及STOP(停止位元)。
定義四個狀態:
/*---------parameter---------*/
localparam IDLE = 2'd0;
localparam START = 2'd1;
localparam SHIFT = 2'd2;
localparam STOP = 2'd3;
再來定義輸入輸出,記得!這邊還不是最外層的 Uart 模塊,別搞混了(想整個寫在一起也很好!)。
輸入:
輸出:
module Uart_TX(
tick_uart,
clk_50M,
rst_n,
count,
rstcount,
countEN,
TX_D,
LDEN,
SHEN,
en,
busy
);
/*---------ports declaration---------*/
input clk_50M;
input rst_n;
input en;
input tick_uart;
input [2:0] count;
output TX_D;
output LDEN;
output SHEN;
output rstcount;
output countEN;
output busy;
reg TX_D;
reg LDEN;
reg SHEN;
reg rstcount;
reg countEN;
wire busy;
/*---------assign wire---------*/
assign busy = (fstate!=IDLE);
宣告跑狀態的變數:
/*---------variables---------*/
reg [1:0] fstate;
狀態邏輯:
/*---------fstate state---------*/
always@(posedge clk_50M or negedge rst_n)begin
if(!rst_n)fstate <= IDLE;
else begin
case(fstate)
IDLE:begin
if(en)fstate <= START;
else fstate <= IDLE;
end
START:begin
if(tick_uart==1'b1)fstate <= SHIFT;
else fstate <= START;
end
SHIFT:begin
if(tick_uart==1'b1&&count==3'd6)fstate <= STOP;
else fstate <= SHIFT;
end
STOP:begin
if(tick_uart==1'b1)fstate <= IDLE;
else fstate <= STOP;
end
default:fstate <= IDLE;
endcase
end
end
輸出邏輯:
/*---------fstate output---------*/
always@(posedge clk_50M or negedge rst_n)begin
if(!rst_n)begin
TX_D <= 1'b1;
SHEN <= 1'b0;
LDEN <= 1'b0;
rstcount <= 1'b0;
countEN <= 1'b0;
end
else begin
if(tick_uart==1'b1)begin
case(fstate)
IDLE:begin
TX_D <= 1'b1;
SHEN <= 1'b0;
LDEN <= 1'b0;
rstcount <= 1'b0;
countEN <= 1'b0;
end
START:begin
TX_D <= 1'b0;
SHEN <= 1'b0;
LDEN <= 1'b1;
rstcount <= 1'b0;
countEN <= 1'b0;
end
SHIFT:begin
TX_D <= 1'b0;
SHEN <= 1'b1;
LDEN <= 1'b0;
if(count==3'd6) rstcount <= 1'b1;
else if(count<3'd6)rstcount <= 1'b0;
else rstcount <= 1'b0;//prevent latch
countEN <= 1'b1;
end
STOP:begin
TX_D <= 1'b1;
SHEN <= 1'b0;
LDEN <= 1'b0;
rstcount <= 1'b0;
countEN <= 1'b0;
end
default:begin
TX_D <= 1'b1;
SHEN <= 1'b0;
LDEN <= 1'b0;
rstcount <= 1'b0;
countEN <= 1'b0;
end
endcase
end
else begin
TX_D <= TX_D;
SHEN <= SHEN;
LDEN <= LDEN;
rstcount <= rstcount;
countEN <= countEN;
end
end
end
endmodule
這邊要注意的是,雖然說要數到 7 才跳下一個狀態,以及要數到 7 時 rstcount 才等於 1,但是!,狀態機模組發送訊號到外模組,外模組要在下一個 clk 才會收到,因此如果打 7 的話,會讓 SHEN 延長至 9 個鮑率週期,導致輸出移位的次數錯誤
以上就是我整個 Uart_TX 狀態機設計的方法~~