module uart_regs #(parameter CNT = 5, DATA_WIDTH = 8) ( input apb_clock, input apb_resetn, input apb_psel, input apb_penable, input apb_pwrite, input [11:0] apb_paddr, input [31:0] apb_pwdata, output reg [31:0] apb_prdata, output reg apb_pready, output reg uart_en, output reg [15:0] ibrd, output reg [5:0] fbrd, output reg [CNT-1:0] tx_write, output reg [CNT-1:0] rx_read, input [DATA_WIDTH*CNT-1:0] rx_data, input [CNT-1:0] tx_full, input [CNT-1:0] tx_empty, input [CNT-1:0] tx_busy, input [CNT-1:0] tx_complete, input [CNT-1:0] rx_full, input [CNT-1:0] rx_empty, input [CNT-1:0] rx_idle, input [CNT-1:0] framing_error, input [CNT-1:0] parity_error, input [CNT-1:0] break_error, input [CNT-1:0] overrun_error, output [CNT-1:0] clear_flags, output reg lcr_sps, // Stick parity select output reg lcr_stp2, // 2 stop bits select output reg lcr_eps, // Even parity select output reg lcr_pen, // Parity enable output reg [CNT-1:0] rx_dma_en, output reg [CNT-1:0] tx_dma_en, output reg [CNT-1:0] interrupts ); parameter ADDR_DR = (8'h00 >> 2); // Data parameter ADDR_SR = (8'h04 >> 2); // Status register/error clear register parameter ADDR_FR = (8'h18 >> 2); // Flag register parameter ADDR_IBRD = (8'h24 >> 2); // Integer baud rate divisor parameter ADDR_FBRD = (8'h28 >> 2); // Fractional baud rate divisor parameter ADDR_LCR = (8'h2c >> 2); // Line control parameter ADDR_CR = (8'h30 >> 2); // Control parameter ADDR_IE = (8'h38 >> 2); // Interrupt enable parameter ADDR_IC = (8'h44 >> 2); // Interrupt clear parameter ADDR_DMACR = (8'h48 >> 2); // DMA control register parameter CNT_BITS = $clog2(CNT); reg [CNT-1:0] rx_not_empty_ie; reg [CNT-1:0] tx_not_full_ie; reg [CNT-1:0] tx_complete_ie; reg [CNT-1:0] rx_idle_ie; reg [CNT-1:0] framing_error_ie; reg [CNT-1:0] parity_error_ie; reg [CNT-1:0] break_error_ie; reg [CNT-1:0] overrun_error_ie; reg [DATA_WIDTH-1:0] rx_reg; reg [4:0] status_reg; wire [5:0] reg_addr = apb_paddr[7:2]; wire [CNT_BITS-1:0] uart_idx = apb_paddr[8+:CNT_BITS]; wire apb_write = (apb_psel && !apb_penable && apb_pwrite); wire apb_read0 = (apb_psel && !apb_penable && !apb_pwrite); wire apb_read1 = (apb_psel && apb_penable && !apb_pwrite); always @(posedge apb_clock or negedge apb_resetn) begin if (!apb_resetn) begin apb_pready <= 1'b1; end else begin apb_pready <= !apb_read0; end end always @(posedge apb_clock or negedge apb_resetn) begin if (!apb_resetn) begin ibrd <= 16'h0; end else if (apb_write && reg_addr == ADDR_IBRD) begin ibrd <= apb_pwdata[15:0]; end end always @(posedge apb_clock or negedge apb_resetn) begin if (!apb_resetn) begin fbrd <= 16'h0; end else if (apb_write && reg_addr == ADDR_FBRD) begin fbrd <= apb_pwdata[5:0]; end end always @(posedge apb_clock or negedge apb_resetn) begin if (!apb_resetn) begin tx_write <= {CNT{1'b0}}; end else if (apb_write && reg_addr == ADDR_DR) begin tx_write <= (1'b1 << uart_idx); end else begin tx_write <= {CNT{1'b0}}; end end always @(posedge apb_clock or negedge apb_resetn) begin if (!apb_resetn) begin rx_read <= {CNT{1'b0}}; end else if (apb_read0 && reg_addr == ADDR_DR) begin rx_read <= (1'b1 << uart_idx); end else begin rx_read <= {CNT{1'b0}}; end end always @(posedge apb_clock or negedge apb_resetn) begin if (!apb_resetn) begin lcr_sps <= 1'b0; lcr_stp2 <= 1'b0; lcr_eps <= 1'b0; lcr_pen <= 1'b0; end else if (apb_write && reg_addr == ADDR_LCR) begin lcr_sps <= apb_pwdata[7]; lcr_stp2 <= apb_pwdata[3]; lcr_eps <= apb_pwdata[2]; lcr_pen <= apb_pwdata[1]; end end always @(posedge apb_clock or negedge apb_resetn) begin if (!apb_resetn) begin uart_en <= 1'b0; end else if (apb_write && reg_addr == ADDR_CR) begin uart_en <= apb_pwdata[0]; end end always @(posedge apb_clock or negedge apb_resetn) begin if (!apb_resetn) begin rx_not_empty_ie <= 0; tx_not_full_ie <= 0; tx_complete_ie <= 0; rx_idle_ie <= 0; framing_error_ie <= 0; parity_error_ie <= 0; break_error_ie <= 0; overrun_error_ie <= 0; end else if (apb_write && reg_addr == ADDR_IE) begin rx_not_empty_ie [uart_idx] <= apb_pwdata[4]; tx_not_full_ie [uart_idx] <= apb_pwdata[5]; framing_error_ie[uart_idx] <= apb_pwdata[7]; parity_error_ie [uart_idx] <= apb_pwdata[8]; break_error_ie [uart_idx] <= apb_pwdata[9]; overrun_error_ie[uart_idx] <= apb_pwdata[10]; rx_idle_ie [uart_idx] <= apb_pwdata[11]; tx_complete_ie [uart_idx] <= apb_pwdata[12]; end end always @(posedge apb_clock or negedge apb_resetn) begin if (!apb_resetn) begin rx_dma_en <= 0; tx_dma_en <= 0; end else if (apb_write && reg_addr == ADDR_DMACR) begin rx_dma_en[uart_idx] <= apb_pwdata[0]; tx_dma_en[uart_idx] <= apb_pwdata[1]; end end always @(posedge apb_clock or negedge apb_resetn) begin if (!apb_resetn) begin interrupts <= {CNT{1'b0}}; end else begin interrupts <= (rx_not_empty_ie & (~rx_empty) ) | (tx_not_full_ie & (~tx_full) ) | (framing_error_ie & framing_error ) | (parity_error_ie & parity_error ) | (break_error_ie & break_error ) | (overrun_error_ie & overrun_error ) | (rx_idle_ie & rx_idle ) | (tx_complete_ie & tx_complete ); end end always @(posedge apb_clock) begin rx_reg <= rx_data[uart_idx*DATA_WIDTH+:DATA_WIDTH]; status_reg <= { tx_empty[uart_idx], rx_full[uart_idx], tx_full[uart_idx], rx_empty[uart_idx], tx_busy[uart_idx] }; end always @(posedge apb_clock or negedge apb_resetn) begin if (!apb_resetn) begin apb_prdata <= 0; end else if (apb_read1) begin case (reg_addr) ADDR_DR: apb_prdata <= { {32-DATA_WIDTH{1'b0}}, rx_reg }; ADDR_SR: apb_prdata <= { 26'h0, tx_complete[uart_idx], rx_idle[uart_idx], overrun_error[uart_idx], break_error[uart_idx], parity_error[uart_idx], framing_error[uart_idx] }; ADDR_FR: apb_prdata <= { 24'h0, status_reg, 3'b0 }; ADDR_IBRD: apb_prdata <= { 16'h0, ibrd }; ADDR_FBRD: apb_prdata <= { 26'h0, fbrd }; ADDR_LCR: apb_prdata <= { 24'b0, lcr_sps, 3'b0, lcr_stp2, lcr_eps, lcr_pen, 1'b0 }; ADDR_CR: apb_prdata <= { 31'b0, uart_en }; ADDR_IE: apb_prdata <= { 19'h0, tx_complete_ie[uart_idx], rx_idle_ie[uart_idx], overrun_error_ie[uart_idx], break_error_ie[uart_idx], parity_error_ie[uart_idx], framing_error_ie[uart_idx], 1'b0, tx_not_full_ie[uart_idx], rx_not_empty_ie[uart_idx], 4'h0 }; ADDR_DMACR: apb_prdata <= { 30'h0, tx_dma_en[uart_idx], rx_dma_en[uart_idx] }; default: apb_prdata <= 32'h0; endcase end end // A simplified approach. Any write to either SR or IC will clear all the errors and interrupts. assign clear_flags = (apb_write && (reg_addr == ADDR_SR || reg_addr == ADDR_IC)) ? (1'b1 << uart_idx) : 0; endmodule