tinyriscv/rtl/perips/uart/uart_rx.sv

117 lines
4.3 KiB
Systemverilog

/*
Copyright 2021 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.
*/
module uart_rx (
input logic clk_i, // 时钟信号
input logic rst_ni, // 异步复位信号,低电平有效
input logic enable_i, // RX模块使能信号
input logic parity_en_i, // 校验使能
input logic parity_odd_i, // 奇校验
input logic [15:0] div_ratio_i, // 波特率分频系数
input logic rx_i, // 来自RX引脚的信号
output logic idle_o, // RX模块空闲
output logic err_o, // 接收出错,帧出错或者校验出错
output logic [7:0] rdata_o, // 接收到的一个字节数据
output logic rvalid_o // 有效接收到一个字节数据
);
logic tick;
logic rx;
logic rx_start;
logic clk_div_rst_n_d, clk_div_rst_n_q;
logic idle_d, idle_q;
logic rx_valid_d, rx_valid_q;
logic [10:0] shift_reg_d, shift_reg_q;
logic [3:0] bit_cnt_d, bit_cnt_q;
logic [15:0] baud_div_d, baud_div_q;
always_comb begin
if (!enable_i) begin
rx_valid_d = 1'b0;
shift_reg_d = '0;
idle_d = 1'b1;
bit_cnt_d = '0;
baud_div_d = '0;
clk_div_rst_n_d = 1'b1;
end else begin
rx_valid_d = 1'b0;
shift_reg_d = shift_reg_q;
idle_d = idle_q;
bit_cnt_d = bit_cnt_q;
baud_div_d = baud_div_q;
clk_div_rst_n_d = 1'b1;
if (rx_start & idle_q) begin
bit_cnt_d = parity_en_i ? 4'd11 : 4'd10;
shift_reg_d = '0;
idle_d = 1'b0;
// 起始位,采中间值
baud_div_d = {1'b0, div_ratio_i[15:1]};
clk_div_rst_n_d = 1'b0;
end else if (tick && (!idle_q)) begin
shift_reg_d = {rx, shift_reg_q[10:1]};
bit_cnt_d = bit_cnt_q - 1'b1;
idle_d = (bit_cnt_q == 4'h1);
rx_valid_d = (bit_cnt_q == 4'h1);
baud_div_d = div_ratio_i;
end
end
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
rx_valid_q <= 1'b0;
shift_reg_q <= '0;
idle_q <= 1'b1;
bit_cnt_q <= '0;
baud_div_q <= '0;
clk_div_rst_n_q <= 1'b1;
end else begin
rx_valid_q <= rx_valid_d;
shift_reg_q <= shift_reg_d;
idle_q <= idle_d;
bit_cnt_q <= bit_cnt_d;
baud_div_q <= baud_div_d;
clk_div_rst_n_q <= clk_div_rst_n_d;
end
end
assign idle_o = idle_q;
assign rvalid_o = rx_valid_q;
assign rdata_o = parity_en_i ? shift_reg_q[8:1] : shift_reg_q[9:2];
assign err_o = rx_valid_q & (~shift_reg_q[10]);
edge_detect u_edge_detect(
.clk_i (clk_i),
.rst_ni (rst_ni),
.sig_i (rx_i),
.sig_o (rx),
.re_o (),
.fe_o (rx_start)
);
clk_div #(
.RATIO_WIDTH(16)
) u_clk_div (
.clk_i(clk_i),
.rst_ni(rst_ni || clk_div_rst_n_q),
.en_i(rx_start || (!idle_q)),
.ratio_i(baud_div_q),
.clk_o(tick)
);
endmodule