1 シリアルポートプロトコル
シリアル ポートの正式名は Universal Asynchronous Receiver Transmitter で、主にデータ間のシリアル伝送に使用され、全二重伝送モードです。データ送信時はパラレルデータをシリアルデータに変換して送信し、データ受信時は受信したシリアルデータをパラレルデータに変換します。
UART の送信または受信中のデータのフレームは、スタート ビット、データ ビット、パリティ ビット、ストップ ビットの 4 つの部分で構成されます。スタート ビットはデータ フレームの始まりを示し、ストップ ビットはデータ フレームの終わりを示します。データ ビットはデータ フレーム内の有効なデータであり、パリティ ビットは奇数パリティまたは偶数パリティに分割できます。
スタートビット:
TX送信信号はデフォルトではローレベルであり、立ち下がりエッジが現れ、ローレベルが1ビット継続した場合にスタートビットが送信されたものとみなされます。
データビットは
送信に有効なデータであり、データのビット幅は 6、7、または 8 ビットから選択できます。
チェックデジット:
送信データの正当性をある程度確認できる
ストップ ビット:
1 ビット続くハイ レベルはデータの終わりとみなされます。
以下に示すように:(データのビット幅は 1 バイトです)
ボーレート
: ボーレートは、1 秒間に何ビットのデータを送信する必要があるかを示します。たとえば、9600bps は、1 秒間に 9600 ビットを送信する必要があることを意味します。
2 シリアルポート送信モジュール
信号の解釈:
カチカチ | 時計 |
---|---|
最初_n | リセット |
tx_data | 送信される1バイトのデータ |
tx_ja | データ送信のイネーブル信号はハイパルスです |
バイト_フィニッシュ | 1バイトのデータが正常に送信されました |
TX | 信号が送信されました |
設計の全体的な考え方 全体的な考え方
は、アイドル状態 (IDLE)、開始状態 (START)、データ送信 (SEND_DATA)、検証 (EVEN_ODD_CHECK)、および停止 (STOP) の 5 つの状態を持つステート マシンを設計することです。
まず、グローバル データをファイル config.v に配置します。コードは次のとおりです。
`define CLK_FRE 50_000_000 //输入的时钟频率
`define BAUD_RATE 115200 //波特率
`define EVEN_CHECK 1 //1 :偶校验 0:奇校验
uart_tx のモジュールコードは次のとおりです。
`include "config.v"
module uart_tx (
input wire clk ,
input wire rst_n ,
input wire [7:0] tx_data ,
input wire tx_en ,
output wire byte_finish ,
output reg tx
);
localparam IDLE = 5'b00001 , //空闲
START = 5'b00010 , //起始
SEND_DATA = 5'b00100 , //发送数据
EVEN_OLD_CHECK = 5'b01000 , //奇偶校验
STOP = 5'b10000 ; //停止
localparam CNT_MAX = `CLK_FRE / `BAUD_RATE ;
reg [31:0] cnt ;
reg [4:0] state ;
reg [4:0] nx_state ;
reg [2:0] cnt_data ;
reg even_check ;
reg odd_check ;
wire start_finish; //起始位结束信号
wire bit_flag ; //发送每个数据的bit信号
wire byte_flag ; //成功发送一字节数据的标致
wire check_flag ; //奇偶校验的标志
wire stop_flag ; //停止位的标志
wire check ; //要发送的奇偶校验信号
assign start_finish = (state == START) && (cnt == CNT_MAX - 1) ? 1'b1 : 1'b0;
assign bit_flag = (state == SEND_DATA) && (cnt == CNT_MAX - 1) ? 1'b1 : 1'b0;
assign byte_flag = bit_flag && (cnt_data == 3'd7) ? 1'b1 : 1'b0;
assign check_flag = ((state == EVEN_OLD_CHECK) && (cnt == CNT_MAX - 1)) ? 1'b1 : 1'b0;
assign stop_flag = ((state == STOP) && (cnt == CNT_MAX - 1)) ? 1'b1 : 1'b0;
assign check = `EVEN_CHECK ? even_check : odd_check;
//状态转移(时序逻辑)
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
state <= IDLE;
end
else begin
state <= nx_state;
end
end
//状态跳转(组合逻辑)
always @(*) begin
//nx_state <= IDLE;
case(state)
IDLE: nx_state = (tx_en) ? START : IDLE;
START: nx_state = (start_finish) ? SEND_DATA : START;
SEND_DATA: nx_state = byte_flag ? EVEN_OLD_CHECK : SEND_DATA;
EVEN_OLD_CHECK: nx_state = check_flag ? STOP : EVEN_OLD_CHECK;
STOP: nx_state = stop_flag ? IDLE : STOP;
default: nx_state = IDLE;
endcase
end
//对cnt计数器赋值
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt <= 'd0;
end
else begin
case(state)
IDLE: cnt <= 'd0;
START: begin
if(nx_state == START) begin
cnt <= cnt + 1'b1;
end
else if(nx_state == SEND_DATA) begin
cnt <= 'd0;
end
else begin
cnt <= cnt;
end
end
SEND_DATA:begin
if(nx_state == SEND_DATA) begin
if(bit_flag) begin
cnt <= 'd0;
end
else begin
cnt <= cnt + 1'b1;
end
end
else if(nx_state == EVEN_OLD_CHECK) begin
cnt <= 'd0;
end
else begin
cnt <= cnt;
end
end
EVEN_OLD_CHECK: cnt <= (nx_state == EVEN_OLD_CHECK) ? cnt + 1'b1 : (nx_state == STOP) ? 'd0 : cnt;
STOP: cnt <= (nx_state == STOP) ? cnt + 1'b1 : 1'b0;
default:cnt <= 'd0;
endcase
end
end
//统计发送的数据的bit数
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt_data <= 3'd0;
end
else if((state == SEND_DATA) && (cnt_data == 3'd7) && (bit_flag)) begin
cnt_data <= 'd0;
end
else if((state == SEND_DATA) && (cnt_data < 3'd7) && (bit_flag)) begin
cnt_data <= cnt_data + 1'b1;
end
else begin
cnt_data <= cnt_data;
end
end
//生成奇校验还是偶校验
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
even_check <= 1'b0;
odd_check <= 1'b0;
end
else if(tx_en) begin
even_check <= ^tx_data;
odd_check <= ~(^tx_data);
end
else begin
even_check <= even_check;
odd_check <= odd_check;
end
end
assign byte_finish = stop_flag;
//发送数据
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
tx <= 1'b1;
end
else begin
case(nx_state)
IDLE: tx <= 1'b1;
START: tx <= 1'b0;
SEND_DATA: tx <= bit_flag ? tx_data[cnt_data+1] : tx_data[cnt_data];
EVEN_OLD_CHECK:tx <= check;
STOP: tx <= 1'b1;
default: tx <= 1'b1;
endcase
end
end
endmodule
テストベンチは次のとおりです。
`timescale 1ns/1ns
`define CLK_CYCLE 20
module tb_uart_tx;
reg clk ;
reg rst_n ;
reg [7:0] tx_data ;
reg tx_en ;
wire byte_finish ;
wire tx ;
uart_tx u_uart_tx(
. clk (clk),
. rst_n (rst_n),
. tx_data (tx_data),
. tx_en (tx_en),
. byte_finish (byte_finish),
. tx (tx)
);
initial begin
clk = 1'b0;
rst_n = 1'b0;
tx_data = 8'h00;
tx_en = 1'b0;
#30
rst_n = 1'b1;
#100
repeat (2) @(posedge clk);
tx_en = 1'b1;
tx_data = 8'h45;
@(posedge clk);
tx_en = 1'b0;
@(negedge byte_finish);
tx_en = 1'b1;
tx_data = 8'h23;
@(posedge clk);
tx_en = 1'b0;
@(byte_finish);
#100
$finish;
end
always #(`CLK_CYCLE / 2) clk = ~clk;
endmodule
シミュレーション波形は以下の通りで、データが正常に送信されていることが分かります。
3シリアルポート受信モジュール
信号の解釈:
カチカチ | 時計 |
---|---|
最初_n | リセット |
TX | シングルビット信号を受信しました |
rx_data | 受信した1バイトのデータ |
バイト_フィニッシュ | 1バイトデータ受信完了信号 |
エラー | パリティチェックに基づいて、受信したデータが信頼できるかどうかを判断します |
設計の全体的な考え方 全体的な考え方
は、アイドル状態 (IDLE)、開始状態 (START)、データ受信 (RV_DATA)、検証 (CHECK)、および停止 (STOP) の 5 つの状態を持つステート マシンを設計することです。データのサンプリングでは、サンプリングされたデータの信頼性が高くなるように、ビット信号が選択されて中央でサンプリングされることに注意してください。
uart_rx のモジュールは次のとおりです。
`include "config.v"
module uart_rx(
input wire clk ,
input wire rst_n ,
input wire tx ,
output wire [7:0] rx_data ,
output reg error ,
output wire byte_finish
);
localparam IDLE = 5'b00001 , //空闲
START = 5'b00010 , //开始
RV_DATA = 5'b00100 , //接收数据
CHECK = 5'b01000 , //奇偶校验
STOP = 5'b10000 ; //停止
localparam CNT_MAX = `CLK_FRE / `BAUD_RATE ;
reg tx_dly ;
reg [31:0] cnt ;
reg [4:0] state ;
reg [4:0] nx_state ;
reg [2:0] cnt_bit ;
reg [7:0] rv_data ;
reg even_check ;
reg odd_check ;
wire tx_negedge ;
wire start_end ;
wire bit_flag ;
wire rv_end ;
wire sample_flag ; //采样数据的信号
wire check ;
wire check_end ;
wire stop_end ;
assign start_end = (state == START) && (cnt == CNT_MAX - 2);
assign bit_flag = (state == RV_DATA) && (cnt == CNT_MAX - 1);
assign rv_end = (state == RV_DATA) && (bit_flag) && (cnt_bit == 3'd7);
assign sample_flag = ((state == RV_DATA) ) && (cnt == CNT_MAX / 2 - 1);
assign check = `EVEN_CHECK ? even_check : odd_check;
assign check_end = (state == CHECK) && (cnt == CNT_MAX - 1);
assign stop_end = (state == STOP) && (cnt == CNT_MAX - 1);
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
tx_dly <= 1'b1;
end
else begin
tx_dly <= tx;
end
end
assign tx_negedge = ((tx_dly) && (!tx)) ? 1'b1 : 1'b0;
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
state <= IDLE;
end
else begin
state <= nx_state;
end
end
always @(*) begin
case(state)
IDLE: nx_state = tx_negedge ? START : IDLE;
START: nx_state = start_end ? RV_DATA : START;
RV_DATA:nx_state = rv_end ? CHECK : RV_DATA;
CHECK: nx_state = check_end ? STOP : CHECK;
STOP: nx_state = stop_end ? IDLE : STOP;
default:nx_state = IDLE;
endcase
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt <= 'd0;
end
else begin
case(state)
IDLE: cnt <= 'd0;
START: begin
if(nx_state == START) begin
cnt <= cnt + 1'b1;
end
else if(nx_state == RV_DATA) begin
cnt <= 'd0;
end
else begin
cnt <= cnt;
end
end
RV_DATA:begin
if(nx_state == RV_DATA) begin
if(bit_flag) begin
cnt <= 'd0;
end
else begin
cnt <= cnt + 1'b1;
end
end
else if(nx_state == CHECK) begin
cnt <= 'd0;
end
else begin
cnt <= cnt;
end
end
CHECK: cnt <= (nx_state == CHECK) ? cnt + 1'b1 : 'd0;
STOP : cnt <= (nx_state == STOP) ? cnt + 1'b1 : 'd0;
default:cnt <= 'd0;
endcase
end
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt_bit <= 3'd0;
end
else if((bit_flag) && (nx_state == CHECK)) begin
cnt_bit <= 3'd0;
end
else if(bit_flag) begin
cnt_bit <= cnt_bit + 1'b1;
end
else begin
cnt_bit <= cnt_bit;
end
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
rv_data <= 8'h00;
end
else if(state == IDLE) begin
rv_data <= 8'h00;
end
else if(sample_flag) begin
rv_data <= {
tx, rv_data[7:1]};
end
else begin
rv_data <= rv_data;
end
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
odd_check <= 1'b0;
even_check <= 1'b0;
end
else if(nx_state == CHECK) begin
even_check <= ^rv_data;
odd_check <= ~(^rv_data);
end
else begin
even_check <= even_check;
odd_check <= odd_check;
end
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
error <= 1'b0;
end
else if(nx_state == IDLE) begin
error <= 1'b0;
end
else if(nx_state == STOP) begin
error <= 1'b0;
end
else if((nx_state == CHECK) && sample_flag) begin
error <= (tx != check) ? 1'b1 : 1'b0;
end
else begin
error <= error;
end
end
assign rx_data = rv_data;
assign byte_finish = (state == STOP) && (nx_state == IDLE);
endmodule
4 UART送受信モジュールの協調シミュレーション
uart の最上位層は次のとおりです。
uart モジュールのコードは次のとおりで、送信モジュールと受信モジュールを接続します。
module uart(
input wire clk ,
input wire rst_n ,
input wire [7:0] tx_data ,
input wire tx_en ,
output wire [7:0] rx_data ,
output wire error ,
output wire rv_byte_finish
);
wire tx_byte_finish ;
wire tx ;
uart_tx u_uart_tx(
. clk (clk),
. rst_n (rst_n),
. tx_data (tx_data),
. tx_en (tx_en),
. byte_finish (tx_byte_finish),
. tx (tx)
);
uart_rx u_uart_rx(
. clk (clk),
. rst_n (rst_n),
. tx (tx),
. rx_data (rx_data),
. error (error),
. byte_finish (rv_byte_finish)
);
endmodule
シミュレーションコードは次のとおりです。
`timescale 1ns/1ns
`define CLK_CYCLE 20
module tb_uart;
reg clk ;
reg rst_n ;
reg [7:0] tx_data ;
reg tx_en ;
wire [7:0] rx_data ;
wire error ;
wire rv_byte_finish;
uart u_uart(
. clk (clk),
. rst_n (rst_n),
. tx_data (tx_data),
. tx_en (tx_en),
. rx_data (rx_data),
. error (error),
. rv_byte_finish(rv_byte_finish)
);
initial begin
clk = 1'b0;
rst_n = 1'b0;
tx_data = 8'h00;
tx_en = 1'b0;
#30
rst_n = 1'b1;
#100
repeat (2) @(posedge clk);
tx_en = 1'b1;
tx_data = 8'h45;
@(posedge clk);
tx_en = 1'b0;
@(negedge rv_byte_finish);
tx_en = 1'b1;
tx_data = 8'h23;
@(posedge clk);
tx_en = 1'b0;
@(rv_byte_finish);
#100
$finish;
end
always #(`CLK_CYCLE / 2) clk = ~clk;
endmodule
シミュレーション波形は以下の通りで、データが正常に受信できていることが分かります。
5 まとめ
ステートマシンの書き方や理解もだいぶ進んできた気がします ここまでで主要な3つの通信プロトコルが完成しました。さあ、さあ、さあ!!!