Skip to content

Commit

Permalink
Convert Memory Mapped UART Example
Browse files Browse the repository at this point in the history
  • Loading branch information
andreasWallner committed Nov 17, 2022
1 parent 74dda33 commit f410407
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 44 deletions.
43 changes: 43 additions & 0 deletions examples/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
*.class
*.log
*.bak

# sbt specific
.cache/
.history/
.lib/
dist/*
target
lib_managed/
src_managed/
project/boot/
project/plugins/project/

# Scala-IDE specific
.scala_dependencies
.worksheet

.idea
out

# Metals
.metals

# Eclipse
bin/
.classpath
.project
.settings
.cache-main

#User
/*.vhd
/*.v
*.cf
*.json
*.vcd
!tester/src/test/resources/*.vhd


simWorkspace/
tmp/
16 changes: 16 additions & 0 deletions examples/build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
ThisBuild / version := "1.0"
ThisBuild / scalaVersion := "2.12.16"
ThisBuild / organization := "org.example"

val spinalVersion = "1.7.3"
val spinalCore = "com.github.spinalhdl" %% "spinalhdl-core" % spinalVersion
val spinalLib = "com.github.spinalhdl" %% "spinalhdl-lib" % spinalVersion
val spinalIdslPlugin = compilerPlugin("com.github.spinalhdl" %% "spinalhdl-idsl-plugin" % spinalVersion)

lazy val mylib = (project in file("."))
.settings(
name := "SpinalDoc-RTD-examples",
libraryDependencies ++= Seq(spinalCore, spinalLib, spinalIdslPlugin)
)

fork := true
1 change: 1 addition & 0 deletions examples/project/build.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sbt.version=1.6.0
Empty file added examples/project/plugins.sbt
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package spinaldoc.examples.advanced

import spinal.core._
import spinal.lib._
import spinal.lib.bus.amba3.apb.{Apb3, Apb3Config, Apb3SlaveFactory}
import spinal.lib.com.uart.{Uart, UartCtrl, UartCtrlGenerics}

object Apb3UartCtrl {
def getApb3Config = Apb3Config(
addressWidth = 4,
dataWidth = 32
)
}
// end object Apb3UartCtrl

<<<<<<< HEAD
// start impl
class Apb3UartCtrl(uartCtrlConfig : UartCtrlGenerics, rxFifoDepth : Int) extends Component{
val io = new Bundle {
=======
case class Apb3UartCtrl(uartCtrlConfig: UartCtrlGenerics, rxFifoDepth: Int) extends Component {
val io = new Bundle{
>>>>>>> 4dcc6d368 (fixup! Convert Memory Mapped UART Example)
val bus = slave(Apb3(Apb3UartCtrl.getApb3Config))
val uart = master(Uart())
}

// Instanciate an simple uart controller
val uartCtrl = new UartCtrl(uartCtrlConfig)
io.uart <> uartCtrl.io.uart

// Create an instance of the Apb3SlaveFactory that will then be used as a slave factory drived by io.bus
val busCtrl = Apb3SlaveFactory(io.bus)

// Ask the busCtrl to create a readable/writable register at the address 0
// and drive uartCtrl.io.config.clockDivider with this register
busCtrl.driveAndRead(uartCtrl.io.config.clockDivider,address = 0)

// Do the same thing than above but for uartCtrl.io.config.frame at the address 4
busCtrl.driveAndRead(uartCtrl.io.config.frame,address = 4)

// Ask the busCtrl to create a writable Flow[Bits] (valid/payload) at the address 8.
// Then convert it into a stream and connect it to the uartCtrl.io.write by using an register stage (>->)
busCtrl.createAndDriveFlow(Bits(uartCtrlConfig.dataWidthMax bits),address = 8).toStream >-> uartCtrl.io.write

// To avoid losing writes commands between the Flow to Stream transformation just above,
// make the occupancy of the uartCtrl.io.write readable at address 8
busCtrl.read(uartCtrl.io.write.valid,address = 8)

// Take uartCtrl.io.read, convert it into a Stream, then connect it to the input of a FIFO of 64 elements
// Then make the output of the FIFO readable at the address 12 by using a non blocking protocol
// (Bit 7 downto 0 => read data <br> Bit 31 => read data valid )
busCtrl.readStreamNonBlocking(uartCtrl.io.read.queue(rxFifoDepth),
address = 12, validBitOffset = 31, payloadBitOffset = 0)
}
// end case class Apb3UartCtrl

object MemoryMappedUart extends App {
SpinalVerilog(Apb3UartCtrl(UartCtrlGenerics(), rxFifoDepth=16))
}
52 changes: 8 additions & 44 deletions source/SpinalHDL/Examples/Advanced ones/memory_mapped_uart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -62,56 +62,20 @@ For this implementation, the Apb3SlaveFactory tool will be used. It allows you t

First, we just need to define the ``Apb3Config`` that will be used for the controller. It is defined in a Scala object as a function to be able to get it from everywhere.

.. code-block:: scala
object Apb3UartCtrl{
def getApb3Config = Apb3Config(
addressWidth = 4,
dataWidth = 32
)
}
.. literalinclude:: /../examples/src/main/scala/spinaldoc/examples/advanced/MemoryMappedUart.scala
:language: scala
:start-at: object Apb3UartCtrl
:end-before: // end object Apb3UartCtrl

Then we can define a ``Apb3UartCtrl`` component which instantiates a ``UartCtrl`` and creates the memory mapping logic between it and the APB3 bus:

.. image:: /asset/picture/memory_mapped_uart.svg
:align: center

.. code-block:: scala
class Apb3UartCtrl(uartCtrlConfig : UartCtrlGenerics, rxFifoDepth : Int) extends Component{
val io = new Bundle{
val bus = slave(Apb3(Apb3UartCtrl.getApb3Config))
val uart = master(Uart())
}
// Instanciate an simple uart controller
val uartCtrl = new UartCtrl(uartCtrlConfig)
io.uart <> uartCtrl.io.uart
// Create an instance of the Apb3SlaveFactory that will then be used as a slave factory drived by io.bus
val busCtrl = Apb3SlaveFactory(io.bus)
// Ask the busCtrl to create a readable/writable register at the address 0
// and drive uartCtrl.io.config.clockDivider with this register
busCtrl.driveAndRead(uartCtrl.io.config.clockDivider,address = 0)
// Do the same thing than above but for uartCtrl.io.config.frame at the address 4
busCtrl.driveAndRead(uartCtrl.io.config.frame,address = 4)
// Ask the busCtrl to create a writable Flow[Bits] (valid/payload) at the address 8.
// Then convert it into a stream and connect it to the uartCtrl.io.write by using an register stage (>->)
busCtrl.createAndDriveFlow(Bits(uartCtrlConfig.dataWidthMax bits),address = 8).toStream >-> uartCtrl.io.write
// To avoid losing writes commands between the Flow to Stream transformation just above,
// make the occupancy of the uartCtrl.io.write readable at address 8
busCtrl.read(uartCtrl.io.write.valid,address = 8)
// Take uartCtrl.io.read, convert it into a Stream, then connect it to the input of a FIFO of 64 elements
// Then make the output of the FIFO readable at the address 12 by using a non blocking protocol
// (Bit 7 downto 0 => read data <br> Bit 31 => read data valid )
busCtrl.readStreamNonBlocking(uartCtrl.io.read.toStream.queue(rxFifoDepth),
address = 12, validBitOffset = 31, payloadBitOffset = 0)
}
.. literalinclude:: /../examples/src/main/scala/spinaldoc/examples/advanced/MemoryMappedUart.scala
:language: scala
:start-at: case class Apb3UartCtrl(
:end-before: // end case class Apb3UartCtrl

.. important::
| Yes, that's all it takes. It's also synthesizable.
Expand Down

0 comments on commit f410407

Please sign in to comment.