Skip to content

UART Transmitter

import dfhdl.* //import all the DFHDL goodness

@top class UART_Tx(
    val CLK_FREQ_KHz: Int <> CONST  = 50000,
    val BAUD_RATE_BPS: Int <> CONST = 115200
) extends RTDesign:
  val data_en    = Bit             <> IN
  val DATA_BITS  = 8
  val data       = Bits(DATA_BITS) <> IN
  val tx         = Bit             <> OUT.REG
  val tx_en      = Bit             <> OUT.REG
  val tx_done    = Bit             <> OUT.REG
  val BIT_CLOCKS = CLK_FREQ_KHz * 1000 / BAUD_RATE_BPS

  enum Status extends Encode.OneHot:
    case Idle, StartBit, DataBits, StopBit, Finalize
  import Status.*
  val status     = Status                 <> VAR.REG init Idle
  val bitClkCnt  = UInt.until(BIT_CLOCKS) <> VAR.REG init 0
  val dataBitCnt = UInt.until(DATA_BITS)  <> VAR.REG init 0
  val shiftData  = Bits(DATA_BITS)        <> VAR.REG

  // To save on writing the "bit clock count wait" 3 times,
  // we use DFHDL's meta-programming capability.
  @internals.metaContextIgnore
  def waitBitAndThen(onThreshold: => Unit): Unit =
    if (bitClkCnt == BIT_CLOCKS - 1)
      bitClkCnt.din := 0
      onThreshold
    else bitClkCnt.din := bitClkCnt + 1

  status match
    case Idle =>
      tx_en.din      := 0
      tx.din         := 1
      tx_done.din    := 0
      bitClkCnt.din  := 0
      dataBitCnt.din := 0
      if (data_en)
        shiftData.din := data
        status.din    := StartBit

    case StartBit =>
      tx_en.din := 1
      tx.din    := 0
      waitBitAndThen { status.din := DataBits }

    case DataBits =>
      tx.din := shiftData.lsbit
      waitBitAndThen {
        shiftData.din := shiftData >> 1
        if (dataBitCnt == DATA_BITS - 1)
          dataBitCnt.din := 0
          status.din     := StopBit
        else dataBitCnt.din := dataBitCnt + 1
      }

    case StopBit =>
      tx.din := 1
      waitBitAndThen {
        tx_done.din := 1
        status.din  := Finalize
      }

    case Finalize =>
      tx_en.din   := 0
      tx_done.din := 1
      status.din  := Idle
  end match
end UART_Tx

////////////////////////////////////////////////////////////////////////////////////////////////
// DFHDL Elaboration Options:                                                                 //
////////////////////////////////////////////////////////////////////////////////////////////////
// Uncomment to set different clock and reset configurations:
// given options.ElaborationOptions.DefaultClkCfg = ClkCfg(ClkCfg.Edge.Rising)
// given options.ElaborationOptions.DefaultRstCfg = RstCfg(RstCfg.Mode.Async, RstCfg.Active.Low)
////////////////////////////////////////////////////////////////////////////////////////////////
// DFHDL Compiler Options:                                                                    //
////////////////////////////////////////////////////////////////////////////////////////////////
// Select backend compiler:
given options.CompilerOptions.Backend = backends.verilog
// Uncomment to enable printing design code after elaboration (before compilation):
// given options.ElaborationOptions.PrintDFHDLCode = true
// Uncomment to enable printing design code after compilation:
// given options.CompilerOptions.PrintDFHDLCode = true
////////////////////////////////////////////////////////////////////////////////////////////////