Files
gps/hardware/soc/scala/fpga/VCU118Peripherals.scala
T
onur 30179df8e6 feat(gps): add timer-based assisted reacquisition
Remove the fixed pseudorange start offset, preserve per-PRN navigation assistance across RF loss and power cycles, and seed coherent navigation measurement phase on reacquisition. Add full LNAV almanac visibility scheduling, power-safe QSPI journaling, RF-domain reset isolation, and PPS Gray CDC hardening with Linux, sanitizer, RTL, and end-to-end coverage.
2026-08-04 02:58:11 +03:00

768 lines
29 KiB
Scala

// See LICENSE.SiFive for license details.
package freechips.rocketchip.fpga
import chisel3._
import chisel3.experimental.IntParam
import chisel3.util._
import org.chipsalliance.cde.config._
import org.chipsalliance.diplomacy.bundlebridge._
import org.chipsalliance.diplomacy.lazymodule._
import freechips.rocketchip.diplomacy.AddressSet
import freechips.rocketchip.amba.apb.{APBSlaveNode, APBSlaveParameters, APBSlavePortParameters}
import freechips.rocketchip.devices.tilelink.{BootROM, BootROMLocated, BootROMParams, MaskROM, MaskROMLocated}
import freechips.rocketchip.interrupts.{IntRange, IntSourceNode, IntSourceParameters, IntSourcePortParameters}
import freechips.rocketchip.regmapper.{RegField, RegFieldDesc, RegReadFn, RegWriteFn}
import freechips.rocketchip.resources.SimpleDevice
import freechips.rocketchip.rocket.{RocketTileConfig, WithNSmallCores}
import freechips.rocketchip.subsystem._
import freechips.rocketchip.system.BaseConfig
import freechips.rocketchip.tile.FPUParams
import freechips.rocketchip.tilelink.{TLFragmenter, TLRAM, TLRegisterNode, TLToAPB, TLWidthWidget}
import freechips.rocketchip.prci.SynchronousCrossing
import freechips.rocketchip.util.{ResetCatchAndSync, SystemFileName}
object FpgaBuildSettings {
// Makefiles pass user-facing DTIM_BASE/DTIM_SIZE as ROCKET_DTIM_* Java properties.
// Reading env too keeps direct generator invocations scriptable without a new Config class.
private def parseBigInt(text: String): BigInt = {
val clean = text.trim.replace("_", "")
if (clean.startsWith("0x") || clean.startsWith("0X")) {
BigInt(clean.drop(2), 16)
} else {
BigInt(clean, 10)
}
}
def bigInt(name: String, default: BigInt): BigInt =
scala.sys.props.get(name).orElse(scala.sys.env.get(name)).map(parseBigInt).getOrElse(default)
}
case class FpgaUARTParams(
address: BigInt = 0x10013000L,
clockFrequency: Int = 100000000,
baudRate: Int = 115200)
class FpgaUARTPort extends Bundle {
val rxd = Input(Bool())
val txd = Output(Bool())
}
class TLFpgaUART(params: FpgaUARTParams, beatBytes: Int)(implicit p: Parameters) extends LazyModule {
val device = new SimpleDevice("uart", Seq("rocket,fpga-uart"))
val rxdNode = BundleBridgeSink[Bool]()
val txdNode = BundleBridgeSource(() => Bool())
val node = TLRegisterNode(
address = Seq(AddressSet(params.address, 0xfff)),
device = device,
beatBytes = beatBytes,
concurrency = 1)
lazy val module = new Impl
class Impl extends LazyModuleImp(this) {
val rxd = rxdNode.bundle
val txd = Wire(Bool())
txdNode.bundle := txd
private val resetDivisor = (params.clockFrequency + params.baudRate / 2) / params.baudRate
val clockFrequency = RegInit(params.clockFrequency.U(32.W))
val baudRate = RegInit(params.baudRate.U(32.W))
val divisor = RegInit((resetDivisor max 2).U(32.W))
val divisorMinusOne = Mux(divisor <= 1.U, 1.U, divisor - 1.U)
val rxStartDelay = Mux(divisor <= 1.U, 1.U, divisor + (divisor >> 1) - 1.U)
val txReg = RegInit(1.U(10.W))
val txBusy = RegInit(false.B)
val txDiv = RegInit(0.U(32.W))
val txCount = RegInit(0.U(4.W))
txd := txReg(0)
when(txBusy) {
when(txDiv === divisorMinusOne) {
txDiv := 0.U
txReg := Cat(1.U(1.W), txReg(9, 1))
when(txCount === 0.U) {
txBusy := false.B
txReg := Fill(10, 1.U(1.W))
}.otherwise {
txCount := txCount - 1.U
}
}.otherwise {
txDiv := txDiv + 1.U
}
}
val rxSync = RegNext(RegNext(rxd, true.B), true.B)
val rxPrev = RegNext(rxSync, true.B)
val rxBusy = RegInit(false.B)
val rxDiv = RegInit(0.U(32.W))
val rxCount = RegInit(0.U(4.W))
val rxShift = RegInit(0.U(8.W))
val rxData = RegInit(0.U(8.W))
val rxValid = RegInit(false.B)
when(!rxBusy && rxPrev && !rxSync) {
rxBusy := true.B
rxDiv := rxStartDelay
rxCount := 0.U
}.elsewhen(rxBusy) {
when(rxDiv === 0.U) {
rxDiv := divisorMinusOne
when(rxCount < 8.U) {
rxShift := Cat(rxSync, rxShift(7, 1))
rxCount := rxCount + 1.U
}.otherwise {
rxBusy := false.B
when(rxSync) {
rxData := rxShift
rxValid := true.B
}
}
}.otherwise {
rxDiv := rxDiv - 1.U
}
}
val txWrite = RegWriteFn { (valid, data) =>
when(valid && !txBusy) {
txReg := Cat(1.U(1.W), data(7, 0), 0.U(1.W))
txBusy := true.B
txDiv := 0.U
txCount := 9.U
}
true.B
}
val rxRead = RegReadFn { ready =>
when(ready) { rxValid := false.B }
(true.B, rxData)
}
node.regmap(
0x00 -> Seq(RegField.w(8, txWrite, RegFieldDesc("txdata", "write byte to transmit"))),
0x04 -> Seq(RegField.r(8, rxRead, RegFieldDesc("rxdata", "read received byte"))),
0x08 -> Seq(RegField.r(1, !txBusy, RegFieldDesc("txready", "transmitter can accept a byte"))),
0x0c -> Seq(RegField.r(1, rxValid, RegFieldDesc("rxvalid", "receiver has a byte"))),
0x10 -> Seq(RegField(32, clockFrequency, RegFieldDesc("clockfreq", "UART input clock frequency"))),
0x14 -> Seq(RegField(32, baudRate, RegFieldDesc("baudrate", "UART baud rate"))),
0x18 -> Seq(RegField(32, divisor, RegFieldDesc("divisor", "UART baud clock divisor")))
)
}
}
case class FpgaSPIParams(
address: BigInt = 0x10014000L,
clockDiv: Int = 8,
deviceName: String = "spi",
compatibility: Seq[String] = Seq("rocket,fpga-spi"))
class FpgaSPIPort extends Bundle {
val sck = Output(Bool())
val csn = Output(Bool())
val dq_i = Input(UInt(4.W))
val dq_o = Output(UInt(4.W))
val dq_oe = Output(UInt(4.W))
}
class TLFpgaSPI(params: FpgaSPIParams, beatBytes: Int)(implicit p: Parameters) extends LazyModule {
val device = new SimpleDevice(params.deviceName, params.compatibility)
val sckNode = BundleBridgeSource(() => Bool())
val csnNode = BundleBridgeSource(() => Bool())
val dqInNode = BundleBridgeSink[UInt]()
val dqOutNode = BundleBridgeSource(() => UInt(4.W))
val dqOeNode = BundleBridgeSource(() => UInt(4.W))
val node = TLRegisterNode(
address = Seq(AddressSet(params.address, 0xfff)),
device = device,
beatBytes = beatBytes,
concurrency = 1)
lazy val module = new Impl
class Impl extends LazyModuleImp(this) {
val dq_i = dqInNode.bundle
val sck_o = Wire(Bool())
val csn_o = Wire(Bool())
val dq_o = Wire(UInt(4.W))
val dq_oe = Wire(UInt(4.W))
sckNode.bundle := sck_o
csnNode.bundle := csn_o
dqOutNode.bundle := dq_o
dqOeNode.bundle := dq_oe
val csn = RegInit(true.B)
val sck = RegInit(false.B)
val quad = RegInit(false.B)
val cpol = RegInit(false.B)
val cpha = RegInit(false.B)
val txShift = RegInit(0.U(32.W))
val rxShift = RegInit(0.U(32.W))
val bitsLeft = RegInit(0.U(6.W))
val busy = RegInit(false.B)
val div = RegInit(0.U(log2Ceil(params.clockDiv max 2).W))
csn_o := csn
sck_o := sck
dq_o := Mux(quad, txShift(31, 28), Cat(0.U(3.W), txShift(31)))
dq_oe := Mux(csn || !busy, 0.U, Mux(quad, "b1111".U, "b0001".U))
when(busy) {
when(div === (params.clockDiv - 1).U) {
div := 0.U
sck := ~sck
val step = Mux(quad, 4.U, 1.U)
/*
* `sck` is the pre-toggle level. The edge away from CPOL is the
* leading edge; CPHA selects leading (0) or trailing (1) sampling.
* TX advances only after the active sample edge, so all four standard
* SPI modes retain a full half-cycle of setup time.
*/
val leadingEdge = sck === cpol
val sampleEdge = Mux(cpha, !leadingEdge, leadingEdge)
when(sampleEdge) {
rxShift := Mux(quad, Cat(rxShift(27, 0), dq_i), Cat(rxShift(30, 0), dq_i(1)))
}
/*
* CPHA=0 completes on the trailing edge after the final leading-edge
* sample. CPHA=1 samples and completes on the final trailing edge.
*/
val advanceEdge = Mux(cpha, sampleEdge, !sampleEdge)
when(advanceEdge) {
txShift := Mux(quad, Cat(txShift(27, 0), 0.U(4.W)), Cat(txShift(30, 0), 0.U(1.W)))
when(bitsLeft <= step) {
busy := false.B
sck := cpol
bitsLeft := 0.U
}.otherwise {
bitsLeft := bitsLeft - step
}
}
}.otherwise {
div := div + 1.U
}
}
val ctrl = Wire(UInt(32.W))
ctrl := Cat(0.U(27.W), cpha, cpol, quad, csn, busy)
val ctrlWrite = RegWriteFn { (valid, data) =>
when(valid && !busy) {
csn := data(1)
quad := data(2)
cpol := data(3)
cpha := data(4)
sck := data(3)
}
true.B
}
val txWrite = RegWriteFn { (valid, data) =>
when(valid && !busy) {
txShift := data
rxShift := 0.U
bitsLeft := Mux(quad, 32.U, 8.U)
busy := true.B
div := 0.U
sck := cpol
}
true.B
}
node.regmap(
0x00 -> Seq(RegField(32, ctrl, ctrlWrite, RegFieldDesc("ctrl", "bit0 busy RO, bit1 csn RW, bit2 quad RW, bit3 CPOL RW, bit4 CPHA RW"))),
0x04 -> Seq(RegField.w(32, txWrite, RegFieldDesc("tx", "start transfer"))),
0x08 -> Seq(RegField.r(32, rxShift, RegFieldDesc("rx", "last received shift register"))),
0x0c -> Seq(RegField.r(1, !busy, RegFieldDesc("ready", "controller ready")))
)
}
}
class WithVCU118UART(address: BigInt = 0x10013000L, clockFrequency: Int = 100000000, baudRate: Int = 115200)
extends Config((site, here, up) => {
case VCU118UARTKey => Some(FpgaUARTParams(address, clockFrequency, baudRate))
})
class WithVCU118SPI(address: BigInt = 0x10014000L)
extends Config((site, here, up) => {
case VCU118SPIKey => Some(FpgaSPIParams(address))
})
class WithVCU118PmodSPI(address: BigInt = 0x10015000L)
extends Config((site, here, up) => {
case VCU118PmodSPIKey => Some(FpgaSPIParams(
address = address,
deviceName = "pmod-spi",
compatibility = Seq("rocket,fpga-pmod-spi", "rocket,fpga-spi")))
})
case class FpgaGPSParams(
baseAddress: BigInt = 0x10020000L,
trackingCount: Int = 9,
windowBytes: BigInt = 0x1000L) {
require(trackingCount > 0)
require(windowBytes == 0x1000L, "GPS APB wrappers use 12-bit APB register offsets")
def acqAddress: BigInt = baseAddress
def trackingAddress(index: Int): BigInt = baseAddress + ((index + 1) * windowBytes)
def ppsAddress: BigInt = baseAddress + ((trackingCount + 1) * windowBytes)
}
class GpsAcqBlackBox(localAddrBits: Int = 12) extends BlackBox(Map(
"HIGH_ADDRESS" -> IntParam((BigInt(1) << localAddrBits) - 1),
"LOW_ADDRESS" -> IntParam(0),
"DATA_BUS_WIDTH" -> IntParam(32),
"ADDR_BUS_WIDTH" -> IntParam(localAddrBits))) {
override def desiredName: String = "acq_wrapper"
val io = IO(new Bundle {
val PCLK = Input(Clock())
val PRESETN = Input(Bool())
val PADDR = Input(UInt(localAddrBits.W))
val PSEL = Input(Bool())
val PENABLE = Input(Bool())
val PWRITE = Input(Bool())
val PWDATA = Input(UInt(32.W))
val PREADY = Output(Bool())
val PRDATA = Output(UInt(32.W))
val PSLVERR = Output(Bool())
val afe_start = Output(Bool())
val sample_counter_clear = Output(Bool())
val clk_80mhz = Input(Clock())
val rf_clk = Input(Clock())
val acq_resetn = Input(Bool())
val afe_resetn = Input(Bool())
val rf_data = Input(UInt(4.W))
val sample_align = Input(Bool())
val start_acq_manuel = Input(Bool())
val acq_intr = Output(Bool())
})
}
class GpsTrackingBlackBox(localAddrBits: Int = 12) extends BlackBox(Map(
"HIGH_ADDRESS" -> IntParam((BigInt(1) << localAddrBits) - 1),
"LOW_ADDRESS" -> IntParam(0),
"DATA_BUS_WIDTH" -> IntParam(32),
"ADDR_BUS_WIDTH" -> IntParam(localAddrBits))) {
override def desiredName: String = "tracking_wrapper"
val io = IO(new Bundle {
val PCLK = Input(Clock())
val PRESETN = Input(Bool())
val PADDR = Input(UInt(localAddrBits.W))
val PSEL = Input(Bool())
val PENABLE = Input(Bool())
val PWRITE = Input(Bool())
val PWDATA = Input(UInt(32.W))
val PREADY = Output(Bool())
val PRDATA = Output(UInt(32.W))
val PSLVERR = Output(Bool())
val trck_resetn = Input(Bool())
val afe_resetn = Input(Bool())
val trck_clk = Input(Clock())
val afe_clk = Input(Clock())
val afe_data = Input(UInt(4.W))
val sample_align = Input(Bool())
val sample_counter = Input(UInt(32.W))
val gps_frame_ready = Output(Bool())
val pps_capture = Output(Bool())
})
}
class GpsPpsBlackBox(localAddrBits: Int = 12, captureChannels: Int = 9) extends BlackBox(Map(
"HIGH_ADDRESS" -> IntParam((BigInt(1) << localAddrBits) - 1),
"LOW_ADDRESS" -> IntParam(0),
"DATA_BUS_WIDTH" -> IntParam(32),
"ADDR_BUS_WIDTH" -> IntParam(localAddrBits),
"NUM_CAPTURE_CHANNELS" -> IntParam(captureChannels))) {
override def desiredName: String = "pps_wrapper"
val io = IO(new Bundle {
val PCLK = Input(Clock())
val PRESETN = Input(Bool())
val PADDR = Input(UInt(localAddrBits.W))
val PSEL = Input(Bool())
val PENABLE = Input(Bool())
val PWRITE = Input(Bool())
val PWDATA = Input(UInt(32.W))
val PREADY = Output(Bool())
val PRDATA = Output(UInt(32.W))
val PSLVERR = Output(Bool())
val pps_clk = Input(Clock())
val pps_resetn = Input(Bool())
val capture_i = Input(UInt(captureChannels.W))
val pps_out = Output(Bool())
val pps_valid = Output(Bool())
})
}
class GpsAfeTopBlackBox extends BlackBox {
override def desiredName: String = "afe_top"
val io = IO(new Bundle {
val afe_rstn = Input(Bool())
val trk_rstn = Input(Bool())
val afe_clk = Input(Clock())
val trk_clk = Input(Clock())
val start_i = Input(Bool())
val sample_counter_clear_i = Input(Bool())
val sample_align_o = Output(Bool())
val sample_counter_o = Output(UInt(32.W))
val rf_data_o = Output(UInt(4.W))
})
}
class APBGpsBlock(params: FpgaGPSParams, beatBytes: Int)(implicit p: Parameters) extends LazyModule {
private val localAddrBits = log2Ceil(params.windowBytes)
private val acqDevice = new SimpleDevice("gps-acq", Seq("anka,gps-acq-apb"))
private val trackingDevices =
Seq.tabulate(params.trackingCount)(i => new SimpleDevice(s"gps-tracking$i", Seq("anka,gps-tracking-apb")))
private val ppsDevice = new SimpleDevice("gps-pps", Seq("anka,gps-pps-apb"))
private val addresses = params.acqAddress +:
(Seq.tabulate(params.trackingCount)(params.trackingAddress) :+ params.ppsAddress)
private val devices = acqDevice +: (trackingDevices :+ ppsDevice)
private val interruptDevices = acqDevice +: trackingDevices
private val apertures = addresses.map(addr => AddressSet(addr, params.windowBytes - 1))
val intnode = IntSourceNode(Seq(IntSourcePortParameters(
interruptDevices.zipWithIndex.map { case (device, index) =>
IntSourceParameters(IntRange(index, index + 1), device.int)
})))
val node = APBSlaveNode(Seq(APBSlavePortParameters(
slaves = apertures.zip(devices).map { case (aperture, device) =>
APBSlaveParameters(
address = Seq(aperture),
resources = device.reg,
supportsRead = true,
supportsWrite = true,
device = Some(device))
},
beatBytes = beatBytes)))
val acqClockNode = BundleBridgeSink[Clock]()
val trackingClockNode = BundleBridgeSink[Clock]()
val afeClockNode = BundleBridgeSink[Clock]()
val rfDataNode = BundleBridgeSink[UInt]()
val clockReadyNode = BundleBridgeSink[Bool]()
val ppsOutNode = BundleBridgeSource(() => Bool())
lazy val module = new Impl
class Impl extends LazyModuleImp(this) {
val (apb, _) = node.in(0)
val acq = Module(new GpsAcqBlackBox(localAddrBits))
val afe = Module(new GpsAfeTopBlackBox)
val tracking = Seq.tabulate(params.trackingCount)(_ => Module(new GpsTrackingBlackBox(localAddrBits)))
val pps = Module(new GpsPpsBlackBox(localAddrBits, params.trackingCount))
val localAddr = apb.paddr(localAddrBits - 1, 0)
val sel = VecInit(apertures.map(_.contains(apb.paddr)))
val hit = sel.asUInt.orR
// GDB ndmreset reaches this reset. Deassert it inside each GPS clock domain so
// APB, acquisition, tracking, and RF/AFE logic do not leave reset on an async edge.
val gpsDomainReset = reset.asBool || !clockReadyNode.bundle
// Keep the APB/fabric side alive when the external RF clock disappears so
// the CPU and its RAM ephemeris cache survive. Only the clock-dependent GPS
// DSP domains receive the lock-qualified local reset.
val apbReset = ResetCatchAndSync(clock, reset.asBool, 3, "gps_apb_reset")
val acqReset = ResetCatchAndSync(acqClockNode.bundle, gpsDomainReset, 3, "gps_acq_reset")
val trckReset = ResetCatchAndSync(trackingClockNode.bundle, gpsDomainReset, 3, "gps_trck_reset")
val afeReset = ResetCatchAndSync(afeClockNode.bundle, gpsDomainReset, 3, "gps_afe_reset")
// PPS uses the fabric-derived tracking clock but must keep counting during
// RF/MMCM loss. Only a real SoC reset may reset its monotonic timebase.
val ppsReset = ResetCatchAndSync(trackingClockNode.bundle, reset.asBool, 3, "gps_pps_reset")
val apbResetn = !apbReset
val acqResetn = !acqReset
val trckResetn = !trckReset
val afeResetn = !afeReset
val ppsResetn = !ppsReset
afe.io.afe_rstn := afeResetn
afe.io.trk_rstn := trckResetn
afe.io.afe_clk := afeClockNode.bundle
afe.io.trk_clk := trackingClockNode.bundle
// A disappearing RF clock resets the AFE while the APB register bank is
// intentionally retained. Generate one local restart pulse when the RF
// domain returns; the normal software pulse remains valid at cold boot.
val afeAutoStart = withClockAndReset(afeClockNode.bundle, afeReset) {
val started = RegInit(false.B)
val pulse = !started
started := true.B
pulse
}
afe.io.start_i := acq.io.afe_start || afeAutoStart
afe.io.sample_counter_clear_i := acq.io.sample_counter_clear
acq.io.PCLK := clock
acq.io.PRESETN := apbResetn
acq.io.PADDR := localAddr
acq.io.PSEL := apb.psel && sel(0)
acq.io.PENABLE := apb.penable
acq.io.PWRITE := apb.pwrite
acq.io.PWDATA := apb.pwdata(31, 0)
acq.io.clk_80mhz := acqClockNode.bundle
acq.io.rf_clk := afeClockNode.bundle
acq.io.acq_resetn := acqResetn
acq.io.afe_resetn := afeResetn
acq.io.rf_data := rfDataNode.bundle
acq.io.sample_align := afe.io.sample_align_o
acq.io.start_acq_manuel := false.B
tracking.zipWithIndex.foreach { case (trck, index) =>
trck.io.PCLK := clock
trck.io.PRESETN := apbResetn
trck.io.PADDR := localAddr
trck.io.PSEL := apb.psel && sel(index + 1)
trck.io.PENABLE := apb.penable
trck.io.PWRITE := apb.pwrite
trck.io.PWDATA := apb.pwdata(31, 0)
trck.io.trck_resetn := trckResetn
trck.io.afe_resetn := afeResetn
trck.io.trck_clk := trackingClockNode.bundle
trck.io.afe_clk := afeClockNode.bundle
trck.io.afe_data := rfDataNode.bundle
trck.io.sample_align := afe.io.sample_align_o
trck.io.sample_counter := afe.io.sample_counter_o
}
pps.io.PCLK := clock
pps.io.PRESETN := apbResetn
pps.io.PADDR := localAddr
pps.io.PSEL := apb.psel && sel(params.trackingCount + 1)
pps.io.PENABLE := apb.penable
pps.io.PWRITE := apb.pwrite
pps.io.PWDATA := apb.pwdata(31, 0)
pps.io.pps_clk := trackingClockNode.bundle
pps.io.pps_resetn := ppsResetn
pps.io.capture_i := VecInit(tracking.map(_.io.pps_capture)).asUInt
ppsOutNode.bundle := pps.io.pps_out
val pready = acq.io.PREADY +: (tracking.map(_.io.PREADY) :+ pps.io.PREADY)
val prdata = acq.io.PRDATA +: (tracking.map(_.io.PRDATA) :+ pps.io.PRDATA)
val pslverr = acq.io.PSLVERR +: (tracking.map(_.io.PSLVERR) :+ pps.io.PSLVERR)
apb.pready := Mux(hit, Mux1H(sel, pready), true.B)
apb.prdata := Mux1H(sel, prdata.map(_.pad(apb.params.dataBits)))
apb.pslverr := Mux(hit, Mux1H(sel, pslverr), true.B)
intnode.out.foreach { case (out, _) =>
out(0) := acq.io.acq_intr
tracking.zipWithIndex.foreach { case (trck, index) =>
out(index + 1) := trck.io.gps_frame_ready
}
}
}
}
class WithVCU118GPS(baseAddress: BigInt = 0x10020000L, trackingCount: Int = 9)
extends Config((site, here, up) => {
case VCU118GPSKey => Some(FpgaGPSParams(baseAddress, trackingCount))
})
class WithVCU118TLRAM(base: BigInt = 0x80000000L, size: BigInt = 0x20000L)
extends Config((site, here, up) => {
case VCU118RAMKey => Some(AddressSet(base, size - 1))
})
// Defaults stay at the original 1 MiB DTIM, but board Makefiles may override base/size.
class WithVCU118OneMiBDTIM(
base: BigInt = FpgaBuildSettings.bigInt("ROCKET_DTIM_BASE", 0x80000000L),
size: BigInt = FpgaBuildSettings.bigInt("ROCKET_DTIM_SIZE", 0x100000L))
extends RocketTileConfig(tp => tp.copy(
core = tp.core.copy(useVM = false),
dcache = tp.dcache.map { dc =>
require(size > 0, s"DTIM size $size must be positive")
require(base % dc.blockBytes == 0, s"DTIM base $base must be aligned to blockBytes ${dc.blockBytes}")
require(size % dc.blockBytes == 0, s"DTIM size $size must be a multiple of blockBytes ${dc.blockBytes}")
dc.copy(
nSets = (size / dc.blockBytes).toInt,
nWays = 1,
nMSHRs = 0,
scratch = Some(base))
}
))
class WithVCU118HardDoubleFPU extends RocketTileConfig(tp => tp.copy(
core = tp.core.copy(fpu = Some(FPUParams(minFLen = 32, fLen = 64, divSqrt = true)))
))
class WithVCU118FlashBootROM(file: String = "./soc/out/software/bootloader/bootloader.img")
extends Config((site, here, up) => {
case BootROMLocated(InSubsystem) => Seq(BootROMParams(
hang = 0x10000,
appendDTB = false,
contentFileName = SystemFileName(file)))
})
case object VCU118UARTKey extends Field[Option[FpgaUARTParams]](None)
case object VCU118SPIKey extends Field[Option[FpgaSPIParams]](None)
case object VCU118PmodSPIKey extends Field[Option[FpgaSPIParams]](None)
case object VCU118GPSKey extends Field[Option[FpgaGPSParams]](None)
case object VCU118RAMKey extends Field[Option[AddressSet]](None)
class VCU118RocketSystem(implicit p: Parameters) extends RocketSubsystem
with HasAsyncExtInterrupts {
val bootROM = p(BootROMLocated(location)).map { BootROM.attach(_, this, CBUS) }
val maskROMs = p(MaskROMLocated(location)).map { MaskROM.attach(_, this, CBUS) }
val uartRXSourceNode = p(VCU118UARTKey).map(_ => BundleBridgeSource(() => Bool()))
val gpsAcqClockSourceNode = p(VCU118GPSKey).map(_ => BundleBridgeSource(() => Clock()))
val gpsTrackingClockSourceNode = p(VCU118GPSKey).map(_ => BundleBridgeSource(() => Clock()))
val gpsRfClockSourceNode = p(VCU118GPSKey).map(_ => BundleBridgeSource(() => Clock()))
val gpsRfDataSourceNode = p(VCU118GPSKey).map(_ => BundleBridgeSource(() => UInt(4.W)))
val gpsClockReadySourceNode = p(VCU118GPSKey).map(_ => BundleBridgeSource(() => Bool()))
private val pmodIRQDevice = new SimpleDevice("pmod-sensor-irq", Seq("rocket,pmod-sensor-irq"))
val pmodIRQIntNode = IntSourceNode(Seq(IntSourcePortParameters(Seq(
IntSourceParameters(IntRange(0, 1), pmodIRQDevice.int)))))
val ram = p(VCU118RAMKey).map { address =>
val tlram = LazyModule(new TLRAM(address, beatBytes = locateTLBusWrapper(SBUS).beatBytes, devName = Some("fpga-sram")))
tlram.node := locateTLBusWrapper(SBUS).coupleTo("fpga_sram") {
TLFragmenter(locateTLBusWrapper(SBUS).beatBytes, locateTLBusWrapper(SBUS).blockBytes) := _
}
tlram
}
val uart = p(VCU118UARTKey).map { params =>
val pbus = locateTLBusWrapper(PBUS)
val domain = pbus.generateSynchronousDomain("FPGAUART").suggestName("fpga_uart_domain")
val dev = domain { LazyModule(new TLFpgaUART(params, pbus.beatBytes)) }
domain {
dev.node := pbus.coupleTo("fpga_uart") {
TLFragmenter(pbus, Some("FPGAUART")) := _
}
}
uartRXSourceNode.foreach { source => dev.rxdNode := source }
dev
}
val uartTXSinkNode = uart.map(_.txdNode.makeSink())
val spiDQISourceNode = p(VCU118SPIKey).map(_ => BundleBridgeSource(() => UInt(4.W)))
val spi = p(VCU118SPIKey).map { params =>
val pbus = locateTLBusWrapper(PBUS)
val domain = pbus.generateSynchronousDomain("FPGASPI").suggestName("fpga_spi_domain")
val dev = domain { LazyModule(new TLFpgaSPI(params, pbus.beatBytes)) }
domain {
dev.node := pbus.coupleTo("fpga_spi") {
TLFragmenter(pbus, Some("FPGASPI")) := _
}
}
spiDQISourceNode.foreach { source => dev.dqInNode := source }
dev
}
val spiSCKSinkNode = spi.map(_.sckNode.makeSink())
val spiCSNSinkNode = spi.map(_.csnNode.makeSink())
val spiDQOSinkNode = spi.map(_.dqOutNode.makeSink())
val spiDQOESinkNode = spi.map(_.dqOeNode.makeSink())
val pmodSPIDQISourceNode = p(VCU118PmodSPIKey).map(_ => BundleBridgeSource(() => UInt(4.W)))
val pmodSPI = p(VCU118PmodSPIKey).map { params =>
val pbus = locateTLBusWrapper(PBUS)
val domain = pbus.generateSynchronousDomain("FPGAPMODSPI").suggestName("fpga_pmod_spi_domain")
val dev = domain { LazyModule(new TLFpgaSPI(params, pbus.beatBytes)) }
domain {
dev.node := pbus.coupleTo("fpga_pmod_spi") {
TLFragmenter(pbus, Some("FPGAPMODSPI")) := _
}
}
pmodSPIDQISourceNode.foreach { source => dev.dqInNode := source }
dev
}
val pmodSPISCKSinkNode = pmodSPI.map(_.sckNode.makeSink())
val pmodSPICSNSinkNode = pmodSPI.map(_.csnNode.makeSink())
val pmodSPIDQOSinkNode = pmodSPI.map(_.dqOutNode.makeSink())
val pmodSPIDQOESinkNode = pmodSPI.map(_.dqOeNode.makeSink())
val gps = p(VCU118GPSKey).map { params =>
val pbus = locateTLBusWrapper(PBUS)
val domain = pbus.generateSynchronousDomain("FPGAGPS").suggestName("fpga_gps_domain")
val gpsApbBeatBytes = 4
val dev = domain { LazyModule(new APBGpsBlock(params, gpsApbBeatBytes)) }
domain {
dev.node := TLToAPB() := pbus.coupleTo("gps") {
TLFragmenter(gpsApbBeatBytes, pbus.blockBytes, nameSuffix = Some("FPGAGPS")) :*= TLWidthWidget(pbus) := _
}
ibus.fromSync := dev.intnode
}
gpsAcqClockSourceNode.foreach { source => dev.acqClockNode := source }
gpsTrackingClockSourceNode.foreach { source => dev.trackingClockNode := source }
gpsRfClockSourceNode.foreach { source => dev.afeClockNode := source }
gpsRfDataSourceNode.foreach { source => dev.rfDataNode := source }
gpsClockReadySourceNode.foreach { source => dev.clockReadyNode := source }
dev
}
val gpsPPSSinkNode = gps.map(_.ppsOutNode.makeSink())
ibus { ibus.fromAsync := pmodIRQIntNode }
override lazy val module = new VCU118RocketSystemModuleImp(this)
}
class VCU118RocketSystemModuleImp[+L <: VCU118RocketSystem](_outer: L)
extends RocketSubsystemModuleImp(_outer)
with HasRTCModuleImp
with HasExtInterruptsModuleImp
with freechips.rocketchip.util.DontTouch {
val uart_rxd = _outer.uartRXSourceNode.map(_.makeIO("uart_rxd"))
val uart_txd = _outer.uartTXSinkNode.map(_.makeIO("uart_txd"))
val spi_sck = _outer.spiSCKSinkNode.map(_.makeIO("spi_sck"))
val spi_csn = _outer.spiCSNSinkNode.map(_.makeIO("spi_csn"))
val spi_dq_i = _outer.spiDQISourceNode.map(_.makeIO("spi_dq_i"))
val spi_dq_o = _outer.spiDQOSinkNode.map(_.makeIO("spi_dq_o"))
val spi_dq_oe = _outer.spiDQOESinkNode.map(_.makeIO("spi_dq_oe"))
val pmod_spi_sck = _outer.pmodSPISCKSinkNode.map(_.makeIO("pmod_spi_sck"))
val pmod_spi_csn = _outer.pmodSPICSNSinkNode.map(_.makeIO("pmod_spi_csn"))
val pmod_spi_dq_i = _outer.pmodSPIDQISourceNode.map(_.makeIO("pmod_spi_dq_i"))
val pmod_spi_dq_o = _outer.pmodSPIDQOSinkNode.map(_.makeIO("pmod_spi_dq_o"))
val pmod_spi_dq_oe = _outer.pmodSPIDQOESinkNode.map(_.makeIO("pmod_spi_dq_oe"))
val pmod_irq = IO(Input(Bool()))
val gps_acq_clk = _outer.gpsAcqClockSourceNode.map(_.makeIO("gps_acq_clk"))
val gps_trck_clk = _outer.gpsTrackingClockSourceNode.map(_.makeIO("gps_trck_clk"))
val gps_rf_clk = _outer.gpsRfClockSourceNode.map(_.makeIO("gps_rf_clk"))
val gps_rf_data = _outer.gpsRfDataSourceNode.map(_.makeIO("gps_rf_data"))
val gps_clock_ready = _outer.gpsClockReadySourceNode.map(_.makeIO("gps_clock_ready"))
val gps_pps = _outer.gpsPPSSinkNode.map(_.makeIO("gps_pps"))
_outer.pmodIRQIntNode.out.foreach { case (out, _) =>
out(0) := pmod_irq
}
}
class VCU118RocketConfig extends Config(
new WithVCU118FlashBootROM ++
new WithDebugSBA ++
new WithoutTLMonitors ++
new WithNoMemPort ++
new WithNoMMIOPort ++
new WithNoSlavePort ++
new WithNMemoryChannels(0) ++
new WithNBanks(0) ++
new WithVCU118HardDoubleFPU ++
new WithVCU118OneMiBDTIM ++
new WithVCU118UART(0x10013000L, 100000000, 115200) ++
new WithVCU118SPI(0x10014000L) ++
new WithVCU118PmodSPI(0x10015000L) ++
new WithVCU118GPS(0x10020000L, 9) ++
new WithNExtTopInterrupts(0) ++
new WithNSmallCores(1) ++
new WithIncoherentBusTopology ++
new BaseConfig)
class VCU118RocketJtagConfig extends Config(
new WithJtagDTM ++
new VCU118RocketConfig)