-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
283 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
// | ||
// Project Caster | ||
// Copyright 2021 EI-2030 | ||
// | ||
// This project is licensed with the CERN Open Hardware Licence version 2. You | ||
// may redistribute and modify this project under the terms of the CERN-OHL-S v2 | ||
// (http://ohwr.org/cernohl). | ||
// This project is distributed WITHOUT ANY EXPRESS OR IMPLIED WARRANTY, | ||
// INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY AND FITNESS FOR A | ||
// PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 for applicable Conditions. | ||
// | ||
package caster | ||
|
||
import spinal.core._ | ||
|
||
case class Bram32K() extends BlackBox{ | ||
val addra = in UInt(14 bits) | ||
val dina = in UInt(32 bits) | ||
val douta = out UInt(2 bits) | ||
val wea = in Bool() | ||
val addrb = in UInt(14 bits) | ||
val dinb = in UInt(32 bits) | ||
val doutb = out UInt(2 bits) | ||
val web = in Bool() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
// | ||
// Project Caster | ||
// Copyright 2021 EI-2030 | ||
// | ||
// This project is licensed with the CERN Open Hardware Licence version 2. You | ||
// may redistribute and modify this project under the terms of the CERN-OHL-S v2 | ||
// (http://ohwr.org/cernohl). | ||
// This project is distributed WITHOUT ANY EXPRESS OR IMPLIED WARRANTY, | ||
// INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY AND FITNESS FOR A | ||
// PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 for applicable Conditions. | ||
// | ||
package caster | ||
|
||
import spinal.core._ | ||
import spinal.lib._ | ||
import spinal.lib.bus.amba3.apb._ | ||
import spinal.lib.bus.amba4.axi._ | ||
|
||
case class FetcherControl( | ||
fg: FetcherGenerics, wg: WaveformGenerics, | ||
axiConfig: Axi4Config, apbConfig: Apb3Config) | ||
extends Component { | ||
|
||
val io = new Bundle { | ||
val axi = master(Axi4ReadOnly(axiConfig)) | ||
val apb = slave(Apb3(apbConfig)) | ||
val sourceLevel = master(Stream(Vec(UInt(wg.pixelWidth bits), wg.lookupWidth))) | ||
val targetLevel = master(Stream(Vec(UInt(wg.pixelWidth bits), wg.lookupWidth))) | ||
val trigger = in Bool() | ||
val busy = out Bool() | ||
} | ||
|
||
assert(wg.pixelWidth * wg.lookupWidth == fg.pixelWidth, | ||
"Fetcher width should match LUT unit width") | ||
|
||
val regApbCtrl = Apb3SlaveFactory(io.apb) | ||
|
||
val fetcherA = Fetcher(fg) | ||
val fetcherB = Fetcher(fg) | ||
|
||
val axiArbiter = Axi4ReadOnlyArbiter( | ||
outputConfig = axiConfig, | ||
inputsCount = 2 | ||
) | ||
|
||
regApbCtrl.drive(fetcherA.io.base, 0x00, 0) | ||
regApbCtrl.drive(fetcherA.io.size, 0x04, 0) | ||
regApbCtrl.drive(fetcherB.io.base, 0x08, 0) | ||
regApbCtrl.drive(fetcherB.io.size, 0x0c, 0) | ||
|
||
io.axi <> axiArbiter.io.output | ||
axiArbiter.io.inputs(0) << fetcherA.io.axi | ||
axiArbiter.io.inputs(1) << fetcherB.io.axi | ||
|
||
val pixelClockArea = new ClockingArea(fg.pixelClock) { | ||
fetcherA.io.trigger := io.trigger | ||
fetcherB.io.trigger := io.trigger | ||
io.busy := fetcherA.io.busy || fetcherB.io.busy | ||
|
||
io.sourceLevel.valid := fetcherA.io.pixel.valid | ||
fetcherA.io.pixel.ready := io.sourceLevel.ready | ||
|
||
io.targetLevel.valid := fetcherB.io.pixel.valid | ||
fetcherB.io.pixel.ready := io.targetLevel.ready | ||
|
||
val pixelA = UInt(fg.pixelWidth bits) | ||
val pixelB = UInt(fg.pixelWidth bits) | ||
pixelA := fetcherA.io.pixel.payload.fragment | ||
pixelB := fetcherB.io.pixel.payload.fragment | ||
|
||
for (i <- 0 until wg.lookupWidth) { | ||
io.sourceLevel.payload(i) := | ||
pixelA((i + 1) * wg.pixelWidth - 1 downto i * wg.pixelWidth) | ||
io.targetLevel.payload(i) := | ||
pixelB((i + 1) * wg.pixelWidth - 1 downto i * wg.pixelWidth) | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// | ||
// Project Caster | ||
// Copyright 2021 EI-2030 | ||
// | ||
// This project is licensed with the CERN Open Hardware Licence version 2. You | ||
// may redistribute and modify this project under the terms of the CERN-OHL-S v2 | ||
// (http://ohwr.org/cernohl). | ||
// This project is distributed WITHOUT ANY EXPRESS OR IMPLIED WARRANTY, | ||
// INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY AND FITNESS FOR A | ||
// PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 for applicable Conditions. | ||
// | ||
package caster | ||
|
||
import spinal.core._ | ||
import math._ | ||
import spinal.lib._ | ||
import spinal.lib.bus.amba3.apb._ | ||
|
||
// Target is 16 bit wide / 8 pixel per clock | ||
// BRAM is capable of doing 2 read ports (2 pixel lookup per clock) | ||
// Use 4 4K BRAMs in parallel to provide 8 read ports | ||
case class TimingGenerics( | ||
outputWidth: Int = 16, // In bits | ||
apbConfig:Apb3Config) { | ||
|
||
} | ||
|
||
case class Timing(g: TimingGenerics) extends Component { | ||
import g._ | ||
|
||
val io = new Bundle { | ||
val apb = slave(Apb3(apbConfig)) | ||
val pixelDrive = slave(Stream(Vec(UInt(2 bits)))) | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
// | ||
// Project Caster | ||
// Copyright 2021 EI-2030 | ||
// | ||
// This project is licensed with the CERN Open Hardware Licence version 2. You | ||
// may redistribute and modify this project under the terms of the CERN-OHL-S v2 | ||
// (http://ohwr.org/cernohl). | ||
// This project is distributed WITHOUT ANY EXPRESS OR IMPLIED WARRANTY, | ||
// INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY AND FITNESS FOR A | ||
// PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 for applicable Conditions. | ||
// | ||
package caster | ||
|
||
import spinal.core._ | ||
import math._ | ||
import spinal.lib._ | ||
import spinal.lib.bus.amba3.apb._ | ||
|
||
import scala.collection.mutable | ||
import scala.collection.mutable.ArrayBuffer | ||
|
||
/* Waveform Format: | ||
* 4 KB waveform RAM, holds 16K values, 14 bit address | ||
* | ||
* 13 12 11 10 09 08 07 06 05 04 03 02 01 00 | ||
* [ Frame Count ] [ Src Lvl ] [ Tgt Lvl ] -> 2 bit driving value | ||
*/ | ||
|
||
case class WaveformGenerics( | ||
waveformSize: Int = 4096, // In bytes | ||
lookupWidth: Int = 8, // In pixels | ||
pixelWidth: Int = 4, // In bits | ||
maxFrame: Int = 63, // In frames | ||
bramLatency: Int = 2, // In cycles | ||
apbConfig: Apb3Config) { | ||
|
||
def addressWidth = log2Up(waveformSize) | ||
def frameWidth = log2Up(maxFrame) | ||
def BRAMCount = lookupWidth / 2 | ||
|
||
// Make sure the waveform RAM is big enough to hold the table | ||
require((log2Up(waveformSize) + 2) >= log2Up(maxFrame) + pixelWidth * 2) | ||
} | ||
|
||
case class Waveform(g: WaveformGenerics) extends Component { | ||
import g._ | ||
|
||
val io = new Bundle { | ||
val apb = slave(Apb3(apbConfig)) | ||
val sourceLevel = slave(Stream(Vec(UInt(pixelWidth bits), lookupWidth))) | ||
val targetLevel = slave(Stream(Vec(UInt(pixelWidth bits), lookupWidth))) | ||
val frameSeq = in UInt(frameWidth bits) | ||
val pixelDrive = master(Stream(Vec(UInt(2 bits), lookupWidth))) | ||
} | ||
|
||
val waveRAMs = ArrayBuffer[Bram32K]() | ||
|
||
// Synchornize 2 inputs, take ready signal | ||
val inputValid = io.sourceLevel.valid && io.targetLevel.valid | ||
val pixelValid = inputValid && io.pixelDrive.ready | ||
io.sourceLevel.ready := io.pixelDrive.ready | ||
io.targetLevel.ready := io.pixelDrive.ready | ||
|
||
val outputValid = Delay(pixelValid, bramLatency) | ||
|
||
// APB slave to BRAM write master | ||
// READ is not supported | ||
io.apb.PREADY := True | ||
io.apb.PRDATA := B(0) | ||
val bramWriteEnable = io.apb.PSEL(0) && io.apb.PENABLE && io.apb.PWRITE | ||
val bramWriteData = io.apb.PWDATA | ||
val bramWriteAddr = io.apb.PADDR(addressWidth - 1 downto 0) | ||
|
||
for (i <- 0 until g.BRAMCount) { | ||
// Each BRAM provides 2 read ports and 1 write port | ||
val blockRAM = new Bram32K() | ||
waveRAMs += blockRAM | ||
|
||
// Lookup Input | ||
val bramReadAddrA = U(Cat( | ||
io.frameSeq, | ||
io.sourceLevel.payload(i * 2), | ||
io.targetLevel.payload(i * 2))) | ||
val bramReadAddrB = U(Cat( | ||
io.frameSeq, | ||
io.sourceLevel.payload(i * 2 + 1), | ||
io.targetLevel.payload(i * 2 + 1))) | ||
|
||
// Connect Port A input to APB slave | ||
blockRAM.addra := bramWriteEnable ? bramWriteAddr | bramReadAddrA | ||
blockRAM.dina := U(bramWriteData) | ||
blockRAM.wea := bramWriteEnable | ||
|
||
// Connect Port B input | ||
blockRAM.addrb := bramReadAddrB | ||
blockRAM.dinb := U(0) | ||
blockRAM.web := False | ||
|
||
io.pixelDrive.payload(i * 2) := blockRAM.douta | ||
io.pixelDrive.payload(i * 2 + 1) := blockRAM.doutb | ||
} | ||
|
||
io.pixelDrive.valid := outputValid | ||
} |