30179df8e6
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.
768 lines
29 KiB
Scala
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)
|