363 lines
12 KiB
Systemverilog
363 lines
12 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.
|
|
|
|
module apb_uart
|
|
#(
|
|
parameter CLOCK_FREQUENCY = 50e6,
|
|
parameter BAUD_RATE = 115200,
|
|
parameter APB_ADDR_WIDTH = 12 //APB slaves are 4KB by default
|
|
)
|
|
(
|
|
input logic CLK,
|
|
input logic RSTN,
|
|
/* verilator lint_off UNUSED */
|
|
input logic [APB_ADDR_WIDTH-1:0] PADDR,
|
|
/* lint_on */
|
|
input logic [31:0] PWDATA,
|
|
input logic PWRITE,
|
|
input logic PSEL,
|
|
input logic PENABLE,
|
|
output logic [31:0] PRDATA,
|
|
output logic PREADY,
|
|
output logic PSLVERR,
|
|
|
|
input logic rx_i, // Receiver input
|
|
output logic tx_o, // Transmitter output
|
|
|
|
output logic rda_o, // rx data avaiable
|
|
output logic tde_o, // tx data empty
|
|
output logic event_o // interrupt/event output
|
|
);
|
|
// register addresses
|
|
parameter RBR = 3'h0, THR = 3'h0, DLL = 3'h0, IER = 3'h1, DLM = 3'h1, IIR = 3'h2,
|
|
FCR = 3'h2, LCR = 3'h3, MCR = 3'h4, LSR = 3'h5, MSR = 3'h6, SCR = 3'h7;
|
|
|
|
parameter TX_FIFO_DEPTH = 16; // in bytes
|
|
parameter RX_FIFO_DEPTH = 16; // in bytes
|
|
|
|
logic [2:0] register_adr;
|
|
logic [9:0][7:0] regs_q, regs_n;
|
|
logic [1:0] trigger_level_n, trigger_level_q;
|
|
|
|
// receive buffer register, read only
|
|
logic [7:0] rx_data;
|
|
// parity error
|
|
logic parity_error;
|
|
logic rx_overrun;
|
|
logic [3:0] IIR_o;
|
|
logic [3:0] clr_int;
|
|
|
|
/* verilator lint_off UNOPTFLAT */
|
|
// tx flow control
|
|
logic tx_ready;
|
|
/* lint_on */
|
|
|
|
// rx flow control
|
|
logic apb_rx_ready;
|
|
logic rx_valid;
|
|
|
|
logic tx_fifo_clr_n, tx_fifo_clr_q;
|
|
logic rx_fifo_clr_n, rx_fifo_clr_q;
|
|
|
|
logic fifo_tx_valid;
|
|
logic tx_valid;
|
|
logic fifo_rx_valid;
|
|
logic fifo_rx_ready;
|
|
logic rx_ready;
|
|
|
|
logic [7:0] fifo_tx_data;
|
|
logic [8:0] fifo_rx_data;
|
|
|
|
logic [7:0] tx_data;
|
|
logic [$clog2(TX_FIFO_DEPTH):0] tx_elements;
|
|
logic [$clog2(RX_FIFO_DEPTH):0] rx_elements;
|
|
|
|
// TODO: check that stop bits are really not necessary here
|
|
uart_rx uart_rx_i
|
|
(
|
|
.clk_i ( CLK ),
|
|
.rstn_i ( RSTN ),
|
|
.rx_i ( rx_i ),
|
|
.cfg_en_i ( 1'b1 ),
|
|
.cfg_div_i ( {regs_q[DLM + 'd8], regs_q[DLL + 'd8]} ),
|
|
.cfg_parity_en_i ( regs_q[LCR][3] ),
|
|
.cfg_even_parity_i ( regs_q[LCR][4] ),
|
|
.cfg_bits_i ( regs_q[LCR][1:0] ),
|
|
// .cfg_stop_bits_i ( regs_q[LCR][2] ),
|
|
/* verilator lint_off PINCONNECTEMPTY */
|
|
.busy_o ( ),
|
|
/* lint_on */
|
|
.parity_error_o ( parity_error ),
|
|
.overrun_o ( rx_overrun ),
|
|
.err_clr_i ( 1'b1 ),
|
|
.rx_data_o ( rx_data ),
|
|
.rx_valid_o ( rx_valid ),
|
|
.rx_ready_i ( rx_ready )
|
|
);
|
|
|
|
uart_tx uart_tx_i
|
|
(
|
|
.clk_i ( CLK ),
|
|
.rstn_i ( RSTN ),
|
|
.tx_o ( tx_o ),
|
|
/* verilator lint_off PINCONNECTEMPTY */
|
|
.busy_o ( ),
|
|
/* lint_on */
|
|
.cfg_en_i ( 1'b1 ),
|
|
.cfg_div_i ( {regs_q[DLM + 'd8], regs_q[DLL + 'd8]} ),
|
|
.cfg_parity_en_i ( regs_q[LCR][3] ),
|
|
.cfg_even_parity_i ( regs_q[LCR][4] ),
|
|
.cfg_bits_i ( regs_q[LCR][1:0] ),
|
|
.cfg_stop_bits_i ( regs_q[LCR][2] ),
|
|
|
|
.tx_data_i ( tx_data ),
|
|
.tx_valid_i ( tx_valid ),
|
|
.tx_ready_o ( tx_ready )
|
|
);
|
|
|
|
io_generic_fifo
|
|
#(
|
|
.DATA_WIDTH ( 9 ),
|
|
.BUFFER_DEPTH ( RX_FIFO_DEPTH )
|
|
)
|
|
uart_rx_fifo_i
|
|
(
|
|
.clk_i ( CLK ),
|
|
.rstn_i ( RSTN ),
|
|
|
|
.clr_i ( rx_fifo_clr_q ),
|
|
|
|
.elements_o ( rx_elements ),
|
|
|
|
.data_o ( fifo_rx_data ),
|
|
.valid_o ( fifo_rx_valid ),
|
|
.ready_i ( fifo_rx_ready ),
|
|
|
|
.valid_i ( rx_valid ),
|
|
.data_i ( { parity_error, rx_data } ),
|
|
.ready_o ( rx_ready )
|
|
);
|
|
|
|
io_generic_fifo
|
|
#(
|
|
.DATA_WIDTH ( 8 ),
|
|
.BUFFER_DEPTH ( TX_FIFO_DEPTH )
|
|
)
|
|
uart_tx_fifo_i
|
|
(
|
|
.clk_i ( CLK ),
|
|
.rstn_i ( RSTN ),
|
|
|
|
.clr_i ( tx_fifo_clr_q ),
|
|
|
|
.elements_o ( tx_elements ),
|
|
|
|
.data_o ( tx_data ),
|
|
.valid_o ( tx_valid ),
|
|
.ready_i ( tx_ready ),
|
|
|
|
.valid_i ( fifo_tx_valid ),
|
|
.data_i ( fifo_tx_data ),
|
|
// not needed since we are getting the status via the fifo population
|
|
.ready_o ( )
|
|
);
|
|
|
|
uart_interrupt
|
|
#(
|
|
.TX_FIFO_DEPTH (TX_FIFO_DEPTH),
|
|
.RX_FIFO_DEPTH (RX_FIFO_DEPTH)
|
|
)
|
|
uart_interrupt_i
|
|
(
|
|
.clk_i ( CLK ),
|
|
.rstn_i ( RSTN ),
|
|
|
|
|
|
.IER_i ( regs_q[IER] ), // interrupt enable register
|
|
.RDA_i ( regs_n[LSR][0] ), // receiver data available
|
|
.overrun_i ( regs_n[LSR][1] ), // rx data overrun
|
|
|
|
|
|
.error_i ( regs_n[LSR][2] ),
|
|
.rx_elements_i ( rx_elements ),
|
|
.tx_elements_i ( tx_elements ),
|
|
.trigger_level_i ( trigger_level_q ),
|
|
|
|
.clr_int_i ( clr_int ), // one hot
|
|
|
|
.interrupt_o ( event_o ),
|
|
.IIR_o ( IIR_o )
|
|
|
|
);
|
|
|
|
// UART Registers
|
|
|
|
// register write and update logic
|
|
always_comb
|
|
begin
|
|
regs_n = regs_q;
|
|
trigger_level_n = trigger_level_q;
|
|
|
|
fifo_tx_valid = 1'b0;
|
|
tx_fifo_clr_n = 1'b0; // self clearing
|
|
rx_fifo_clr_n = 1'b0; // self clearing
|
|
|
|
// rx status
|
|
regs_n[LSR][0] = fifo_rx_valid; // fifo is empty
|
|
|
|
// rx data discarded (no overrun in fact, the last rx byte was discarded.)
|
|
regs_n[LSR][1] = rx_overrun;
|
|
|
|
// parity error on receiving part has occured
|
|
regs_n[LSR][2] = parity_error;
|
|
|
|
// tx status register
|
|
regs_n[LSR][5] = ~ (|tx_elements); // fifo is empty
|
|
regs_n[LSR][6] = tx_ready & ~ (|tx_elements); // shift register and fifo are empty
|
|
|
|
if (PSEL && PENABLE && PWRITE)
|
|
begin
|
|
case (register_adr)
|
|
|
|
THR: // either THR or DLL
|
|
begin
|
|
if (regs_q[LCR][7]) // Divisor Latch Access Bit (DLAB)
|
|
begin
|
|
regs_n[DLL + 'd8] = PWDATA[7:0];
|
|
end
|
|
else
|
|
begin
|
|
fifo_tx_data = PWDATA[7:0];
|
|
fifo_tx_valid = 1'b1;
|
|
end
|
|
end
|
|
|
|
IER: // either IER or DLM
|
|
begin
|
|
if (regs_q[LCR][7]) // Divisor Latch Access Bit (DLAB)
|
|
regs_n[DLM + 'd8] = PWDATA[7:0];
|
|
else
|
|
regs_n[IER] = PWDATA[7:0];
|
|
end
|
|
|
|
LCR:
|
|
regs_n[LCR] = PWDATA[7:0];
|
|
|
|
FCR: // write only register, fifo control register
|
|
begin
|
|
rx_fifo_clr_n = PWDATA[1];
|
|
tx_fifo_clr_n = PWDATA[2];
|
|
trigger_level_n = PWDATA[7:6];
|
|
end
|
|
|
|
default: ;
|
|
endcase
|
|
|
|
end
|
|
|
|
end
|
|
|
|
// register read logic
|
|
always_comb
|
|
begin
|
|
PRDATA = 'b0;
|
|
apb_rx_ready = 1'b0;
|
|
fifo_rx_ready = 1'b0;
|
|
clr_int = 4'b0;
|
|
|
|
if (PSEL && PENABLE && !PWRITE)
|
|
begin
|
|
case (register_adr)
|
|
RBR: // either RBR or DLL
|
|
begin
|
|
if (regs_q[LCR][7]) // Divisor Latch Access Bit (DLAB)
|
|
PRDATA = {24'b0, regs_q[DLL + 'd8]};
|
|
else
|
|
begin
|
|
|
|
fifo_rx_ready = 1'b1;
|
|
|
|
PRDATA = {24'b0, fifo_rx_data[7:0]};
|
|
|
|
clr_int = 4'b1100; // clear Received Data Available interrupt
|
|
end
|
|
end
|
|
|
|
LSR: // Line Status Register
|
|
begin
|
|
PRDATA = {24'b0, regs_q[LSR]};
|
|
clr_int = 4'b0110; // clear parrity interrupt error
|
|
end
|
|
|
|
LCR: // Line Control Register
|
|
PRDATA = {24'b0, regs_q[LCR]};
|
|
|
|
IER: // either IER or DLM
|
|
begin
|
|
if (regs_q[LCR][7]) // Divisor Latch Access Bit (DLAB)
|
|
PRDATA = {24'b0, regs_q[DLM + 'd8]};
|
|
else
|
|
PRDATA = {24'b0, regs_q[IER]};
|
|
end
|
|
|
|
IIR: // interrupt identification register read only
|
|
begin
|
|
PRDATA = {24'b0, 1'b1, 1'b1, 2'b0, IIR_o};
|
|
clr_int = 4'b0010; // clear Transmitter Holding Register Empty
|
|
end
|
|
|
|
default: ;
|
|
endcase
|
|
end
|
|
end
|
|
|
|
// synchronouse part
|
|
always_ff @(posedge CLK, negedge RSTN)
|
|
begin
|
|
if(~RSTN)
|
|
begin
|
|
|
|
regs_q[IER] <= 8'h0;
|
|
regs_q[IIR] <= 8'h1;
|
|
regs_q[LCR] <= 8'h3;
|
|
regs_q[MCR] <= 8'h0;
|
|
regs_q[LSR] <= 8'h60;
|
|
regs_q[MSR] <= 8'h0;
|
|
regs_q[SCR] <= 8'h0;
|
|
regs_q[DLM + 'd8] <= 8'd1; // 50e6/115200 = 434
|
|
regs_q[DLL + 'd8] <= 8'd178;
|
|
|
|
trigger_level_q <= 2'b00;
|
|
tx_fifo_clr_q <= 1'b0;
|
|
rx_fifo_clr_q <= 1'b0;
|
|
|
|
end
|
|
else
|
|
begin
|
|
regs_q <= regs_n;
|
|
|
|
trigger_level_q <= trigger_level_n;
|
|
tx_fifo_clr_q <= tx_fifo_clr_n;
|
|
rx_fifo_clr_q <= rx_fifo_clr_n;
|
|
|
|
end
|
|
end
|
|
|
|
assign register_adr = {PADDR[2:0]};
|
|
// APB logic: we are always ready to capture the data into our regs
|
|
// not supporting transfare failure
|
|
assign PREADY = 1'b1;
|
|
assign PSLVERR = 1'b0;
|
|
|
|
assign rda_o = regs_q[LSR][0]; // rx data avaiable
|
|
assign tde_o = regs_q[LSR][5]; // tx data empty
|
|
|
|
endmodule
|