uart: Eliminate systemic baud rate error with low divisor values
[sifive-blocks.git] / src / main / scala / devices / uart / UART.scala
index e69689270db7830269a4f21e8650b776c4ca838d..d0220e7cbe2966eecd26b6d901dccdef68ae37b2 100644 (file)
@@ -2,18 +2,19 @@
 package sifive.blocks.devices.uart
 
 import Chisel._
-import config._
-import regmapper._
-import uncore.tilelink2._
-import junctions._
-import util._
-import rocketchip.PeripheryBusConfig
+import chisel3.experimental.MultiIOModule
+import freechips.rocketchip.config.Parameters
+import freechips.rocketchip.regmapper._
+import freechips.rocketchip.tilelink._
+import freechips.rocketchip.util._
+
 import sifive.blocks.util.{NonBlockingEnqueue, NonBlockingDequeue}
 
-case class UARTConfig(
+case class UARTParams(
   address: BigInt,
   dataBits: Int = 8,
   stopBits: Int = 2,
+  divisorInit: Int = 0,
   divisorBits: Int = 16,
   oversample: Int = 4,
   nSamples: Int = 3,
@@ -21,23 +22,25 @@ case class UARTConfig(
   nRxEntries: Int = 8)
 
 trait HasUARTParameters {
-  val c: UARTConfig
-  val uartDataBits = c.dataBits
-  val uartStopBits = c.stopBits
-  val uartDivisorBits = c.divisorBits
+  def c: UARTParams
+  def uartDataBits = c.dataBits
+  def uartStopBits = c.stopBits
+  def uartDivisorInit = c.divisorInit
+  def uartDivisorBits = c.divisorBits
 
-  val uartOversample = c.oversample
-  val uartOversampleFactor = 1 << uartOversample
-  val uartNSamples = c.nSamples
+  def uartOversample = c.oversample
+  def uartOversampleFactor = 1 << uartOversample
+  def uartNSamples = c.nSamples
 
-  val uartNTxEntries = c.nTxEntries
-  val uartNRxEntries = c.nRxEntries
+  def uartNTxEntries = c.nTxEntries
+  def uartNRxEntries = c.nRxEntries
 
+  require(uartDivisorInit != 0) // should have been initialized during instantiation
   require(uartDivisorBits > uartOversample)
   require(uartOversampleFactor > uartNSamples)
 }
 
-abstract class UARTModule(val c: UARTConfig)(implicit val p: Parameters)
+abstract class UARTModule(val c: UARTParams)(implicit val p: Parameters)
     extends Module with HasUARTParameters
 
 class UARTPortIO extends Bundle {
@@ -45,17 +48,11 @@ class UARTPortIO extends Bundle {
   val rxd = Bool(INPUT)
 }
 
-trait MixUARTParameters {
-  implicit val p: Parameters
-  val params: UARTConfig
-  val c = params
-}
-
-trait UARTTopBundle extends Bundle with MixUARTParameters with HasUARTParameters {
+trait HasUARTTopBundleContents extends Bundle {
   val port = new UARTPortIO
 }
 
-class UARTTx(c: UARTConfig)(implicit p: Parameters) extends UARTModule(c)(p) {
+class UARTTx(c: UARTParams)(implicit p: Parameters) extends UARTModule(c)(p) {
   val io = new Bundle {
     val en = Bool(INPUT)
     val in = Decoupled(Bits(width = uartDataBits)).flip
@@ -73,10 +70,14 @@ class UARTTx(c: UARTConfig)(implicit p: Parameters) extends UARTModule(c)(p) {
   val out = Reg(init = Bits(1, 1))
   io.out := out
 
+  val plusarg_tx = PlusArg("uart_tx", 1, "Enable/disable the TX to speed up simulation").orR
+
   val busy = (counter =/= UInt(0))
   io.in.ready := io.en && !busy
   when (io.in.fire()) {
-    printf("%c", io.in.bits)
+    printf("UART TX (%x): %c\n", io.in.bits, io.in.bits)
+  }
+  when (io.in.fire() && plusarg_tx) {
     shifter := Cat(io.in.bits, Bits(0, 1))
     counter := Mux1H((0 until uartStopBits).map(i =>
       (io.nstop === UInt(i)) -> UInt(n + i + 1)))
@@ -91,7 +92,7 @@ class UARTTx(c: UARTConfig)(implicit p: Parameters) extends UARTModule(c)(p) {
   }
 }
 
-class UARTRx(c: UARTConfig)(implicit p: Parameters) extends UARTModule(c)(p) {
+class UARTRx(c: UARTParams)(implicit p: Parameters) extends UARTModule(c)(p) {
   val io = new Bundle {
     val en = Bool(INPUT)
     val in = Bits(INPUT, 1)
@@ -103,46 +104,38 @@ class UARTRx(c: UARTConfig)(implicit p: Parameters) extends UARTModule(c)(p) {
   val debounce_max = (debounce === UInt(3))
   val debounce_min = (debounce === UInt(0))
 
-  val prescaler = Reg(init = UInt(0, uartDivisorBits - uartOversample))
+  val prescaler = Reg(UInt(width = uartDivisorBits - uartOversample + 1))
   val start = Wire(init = Bool(false))
-  val busy = Wire(init = Bool(false))
-  val pulse = (prescaler === UInt(0)) && busy
+  val pulse = (prescaler === UInt(0))
 
-  when (busy) {
-    prescaler := prescaler - UInt(1)
-  }
-  when (start || pulse) {
-    prescaler := io.div >> uartOversample
-  }
+  private val dataCountBits = log2Floor(uartDataBits) + 1
 
-  val sample = Reg(Bits(width = uartNSamples))
-  val voter = new Majority(sample.toBools.toSet)
-  when (pulse) {
-    sample := Cat(sample, io.in)
-  }
+  val data_count = Reg(UInt(width = dataCountBits))
+  val data_last = (data_count === UInt(0))
+  val sample_count = Reg(UInt(width = uartOversample))
+  val sample_mid = (sample_count === UInt((uartOversampleFactor - uartNSamples + 1) >> 1))
+  val sample_last = (sample_count === UInt(0))
+  val countdown = Cat(data_count, sample_count) - UInt(1)
 
-  private val delay0 = (uartOversampleFactor + uartNSamples) >> 1
-  private val delay1 = uartOversampleFactor
+  // Compensate for the divisor not being a multiple of the oversampling period.
+  // Let remainder k = (io.div % uartOversampleFactor).
+  // For the last k samples, extend the sampling delay by 1 cycle.
+  val remainder = io.div(uartOversample-1, 0)
+  val extend = (sample_count < remainder) // Pad head: (sample_count > ~remainder)
+  val restore = start || pulse
+  val prescaler_in = Mux(restore, io.div >> uartOversample, prescaler)
+  val prescaler_next = prescaler_in - Mux(restore && extend, UInt(0), UInt(1))
 
-  val timer = Reg(UInt(width = uartOversample + 1))
-  val counter = Reg(UInt(width = log2Floor(uartDataBits) + 1))
+  val sample = Reg(Bits(width = uartNSamples))
+  val voter = Majority(sample.toBools.toSet)
   val shifter = Reg(Bits(width = uartDataBits))
-  val expire = (timer === UInt(0)) && pulse
-
-  val sched = Wire(init = Bool(false))
-  when (pulse) {
-    timer := timer - UInt(1)
-  }
-  when (sched) {
-    timer := UInt(delay1-1)
-  }
 
   val valid = Reg(init = Bool(false))
   valid := Bool(false)
   io.out.valid := valid
   io.out.bits := shifter
 
-  val (s_idle :: s_start :: s_data :: Nil) = Enum(UInt(), 3)
+  val (s_idle :: s_data :: Nil) = Enum(UInt(), 2)
   val state = Reg(init = s_idle)
 
   switch (state) {
@@ -153,36 +146,29 @@ class UARTRx(c: UARTConfig)(implicit p: Parameters) extends UARTModule(c)(p) {
       when (!io.in) {
         debounce := debounce + UInt(1)
         when (debounce_max) {
-          state := s_start
-          start := Bool(true)
-          timer := UInt(delay0-1)
-        }
-      }
-    }
-
-    is (s_start) {
-      busy := Bool(true)
-      when (expire) {
-        sched := Bool(true)
-        when (voter.out) {
-          state := s_idle
-        } .otherwise {
           state := s_data
-          counter := UInt(uartDataBits)
+          start := Bool(true)
+          prescaler := prescaler_next
+          data_count := UInt(uartDataBits+1)
+          sample_count := UInt(uartOversampleFactor - 1)
         }
       }
     }
 
     is (s_data) {
-      busy := Bool(true)
-      when (expire) {
-        counter := counter - UInt(1)
-        when (counter === UInt(0)) {
-          state := s_idle
-          valid := Bool(true)
-        } .otherwise {
-          shifter := Cat(voter.out, shifter >> 1)
-          sched := Bool(true)
+      prescaler := prescaler_next
+      when (pulse) {
+        sample := Cat(sample, io.in)
+        data_count := countdown >> uartOversample
+        sample_count := countdown(uartOversample-1, 0)
+
+        when (sample_mid) {
+          when (data_last) {
+            state := s_idle
+            valid := Bool(true)
+          } .otherwise {
+            shifter := Cat(voter, shifter >> 1)
+          }
         }
       }
     }
@@ -198,17 +184,19 @@ class UARTInterrupts extends Bundle {
   val txwm = Bool()
 }
 
-trait UARTTopModule extends Module with MixUARTParameters with HasUARTParameters with HasRegMap {
-  val io: UARTTopBundle
+trait HasUARTTopModuleContents extends MultiIOModule with HasUARTParameters with HasRegMap {
+  val io: HasUARTTopBundleContents
+  implicit val p: Parameters
+  def params: UARTParams
+  def c = params
 
-  val txm = Module(new UARTTx(c))
+  val txm = Module(new UARTTx(params))
   val txq = Module(new Queue(txm.io.in.bits, uartNTxEntries))
 
-  val rxm = Module(new UARTRx(c))
+  val rxm = Module(new UARTRx(params))
   val rxq = Module(new Queue(rxm.io.out.bits, uartNRxEntries))
 
-  val divinit = 542 // (62.5MHz / 115200)
-  val div = Reg(init = UInt(divinit, uartDivisorBits))
+  val div = Reg(init = UInt(uartDivisorInit, uartDivisorBits))
 
   private val stopCountBits = log2Up(uartStopBits)
   private val txCountBits = log2Floor(uartNTxEntries) + 1
@@ -262,14 +250,8 @@ trait UARTTopModule extends Module with MixUARTParameters with HasUARTParameters
   )
 }
 
-class Majority(in: Set[Bool]) {
-  private val n = (in.size >> 1) + 1
-  private val clauses = in.subsets(n).map(_.reduce(_ && _))
-  val out = clauses.reduce(_ || _)
-}
-
-// Magic TL2 Incantation to create a TL2 Slave
-class UART(c: UARTConfig)(implicit p: Parameters)
-  extends TLRegisterRouter(c.address, interrupts = 1, beatBytes = p(PeripheryBusConfig).beatBytes)(
-  new TLRegBundle(c, _)    with UARTTopBundle)(
-  new TLRegModule(c, _, _) with UARTTopModule)
+// Magic TL2 Incantation to create a TL2 UART
+class TLUART(w: Int, c: UARTParams)(implicit p: Parameters)
+  extends TLRegisterRouter(c.address, "serial", Seq("sifive,uart0"), interrupts = 1, beatBytes = w)(
+  new TLRegBundle(c, _)    with HasUARTTopBundleContents)(
+  new TLRegModule(c, _, _) with HasUARTTopModuleContents)