Skip to content

Commit

Permalink
fpga: implement fetcher
Browse files Browse the repository at this point in the history
  • Loading branch information
zephray committed Jul 29, 2021
1 parent 1e16138 commit 089fb40
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 20 deletions.
6 changes: 3 additions & 3 deletions fpga/Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
syn:
sbt "runMain caster.TopLevel"

sim:
sbt "testOnly caster.TestBench"

syn:
sbt "runMain caster.MyTopLevelVerilog"

clean:
rm *.v
58 changes: 47 additions & 11 deletions fpga/src/main/scala/caster/Caster.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ object CasterConfig{
val config = CasterConfig(
axiFrequency = 200 MHz,
axiDataWidth = 256,
sdramLayout = MT41K128M16JT.layout
burstBytes = 32 // Must be larger or equal to axi data width
// TODO: does it work if it is actually larger than axi data width
)
Expand All @@ -44,9 +43,10 @@ object Caster {
useId = true,
useLock = false,
useRegion = false,
useCache = false,
useProt = false,
useQos = false
useBurst = false,
useLock = false,
useQos = false,
useResp = false
)

def getApbConfig(config: CasterConfig) = Apb3Config(
Expand All @@ -55,37 +55,73 @@ object Caster {
selWidth = 1,
useSlaveError = true
)

def getFetcherConfig(config: CasterConfig, clock: ClockDomain) = FetcherGenerics(
axiAddressWidth = 32,
axiDataWidth = config.axiDataWidth,
burstLength = config.burstBytes,
frameSizeMax = 2560 * 2560,
fifoSize = config.burstBytes * 4,
pixelWidth = 8,
pixelClock = clock
)
}

class Caster(config: CasterConfig) extends Component {
val io = new Bundle{
// Use parent clock domain, no explict clk/rst here

// AXI
val axi = master(Axi4(Caster.getAxiConfig(config)))
val axi = master(Axi4ReadOnly(Caster.getAxiConfig(config)))
// APB
val apb = slave(Apb3(Caster.getApbConfig(config)))
// GPIO for LED/KEY/LCD/IIC etc.
val gpio = master(TriStateArray(32 bits))

val start = in(Bool)
val busy = out(Bool)
}

noIoPrefix()

val pixelClockDomain = ClockDomain.external("pixel")

val gpioCtrl = Apb3Gpio(
gpioWidth = 32,
withReadSync = true
)


val miscRegApb = slave(Apb3(Caster.getApbConfig(config)))
val miscRegApbCtrl = Apb3SlaveFactory(miscRegApb)

val apbDecoder = Apb3Decoder(
master = io.apb,
slaves = List(
gpioCtrl.io.apb -> (0xff000000L, 4 KiB)
miscRegApb.io.apb -> (0xff000000L, 4 KiB)
gpioCtrl.io.apb -> (0xff001000L, 4 KiB)
)
)

val fetcherA = Fetcher(Caster.getFetcherConfig(config, pixelClockDomain))
val fetcherB = Fetcher(Caster.getFetcherConfig(config, pixelClockDomain))

miscRegApbCtrl.drive(fetcherA.io.base, 0x00, 32)
miscRegApbCtrl.drive(fetcherA.io.size, 0x04, 32)
miscRegApbCtrl.drive(fetcherB.io.base, 0x08, 32)
miscRegApbCtrl.drive(fetcherB.io.size, 0x0c, 32)

val pixelClockArea = new ClockingArea(pixelClockDomain) {
fetcherA.io.trigger := False
fetcherA.io.pixel.ready := True
fetcherB.io.trigger := False
fetcherB.io.pixel.ready := True
}

val axiArbiter = Axi4ReadOnlyArbiter(
outputConfig = Caster.getAxiConfig(config),
inputsCount = 2
)

io.axi <> axiArbiter.io.output
axiArbiter.io.inputs(0) << fetcherA.io.axi
axiArbiter.io.inputs(1) << fetcherB.io.axi

io.gpio <> gpioCtrl.io.gpio
io.axi <> busArbiter.io.axi
}
51 changes: 45 additions & 6 deletions fpga/src/main/scala/caster/Fetcher.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,59 @@ package caster
import spinal.core._
import math._
import spinal.lib._
import spinal.lib.bus.amba3.apb._
import spinal.lib.bus.amba4.axi._
import spinal.lib.io._
import spinal.lib.graphic.{VideoDmaGeneric, VideoDma}

// The fetcher fetch pixel from memory and feed into the fifo

class Fetcher(burstBytes: Int,
axi4Config: Axi4Config,
apb3Config: Apb3Config) extends Component {
case class FetcherGenerics(
axiAddressWidth: Int,
axiDataWidth: Int,
burstLength: Int,
frameSizeMax: Int,
fifoSize: Int,
pixelWidth: Int,
pendingRequestMax: Int = 7, // Should be power of two minus one
pixelClock: ClockDomain = ClockDomain.current) {

require(isPow2(burstBytes))
def axi4Config = dmaGenerics.getAxi4ReadOnlyConfig

def dmaGenerics = VideoDmaGeneric(
addressWidth = axiAddressWidth - log2Up(bytePerAddress),
dataWidth = axiDataWidth,
beatPerAccess = burstLength,
sizeWidth = log2Up(frameSizeMax) - log2Up(bytePerAddress),
frameFragmentType = UInt(pixelWidth bits),
pendingRequetMax = pendingRequestMax,
fifoSize = fifoSize,
frameClock = pixelClock
)

def bytePerAddress = axiDataWidth/8 * burstLength
}

case class Fetcher(g: FetcherGenerics) extends Component{
import g._

val io = new Bundle{

val axi = master(Axi4ReadOnly(axi4Config))
val base = in UInt(dmaGenerics.addressWidth bits)
val size = in UInt(dmaGenerics.sizeWidth bits)
val pixel = master(Stream(Fragment(UInt(pixelWidth bits))))
val trigger = in Bool()
val busy = out Bool()
}

val dma = VideoDma(dmaGenerics)
dma.io.mem.toAxi4ReadOnly <> io.axi
dma.io.size <> io.size
dma.io.base <> io.base
io.busy := BufferCC(dma.io.busy)
dma.io.start := PulseCCByToggle(io.trigger, clockIn = pixelClock,
clockOut = ClockDomain.current)

val pixel = new ClockingArea(pixelClock) {
dma.io.frame <> io.pixel
}
}

0 comments on commit 089fb40

Please sign in to comment.