Skip to content
This repository has been archived by the owner on May 28, 2024. It is now read-only.

Commit

Permalink
add uart
Browse files Browse the repository at this point in the history
  • Loading branch information
SinaKarvandi committed Feb 23, 2024
1 parent bcc9324 commit 462a23a
Show file tree
Hide file tree
Showing 9 changed files with 463 additions and 69 deletions.
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

ThisBuild / scalaVersion := "2.13.12"
ThisBuild / version := "0.1.0"
ThisBuild / organization := "com.github.hyperdbg"
ThisBuild / organization := "org.hyperdbg"

val chiselVersion = "6.0.0"

Expand Down
33 changes: 33 additions & 0 deletions generated/Buffer.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Generated by CIRCT firtool-1.62.0
module Buffer(
input clock,
reset,
output io_in_ready,
input io_in_valid,
input [7:0] io_in_bits,
input io_out_ready,
output io_out_valid,
output [7:0] io_out_bits
);

reg stateReg;
reg [7:0] dataReg;
always @(posedge clock) begin
if (reset) begin
stateReg <= 1'h0;
dataReg <= 8'h0;
end
else begin
if (stateReg)
stateReg <= ~io_out_ready & stateReg;
else
stateReg <= io_in_valid | stateReg;
if (~stateReg & io_in_valid)
dataReg <= io_in_bits;
end
end // always @(posedge)
assign io_in_ready = ~stateReg;
assign io_out_valid = stateReg;
assign io_out_bits = dataReg;
endmodule

33 changes: 33 additions & 0 deletions generated/BufferedTx.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Generated by CIRCT firtool-1.62.0
module BufferedTx(
input clock,
reset,
output io_txd,
io_channel_ready,
input io_channel_valid,
input [7:0] io_channel_bits
);

wire _buf_io_out_valid;
wire [7:0] _buf_io_out_bits;
wire _tx_io_channel_ready;
Tx tx (
.clock (clock),
.reset (reset),
.io_txd (io_txd),
.io_channel_ready (_tx_io_channel_ready),
.io_channel_valid (_buf_io_out_valid),
.io_channel_bits (_buf_io_out_bits)
);
Buffer buf_0 (
.clock (clock),
.reset (reset),
.io_in_ready (io_channel_ready),
.io_in_valid (io_channel_valid),
.io_in_bits (io_channel_bits),
.io_out_ready (_tx_io_channel_ready),
.io_out_valid (_buf_io_out_valid),
.io_out_bits (_buf_io_out_bits)
);
endmodule

43 changes: 43 additions & 0 deletions generated/Sender.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Generated by CIRCT firtool-1.62.0
module Sender(
input clock,
reset,
output io_txd
);

wire _tx_io_channel_ready;
wire [15:0][6:0] _GEN =
'{7'h48,
7'h48,
7'h48,
7'h48,
7'h21,
7'h64,
7'h6C,
7'h72,
7'h6F,
7'h57,
7'h20,
7'h6F,
7'h6C,
7'h6C,
7'h65,
7'h48};
reg [7:0] cntReg;
wire _tx_io_channel_valid_T = cntReg != 8'hC;
always @(posedge clock) begin
if (reset)
cntReg <= 8'h0;
else if (_tx_io_channel_ready & _tx_io_channel_valid_T)
cntReg <= cntReg + 8'h1;
end // always @(posedge)
BufferedTx tx (
.clock (clock),
.reset (reset),
.io_txd (io_txd),
.io_channel_ready (_tx_io_channel_ready),
.io_channel_valid (_tx_io_channel_valid_T),
.io_channel_bits ({1'h0, _GEN[cntReg[3:0]]})
);
endmodule

39 changes: 39 additions & 0 deletions generated/Tx.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Generated by CIRCT firtool-1.62.0
module Tx(
input clock,
reset,
output io_txd,
io_channel_ready,
input io_channel_valid,
input [7:0] io_channel_bits
);

reg [10:0] shiftReg;
reg [19:0] cntReg;
reg [3:0] bitsReg;
wire _io_channel_ready_T = cntReg == 20'h0;
always @(posedge clock) begin
if (reset) begin
shiftReg <= 11'h7FF;
cntReg <= 20'h0;
bitsReg <= 4'h0;
end
else if (_io_channel_ready_T) begin
if (|bitsReg) begin
shiftReg <= {1'h1, shiftReg[10:1]};
bitsReg <= bitsReg - 4'h1;
end
else begin
shiftReg <= io_channel_valid ? {2'h3, io_channel_bits, 1'h0} : 11'h7FF;
if (io_channel_valid)
bitsReg <= 4'hB;
end
cntReg <= 20'h1B1;
end
else
cntReg <= cntReg - 20'h1;
end // always @(posedge)
assign io_txd = shiftReg[0];
assign io_channel_ready = _io_channel_ready_T & ~(|bitsReg);
endmodule

15 changes: 15 additions & 0 deletions generated/UartMain.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Generated by CIRCT firtool-1.62.0
module UartMain(
input clock,
reset,
io_rxd,
output io_txd
);

Sender s (
.clock (clock),
.reset (reset),
.io_txd (io_txd)
);
endmodule

222 changes: 222 additions & 0 deletions src/main/scala/hwdbg/lib/uart/Uart.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
/*
*
* A UART is a serial port, also called an RS232 interface.
*
* Author: Martin Schoeberl ([email protected])
*
*/
package hwdbg.lib.uart

import chisel3._
import circt.stage.ChiselStage
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).asUInt

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
}

/**
* Send a string.
*/
class Sender(frequency: Int, baudRate: Int) extends Module {
val io = IO(new Bundle {
val txd = Output(UInt(1.W))
})

val tx = Module(new BufferedTx(frequency, baudRate))

io.txd := tx.io.txd

val msg = "Hello World!"
val text = VecInit(msg.map(_.U))
val len = msg.length.U

val cntReg = RegInit(0.U(8.W))

tx.io.channel.bits := text(cntReg)
tx.io.channel.valid := cntReg =/= len

when(tx.io.channel.ready && cntReg =/= len) {
cntReg := cntReg + 1.U
}
}

class Echo(frequency: Int, baudRate: Int) extends Module {
val io = IO(new Bundle {
val txd = Output(UInt(1.W))
val rxd = Input(UInt(1.W))
})
// io.txd := RegNext(io.rxd)
val tx = Module(new BufferedTx(frequency, baudRate))
val rx = Module(new Rx(frequency, baudRate))
io.txd := tx.io.txd
rx.io.rxd := io.rxd
tx.io.channel <> rx.io.channel
}

class UartMain(frequency: Int, baudRate: Int) extends Module {
val io = IO(new Bundle {
val rxd = Input(UInt(1.W))
val txd = Output(UInt(1.W))
})

val doSender = true

if (doSender) {
val s = Module(new Sender(frequency, baudRate))
io.txd := s.io.txd
} else {
val e = Module(new Echo(frequency, baudRate))
e.io.rxd := io.rxd
io.txd := e.io.txd
}

}

object Main extends App {
// These lines generate the Verilog output
println(
ChiselStage.emitSystemVerilog(
new UartMain(50000000, 115200),
firtoolOpts = Array(
"-disable-all-randomization",
"-strip-debug-info",
"--split-verilog", // The intention for this argument (and next argument) is to separate generated files.
"-o",
"generated/",
)
)
)
}
Loading

0 comments on commit 462a23a

Please sign in to comment.