blob: 1d1b9363dd7e7988d9826691ea226cd8da1606e5 [file] [log] [blame]
// SPDX-FileCopyrightText: 2020 Muhammad Hadir Khan
//
// 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.
// SPDX-License-Identifier: Apache-2.0
package uart
import chisel3._
import chisel3.util.{Cat, Enum}
import primitives.{Fifo, FlopSynchronizer, IntrHardware}
class UartCore extends Module {
val io = IO(new Bundle {
val reg2hw = Flipped(new UartReg2HW)
val hw2reg = Flipped(new UartHw2Reg)
val rx = Input(UInt(1.W))
val tx = Output(UInt(1.W))
val intr_tx_empty_o = Output(Bool())
val intr_rx_overflow_o = Output(Bool())
val intr_rx_frame_err_o = Output(Bool())
val intr_rx_break_err_o = Output(Bool())
val intr_rx_parity_err_o = Output(Bool())
})
val NcoWidth = io.reg2hw.ctrl.nco.q.getWidth
val tx_enable = Wire(Bool())
val rx_enable = Wire(Bool())
val sys_loopback = Wire(Bool())
val line_loopback = Wire(Bool())
val uart_fifo_rxrst = Wire(Bool())
val uart_fifo_txrst = Wire(Bool())
val ovrd_tx_en = Wire(Bool())
val ovrd_tx_val = Wire(Bool())
val not_allzero_char = Wire(Bool())
val allzero_err = Wire(Bool())
val allzero_cnt_d = Wire(UInt(5.W))
val allzero_cnt_q = RegInit(0.U(5.W))
val rx_valid = Wire(Bool())
val rx_uart_idle = Wire(Bool())
val event_rx_frame_err = Wire(Bool())
val event_rx_break_err = Wire(Bool())
val event_rx_overflow = Wire(Bool())
val event_rx_parity_err = Wire(Bool())
val event_tx_empty = Wire(Bool())
val rx_fifo_data = Wire(UInt(8.W))
val tx_fifo_data = Wire(UInt(8.W))
val rx_fifo_wready = Wire(Bool())
val rx_fifo_wvalid = Wire(Bool())
val tx_fifo_wready = Wire(Bool())
val tx_fifo_rready = Wire(Bool())
val rx_fifo_rvalid = Wire(Bool())
val tx_fifo_rvalid = Wire(Bool())
val tx_uart_idle = Wire(Bool())
val tx_uart_idle_q = RegInit(true.B)
val tx_fifo_depth = Wire(UInt(6.W))
val rx_fifo_depth = Wire(UInt(6.W))
val rx_tick_baud = Wire(Bool())
val tick_baud_x16 = Wire(Bool())
val tx_out_q = RegInit(1.U(1.W))
val tx_out = Wire(UInt(1.W))
val rx_sync = Wire(UInt(1.W))
val breakChk :: breakWait :: Nil = Enum(2)
val break_st_q = RegInit(breakChk)
val break_err = Wire(Bool())
val rx_in = Wire(Bool())
val uart_rdata = Wire(UInt(8.W))
tx_enable := io.reg2hw.ctrl.tx.q
rx_enable := io.reg2hw.ctrl.rx.q
sys_loopback := io.reg2hw.ctrl.slpbk.q
line_loopback := io.reg2hw.ctrl.llpbk.q
uart_fifo_rxrst := io.reg2hw.fifo_ctrl.rxrst.q & io.reg2hw.fifo_ctrl.rxrst.qe
uart_fifo_txrst := io.reg2hw.fifo_ctrl.txrst.q & io.reg2hw.fifo_ctrl.txrst.qe
ovrd_tx_en := io.reg2hw.ovrd.txen.q
ovrd_tx_val := io.reg2hw.ovrd.txval.q
not_allzero_char := rx_valid & (~event_rx_frame_err | (rx_fifo_data =/= 0.U))
allzero_err := event_rx_frame_err & (rx_fifo_data === 0.U)
allzero_cnt_d := Mux(break_st_q === breakWait || not_allzero_char, 0.U, Mux(allzero_err, allzero_cnt_q + 1.U(5.W), allzero_cnt_q))
when(rx_enable) {
allzero_cnt_q := allzero_cnt_d
}
when(io.reg2hw.ctrl.rxblvl.q === 0.U(2.W)) {
break_err := allzero_cnt_d >= 2.U(5.W)
}.elsewhen(io.reg2hw.ctrl.rxblvl.q === 1.U(2.W)) {
break_err := allzero_cnt_d >= 4.U(5.W)
}.elsewhen(io.reg2hw.ctrl.rxblvl.q === 2.U(2.W)) {
break_err := allzero_cnt_d >= 8.U(5.W)
} .otherwise {
break_err := allzero_cnt_d >= 16.U(5.W)
}
when(break_st_q === breakChk) {
when(event_rx_break_err) {
break_st_q := breakWait
}
}
when(break_st_q === breakWait) {
when(rx_in) {
break_st_q := breakChk
}
}
io.hw2reg.rdata.d := uart_rdata
io.hw2reg.status.rxempty.d := ~rx_fifo_rvalid
io.hw2reg.status.rxidle.d := rx_uart_idle
io.hw2reg.status.txidle.d := tx_uart_idle & ~tx_fifo_rvalid
io.hw2reg.status.txempty.d := ~tx_fifo_rvalid
io.hw2reg.status.rxfull.d := ~rx_fifo_wready
io.hw2reg.status.txfull.d := ~tx_fifo_wready
io.hw2reg.fifo_status.txlvl.d := tx_fifo_depth
io.hw2reg.fifo_status.rxlvl.d := rx_fifo_depth
val nco_sum_q = RegInit(0.U((NcoWidth+1).W))
when(tx_enable || rx_enable) {
nco_sum_q := Cat(0.U(1.W), nco_sum_q(NcoWidth-1,0)) + Cat(0.U(1.W), io.reg2hw.ctrl.nco.q(NcoWidth-1, 0))
}
tick_baud_x16 := nco_sum_q(16)
// TX Logic
tx_fifo_rready := tx_uart_idle & tx_fifo_rvalid & tx_enable
val txfifo = Module(new Fifo(width = 8, pass = false, depth = 32))
txfifo.io.clr_i := uart_fifo_txrst
txfifo.io.wvalid_i := io.reg2hw.wdata.qe
tx_fifo_wready := txfifo.io.wready_o
txfifo.io.wdata_i := io.reg2hw.wdata.q
tx_fifo_depth := txfifo.io.depth_o
tx_fifo_rvalid := txfifo.io.rvalid_o
txfifo.io.rready_i := tx_fifo_rready
tx_fifo_data := txfifo.io.rdata_o
val uart_tx = Module(new UartTx())
uart_tx.io.tx_enable := tx_enable
uart_tx.io.tick_baud_x16 := tick_baud_x16
uart_tx.io.parity_en := io.reg2hw.ctrl.parity_en.q
uart_tx.io.wr := tx_fifo_rready
uart_tx.io.wr_parity := (tx_fifo_data.xorR()) ^ io.reg2hw.ctrl.parity_odd.q
uart_tx.io.wr_data := tx_fifo_data
tx_uart_idle := uart_tx.io.idle
tx_out := uart_tx.io.tx
io.tx := Mux(line_loopback, io.rx, tx_out_q)
when(ovrd_tx_en) {
tx_out_q := ovrd_tx_val
} .elsewhen(sys_loopback) {
tx_out_q := 1.U
} .otherwise {
tx_out_q := tx_out
}
// RX Logic
// synchronizing the asynchronous rx input coming from another domain to resolve metastability issues
// Read more here: https://github.com/lowRISC/opentitan/issues/3439
val flopSynchronizer = Module(new FlopSynchronizer(width = 1, resetVal = 1.U))
flopSynchronizer.io.d_in := io.rx
rx_sync := flopSynchronizer.io.q_out
rx_in := Mux(sys_loopback, tx_out, Mux(line_loopback, 1.U, rx_sync))
val uart_rx = Module(new UartRx)
uart_rx.io.rx_enable := rx_enable
uart_rx.io.tick_baud_x16 := tick_baud_x16
uart_rx.io.parity_en := io.reg2hw.ctrl.parity_en.q
uart_rx.io.parity_odd := io.reg2hw.ctrl.parity_odd.q
rx_tick_baud := uart_rx.io.tick_baud
rx_valid := uart_rx.io.rx_valid
rx_fifo_data := uart_rx.io.rx_data
rx_uart_idle := uart_rx.io.idle
event_rx_frame_err := uart_rx.io.frame_err
uart_rx.io.rx := rx_in
event_rx_parity_err := uart_rx.io.rx_parity_err
rx_fifo_wvalid := rx_valid & ~event_rx_frame_err & ~event_rx_parity_err
val rxfifo = Module(new Fifo(width = 8, pass = false, depth = 32))
rxfifo.io.clr_i := uart_fifo_rxrst
rxfifo.io.wvalid_i := rx_fifo_wvalid
rx_fifo_wready := rxfifo.io.wready_o
rxfifo.io.wdata_i := rx_fifo_data
rx_fifo_depth := rxfifo.io.depth_o
rx_fifo_rvalid := rxfifo.io.rvalid_o
rxfifo.io.rready_i := io.reg2hw.rdata.re
uart_rdata := rxfifo.io.rdata_o
// Interrupt and Status
// handling the empty interrupt by qualifying it with idle as well to give software
// a chance to write the desired data in the FIFO before enabling it.
// Read more here: https://github.com/lowRISC/opentitan/blob/master/hw/ip/uart/rtl/uart_core.sv#L308
// if the software disabled the tx_enable previously, the tx_uart_idle_q register will hold false.
// after writing the desired values software will enable the tx_enable
event_tx_empty := ~tx_fifo_rvalid & tx_uart_idle & ~tx_uart_idle_q
tx_uart_idle_q := tx_uart_idle
// generate rx overflow event if the rx wants to write a valid data in FIFO
// but the FIFO is full and cannot accept it.
event_rx_overflow := rx_fifo_wvalid & ~rx_fifo_wready
event_rx_break_err := break_err & (break_st_q === breakChk)
val intr_hw_tx_empty = Module(new IntrHardware(width = 1))
intr_hw_tx_empty.io.event_intr_i := event_tx_empty
intr_hw_tx_empty.io.reg2hw_intr_enable_q_i := io.reg2hw.intr_enable.tx_empty.q
intr_hw_tx_empty.io.reg2hw_intr_test_q_i := io.reg2hw.intr_test.tx_empty.q
intr_hw_tx_empty.io.reg2hw_intr_test_qe_i := io.reg2hw.intr_test.tx_empty.qe
intr_hw_tx_empty.io.reg2hw_intr_state_q_i := io.reg2hw.intr_state.tx_empty.q
io.hw2reg.intr_state.tx_empty.de := intr_hw_tx_empty.io.hw2reg_intr_state_de_o
io.hw2reg.intr_state.tx_empty.d := intr_hw_tx_empty.io.hw2reg_intr_state_d_o
io.intr_tx_empty_o := intr_hw_tx_empty.io.intr_o
val intr_hw_rx_overflow = Module(new IntrHardware(width = 1))
intr_hw_rx_overflow.io.event_intr_i := event_rx_overflow
intr_hw_rx_overflow.io.reg2hw_intr_enable_q_i := io.reg2hw.intr_enable.rx_overflow.q
intr_hw_rx_overflow.io.reg2hw_intr_test_q_i := io.reg2hw.intr_test.rx_overflow.q
intr_hw_rx_overflow.io.reg2hw_intr_test_qe_i := io.reg2hw.intr_test.rx_overflow.qe
intr_hw_rx_overflow.io.reg2hw_intr_state_q_i := io.reg2hw.intr_state.rx_overflow.q
io.hw2reg.intr_state.rx_overflow.de := intr_hw_rx_overflow.io.hw2reg_intr_state_de_o
io.hw2reg.intr_state.rx_overflow.d := intr_hw_rx_overflow.io.hw2reg_intr_state_d_o
io.intr_rx_overflow_o := intr_hw_rx_overflow.io.intr_o
val intr_hw_rx_frame_err = Module(new IntrHardware(width = 1))
intr_hw_rx_frame_err.io.event_intr_i := event_rx_frame_err
intr_hw_rx_frame_err.io.reg2hw_intr_enable_q_i := io.reg2hw.intr_enable.rx_frame_err.q
intr_hw_rx_frame_err.io.reg2hw_intr_test_q_i := io.reg2hw.intr_test.rx_frame_err.q
intr_hw_rx_frame_err.io.reg2hw_intr_test_qe_i := io.reg2hw.intr_test.rx_frame_err.qe
intr_hw_rx_frame_err.io.reg2hw_intr_state_q_i := io.reg2hw.intr_state.rx_frame_err.q
io.hw2reg.intr_state.rx_frame_err.de := intr_hw_rx_frame_err.io.hw2reg_intr_state_de_o
io.hw2reg.intr_state.rx_frame_err.d := intr_hw_rx_frame_err.io.hw2reg_intr_state_d_o
io.intr_rx_frame_err_o := intr_hw_rx_frame_err.io.intr_o
val intr_hw_rx_break_err = Module(new IntrHardware(width = 1))
intr_hw_rx_break_err.io.event_intr_i := event_rx_break_err
intr_hw_rx_break_err.io.reg2hw_intr_enable_q_i := io.reg2hw.intr_enable.rx_break_err.q
intr_hw_rx_break_err.io.reg2hw_intr_test_q_i := io.reg2hw.intr_test.rx_break_err.q
intr_hw_rx_break_err.io.reg2hw_intr_test_qe_i := io.reg2hw.intr_test.rx_break_err.qe
intr_hw_rx_break_err.io.reg2hw_intr_state_q_i := io.reg2hw.intr_state.rx_break_err.q
io.hw2reg.intr_state.rx_break_err.de := intr_hw_rx_break_err.io.hw2reg_intr_state_de_o
io.hw2reg.intr_state.rx_break_err.d := intr_hw_rx_break_err.io.hw2reg_intr_state_d_o
io.intr_rx_break_err_o := intr_hw_rx_break_err.io.intr_o
val intr_hw_rx_parity_err = Module(new IntrHardware(width = 1))
intr_hw_rx_parity_err.io.event_intr_i := event_rx_parity_err
intr_hw_rx_parity_err.io.reg2hw_intr_enable_q_i := io.reg2hw.intr_enable.rx_parity_err.q
intr_hw_rx_parity_err.io.reg2hw_intr_test_q_i := io.reg2hw.intr_test.rx_parity_err.q
intr_hw_rx_parity_err.io.reg2hw_intr_test_qe_i := io.reg2hw.intr_test.rx_parity_err.qe
intr_hw_rx_parity_err.io.reg2hw_intr_state_q_i := io.reg2hw.intr_state.rx_parity_err.q
io.hw2reg.intr_state.rx_parity_err.de := intr_hw_rx_parity_err.io.hw2reg_intr_state_de_o
io.hw2reg.intr_state.rx_parity_err.d := intr_hw_rx_parity_err.io.hw2reg_intr_state_d_o
io.intr_rx_parity_err_o := intr_hw_rx_parity_err.io.intr_o
}