设计核心内容:
uart模块,包含tx/rx两个部分:
module uart(
input clk,
input rstn,
//tx
input [7:0] tx_data,
input tx_data_valid,
output tx_ready,
output tx,
//rx
input rx,
output [7:0] rx_data,
output rx_data_valid);
uart_tx u_tx(
.clk(clk),
.rstn(rstn),
.tx_data(tx_data),
.tx_data_valid(tx_data_valid),
.tx(tx),
.tx_ready(tx_ready));
uart_rx u_rx(
.clk(clk),
.rstn(rstn),
.tx_data_valid(tx_data_valid),
.rx(rx),
.rx_data(rx_data));
endmodule
下面阐述tx和rx部分:
module urat_tx(
input clk,
input rstn,
input [7:0] tx_data,
input tx_data_valid,
output tx_ready,
output tx
);
parameter CHECK_MODE = 1;//1 odd
localparam IDLE = 3'd0;
localparam START = 3'd1;
localparam TDATA = 3'd2;
localparam CHECK = 3'd3;
localparam STOP = 3'd4;
reg [2:0] st_cur;
reg [2:0] st_next;
//---------------------------------------------------
//data buffer
reg [7:0] tx_data_r;
reg tx_ready_r;
always @(posedge clk or negedge rstn)begin
if(!rstn)begin
tx_data_r <= 8'hFF;
tx_ready_r <= 1'b1;
end
//外部给valid信号拉高,同时内部不再工作状态(ready为高),将ready拉低,同时将外部数据缓存
else if(tx_data_valid && tx_ready)begin
tx_data_r <= tx_data;
tx_ready_r <= 1'b0;
end
//一个传输过程结束,将ready拉高
else if(st_cur == STOP && baud_cnt_half && st_next == IDLE)begin
tx_ready_r <= 1'b1;
end
end
assign tx_ready = tx_ready_r;
//---------------------------------------------------
//baud counter
parameter BAUND_NUM = 50_000_000/115200
reg [8:0] baud_cnt_r;
wire baud_cnt_end = (baud_cnt_r == BAUND_NUM-1);
wire baud_cnt_half = (baud_cnt_r == BAUND_NUM/2 - 1);
always @(posedge clk or negedge rstn)begin
if(!rstn)begin
baud_cnt_r <= 'b0;
end
else if(st_cur == IDLE)begin
baud_cnt_r <= 'b0;
end
else begin
if(baud_cnt_end)begin
baud_cnt_r <= 'b0;
end
else begin
baud_cnt_r <= baud_cnt_r + 1;
end
end
end
//---------------------------------------------------------
//bit count
reg [2:0] bit_cnt_r;
always @(posedge clk or negedge rstn)begin
if(!rstn)
bit_cnt_r <= 'b0;
else if(st_cur == TDATA) begin
if(baud_cnt_end)begin
bit_cnt_r <= bit_cnt_r + 1'b1;
end
end
else begin
bit_cnt_r <= 1'b0;
end
end
//----------------------------------------------------------
//Receiving FSM
//(1) state transfer
always @(posedge clk or negedge rstn)begin
if(!rstn)
st_cur <= IDLE;
else
st_cur <= st_next;
end
//(2)state switch
reg check_mode_ok;
always @(*)begin
case(st_cur)
IDLE:begin
if(tx_data_valid && tx_ready)//start condition
st_next = START;
else
st_next = IDLE;
end
START:begin
if(baud_cnt_end)
st_next = TDATA;
else
st_next = START;
end
TDATA:begin
if(baud_cnt_end && bit_cnt_r == 7)
st_next = CHECK;
else
st_next = TDATA;
end
CHECK:begin
if(baud_cnt_end)begin
st_next = STOP;
else
st_next = CHECK;
end
end
STOP:begin
if(baud_cnt_half)
st_next = IDLE;
else
st_next = STOP;
end
default: st_next = IDLE;
endcase
end
//(3) data out
reg tx_r;
always @(posedge clk or negedge rstn) begin
if(!rstn)begin
tx_r <= 1'b1;
end
else if(st_cur == IDLE && baud_cnt_r == 5)begin
tx_r <= 1'b1;
end
else if(st_cur == START && baud_cnt_r == 5)begin
tx_r <= 1'b0;
end
else if(st_cur == TDATA && baud_cnt_r == 5)begin
tx_r <= tx_data_r[bit_cnt_r];
end
else if(st_cur == CHECK && baud_cnt_r == 5)begin
tx_r <= ~(^tx_data_r[7:0]);//奇校验
// tx_r <= ^{tx_data_r,CHECK_MODE};//更为全面的写法
// for odd check,tx_r <=~(^tx_data_r[7:0]); for even check ,(^tx_data_r[7:0])
//so tx_r <= ^{tx_data_r[7:0],CHECK_MODE},check_mode equal 1 for odd,equal 0 for even
end
else if(st_cur == STOP && baud_cnt_r == 5)begin
tx_r <= 1'b1;
end
end
assign tx = tx_r;
endmodule
module urat_rx(
input clk,
input rstn,
input rx,
output [7:0] rx_data,
output rx_data_valid
);
parameter CHECK_MODE = 1;//1 odd
localparam IDLE = 3'd0;
localparam START = 3'd1;
localparam RDATA = 3'd2;
localparam CHECK = 3'd3;
localparam STOP = 3'd4;
reg [2:0] st_cur;
reg [2:0] st_next;
//---------------------------------------------------
//rx negedge detect
//检测下降沿,表示数据传输的开始;另外进行数据打拍,这是因为可能是异步数据,打拍打了
reg [3:0] rx_r;
always @(posedge clk or negedge rstn)begin
if(!rstn)
rx_r <= 4'b1111;
else
rx_r <= {rx_r[2:0],rx};
end
wire rx_neg = rx_r[3] & !rx_r[2];
/*打拍的写法等价于:
else
rx_r[0] <= rx;
rx_r[1] <= rx_r[0];
rx_r[2] <= rx_r[1];
rx_r[3] <= rx_r[2];
end
这里是打了四拍,可以用第三拍的数据,实际可以根据情况去打。
*/
//---------------------------------------------------
//baud counter
parameter BAUD_NUM = 50_000_000/115200
reg [8:0] baud_cnt_r;
wire baud_cnt_end = (baud_cnt_r == BAUD_NUM-1);
wire baud_cnt_half = (baud_cnt_r == BAUD_NUM/2 - 1);
always @(posedge clk or negedge rstn)begin
if(!rstn)begin
baud_cnt_r <= 'b0;
end
else if(st_cur == IDLE)begin
baud_cnt_r <= 'b0;
end
else begin
if(baud_cnt_end)begin
baud_cnt_r <= 'b0;
end
else begin
baud_cnt_r <= baud_cnt_r + 1;
end
end
end
//---------------------------------------------------------
//bit count
reg [2:0] bit_cnt_r;
always @(posedge clk or negedge rstn)begin
if(!rstn)
bit_cnt_r <= 'b0;
else if(st_cur == RDATA) begin
if(baud_cnt_end)begin
bit_cnt_r <= bit_cnt_r + 1'b1;
end
end
else begin
bit_cnt_r <= 1'b0;
end
end
//----------------------------------------------------------
//Receiving FSM
//(1) state transfer
always @(posedge clk or negedge rstn)begin
if(!rstn)
st_cur <= IDLE;
else
st_cur <= st_next;
end
//(2)state switch
reg check_mode_ok;
always @(*)begin
case(st_cur)
IDLE:begin
if(rx_neg)
st_next = START;
else
st_next = IDLE;
end
START:begin
if(baud_cnt_end)
st_next = RDATA;
else
st_next = START;
end
RDATA:begin
if(baud_cnt_end && bit_cnt_r == 7)
st_next = CHECK;
else
st_next = RDATA;
end
CHECK:begin
if(baud_cnt_end)begin
if(check_mode_ok)//必须再检测条件满足的条件下才会进入下个状态,否则到idle
st_next = STOP;
else
st_next = IDLE;
else
st_next = IDLE;
end
end
STOP:begin
if(baud_cnt_half)
st_next = IDLE;
else
st_next = STOP;
end
default: st_next = IDLE;
endcase
end
//(3) data out
reg [7:0] rx_data_r;
reg rx_data_valid_r;
always @(posedge clk or negedge rstn) begin
if(!rstn)begin
rx_data_r <= 0;
rx_data_valid_r <= 0;
end
//RDATA状态下,每次的baund_cnt_half状态下把单bit数据放到缓存中
else if(st_cur == RDATA && baud_cnt_half)begin
rx_data_r[bit_cnt_r] <= rx_r[2];
end
//CHECK状态下,如果检测满足的条件下,将valid拉高,表示往外给的数据是正确的
else if(st_cur == CHECK && st_next == STOP && check_mode_ok)begin
rx_data_valid_r <= 1'b1;
end
else begin
rx_data_valid_r <= 1'b0;
end
end
assign rx_data = rx_data_r;
assign rx_data_valid = rx_data_valid_r;
//check logic
always @(posedge clk or negedge rstn)begin
if(!rstn)begin
check_mode_ok <= 1'b1;
end
//奇偶校验逻辑
else if(st_cur == CHECK && baud_cnt_half && (^{rx_data_r,rx_r[2]} == CHECK_MODE))begin
check_mode_ok <= 1'b1;
end
else if(st_cur == IDLE)begin
check_mode_ok <= 1'b0;
end
end
endmodule
整体框架如下图: