mirror of
https://github.com/handsomezhuzhu/2025-yatcpu.git
synced 2026-02-20 20:10:14 +00:00
205 lines
5.0 KiB
Scala
205 lines
5.0 KiB
Scala
// Copyright 2021 Howard Lau
|
|
//
|
|
// 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.
|
|
|
|
package peripheral
|
|
|
|
import chisel3._
|
|
import chisel3.util._
|
|
|
|
class UartIO extends DecoupledIO(UInt(8.W)) {
|
|
}
|
|
|
|
|
|
/**
|
|
* Transmit part of the UART.
|
|
* A minimal version without any additional buffering.
|
|
* Use a ready/valid handshaking.
|
|
*/
|
|
class Tx(frequency: Int, baudRate: Int) extends Module {
|
|
val io = IO(new Bundle {
|
|
val txd = Output(UInt(1.W))
|
|
val channel = Flipped(new UartIO())
|
|
|
|
})
|
|
|
|
val BIT_CNT = ((frequency + baudRate / 2) / baudRate - 1).U
|
|
|
|
val shiftReg = RegInit(0x7ff.U)
|
|
val cntReg = RegInit(0.U(20.W))
|
|
val bitsReg = RegInit(0.U(4.W))
|
|
|
|
io.channel.ready := (cntReg === 0.U) && (bitsReg === 0.U)
|
|
io.txd := shiftReg(0)
|
|
|
|
when(cntReg === 0.U) {
|
|
|
|
cntReg := BIT_CNT
|
|
when(bitsReg =/= 0.U) {
|
|
val shift = shiftReg >> 1
|
|
shiftReg := Cat(1.U, shift(9, 0))
|
|
bitsReg := bitsReg - 1.U
|
|
}.otherwise {
|
|
when(io.channel.valid) {
|
|
shiftReg := Cat(Cat(3.U, io.channel.bits), 0.U) // two stop bits, data, one start bit
|
|
bitsReg := 11.U
|
|
}.otherwise {
|
|
shiftReg := 0x7ff.U
|
|
}
|
|
}
|
|
|
|
}.otherwise {
|
|
cntReg := cntReg - 1.U
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Receive part of the UART.
|
|
* A minimal version without any additional buffering.
|
|
* Use a ready/valid handshaking.
|
|
*
|
|
* The following code is inspired by Tommy's receive code at:
|
|
* https://github.com/tommythorn/yarvi
|
|
*/
|
|
class Rx(frequency: Int, baudRate: Int) extends Module {
|
|
val io = IO(new Bundle {
|
|
val rxd = Input(UInt(1.W))
|
|
val channel = new UartIO()
|
|
|
|
})
|
|
|
|
val BIT_CNT = ((frequency + baudRate / 2) / baudRate - 1).U
|
|
val START_CNT = ((3 * frequency / 2 + baudRate / 2) / baudRate - 1).U
|
|
|
|
// Sync in the asynchronous RX data, reset to 1 to not start reading after a reset
|
|
val rxReg = RegNext(RegNext(io.rxd, 1.U), 1.U)
|
|
|
|
val shiftReg = RegInit(0.U(8.W))
|
|
val cntReg = RegInit(0.U(20.W))
|
|
val bitsReg = RegInit(0.U(4.W))
|
|
val valReg = RegInit(false.B)
|
|
|
|
when(cntReg =/= 0.U) {
|
|
cntReg := cntReg - 1.U
|
|
}.elsewhen(bitsReg =/= 0.U) {
|
|
cntReg := BIT_CNT
|
|
shiftReg := Cat(rxReg, shiftReg >> 1)
|
|
bitsReg := bitsReg - 1.U
|
|
// the last shifted in
|
|
when(bitsReg === 1.U) {
|
|
valReg := true.B
|
|
}
|
|
}.elsewhen(rxReg === 0.U) { // wait 1.5 bits after falling edge of start
|
|
cntReg := START_CNT
|
|
bitsReg := 8.U
|
|
}
|
|
|
|
when(valReg && io.channel.ready) {
|
|
valReg := false.B
|
|
}
|
|
|
|
io.channel.bits := shiftReg
|
|
io.channel.valid := valReg
|
|
}
|
|
|
|
/**
|
|
* A single byte buffer with a ready/valid interface
|
|
*/
|
|
class Buffer extends Module {
|
|
val io = IO(new Bundle {
|
|
val in = Flipped(new UartIO())
|
|
val out = new UartIO()
|
|
})
|
|
|
|
val empty :: full :: Nil = Enum(2)
|
|
val stateReg = RegInit(empty)
|
|
val dataReg = RegInit(0.U(8.W))
|
|
|
|
io.in.ready := stateReg === empty
|
|
io.out.valid := stateReg === full
|
|
|
|
when(stateReg === empty) {
|
|
when(io.in.valid) {
|
|
dataReg := io.in.bits
|
|
stateReg := full
|
|
}
|
|
}.otherwise { // full
|
|
when(io.out.ready) {
|
|
stateReg := empty
|
|
}
|
|
}
|
|
io.out.bits := dataReg
|
|
}
|
|
|
|
/**
|
|
* A transmitter with a single buffer.
|
|
*/
|
|
class BufferedTx(frequency: Int, baudRate: Int) extends Module {
|
|
val io = IO(new Bundle {
|
|
val txd = Output(UInt(1.W))
|
|
val channel = Flipped(new UartIO())
|
|
|
|
})
|
|
val tx = Module(new Tx(frequency, baudRate))
|
|
val buf = Module(new Buffer)
|
|
|
|
buf.io.in <> io.channel
|
|
tx.io.channel <> buf.io.out
|
|
io.txd <> tx.io.txd
|
|
}
|
|
|
|
class Uart(frequency: Int, baudRate: Int) extends Module {
|
|
val io = IO(new Bundle {
|
|
val bundle = new RAMBundle
|
|
val rxd = Input(UInt(1.W))
|
|
val txd = Output(UInt(1.W))
|
|
|
|
val signal_interrupt = Output(Bool())
|
|
})
|
|
val interrupt = RegInit(false.B)
|
|
val rxData = RegInit(0.U)
|
|
|
|
val tx = Module(new BufferedTx(frequency, baudRate))
|
|
val rx = Module(new Rx(frequency, baudRate))
|
|
|
|
io.bundle.read_data := 0.U
|
|
when(io.bundle.address === 0x4.U) {
|
|
io.bundle.read_data := baudRate.U
|
|
}.elsewhen(io.bundle.address === 0xC.U) {
|
|
io.bundle.read_data := rxData
|
|
interrupt := false.B
|
|
}
|
|
|
|
tx.io.channel.valid := false.B
|
|
tx.io.channel.bits := 0.U
|
|
when(io.bundle.write_enable) {
|
|
when(io.bundle.address === 0x8.U) {
|
|
interrupt := io.bundle.write_data =/= 0.U
|
|
}.elsewhen(io.bundle.address === 0x10.U) {
|
|
tx.io.channel.valid := true.B
|
|
tx.io.channel.bits := io.bundle.write_data
|
|
}
|
|
}
|
|
|
|
io.txd := tx.io.txd
|
|
rx.io.rxd := io.rxd
|
|
|
|
io.signal_interrupt := interrupt
|
|
rx.io.channel.ready := false.B
|
|
when(rx.io.channel.valid) {
|
|
rx.io.channel.ready := true.B
|
|
rxData := rx.io.channel.bits
|
|
interrupt := true.B
|
|
}
|
|
}
|