| // Copyright lowRISC contributors. | |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. | |
| // SPDX-License-Identifier: Apache-2.0 | |
| // | |
| // Description: UART Receive Module | |
| // | |
| module uart_rx ( | |
| input clk_i, | |
| input rst_ni, | |
| input rx_enable, | |
| input tick_baud_x16, | |
| input parity_enable, | |
| input parity_odd, | |
| output logic tick_baud, | |
| output logic rx_valid, | |
| output [7:0] rx_data, | |
| output logic idle, | |
| output frame_err, | |
| output rx_parity_err, | |
| input rx | |
| ); | |
| logic rx_valid_q; | |
| logic [10:0] sreg_q, sreg_d; | |
| logic [3:0] bit_cnt_q, bit_cnt_d; | |
| logic [3:0] baud_div_q, baud_div_d; | |
| logic tick_baud_d, tick_baud_q; | |
| logic idle_d, idle_q; | |
| assign tick_baud = tick_baud_q; | |
| assign idle = idle_q; | |
| always_ff @(posedge clk_i or negedge rst_ni) begin | |
| if (!rst_ni) begin | |
| sreg_q <= 11'h0; | |
| bit_cnt_q <= 4'h0; | |
| baud_div_q <= 4'h0; | |
| tick_baud_q <= 1'b0; | |
| idle_q <= 1'b1; | |
| end else begin | |
| sreg_q <= sreg_d; | |
| bit_cnt_q <= bit_cnt_d; | |
| baud_div_q <= baud_div_d; | |
| tick_baud_q <= tick_baud_d; | |
| idle_q <= idle_d; | |
| end | |
| end | |
| always_comb begin | |
| if (!rx_enable) begin | |
| sreg_d = 11'h0; | |
| bit_cnt_d = 4'h0; | |
| baud_div_d = 4'h0; | |
| tick_baud_d = 1'b0; | |
| idle_d = 1'b1; | |
| end else begin | |
| tick_baud_d = 1'b0; | |
| sreg_d = sreg_q; | |
| bit_cnt_d = bit_cnt_q; | |
| baud_div_d = baud_div_q; | |
| idle_d = idle_q; | |
| if (tick_baud_x16) begin | |
| {tick_baud_d, baud_div_d} = {1'b0,baud_div_q} + 5'h1; | |
| end | |
| if (idle_q && !rx) begin | |
| // start of char, sample in the middle of the bit time | |
| baud_div_d = 4'd8; | |
| tick_baud_d = 1'b0; | |
| bit_cnt_d = (parity_enable ? 4'd11 : 4'd10); | |
| sreg_d = 11'h0; | |
| idle_d = 1'b0; | |
| end else if (!idle_q && tick_baud_q) begin | |
| if ((bit_cnt_q == (parity_enable ? 4'd11 : 4'd10)) && rx) begin | |
| // must have been a glitch on the input, start bit is not set | |
| // in the middle of the bit time, abort | |
| idle_d = 1'b1; | |
| bit_cnt_d = 4'h0; | |
| end else begin | |
| sreg_d = {rx, sreg_q[10:1]}; | |
| bit_cnt_d = bit_cnt_q - 4'h1; | |
| idle_d = (bit_cnt_q == 4'h1); | |
| end | |
| end | |
| end | |
| end | |
| always_ff @(posedge clk_i or negedge rst_ni) begin | |
| if (!rst_ni) rx_valid_q <= 1'b0; | |
| else rx_valid_q <= tick_baud_q & (bit_cnt_q == 4'h1); | |
| end | |
| assign rx_valid = rx_valid_q; | |
| assign rx_data = parity_enable ? sreg_q[8:1] : sreg_q[9:2]; | |
| // (rx_parity = sreg_q[9]) | |
| assign frame_err = rx_valid_q & ~sreg_q[10]; | |
| assign rx_parity_err = parity_enable & rx_valid_q & | |
| (^{sreg_q[9:1],parity_odd}); | |
| endmodule |