mirror of
https://github.com/retroSoC/uart.git
synced 2026-11-04 22:32:39 +00:00
206 lines
6.3 KiB
Systemverilog
206 lines
6.3 KiB
Systemverilog
// Copyright 2017 ETH Zurich and University of Bologna.
|
|
// Copyright and related rights are licensed under the Solderpad Hardware
|
|
// License, Version 0.51 (the “License”); you may not use this file except in
|
|
// compliance with the License. You may obtain a copy of the License at
|
|
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
|
|
// or agreed to in writing, software, hardware and materials distributed under
|
|
// this 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.
|
|
//
|
|
// -- Adaptable modifications are redistributed under compatible License --
|
|
//
|
|
// Copyright (c) 2023 Beijing Institute of Open Source Chip
|
|
// uart is licensed under Mulan PSL v2.
|
|
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
// You may obtain a copy of Mulan PSL v2 at:
|
|
// http://license.coscl.org.cn/MulanPSL2
|
|
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
// See the Mulan PSL v2 for more details.
|
|
|
|
module uart_rx (
|
|
input logic clk_i,
|
|
input logic rst_n_i,
|
|
input logic rx_i,
|
|
output logic busy_o,
|
|
input logic cfg_en_i,
|
|
input logic [15:0] cfg_div_i,
|
|
input logic cfg_parity_en_i,
|
|
input logic [ 1:0] cfg_parity_sel_i,
|
|
input logic [ 1:0] cfg_bits_i,
|
|
output logic err_o,
|
|
input logic err_clr_i,
|
|
output logic [ 7:0] rx_data_o,
|
|
output logic rx_valid_o,
|
|
input logic rx_ready_i
|
|
);
|
|
|
|
typedef enum logic [2:0] {
|
|
IDLE,
|
|
START_BIT,
|
|
DATA,
|
|
SAVE_DATA,
|
|
PARITY,
|
|
STOP_BIT
|
|
} fsm_t;
|
|
|
|
fsm_t s_fsm_d, s_fsm_q;
|
|
|
|
logic [7:0] s_reg_data_d, s_reg_data_q;
|
|
logic [2:0] reg_rx_sync, s_reg_bit_cnt_d, s_reg_bit_cnt_q;
|
|
logic [2:0] s_target_bits;
|
|
logic s_parity_bit_d, s_parity_bit_q;
|
|
logic s_sample_data, s_baudgen_en, s_bit_done;
|
|
logic s_start_bit, s_set_error, s_rx_fall;
|
|
logic [15:0] s_baud_cnt;
|
|
|
|
assign busy_o = (s_fsm_q != IDLE);
|
|
always_comb begin
|
|
unique case (cfg_bits_i)
|
|
2'b00: s_target_bits = 3'd4;
|
|
2'b01: s_target_bits = 3'd5;
|
|
2'b10: s_target_bits = 3'd6;
|
|
2'b11: s_target_bits = 3'd7;
|
|
endcase
|
|
end
|
|
|
|
always_comb begin
|
|
s_fsm_d = s_fsm_q;
|
|
s_sample_data = 1'b0;
|
|
s_reg_bit_cnt_d = s_reg_bit_cnt_q;
|
|
s_reg_data_d = s_reg_data_q;
|
|
rx_valid_o = 1'b0;
|
|
s_baudgen_en = 1'b0;
|
|
s_start_bit = 1'b0;
|
|
s_parity_bit_d = s_parity_bit_q;
|
|
s_set_error = 1'b0;
|
|
|
|
unique case (s_fsm_q)
|
|
IDLE: begin
|
|
if (s_rx_fall) begin
|
|
s_fsm_d = START_BIT;
|
|
s_baudgen_en = 1'b1;
|
|
s_start_bit = 1'b1;
|
|
end
|
|
end
|
|
START_BIT: begin
|
|
s_parity_bit_d = 1'b0;
|
|
s_baudgen_en = 1'b1;
|
|
s_start_bit = 1'b1;
|
|
if (s_bit_done) s_fsm_d = DATA;
|
|
end
|
|
DATA: begin
|
|
s_baudgen_en = 1'b1;
|
|
s_parity_bit_d = s_parity_bit_q ^ reg_rx_sync[2];
|
|
case (cfg_bits_i)
|
|
2'b00: s_reg_data_d = {3'b000, reg_rx_sync[2], s_reg_data_q[4:1]};
|
|
2'b01: s_reg_data_d = {2'b00, reg_rx_sync[2], s_reg_data_q[5:1]};
|
|
2'b10: s_reg_data_d = {1'b0, reg_rx_sync[2], s_reg_data_q[6:1]};
|
|
2'b11: s_reg_data_d = {reg_rx_sync[2], s_reg_data_q[7:1]};
|
|
endcase
|
|
|
|
if (s_bit_done) begin
|
|
s_sample_data = 1'b1;
|
|
if (s_reg_bit_cnt_q == s_target_bits) begin
|
|
s_reg_bit_cnt_d = 'h0;
|
|
s_fsm_d = SAVE_DATA;
|
|
end else begin
|
|
s_reg_bit_cnt_d = s_reg_bit_cnt_q + 1;
|
|
end
|
|
end
|
|
end
|
|
SAVE_DATA: begin
|
|
s_baudgen_en = 1'b1;
|
|
rx_valid_o = 1'b1;
|
|
if (rx_ready_i)
|
|
if (cfg_parity_en_i) s_fsm_d = PARITY;
|
|
else s_fsm_d = STOP_BIT;
|
|
end
|
|
PARITY: begin
|
|
s_baudgen_en = 1'b1;
|
|
if (s_bit_done) begin
|
|
unique case (cfg_parity_sel_i)
|
|
2'b00: if (reg_rx_sync[2] != ~s_parity_bit_q) s_set_error = 1'b1;
|
|
2'b01: if (reg_rx_sync[2] != s_parity_bit_q) s_set_error = 1'b1;
|
|
2'b10: if (reg_rx_sync[2] != 1'b0) s_set_error = 1'b1;
|
|
2'b11: if (reg_rx_sync[2] != 1'b1) s_set_error = 1'b1;
|
|
endcase
|
|
s_fsm_d = STOP_BIT;
|
|
end
|
|
end
|
|
STOP_BIT: begin
|
|
s_baudgen_en = 1'b1;
|
|
if (s_bit_done) begin
|
|
s_fsm_d = IDLE;
|
|
end
|
|
end
|
|
default: s_fsm_d = IDLE;
|
|
endcase
|
|
end
|
|
|
|
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
|
if (rst_n_i == 1'b0) begin
|
|
s_fsm_q <= IDLE;
|
|
s_reg_data_q <= 8'hFF;
|
|
s_reg_bit_cnt_q <= 'h0;
|
|
s_parity_bit_q <= 1'b0;
|
|
end else begin
|
|
if (s_bit_done) s_parity_bit_q <= s_parity_bit_d;
|
|
if (s_sample_data) s_reg_data_q <= s_reg_data_d;
|
|
|
|
s_reg_bit_cnt_q <= s_reg_bit_cnt_d;
|
|
if (cfg_en_i) s_fsm_q <= s_fsm_d;
|
|
else s_fsm_q <= IDLE;
|
|
end
|
|
end
|
|
|
|
assign s_rx_fall = ~reg_rx_sync[1] & reg_rx_sync[2];
|
|
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
|
if (rst_n_i == 1'b0) reg_rx_sync <= 3'b111;
|
|
else begin
|
|
if (cfg_en_i) reg_rx_sync <= {reg_rx_sync[1:0], rx_i};
|
|
else reg_rx_sync <= 3'b111;
|
|
end
|
|
end
|
|
|
|
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
|
if (rst_n_i == 1'b0) begin
|
|
s_baud_cnt <= 'h0;
|
|
s_bit_done <= 1'b0;
|
|
end else begin
|
|
if (s_baudgen_en) begin
|
|
if (!s_start_bit && (s_baud_cnt == cfg_div_i)) begin
|
|
s_baud_cnt <= 'h0;
|
|
s_bit_done <= 1'b1;
|
|
end else if (s_start_bit && (s_baud_cnt == {1'b0, cfg_div_i[15:1]})) begin
|
|
s_baud_cnt <= 'h0;
|
|
s_bit_done <= 1'b1;
|
|
end else begin
|
|
s_baud_cnt <= s_baud_cnt + 1;
|
|
s_bit_done <= 1'b0;
|
|
end
|
|
end else begin
|
|
s_baud_cnt <= 'h0;
|
|
s_bit_done <= 1'b0;
|
|
end
|
|
end
|
|
end
|
|
|
|
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
|
if (rst_n_i == 1'b0) begin
|
|
err_o <= 1'b0;
|
|
end else begin
|
|
if (err_clr_i) begin
|
|
err_o <= 1'b0;
|
|
end else begin
|
|
if (s_set_error) err_o <= 1'b1;
|
|
end
|
|
end
|
|
end
|
|
|
|
assign rx_data_o = s_reg_data_q;
|
|
|
|
endmodule
|