Skip to content

Commit

Permalink
Add memory mapped UART + GPIOs and instruction memory programmable ov…
Browse files Browse the repository at this point in the history
…er APB
  • Loading branch information
SyedAnasAlam committed Dec 1, 2024
1 parent a307144 commit 72f9d8a
Show file tree
Hide file tree
Showing 12 changed files with 283 additions and 50 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ verification/lib/*
verification/lib64
verification/sim_build/*
verification/pyvenv.cfg
verification/results.xml
verification/results.xml
src/sv/*
17 changes: 17 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,13 +1,30 @@
# Just remember commands, for now.

PROG_DIR ?= leros/asm/test
ROM ?= base.s
APB_BASE_ADDR ?= 01050000
BUILD_DIR ?= src/sv/

init:
git submodule update --init --recursive

generate:
sbt run

generate_dtu:
sbt "runMain DtuTop $(APB_BASE_ADDR) $(PROG_DIR)/$(ROM) $(BUILD_DIR)"

clean:
rm -rf $(BUILD_DIR)/*.sv
rm -rf $(BUILD_DIR)/*.json
rm -rf $(BUILD_DIR)/*.f
rm -rf $(BUILD_DIR)/*.fir

test:
sbt test
cd leros; make init
cd leros; sbt test
cd hello-morse; sbt test

test_dtu:
sbt "testOnly DtuTopTester"
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,14 @@ Red signals are IOs connected to the PMOD connector. Blue signals are IOs betwee
| `irqEn1` | input | Interrupt enable | N/A
| `ssCtrl1` | input | -- | N/A
| `boot` | input | select boot source | `pmod0[0]`
| `uart_rx_prog` | input | UART program interface | `pmod0[1]`
| `uart_tx_leros` | output | Leros UART interface | `pmod0[2]`
| `uart_rx_leros` | input | Leros UART interface | `pmod0[3]`
| `led` | output | Led output | `pmod1[0]`
| `morse` | output | Morse output | `pmod1[1]`
| `uart_rx` | output | Leros UART interface | `pmod0[1]`
| `uart_tx` | input | Leros UART interface | `pmod0[2]`
| unused | -- | -- | `pmod0[3]`
| `gpio[0]` | input/output | Leros GPIO | `pmod1[0]`
| `gpio[1]` | input/output | Leros GPIO | `pmod1[1]`
| `gpio[2]` | input/output | Leros GPIO | `pmod1[2]`
| `gpio[3]` | input/output | Leros GPIO | `pmod1[3]`



Currently `irq1`, `irqEn1` and `ssCtrl1` are unused. `irq1` is tied to 0.
Expand Down
128 changes: 88 additions & 40 deletions src/main/scala/DtuTop.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import chisel3._
import chisel3.stage.{ChiselStage, ChiselGeneratorAnnotation}

import chisel3.util._
import leros._
import leros.wrmem._
import leros.uart._
import io._


/*
* DTU Top level
Expand All @@ -13,10 +15,22 @@ import leros.wrmem._
* A verilog blackbox module is added that generates a synchronous active high reset from an asynchrnous active low reset
*/

class DtuTop(addrWidth:Int = 32, dataWidth:Int = 32, prog:String = "", resetSyncFact:() => ResetSyncBase = () => Module(new ResetSync())) extends Module {
class DtuTop(apbBaseAddr:Int, progROM:String, resetSyncFact:() => ResetSyncBase = () => Module(new ResetSync())) extends Module {

val apbAddrWidth = 32
val apbDataWidth = 32

val lerosSize = 32
val lerosMemAddrWidth = 8
val lerosInstrWidth = 16
val lerosClockFreq = 100000000
val lerosUartBaudrate = 115200
// IO is now mapped to 0x0f00, but wrAddr counts in 32-bit words
val lerosIoBaseAddr = 0x03c0

val io = IO(new Bundle {
// Interface: APB
val apb = new ApbTargetPort(addrWidth, dataWidth)
val apb = new ApbTargetPort(apbAddrWidth, apbDataWidth)

// Clock and reset are added implicity
// Reset is asynchronous active low
Expand All @@ -42,58 +56,92 @@ class DtuTop(addrWidth:Int = 32, dataWidth:Int = 32, prog:String = "", resetSync

val syncReset = RegNext(RegNext(!reset.asBool))

val lerosSize = 32
val lerosMemAddrWidth = 8
val lerosClockFreq = 100000000
val lerosUartBaudrate = 115200

// All modules instantiated here are reset by synchronous active high reset
// All registers must be instantiated within this to ensure all have the same reset
// MS: maybe we should have yet another top level jut for the reset handling to avoid this withReset
// MS: what is the meaning of the leros value here?
val leros = withReset((ResetSync.io.resetOut.asBool)) {
val apbReg = Module(new ApbRegTarget(addrWidth, dataWidth, 0x01050000, 5))
io.apb <> apbReg.io.apb

// pmod 0 set to output
val pmod0oeReg = RegInit(0.U(8.W))
val pmod0gpoReg = RegInit(0.U(1.W))
io.pmod0.oe := pmod0oeReg
io.pmod0.gpo := pmod0gpoReg

if (!prog.isEmpty) {
val uartRx = Module(new UARTRx(lerosClockFreq, lerosUartBaudrate))
val uartTx = Module(new BufferedTx(lerosClockFreq, lerosUartBaudrate))
val registerMap = RegInit(VecInit(Seq.fill(Register_Map_Index.COUNT)(0.U(8.W))))

registerMap(Register_Map_Index.UART_RX_DATA.U) := uartRx.io.out.bits
registerMap(Register_Map_Index.UART_RX_VALID) := uartRx.io.out.valid
uartRx.io.out.ready := registerMap(Register_Map_Index.UART_RX_READY)

uartTx.io.channel.bits := registerMap(Register_Map_Index.UART_TX_DATA.U)
uartTx.io.channel.valid := registerMap(Register_Map_Index.UART_TX_VALID)
registerMap(Register_Map_Index.UART_TX_READY) := uartTx.io.channel.ready

// interrup not generated
io.irq1 := false.B

/* pmod 0
pin 0: boot select
pin 1: uart rx
pin 2: uart tx
pin 3: unused
*/
io.pmod0.oe := "b0011".U
val bootSel = io.pmod0.gpi(0)
uartRx.io.rxd := io.pmod0.gpi(1)
io.pmod0.gpo := "b0".U ## uartTx.io.txd ## "b00".U

// pmod 1 are GPIO pins memory mapped to Leros
io.pmod1.oe := registerMap(Register_Map_Index.PMOD_OE)
io.pmod1.gpo := registerMap(Register_Map_Index.PMOD_GPO)
registerMap(Register_Map_Index.PMOD_GPI) := io.pmod1.gpi

if (!progROM.isEmpty) {
val leros = Module(new Leros(prog = "notused", size = lerosSize, memAddrWidth = lerosMemAddrWidth))
val instrMem = Module(new InstrMem(lerosMemAddrWidth, prog))
instrMem.io <> leros.imemIO

// val instrMem = Module(new WrInstrMemory(lerosMemAddrWidth, lerosClockFreq, lerosUartBaudrate))
// instrMem.io.instrAddr := leros.imemIO.addr
// leros.imemIO.instr := instrMem.io.instr
// instrMem.io.uartRX := io.pmod0.gpi(0)
val lerosCtrlRegAddr = math.pow(2, lerosMemAddrWidth).toInt + apbBaseAddr
val apbLoader = Module(new ApbLoader(apbAddrWidth, apbDataWidth, apbBaseAddr, lerosCtrlRegAddr, lerosMemAddrWidth, lerosInstrWidth))
io.apb <> apbLoader.io.apb

val instrMem = Module(new SramSim(lerosMemAddrWidth, lerosInstrWidth))
val instrROM = Module(new InstrMem(lerosMemAddrWidth, progROM))


instrMem.io.wr := apbLoader.io.wr
instrMem.io.req := apbLoader.io.req
instrMem.io.wrMask := apbLoader.io.wrMask
instrMem.io.wrAddr := apbLoader.io.wrAddr
instrMem.io.wrData := apbLoader.io.wrData

instrMem.io.rdAddr := leros.imemIO.addr
instrROM.io.addr := leros.imemIO.addr

leros.imemIO.instr := Mux(bootSel, instrMem.io.rdData, instrROM.io.instr)

leros.reset := apbLoader.io.lerosReset | reset.asBool

val dataMem = Module(new DataMem(lerosMemAddrWidth, false))
dataMem.io <> leros.dmemIO

val readIO = leros.dmemIO.rdAddr === lerosIoBaseAddr.U
val writeIO = (leros.dmemIO.wrAddr === lerosIoBaseAddr.U) && leros.dmemIO.wr

val registerReadIndex = leros.dmemIO.rdAddr - lerosIoBaseAddr.U
val registerWriteIndex = leros.dmemIO.wrAddr - lerosIoBaseAddr.U

// IO is now mapped to 0x0f00, but wrAddr counts in 32-bit words
when((leros.dmemIO.wrAddr === 0x03c0.U) && leros.dmemIO.wr) {
dataMem.io.wr := false.B
pmod0oeReg := 1.U
pmod0gpoReg := leros.dmemIO.wrData(7, 0)
dataMem.io.rdAddr := leros.dmemIO.rdAddr
leros.dmemIO.rdData := Mux(readIO, registerMap(registerReadIndex), dataMem.io.rdData)

dataMem.io.wr := leros.dmemIO.wr
dataMem.io.wrAddr := leros.dmemIO.wrAddr
dataMem.io.wrData := leros.dmemIO.wrData
dataMem.io.wrMask := leros.dmemIO.wrMask
when(writeIO) {
registerMap(registerWriteIndex) := leros.dmemIO.wrData
}


leros
} else null.asInstanceOf[Leros]
}

// interrup not generated
io.irq1 := false.B

// pmod 1 set to output
io.pmod1.oe := 0.U
io.pmod1.gpo := 0.U


}

object DtuTop extends App {
(new ChiselStage).emitSystemVerilog(new DtuTop(addrWidth=32, dataWidth=32), Array("-X", "sverilog", "-e", "sverilog", "--target-dir", "src/sv"))
(new ChiselStage).emitSystemVerilog(new DtuTop(Integer.parseInt(args(0), 16), args(1)), Array("--target-dir", args(2)))
}
15 changes: 15 additions & 0 deletions src/main/scala/RegisterMap.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
object Register_Map_Index {
val COUNT = 9

val UART_RX_DATA = 0
val UART_RX_VALID = 1
val UART_RX_READY = 2

val UART_TX_DATA = 3
val UART_TX_VALID = 4
val UART_TX_READY = 5

val PMOD_OE = 6
val PMOD_GPO = 7
val PMOD_GPI = 8
}
93 changes: 93 additions & 0 deletions src/main/scala/io/ApbLoader.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package io

import chisel3._
import chisel3.util._

class ApbLoader(apbAddrWidth:Int, apbDataWidth:Int, apbBaseAddr:Int, lerosCtrlRegAddr:Int, lerosMemAddrWidth:Int, lerosMemDataWidth:Int) extends Module {
val io = IO(new Bundle{
val apb = new ApbTargetPort(apbAddrWidth, apbDataWidth)

val req = Output(Bool())
val wrAddr = Output(UInt(lerosMemAddrWidth.W))
val wrData = Output(UInt(lerosMemDataWidth.W))
val wr = Output(Bool())
val wrMask = Output(UInt((lerosMemDataWidth/8).W))

val lerosReset = Output(Bool())
})

val idle :: write :: read :: Nil = Enum(3)
val state = RegInit(idle)

val lerosResetReg = RegInit(false.B)

io.apb.pready := false.B
io.apb.prdata := 0.U
io.apb.pslverr := false.B

io.req := false.B
io.wrAddr := 0.U
io.wrData := 0.U
io.wrMask := 0.U
io.wr := false.B

io.lerosReset := lerosResetReg

switch(state) {
is(idle) {
io.apb.pready := false.B
io.apb.prdata := 0.U
io.apb.pslverr := false.B

io.req := false.B
io.wrAddr := 0.U
io.wrData := 0.U
io.wrMask := 0.U
io.wr := false.B

when(io.apb.psel && io.apb.pwrite) {
state := write
}
. elsewhen(io.apb.psel && !io.apb.pwrite) {
state := read
}
}
is(write) {
when(io.apb.paddr === lerosCtrlRegAddr.U) {
lerosResetReg := Mux(io.apb.pwdata === 0.U, false.B, true.B)
}
.otherwise {
io.req := true.B
io.wrAddr := io.apb.paddr - apbBaseAddr.U
io.wrData := io.apb.pwdata
io.wrMask := "b11".U
io.wr := true.B
}

io.apb.pslverr := (apbBaseAddr.U > io.apb.paddr)
io.apb.pready := true.B

when(~io.apb.psel) {
state := idle
}
}
// Read not supported therefore raise error
is(read) {
io.apb.prdata := 0.U

io.req := false.B
io.wrAddr := 0.U
io.wrData := 0.U
io.wrMask := 0.U
io.wr := false.B

io.apb.pready := true.B
io.apb.pslverr := true.B

when(~io.apb.psel) {
state := idle
}
}
}

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package io

import chisel3._
import chisel3.util._

class ApbTargetPort(addrWidth:Int = 32, dataWidth:Int = 32) extends Bundle() {
class ApbTargetPort(addrWidth:Int, dataWidth:Int) extends Bundle() {
val paddr = Input(UInt(addrWidth.W))
val psel = Input(Bool())
val penable = Input(Bool())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
package io

import chisel3._
import chisel3.util._

Expand All @@ -9,7 +11,7 @@ class ApbRegTarget(addrWidth:Int = 32, dataWidth:Int = 32, baseAddr:Int = 0x0105
val idle :: write :: read :: Nil = Enum(3)
val state = RegInit(idle)

val registerMap = RegInit(VecInit(Seq.fill(5)(0.U(dataWidth.W))))
val registerMap = RegInit(VecInit(Seq.fill(registerCount)(0.U(dataWidth.W))))
val registerIndex = WireDefault(0.U(log2Ceil(registerCount).W))
registerIndex := (io.apb.paddr - baseAddr.U) >> 2

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
package io

import chisel3._
import chisel3.util._

Expand Down
Loading

0 comments on commit 72f9d8a

Please sign in to comment.