From 089fb401c55331b21a5248457d90a7d5a4e65739 Mon Sep 17 00:00:00 2001 From: Wenting Zhang Date: Wed, 28 Jul 2021 22:32:07 -0400 Subject: [PATCH] fpga: implement fetcher --- fpga/Makefile | 6 +-- fpga/src/main/scala/caster/Caster.scala | 58 +++++++++++++++++++----- fpga/src/main/scala/caster/Fetcher.scala | 51 ++++++++++++++++++--- 3 files changed, 95 insertions(+), 20 deletions(-) diff --git a/fpga/Makefile b/fpga/Makefile index 06ab51e..d8deb5d 100644 --- a/fpga/Makefile +++ b/fpga/Makefile @@ -1,8 +1,8 @@ +syn: + sbt "runMain caster.TopLevel" + sim: sbt "testOnly caster.TestBench" -syn: - sbt "runMain caster.MyTopLevelVerilog" - clean: rm *.v \ No newline at end of file diff --git a/fpga/src/main/scala/caster/Caster.scala b/fpga/src/main/scala/caster/Caster.scala index 16414c7..dcc6eab 100644 --- a/fpga/src/main/scala/caster/Caster.scala +++ b/fpga/src/main/scala/caster/Caster.scala @@ -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 ) @@ -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( @@ -55,6 +55,16 @@ 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 { @@ -62,30 +72,56 @@ class Caster(config: CasterConfig) extends Component { // 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 } \ No newline at end of file diff --git a/fpga/src/main/scala/caster/Fetcher.scala b/fpga/src/main/scala/caster/Fetcher.scala index df78916..6e05a72 100644 --- a/fpga/src/main/scala/caster/Fetcher.scala +++ b/fpga/src/main/scala/caster/Fetcher.scala @@ -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 + } } \ No newline at end of file