目录
前言
我好久之前就想做写通信协议接口的,之前写了RISC-V的处理器内核,想试试看SOC怎么搭,所以学习了下UART接口。
一、UART相关
UART(Universal Asynchronous Receiver/Transmitter,通用异步收发器)是一种通用串行数据总线,用于异步通信。该总线双向通信,可以实现全双工传输和接收。因此包含一个发送器和一个接收器。
工作原理是将传输数据的每个字符以串行方式一位接一位的传输,通信双方需要事先约定传输速率(波特率)。其具体工作时序图如下:
二、本次设计
本次设计思路为先分别设计收发模块,然后再在顶层组合。发送模块采取状态机写法,一帧数据包含起始位、8位数据位、奇校验位(忘了是奇校验还是偶校验)、结束位。接收模块采用16倍采样接收,从而保证采样准确性。为了能够在FPGA上测试,增加了单周期脉冲模块,用以将按键脉冲变成一个单时钟周期的脉冲信号。
(一)顶层模块
module top(
input wire clk,
input wire key,
input wire key2,
input wire [7:0] data,
output wire uart_txd,
output wire [7:0] o_data,
output wire over_flag
);
wire clk_divider1;
wire clk_divider2;
wire o_done;
wire signal_send_request;
divider #(7) divider_init1 (clk_divider2,key2,clk_divider1);
divider #(13) divider_init2 (clk,key2,clk_divider2);
UART_Tx UART_Tx_init(.data(data),.clk(clk_divider1),.signal_send_request(signal_send_request),.rst_n(key2),.uart_txd(uart_txd),.o_done(o_done));
Monopulse_clock Monopulse_clock_init(
.clk(clk_divider1),
.signal(key),
.rst_n(key2),
.o_monopulse(signal_send_request)
);
UART_Rx UART_Rx_init(
.uart_txd(uart_txd),
.clk(clk_divider2),
.rst_n(key2),
.data(o_data),
.over_flag(over_flag)
);
endmodule
(二)发送模块UART_Tx
module UART_Tx(
input wire [7:0] data,
input wire clk,
input wire signal_send_request,
input wire rst_n,
output reg uart_txd,
output reg o_done
);
parameter S_IDLE = 4'b0000;
parameter S_START = 4'b0001;
parameter S_DATA0 = 4'b0011;
parameter S_DATA1 = 4'b0010;
parameter S_DATA2 = 4'b0110;
parameter S_DATA3 = 4'b0111;
parameter S_DATA4 = 4'b0101;
parameter S_DATA5 = 4'b0100;
parameter S_DATA6 = 4'b1100;
parameter S_DATA7 = 4'b1101;
parameter S_CHECK = 4'b1111;
parameter S_STOP = 4'b1110;
wire bit_check;//奇校验码
reg [7:0] data_in;//数据缓存单元
/**********数据缓存***********/
always@(posedge signal_send_request or negedge rst_n) begin
if(!rst_n) begin
data_in <= 8'b0;
end
else data_in <= data;
end
/********状态机********/
reg [3:0] state_current;
reg [3:0] state_next;
/************FSM-1*************/
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
state_current <= S_IDLE;
end
else begin
state_current <= state_next;
end
end
/************FSM-2*************/
always@(state_current or signal_send_request) begin
state_next <= S_IDLE;
case (state_current)
S_IDLE :if(signal_send_request) begin
state_next <= S_START;
end
else state_next <= S_IDLE;
S_START:state_next <= S_DATA0;
S_DATA0:state_next <= S_DATA1;
S_DATA1:state_next <= S_DATA2;
S_DATA2:state_next <= S_DATA3;
S_DATA3:state_next <= S_DATA4;
S_DATA4:state_next <= S_DATA5;
S_DATA5:state_next <= S_DATA6;
S_DATA6:state_next <= S_DATA7;
S_DATA7:state_next <= S_CHECK;
S_CHECK:state_next <= S_STOP;
S_STOP :state_next <= S_IDLE;
default:state_next <= S_IDLE;
endcase
end
/************FSM-3*************/
always@(*) begin
if(!rst_n) begin
uart_txd <= 1'b1;
o_done <= 1'b0;
end
else begin
case (state_current)
S_IDLE :begin uart_txd <= 1'b1;o_done <= 1'b0;end
S_START : uart_txd <= 1'b0;
S_DATA0 : uart_txd <= data_in[0];
S_DATA1 : uart_txd <= data_in[1];
S_DATA2 : uart_txd <= data_in[2];
S_DATA3 : uart_txd <= data_in[3];
S_DATA4 : uart_txd <= data_in[4];
S_DATA5 : uart_txd <= data_in[5];
S_DATA6 : uart_txd <= data_in[6];
S_DATA7 : uart_txd <= data_in[7];
S_CHECK : uart_txd <= bit_check;
S_STOP :begin uart_txd <= 1'b1;o_done <= 1'b1;end
default : uart_txd <= 1'b1;
endcase
end
end
Parity_Checker parity_checker_init(data_in,bit_check);//奇校验码产生
endmodule
(三)奇校验码产生模块
module Parity_Checker(
input wire [7:0] data,
output wire bit_check
);
assign bit_check = (data[7]^data[6])^(data[5]^data[4])^(data[3]^data[2])^(data[1]^data[0]);
endmodule
(四)接收模块UART_Rx
module UART_Rx(
input wire uart_txd,
input wire clk,
input wire rst_n,
output reg [7:0] data,
output reg over_flag
);
parameter S_IDLE = 1'b0;
parameter S_EXE = 1'b1;
reg [3:0] cmt;
reg [3:0] c_cmt;
reg current_state;
reg next_state;
reg [10:0] temp;
reg [3:0] buffer;
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cmt <= 4'b0;
c_cmt <= 4'b0;
buffer <= 4'b0;
temp <= 11'b0;
end
else begin
if(current_state == S_EXE) begin
if(cmt == 4'b1111) begin
cmt <= 4'b0;
c_cmt <= c_cmt + 4'b0001;
temp[c_cmt] <= (buffer >4'd8);
buffer <= 4'b0;
end
else begin
cmt <= cmt + 4'b0001;
buffer <= buffer + {3'b000,uart_txd};
end
end
else begin
cmt <= 4'b0;
c_cmt <= 4'b0;
buffer <= 4'b0;
end
end
end
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
current_state <= S_IDLE;
end
else begin
current_state <= next_state;
end
end
always@(uart_txd,current_state,over_flag) begin
case (current_state)
S_IDLE:begin
if(!uart_txd) begin
next_state <= S_EXE;
end
else next_state <= S_IDLE;
end
S_EXE :begin
if(over_flag) begin
next_state <= S_IDLE;
end
else next_state <= S_EXE;
end
default:next_state <= S_IDLE;
endcase
end
always@(current_state,c_cmt) begin
case (current_state)
S_IDLE:begin
over_flag <= 1'b0;
end
S_EXE :begin
if(c_cmt >= 4'd11) begin
over_flag <= 1'b1;
end
else begin
over_flag <= 1'b0;
end
end
default:over_flag <= 1'b0;
endcase
end
always@(posedge over_flag or negedge rst_n) begin
if(!rst_n) begin
data <= 8'b0;
end
else begin
data <= temp[8:1];
end
end
endmodule
(五)单周期脉冲产生模块
module Monopulse_clock(
input wire clk,
input wire signal,
input wire rst_n,
output wire o_monopulse
);
reg signal_rst_pre;
reg signal_rst;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
signal_rst <= 1'b1;
signal_rst_pre <= 1'b1;
end
else begin
signal_rst <= signal;
signal_rst_pre <= signal_rst;
end
end
assign o_monopulse = signal_rst_pre & (~signal_rst);
endmodule
(六)分频器
module divider(
input wire clk,
input wire rst_n,
output reg clk_divider
);
parameter TIMESCALE = 216;
reg [15:0] cmt;
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cmt<= 16'b0;
clk_divider <= 1'b0;
end
else begin
if(cmt >= TIMESCALE) begin
cmt <= 16'b0;
clk_divider <= ~clk_divider;
end
else begin
cmt <= cmt + 16'b1;
end
end
end
endmodule
(七)测试平台
`timescale 1ns/1ns
module top_tp();
reg clk;
reg key;
reg key2;
reg [7:0] data;
wire uart_txd;
wire [7:0] o_data;
wire over_flag;
top top_init(
.clk(clk),
.key(key),
.key2(key2),
.data(data),
.uart_txd(uart_txd),
.o_data(o_data),
.over_flag(over_flag)
);
initial begin
clk = 1'b0;
forever begin
#10 clk = ~clk;
end
end
initial begin
#100 data = 8'b01101101;
end
initial begin
key = 1'b0;
key2 = 1'b1;
#300 key2 = 1'b0;
#500 key2 = 1'b1;
#500 key = 1'b1;
#500 key = 1'b0;
end
endmodule
结束语:很久才想起来这个坑,懒得加注释讲解了,略