Serial communication is a commonly used low-speed communication interface. It is also used more frequently during debugging. First, implement the serial port.
UART serial communication requires two signal lines, one for serial transmission and the other for serial reception. A frame of data in the UART transmission or reception process consists of four parts: start bit, data bit, parity bit and stop bit.
The protocol is as follows:
Because the serial port is asynchronous, the receiving and sending are implemented separately. The following is the receiving end:
`timescale 1ns / 1ns
module uartrx(
input clk,
input rst_n,
//uart interface
input uart_rx,
//user interface
output dataout_vld ,
output [7:0] dataout
);
//localparam
localparam BPS = 9600 ;
localparam BPS_CNT = 2500 ; // T/BPS
//reg define
reg [12:0] pcnt ;
reg [2:0] rx_dly ;
reg [3:0] rx_cnt ;
reg [7:0] rx_reg ;
reg rx_en ;
//wire define
wire rx_falling ;
assign rx_falling = rx_dly[2] & ~rx_dly[1] ;
//打拍 同步
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
rx_dly <= 3'b000 ;
else
rx_dly <= {rx_dly[1:0], uart_rx} ;
end
//接收到起始信号,开始标志
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
rx_en <= 1'b0 ;
else if(rx_falling == 1'b1)
rx_en <= 1'b1 ;
else if(rx_cnt == 4'd9)
rx_en <= 1'b0 ;
else ;
end
//rx 接收字节计数
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
rx_cnt <= 4'd0;
else if(rx_en == 1'b1)
if(pcnt == BPS_CNT -1'b1)
rx_cnt <= rx_cnt + 1'b1;
else;
else
rx_cnt <= 4'd0;
end
//PBS CNT
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
pcnt <= 13'd0;
else if(rx_en == 1'b1)
if(pcnt == BPS_CNT -1'b1)
pcnt <= 13'd0 ;
else
pcnt <= pcnt + 1'b1 ;
else
pcnt <= 13'd0;
end
//rx 接收数据
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
rx_reg <= 8'b0;
else if(pcnt == BPS_CNT/2 -1'b1)
case(rx_cnt)
4'd1: rx_reg[0] <= uart_rx ;
4'd2: rx_reg[1] <= uart_rx ;
4'd3: rx_reg[2] <= uart_rx ;
4'd4: rx_reg[3] <= uart_rx ;
4'd5: rx_reg[4] <= uart_rx ;
4'd6: rx_reg[5] <= uart_rx ;
4'd7: rx_reg[6] <= uart_rx ;
4'd8: rx_reg[7] <= uart_rx ;
default : ;
endcase
else ;
end
assign dataout_vld = (rx_cnt == 4'd9) ? 1'b1 : 1'b0;
assign dataout = rx_reg ;
endmodule
The sending end and the receiving end are similar, that is, one sends data and the other receives data.
`timescale 1ns / 1ns
module uarttx(
input clk,
input rst_n,
//uart interface
output uart_tx,
//user interface
input datain_vld,
input [7:0] datain,
output uart_ack
);
//localparam
localparam BPS = 9600 ;
localparam BPS_CNT = 2500 ; // T/BPS
//reg define
reg [12:0] pcnt ;
reg [2:0] tx_dly ;
reg [3:0] tx_cnt ;
reg [7:0] tx_reg ;
reg tx_en ;
reg uart_tx ;
//wire define
wire tx_rising ;
assign tx_rising = ~tx_dly[2] & tx_dly[1] ;
//打拍 同步
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
tx_dly <= 3'b000 ;
else
tx_dly <= {tx_dly[1:0], datain_vld} ;
end
//接收到起始信号,开始标志
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
tx_en <= 1'b0 ;
else if(tx_rising == 1'b1)
tx_en <= 1'b1 ;
else if(tx_cnt == 4'd9)
tx_en <= 1'b0 ;
else ;
end
//rx 接收字节计数
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
tx_cnt <= 4'd0;
else if(tx_en == 1'b1)
if(pcnt == BPS_CNT -1'b1)
tx_cnt <= tx_cnt + 1'b1;
else;
else
tx_cnt <= 4'd0;
end
//PBS CNT
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
pcnt <= 13'd0;
else if(tx_en == 1'b1)
if(pcnt == BPS_CNT -1'b1)
pcnt <= 13'd0 ;
else
pcnt <= pcnt + 1'b1 ;
else
pcnt <= 13'd0;
end
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
tx_reg <= 8'b0;
else if(tx_en == 1'b1)
// tx_reg <= 8'h55;
tx_reg <= datain;
else ;
end
//tx 接收数据
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
uart_tx <= 1'b1;
else if(tx_en == 1'b1)
case(tx_cnt)
4'd0: uart_tx <= 1'b0 ;
4'd1: uart_tx <= tx_reg[0] ;
4'd2: uart_tx <= tx_reg[1] ;
4'd3: uart_tx <= tx_reg[2] ;
4'd4: uart_tx <= tx_reg[3] ;
4'd5: uart_tx <= tx_reg[4] ;
4'd6: uart_tx <= tx_reg[5] ;
4'd7: uart_tx <= tx_reg[6] ;
4'd8: uart_tx <= tx_reg[7] ;
4'd9: uart_tx <= 1'b1 ;
default : ;
endcase
else ;
end
assign uart_ack = (tx_en == 1'b1) ? 1'b0 : 1'b1;
assign dataout = tx_reg ;
endmodule
In fact, it is better to use one module to represent the baud rate between TX and RX, so that it is convenient to modify the baud rate of different rates later and realize adjustable baud rate.
Then implement the top file, associate tx, rx, and implement receiving and sending. Because nothing else has been implemented, the byte is sent directly after receiving the data.
wire [7:0] datain ;
wire datain_vld;
wire [7:0] dataout ;
wire dataout_vld ;
assign datain = dataout ;
assign datain_vld = dataout_vld ;
uarttx u_uart_tx(
.clk ( CLK_24M ),
.rst_n ( rst_n ),
.uart_tx ( UART_TX ),
.datain_vld ( datain_vld ),
.datain ( datain ),
.uart_ack ( uart_ack )
);
uartrx u_uart_rx(
.clk ( CLK_24M ),
.rst_n ( rst_n ),
.uart_rx ( UART_RX ),
.dataout_vld ( dataout_vld),
.dataout ( dataout )
);
If you don't simulate the board, it's just a rogue. Then use the modelsim function to simulate and check whether the basic sending and receiving functions are normal.
Simply check that the data bits sent and received meet the requirements, and then test the performance directly on the board.
Use the serial communication tool to connect to the virtual interface, and keep sending data "Anlogic fpga" to check whether the data received by the serial tool is normal.
Through verification, the basic functions of the serial communication tool are normal.
The UART to localbus protocol will be added later to implement the control of each chip on the board by issuing instructions through the serial port, which will also facilitate later debugging.
|