tinyriscv/rtl/perips/uart.sv

426 lines
13 KiB
Systemverilog
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
Copyright 2020 Blue Liang, liangkangnan@163.com
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
`include "../core/defines.sv"
// 串口收发模块(默认: 115200, 8N1)
module uart(
input wire clk,
input wire rst_n,
input wire[31:0] addr_i,
input wire[31:0] data_i,
input wire[3:0] sel_i,
input wire we_i,
output wire[31:0] data_o,
output wire tx_pin,
input wire rx_pin
);
// 波特率115200bps
localparam BAUD_115200 = `CPU_CLOCK_HZ / 115200;
localparam S_IDLE = 4'b0001;
localparam S_START = 4'b0010;
localparam S_SEND_BYTE = 4'b0100;
localparam S_STOP = 4'b1000;
reg[3:0] state;
reg[3:0] next_state;
reg[15:0] cycle_cnt;
reg tx_bit;
reg[3:0] bit_cnt;
reg rx_q0;
reg rx_q1;
wire rx_negedge;
reg rx_start; // RX使能
reg[3:0] rx_clk_edge_cnt; // clk沿的个数
reg rx_clk_edge_level; // clk沿电平
reg rx_done;
reg[15:0] rx_clk_cnt;
reg[15:0] rx_div_cnt;
reg[7:0] rx_data;
reg rx_over;
// 寄存器(偏移)地址
localparam UART_CTRL = 8'h0;
localparam UART_STATUS = 8'h4;
localparam UART_BAUD = 8'h8;
localparam UART_TXDATA = 8'hc;
localparam UART_RXDATA = 8'h10;
// UART控制寄存器可读可写
// bit[0]: UART TX使能, 1: enable, 0: disable
// bit[1]: UART RX使能, 1: enable, 0: disable
reg[31:0] uart_ctrl;
// UART状态寄存器
// 只读bit[0]: TX空闲状态标志, 1: busy, 0: idle
// 可读可写bit[1]: RX接收完成标志, 1: over, 0: receiving
reg[31:0] uart_status;
// UART波特率寄存器(分频系数),可读可写
reg[31:0] uart_baud;
// UART发送数据寄存器可读可写
reg[31:0] uart_tx;
// UART接收数据寄存器只读
reg[31:0] uart_rx;
wire wen = we_i;
wire ren = (~we_i);
wire write_reg_ctrl_en = wen & (addr_i[7:0] == UART_CTRL);
wire write_reg_status_en = wen & (addr_i[7:0] == UART_STATUS);
wire write_reg_baud_en = wen & (addr_i[7:0] == UART_BAUD);
wire write_reg_txdata_en = wen & (addr_i[7:0] == UART_TXDATA);
wire tx_start = write_reg_txdata_en & sel_i[0] & uart_ctrl[0] & (~uart_status[0]);
wire rx_recv_over = uart_ctrl[1] & rx_over;
assign tx_pin = tx_bit;
// 写uart_rxdata
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
uart_rx <= 32'h0;
end else begin
// 接收完成时,保存接收到的数据
if (rx_recv_over) begin
uart_rx[7:0] <= rx_data;
end
end
end
// 写uart_txdata
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
uart_tx <= 32'h0;
end else begin
// 开始发送时,保存要发送的数据
if (tx_start) begin
uart_tx[7:0] <= data_i[7:0];
end
end
end
// 写uart_status
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
uart_status <= 32'h0;
end else begin
if (write_reg_status_en & sel_i[0]) begin
// 写RX完成标志
uart_status[1] <= data_i[1];
end else begin
// 开始发送数据时置位TX忙标志
if (tx_start) begin
uart_status[0] <= 1'b1;
// 发送完成时清TX忙标志
end else if ((state == S_STOP) & (cycle_cnt == uart_baud[15:0])) begin
uart_status[0] <= 1'b0;
// 接收完成,置位接收完成标志
end
if (rx_recv_over) begin
uart_status[1] <= 1'b1;
end
end
end
end
// 写uart_ctrl
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
uart_ctrl <= 32'h0;
end else begin
if (write_reg_ctrl_en & sel_i[0]) begin
uart_ctrl[7:0] <= data_i[7:0];
end
end
end
// 写uart_baud
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
uart_baud <= BAUD_115200;
end else begin
if (write_reg_baud_en) begin
if (sel_i[0]) begin
uart_baud[7:0] <= data_i[7:0];
end
if (sel_i[1]) begin
uart_baud[15:8] <= data_i[15:8];
end
end
end
end
reg[31:0] data_r;
// 读寄存器
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
data_r <= 32'h0;
end else begin
if (ren) begin
case (addr_i[7:0])
UART_CTRL: data_r <= uart_ctrl;
UART_STATUS: data_r <= uart_status;
UART_BAUD: data_r <= uart_baud;
UART_RXDATA: data_r <= uart_rx;
default: data_r <= 32'h0;
endcase
end else begin
data_r <= 32'h0;
end
end
end
assign data_o = data_r;
// *************************** TX发送 ****************************
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= S_IDLE;
end else begin
state <= next_state;
end
end
always @ (*) begin
case (state)
S_IDLE: begin
if (tx_start) begin
next_state = S_START;
end else begin
next_state = S_IDLE;
end
end
S_START: begin
if (cycle_cnt == uart_baud[15:0]) begin
next_state = S_SEND_BYTE;
end else begin
next_state = S_START;
end
end
S_SEND_BYTE: begin
if ((cycle_cnt == uart_baud[15:0]) & (bit_cnt == 4'd7)) begin
next_state = S_STOP;
end else begin
next_state = S_SEND_BYTE;
end
end
S_STOP: begin
if (cycle_cnt == uart_baud[15:0]) begin
next_state = S_IDLE;
end else begin
next_state = S_STOP;
end
end
default: begin
next_state = S_IDLE;
end
endcase
end
// cycle_cnt
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
cycle_cnt <= 16'h0;
end else begin
if (state == S_IDLE) begin
cycle_cnt <= 16'h0;
end else begin
if (cycle_cnt == uart_baud[15:0]) begin
cycle_cnt <= 16'h0;
end else begin
cycle_cnt <= cycle_cnt + 16'h1;
end
end
end
end
// bit_cnt
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
bit_cnt <= 4'h0;
end else begin
case (state)
S_IDLE: begin
bit_cnt <= 4'h0;
end
S_SEND_BYTE: begin
if (cycle_cnt == uart_baud[15:0]) begin
bit_cnt <= bit_cnt + 4'h1;
end
end
endcase
end
end
// tx_bit
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
tx_bit <= 1'b0;
end else begin
case (state)
S_IDLE, S_STOP: begin
tx_bit <= 1'b1;
end
S_START: begin
tx_bit <= 1'b0;
end
S_SEND_BYTE: begin
tx_bit <= uart_tx[bit_cnt];
end
endcase
end
end
// *************************** RX接收 ****************************
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
rx_q0 <= 1'b0;
rx_q1 <= 1'b0;
end else begin
rx_q0 <= rx_pin;
rx_q1 <= rx_q0;
end
end
// 下降沿检测(检测起始信号)
assign rx_negedge = rx_q1 & (~rx_q0);
// 产生开始接收数据信号,接收期间一直有效
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
rx_start <= 1'b0;
end else begin
if (uart_ctrl[1]) begin
if (rx_negedge) begin
rx_start <= 1'b1;
end else if (rx_clk_edge_cnt == 4'd9) begin
rx_start <= 1'b0;
end
end else begin
rx_start <= 1'b0;
end
end
end
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
rx_div_cnt <= 16'h0;
end else begin
// 第一个时钟沿只需波特率分频系数的一半
if (rx_start == 1'b1 && rx_clk_edge_cnt == 4'h0) begin
rx_div_cnt <= {1'b0, uart_baud[15:1]};
end else begin
rx_div_cnt <= uart_baud[15:0];
end
end
end
// 对时钟进行计数
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
rx_clk_cnt <= 16'h0;
end else if (rx_start == 1'b1) begin
// 计数达到分频值
if (rx_clk_cnt == rx_div_cnt) begin
rx_clk_cnt <= 16'h0;
end else begin
rx_clk_cnt <= rx_clk_cnt + 16'h1;
end
end else begin
rx_clk_cnt <= 16'h0;
end
end
// 每当时钟计数达到分频值时产生一个上升沿脉冲
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
rx_clk_edge_cnt <= 4'h0;
rx_clk_edge_level <= 1'b0;
end else if (rx_start == 1'b1) begin
// 计数达到分频值
if (rx_clk_cnt == rx_div_cnt) begin
// 时钟沿个数达到最大值
if (rx_clk_edge_cnt == 4'd9) begin
rx_clk_edge_cnt <= 4'h0;
rx_clk_edge_level <= 1'b0;
end else begin
// 时钟沿个数加1
rx_clk_edge_cnt <= rx_clk_edge_cnt + 4'h1;
// 产生上升沿脉冲
rx_clk_edge_level <= 1'b1;
end
end else begin
rx_clk_edge_level <= 1'b0;
end
end else begin
rx_clk_edge_cnt <= 4'h0;
rx_clk_edge_level <= 1'b0;
end
end
// bit序列
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
rx_data <= 8'h0;
rx_over <= 1'b0;
end else begin
if (rx_start == 1'b1) begin
// 上升沿
if (rx_clk_edge_level == 1'b1) begin
case (rx_clk_edge_cnt)
// 起始位
1: begin
end
// 第1位数据位
2: begin
if (rx_pin) begin
rx_data <= 8'h80;
end else begin
rx_data <= 8'h0;
end
end
// 剩余数据位
3, 4, 5, 6, 7, 8, 9: begin
rx_data <= {rx_pin, rx_data[7:1]};
// 最后一位接收完成,置位接收完成标志
if (rx_clk_edge_cnt == 4'h9) begin
rx_over <= 1'b1;
end
end
endcase
end
end else begin
rx_data <= 8'h0;
rx_over <= 1'b0;
end
end
end
endmodule