Add ucb-art/chisel-jtag (#612)
* jtag: Add ucb-art/chisel-jtag to junctions. * jtag: Add missing Utils file for Tristate and NegativeEdgeLatch * jtag: move to a top-level package
This commit is contained in:
parent
0e2b780089
commit
8e6beb80be
26
LICENSE.jtag
Normal file
26
LICENSE.jtag
Normal file
@ -0,0 +1,26 @@
|
||||
chisel-jtag license terms
|
||||
|
||||
Copyright (c) 2016 The Regents of the University of
|
||||
California (Regents). All Rights Reserved. Redistribution and use in
|
||||
source and binary forms, with or without modification, are permitted
|
||||
provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the following
|
||||
two paragraphs of disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
two paragraphs of disclaimer in the documentation and/or other materials
|
||||
provided with the distribution.
|
||||
* Neither the name of the Regents nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
|
||||
SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
|
||||
ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
|
||||
REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF
|
||||
ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION
|
||||
TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
|
||||
MODIFICATIONS.
|
185
src/main/scala/jtag/JtagShifter.scala
Normal file
185
src/main/scala/jtag/JtagShifter.scala
Normal file
@ -0,0 +1,185 @@
|
||||
// See LICENSE.jtag for license details.
|
||||
|
||||
package jtag
|
||||
|
||||
import chisel3._
|
||||
import chisel3.core.DataMirror
|
||||
import chisel3.internal.firrtl.KnownWidth
|
||||
import chisel3.util._
|
||||
|
||||
/** Base JTAG shifter IO, viewed from input to shift register chain.
|
||||
* Can be chained together.
|
||||
*/
|
||||
class ShifterIO extends Bundle {
|
||||
val shift = Bool() // advance the scan chain on clock high
|
||||
val data = Bool() // as input: bit to be captured into shifter MSB on next rising edge; as output: value of shifter LSB
|
||||
val capture = Bool() // high in the CaptureIR/DR state when this chain is selected
|
||||
val update = Bool() // high in the UpdateIR/DR state when this chain is selected
|
||||
|
||||
/** Sets a output shifter IO's control signals from a input shifter IO's control signals.
|
||||
*/
|
||||
def chainControlFrom(in: ShifterIO) {
|
||||
shift := in.shift
|
||||
capture := in.capture
|
||||
update := in.update
|
||||
}
|
||||
}
|
||||
|
||||
trait ChainIO extends Bundle {
|
||||
val chainIn = Input(new ShifterIO)
|
||||
val chainOut = Output(new ShifterIO)
|
||||
}
|
||||
|
||||
class Capture[+T <: Data](gen: T) extends Bundle {
|
||||
val bits = Input(gen) // data to capture, should be always valid
|
||||
val capture = Output(Bool()) // will be high in capture state (single cycle), captured on following rising edge
|
||||
override def cloneType = Capture(gen).asInstanceOf[this.type]
|
||||
}
|
||||
|
||||
object Capture {
|
||||
def apply[T <: Data](gen: T): Capture[T] = new Capture(gen)
|
||||
}
|
||||
|
||||
/** Trait that all JTAG chains (data and instruction registers) must extend, providing basic chain
|
||||
* IO.
|
||||
*/
|
||||
trait Chain extends Module {
|
||||
val io: ChainIO
|
||||
}
|
||||
|
||||
/** One-element shift register, data register for bypass mode.
|
||||
*
|
||||
* Implements Clause 10.
|
||||
*/
|
||||
class JtagBypassChain extends Chain {
|
||||
class ModIO extends ChainIO
|
||||
val io = IO(new ModIO)
|
||||
io.chainOut chainControlFrom io.chainIn
|
||||
|
||||
val reg = Reg(Bool()) // 10.1.1a single shift register stage
|
||||
|
||||
io.chainOut.data := reg
|
||||
|
||||
when (io.chainIn.capture) {
|
||||
reg := false.B // 10.1.1b capture logic 0 on TCK rising
|
||||
} .elsewhen (io.chainIn.shift) {
|
||||
reg := io.chainIn.data
|
||||
}
|
||||
assert(!(io.chainIn.capture && io.chainIn.update)
|
||||
&& !(io.chainIn.capture && io.chainIn.shift)
|
||||
&& !(io.chainIn.update && io.chainIn.shift))
|
||||
}
|
||||
|
||||
object JtagBypassChain {
|
||||
def apply() = new JtagBypassChain
|
||||
}
|
||||
|
||||
/** Simple shift register with parallel capture only, for read-only data registers.
|
||||
*
|
||||
* Number of stages is the number of bits in gen, which must have a known width.
|
||||
*
|
||||
* Useful notes:
|
||||
* 7.2.1c shifter shifts on TCK rising edge
|
||||
* 4.3.2a TDI captured on TCK rising edge, 6.1.2.1b assumed changes on TCK falling edge
|
||||
*/
|
||||
class CaptureChain[+T <: Data](gen: T) extends Chain {
|
||||
class ModIO extends ChainIO {
|
||||
val capture = Capture(gen)
|
||||
}
|
||||
val io = IO(new ModIO)
|
||||
io.chainOut chainControlFrom io.chainIn
|
||||
|
||||
val n = DataMirror.widthOf(gen) match {
|
||||
case KnownWidth(x) => x
|
||||
case _ => require(false, s"can't generate chain for unknown width data type $gen"); -1 // TODO: remove -1 type hack
|
||||
}
|
||||
|
||||
val regs = (0 until n) map (x => Reg(Bool()))
|
||||
|
||||
io.chainOut.data := regs(0)
|
||||
|
||||
when (io.chainIn.capture) {
|
||||
(0 until n) map (x => regs(x) := io.capture.bits.asUInt()(x))
|
||||
io.capture.capture := true.B
|
||||
} .elsewhen (io.chainIn.shift) {
|
||||
regs(n-1) := io.chainIn.data
|
||||
(0 until n-1) map (x => regs(x) := regs(x+1))
|
||||
io.capture.capture := false.B
|
||||
} .otherwise {
|
||||
io.capture.capture := false.B
|
||||
}
|
||||
assert(!(io.chainIn.capture && io.chainIn.update)
|
||||
&& !(io.chainIn.capture && io.chainIn.shift)
|
||||
&& !(io.chainIn.update && io.chainIn.shift))
|
||||
}
|
||||
|
||||
object CaptureChain {
|
||||
def apply[T <: Data](gen: T) = new CaptureChain(gen)
|
||||
}
|
||||
|
||||
/** Simple shift register with parallel capture and update. Useful for general instruction and data
|
||||
* scan registers.
|
||||
*
|
||||
* Number of stages is the max number of bits in genCapture and genUpdate, both of which must have
|
||||
* known widths. If there is a width mismatch, the unused most significant bits will be zero.
|
||||
*
|
||||
* Useful notes:
|
||||
* 7.2.1c shifter shifts on TCK rising edge
|
||||
* 4.3.2a TDI captured on TCK rising edge, 6.1.2.1b assumed changes on TCK falling edge
|
||||
*/
|
||||
class CaptureUpdateChain[+T <: Data, +V <: Data](genCapture: T, genUpdate: V) extends Chain {
|
||||
class ModIO extends ChainIO {
|
||||
val capture = Capture(genCapture)
|
||||
val update = Valid(genUpdate) // valid high when in update state (single cycle), contents may change any time after
|
||||
}
|
||||
val io = IO(new ModIO)
|
||||
io.chainOut chainControlFrom io.chainIn
|
||||
|
||||
val captureWidth = DataMirror.widthOf(genCapture) match {
|
||||
case KnownWidth(x) => x
|
||||
case _ => require(false, s"can't generate chain for unknown width data type $genCapture"); -1 // TODO: remove -1 type hack
|
||||
}
|
||||
val updateWidth = DataMirror.widthOf(genUpdate) match {
|
||||
case KnownWidth(x) => x
|
||||
case _ => require(false, s"can't generate chain for unknown width data type $genUpdate"); -1 // TODO: remove -1 type hack
|
||||
}
|
||||
val n = math.max(captureWidth, updateWidth)
|
||||
|
||||
val regs = (0 until n) map (x => Reg(Bool()))
|
||||
|
||||
io.chainOut.data := regs(0)
|
||||
|
||||
val updateBits = Cat(regs.reverse)(updateWidth-1, 0)
|
||||
io.update.bits := io.update.bits.fromBits(updateBits)
|
||||
|
||||
val captureBits = io.capture.bits.asUInt()
|
||||
|
||||
when (io.chainIn.capture) {
|
||||
(0 until math.min(n, captureWidth)) map (x => regs(x) := captureBits(x))
|
||||
(captureWidth until n) map (x => regs(x) := 0.U)
|
||||
io.capture.capture := true.B
|
||||
io.update.valid := false.B
|
||||
} .elsewhen (io.chainIn.update) {
|
||||
io.capture.capture := false.B
|
||||
io.update.valid := true.B
|
||||
} .elsewhen (io.chainIn.shift) {
|
||||
regs(n-1) := io.chainIn.data
|
||||
(0 until n-1) map (x => regs(x) := regs(x+1))
|
||||
io.capture.capture := false.B
|
||||
io.update.valid := false.B
|
||||
} .otherwise {
|
||||
io.capture.capture := false.B
|
||||
io.update.valid := false.B
|
||||
}
|
||||
assert(!(io.chainIn.capture && io.chainIn.update)
|
||||
&& !(io.chainIn.capture && io.chainIn.shift)
|
||||
&& !(io.chainIn.update && io.chainIn.shift))
|
||||
}
|
||||
|
||||
object CaptureUpdateChain {
|
||||
/** Capture-update chain with matching capture and update types.
|
||||
*/
|
||||
def apply[T <: Data](gen: T) = new CaptureUpdateChain(gen, gen)
|
||||
def apply[T <: Data, V <: Data](genCapture: T, genUpdate: V) =
|
||||
new CaptureUpdateChain(genCapture, genUpdate)
|
||||
}
|
143
src/main/scala/jtag/JtagStateMachine.scala
Normal file
143
src/main/scala/jtag/JtagStateMachine.scala
Normal file
@ -0,0 +1,143 @@
|
||||
// See LICENSE.jtag for license details.
|
||||
|
||||
package jtag
|
||||
|
||||
import util.{AsyncResetRegVec}
|
||||
import chisel3._
|
||||
import chisel3.util._
|
||||
|
||||
object JtagState {
|
||||
sealed abstract class State(val id: Int) {
|
||||
def U: UInt = id.U(State.width.W)
|
||||
}
|
||||
|
||||
object State {
|
||||
import scala.language.implicitConversions
|
||||
|
||||
implicit def toInt(x: State) = x.id
|
||||
implicit def toBigInt(x: State):BigInt = x.id
|
||||
|
||||
// TODO: this could be automatically generated with macros and stuff
|
||||
val all: Set[State] = Set(
|
||||
TestLogicReset,
|
||||
RunTestIdle,
|
||||
SelectDRScan,
|
||||
CaptureDR,
|
||||
ShiftDR,
|
||||
Exit1DR,
|
||||
PauseDR,
|
||||
Exit2DR,
|
||||
UpdateDR,
|
||||
SelectIRScan,
|
||||
CaptureIR,
|
||||
ShiftIR,
|
||||
Exit1IR,
|
||||
PauseIR,
|
||||
Exit2IR,
|
||||
UpdateIR
|
||||
)
|
||||
val width = log2Up(all.size)
|
||||
def chiselType() = UInt(width.W)
|
||||
}
|
||||
|
||||
// States as described in 6.1.1.2, numeric assignments from example in Table 6-3
|
||||
case object TestLogicReset extends State(15) // no effect on system logic, entered when TMS high for 5 TCK rising edges
|
||||
case object RunTestIdle extends State(12) // runs active instruction (which can be idle)
|
||||
case object SelectDRScan extends State(7)
|
||||
case object CaptureDR extends State(6) // parallel-load DR shifter when exiting this state (if required)
|
||||
case object ShiftDR extends State(2) // shifts DR shifter from TDI towards TDO, last shift occurs on rising edge transition out of this state
|
||||
case object Exit1DR extends State(1)
|
||||
case object PauseDR extends State(3) // pause DR shifting
|
||||
case object Exit2DR extends State(0)
|
||||
case object UpdateDR extends State(5) // parallel-load output from DR shifter on TCK falling edge while in this state (not a rule?)
|
||||
case object SelectIRScan extends State(4)
|
||||
case object CaptureIR extends State(14) // parallel-load IR shifter with fixed logic values and design-specific when exiting this state (if required)
|
||||
case object ShiftIR extends State(10) // shifts IR shifter from TDI towards TDO, last shift occurs on rising edge transition out of this state
|
||||
case object Exit1IR extends State(9)
|
||||
case object PauseIR extends State(11) // pause IR shifting
|
||||
case object Exit2IR extends State(8)
|
||||
case object UpdateIR extends State(13) // latch IR shifter into IR (changes to IR may only occur while in this state, latch on TCK falling edge)
|
||||
}
|
||||
|
||||
/** The JTAG state machine, implements spec 6.1.1.1a (Figure 6.1)
|
||||
*
|
||||
* Usage notes:
|
||||
* - 6.1.1.1b state transitions occur on TCK rising edge
|
||||
* - 6.1.1.1c actions can occur on the following TCK falling or rising edge
|
||||
*
|
||||
*
|
||||
*/
|
||||
class JtagStateMachine extends Module(override_reset=Some(false.B)) {
|
||||
class StateMachineIO extends Bundle {
|
||||
val tms = Input(Bool())
|
||||
val currState = Output(JtagState.State.chiselType())
|
||||
|
||||
val jtag_reset = Input(Bool())
|
||||
}
|
||||
val io = IO(new StateMachineIO)
|
||||
|
||||
val nextState = Wire(JtagState.State.chiselType())
|
||||
|
||||
val currStateReg = Module (new AsyncResetRegVec(w = JtagState.State.width,
|
||||
init = JtagState.State.toInt(JtagState.TestLogicReset)))
|
||||
|
||||
currStateReg.clock := clock
|
||||
currStateReg.reset := io.jtag_reset
|
||||
currStateReg.io.en := true.B
|
||||
currStateReg.io.d := nextState
|
||||
|
||||
val currState = currStateReg.io.q
|
||||
|
||||
switch (currState) {
|
||||
is (JtagState.TestLogicReset.U) {
|
||||
nextState := Mux(io.tms, JtagState.TestLogicReset.U, JtagState.RunTestIdle.U)
|
||||
}
|
||||
is (JtagState.RunTestIdle.U) {
|
||||
nextState := Mux(io.tms, JtagState.SelectDRScan.U, JtagState.RunTestIdle.U)
|
||||
}
|
||||
is (JtagState.SelectDRScan.U) {
|
||||
nextState := Mux(io.tms, JtagState.SelectIRScan.U, JtagState.CaptureDR.U)
|
||||
}
|
||||
is (JtagState.CaptureDR.U) {
|
||||
nextState := Mux(io.tms, JtagState.Exit1DR.U, JtagState.ShiftDR.U)
|
||||
}
|
||||
is (JtagState.ShiftDR.U) {
|
||||
nextState := Mux(io.tms, JtagState.Exit1DR.U, JtagState.ShiftDR.U)
|
||||
}
|
||||
is (JtagState.Exit1DR.U) {
|
||||
nextState := Mux(io.tms, JtagState.UpdateDR.U, JtagState.PauseDR.U)
|
||||
}
|
||||
is (JtagState.PauseDR.U) {
|
||||
nextState := Mux(io.tms, JtagState.Exit2DR.U, JtagState.PauseDR.U)
|
||||
}
|
||||
is (JtagState.Exit2DR.U) {
|
||||
nextState := Mux(io.tms, JtagState.UpdateDR.U, JtagState.ShiftDR.U)
|
||||
}
|
||||
is (JtagState.UpdateDR.U) {
|
||||
nextState := Mux(io.tms, JtagState.SelectDRScan.U, JtagState.RunTestIdle.U)
|
||||
}
|
||||
is (JtagState.SelectIRScan.U) {
|
||||
nextState := Mux(io.tms, JtagState.TestLogicReset.U, JtagState.CaptureIR.U)
|
||||
}
|
||||
is (JtagState.CaptureIR.U) {
|
||||
nextState := Mux(io.tms, JtagState.Exit1IR.U, JtagState.ShiftIR.U)
|
||||
}
|
||||
is (JtagState.ShiftIR.U) {
|
||||
nextState := Mux(io.tms, JtagState.Exit1IR.U, JtagState.ShiftIR.U)
|
||||
}
|
||||
is (JtagState.Exit1IR.U) {
|
||||
nextState := Mux(io.tms, JtagState.UpdateIR.U, JtagState.PauseIR.U)
|
||||
}
|
||||
is (JtagState.PauseIR.U) {
|
||||
nextState := Mux(io.tms, JtagState.Exit2IR.U, JtagState.PauseIR.U)
|
||||
}
|
||||
is (JtagState.Exit2IR.U) {
|
||||
nextState := Mux(io.tms, JtagState.UpdateIR.U, JtagState.ShiftIR.U)
|
||||
}
|
||||
is (JtagState.UpdateIR.U) {
|
||||
nextState := Mux(io.tms, JtagState.SelectDRScan.U, JtagState.RunTestIdle.U)
|
||||
}
|
||||
}
|
||||
|
||||
io.currState := currState
|
||||
}
|
241
src/main/scala/jtag/JtagTap.scala
Normal file
241
src/main/scala/jtag/JtagTap.scala
Normal file
@ -0,0 +1,241 @@
|
||||
// See LICENSE.jtag for license details.
|
||||
|
||||
package jtag
|
||||
|
||||
import chisel3._
|
||||
import chisel3.util._
|
||||
/** JTAG signals, viewed from the master side
|
||||
*/
|
||||
class JTAGIO(hasTRSTn: Boolean = false) extends Bundle {
|
||||
val TRSTn = if (hasTRSTn) Some(Output(Bool())) else None
|
||||
val TCK = Clock(OUTPUT)
|
||||
val TMS = Output(Bool())
|
||||
val TDI = Output(Bool())
|
||||
val TDO = Input(new Tristate())
|
||||
|
||||
override def cloneType = new JTAGIO(hasTRSTn).asInstanceOf[this.type]
|
||||
}
|
||||
|
||||
/** JTAG block output signals.
|
||||
*/
|
||||
class JtagOutput(irLength: Int) extends Bundle {
|
||||
val state = Output(JtagState.State.chiselType()) // state, transitions on TCK rising edge
|
||||
val instruction = Output(UInt(irLength.W)) // current active instruction
|
||||
val reset = Output(Bool()) // synchronous reset asserted in Test-Logic-Reset state, should NOT hold the FSM in reset
|
||||
|
||||
override def cloneType = new JtagOutput(irLength).asInstanceOf[this.type]
|
||||
}
|
||||
|
||||
class JtagControl extends Bundle {
|
||||
val jtag_reset = Input(Bool())
|
||||
}
|
||||
|
||||
/** Aggregate JTAG block IO.
|
||||
*/
|
||||
class JtagBlockIO(irLength: Int) extends Bundle {
|
||||
val jtag = Flipped(new JTAGIO())
|
||||
val control = new JtagControl
|
||||
val output = new JtagOutput(irLength)
|
||||
|
||||
override def cloneType = new JtagBlockIO(irLength).asInstanceOf[this.type]
|
||||
}
|
||||
|
||||
/** Internal controller block IO with data shift outputs.
|
||||
*/
|
||||
class JtagControllerIO(irLength: Int) extends JtagBlockIO(irLength) {
|
||||
val dataChainOut = Output(new ShifterIO)
|
||||
val dataChainIn = Input(new ShifterIO)
|
||||
|
||||
override def cloneType = new JtagControllerIO(irLength).asInstanceOf[this.type]
|
||||
}
|
||||
|
||||
/** JTAG TAP controller internal block, responsible for instruction decode and data register chain
|
||||
* control signal generation.
|
||||
*
|
||||
* Misc notes:
|
||||
* - Figure 6-3 and 6-4 provides examples with timing behavior
|
||||
*/
|
||||
class JtagTapController(irLength: Int, initialInstruction: BigInt) extends Module {
|
||||
require(irLength >= 2) // 7.1.1a
|
||||
|
||||
val io = IO(new JtagControllerIO(irLength))
|
||||
|
||||
val tdo = Wire(Bool()) // 4.4.1c TDI should appear here uninverted after shifting
|
||||
val tdo_driven = Wire(Bool())
|
||||
io.jtag.TDO.data := NegativeEdgeLatch(clock, tdo, name = Some("tdoReg")) // 4.5.1a TDO changes on falling edge of TCK, 6.1.2.1d driver active on first TCK falling edge in ShiftIR and ShiftDR states
|
||||
io.jtag.TDO.driven := NegativeEdgeLatch(clock, tdo_driven, name = Some("tdoeReg"))
|
||||
|
||||
//
|
||||
// JTAG state machine
|
||||
//
|
||||
val stateMachine = Module(new JtagStateMachine)
|
||||
stateMachine.io.tms := io.jtag.TMS
|
||||
val currState = stateMachine.io.currState
|
||||
io.output.state := stateMachine.io.currState
|
||||
|
||||
// At this point, the TRSTn should already have been
|
||||
// combined with any POR, and it should also be
|
||||
// synchronized to TCK.
|
||||
require(!io.jtag.TRSTn.isDefined, "TRSTn should be absorbed into jtckPOReset outside of JtagTapController.")
|
||||
stateMachine.io.jtag_reset := io.control.jtag_reset
|
||||
|
||||
//
|
||||
// Instruction Register
|
||||
//
|
||||
// 7.1.1d IR shifter two LSBs must be b01 pattern
|
||||
// TODO: 7.1.1d allow design-specific IR bits, 7.1.1e (rec) should be a fixed pattern
|
||||
// 7.2.1a behavior of instruction register and shifters
|
||||
val irChain = Module(CaptureUpdateChain(UInt(irLength.W)))
|
||||
irChain.suggestName("irChain")
|
||||
irChain.io.chainIn.shift := currState === JtagState.ShiftIR.U
|
||||
irChain.io.chainIn.data := io.jtag.TDI
|
||||
irChain.io.chainIn.capture := currState === JtagState.CaptureIR.U
|
||||
irChain.io.chainIn.update := currState === JtagState.UpdateIR.U
|
||||
irChain.io.capture.bits := "b01".U
|
||||
|
||||
val updateInstruction = Wire(Bool())
|
||||
|
||||
val nextActiveInstruction = Wire(UInt(irLength.W))
|
||||
val activeInstruction = NegativeEdgeLatch(clock, nextActiveInstruction, updateInstruction, name = Some("irReg")) // 7.2.1d active instruction output latches on TCK falling edge
|
||||
|
||||
when (reset) {
|
||||
nextActiveInstruction := initialInstruction.U(irLength.W)
|
||||
updateInstruction := true.B
|
||||
} .elsewhen (currState === JtagState.UpdateIR.U) {
|
||||
nextActiveInstruction := irChain.io.update.bits
|
||||
updateInstruction := true.B
|
||||
} .otherwise {
|
||||
updateInstruction := false.B
|
||||
}
|
||||
io.output.instruction := activeInstruction
|
||||
|
||||
io.output.reset := currState === JtagState.TestLogicReset.U
|
||||
|
||||
//
|
||||
// Data Register
|
||||
//
|
||||
io.dataChainOut.shift := currState === JtagState.ShiftDR.U
|
||||
io.dataChainOut.data := io.jtag.TDI
|
||||
io.dataChainOut.capture := currState === JtagState.CaptureDR.U
|
||||
io.dataChainOut.update := currState === JtagState.UpdateDR.U
|
||||
|
||||
//
|
||||
// Output Control
|
||||
//
|
||||
when (currState === JtagState.ShiftDR.U) {
|
||||
tdo := io.dataChainIn.data
|
||||
tdo_driven := true.B
|
||||
} .elsewhen (currState === JtagState.ShiftIR.U) {
|
||||
tdo := irChain.io.chainOut.data
|
||||
tdo_driven := true.B
|
||||
} .otherwise {
|
||||
tdo_driven := false.B
|
||||
}
|
||||
}
|
||||
|
||||
object JtagTapGenerator {
|
||||
/** JTAG TAP generator, enclosed module must be clocked from TCK and reset from output of this
|
||||
* block.
|
||||
*
|
||||
* @param irLength length, in bits, of instruction register, must be at least 2
|
||||
* @param instructions map of instruction codes to data register chains that select that data
|
||||
* register; multiple instructions may map to the same data chain
|
||||
* @param idcode optional idcode, tuple of (instruction code, idcode)
|
||||
*
|
||||
* @note all other instruction codes (not part of instructions or idcode) map to BYPASS
|
||||
* @note initial instruction is idcode (if supported), otherwise all ones BYPASS
|
||||
*
|
||||
* Usage notes:
|
||||
* - 4.3.1b TMS must appear high when undriven
|
||||
* - 4.3.1c (rec) minimize load presented by TMS
|
||||
* - 4.4.1b TDI must appear high when undriven
|
||||
* - 4.5.1b TDO must be inactive except when shifting data (undriven? 6.1.2)
|
||||
* - 6.1.3.1b TAP controller must not be (re-?)initialized by system reset (allows
|
||||
* boundary-scan testing of reset pin)
|
||||
* - 6.1 TAP controller can be initialized by a on-chip power on reset generator, the same one
|
||||
* that would initialize system logic
|
||||
*
|
||||
* TODO:
|
||||
* - support concatenated scan chains
|
||||
*/
|
||||
def apply(irLength: Int, instructions: Map[BigInt, Chain], idcode:Option[(BigInt, BigInt)]=None): JtagBlockIO = {
|
||||
// Create IDCODE chain if needed
|
||||
val allInstructions = idcode match {
|
||||
case Some((icode, idcode)) => {
|
||||
val idcodeChain = Module(CaptureChain(UInt(32.W)))
|
||||
idcodeChain.suggestName("idcodeChain")
|
||||
require(idcode % 2 == 1, "LSB must be set in IDCODE, see 12.1.1d")
|
||||
require(((idcode >> 1) & ((1 << 11) - 1)) != JtagIdcode.dummyMfrId, "IDCODE must not have 0b00001111111 as manufacturer identity, see 12.2.1b")
|
||||
require(!(instructions contains icode), "instructions may not contain IDCODE")
|
||||
idcodeChain.io.capture.bits := idcode.U(32.W)
|
||||
instructions + (icode -> idcodeChain)
|
||||
} case None => instructions
|
||||
}
|
||||
|
||||
val bypassIcode = (BigInt(1) << irLength) - 1 // required BYPASS instruction
|
||||
val initialInstruction = idcode match { // 7.2.1e load IDCODE or BYPASS instruction after entry into TestLogicReset
|
||||
case Some((icode, _)) => icode
|
||||
case None => bypassIcode
|
||||
}
|
||||
|
||||
require(!(allInstructions contains bypassIcode), "instructions may not contain BYPASS code")
|
||||
|
||||
val controllerInternal = Module(new JtagTapController(irLength, initialInstruction))
|
||||
|
||||
val unusedChainOut = Wire(new ShifterIO) // De-selected chain output
|
||||
unusedChainOut.shift := false.B
|
||||
unusedChainOut.data := false.B
|
||||
unusedChainOut.capture := false.B
|
||||
unusedChainOut.update := false.B
|
||||
|
||||
val bypassChain = Module(JtagBypassChain())
|
||||
|
||||
// The Great Data Register Chain Mux
|
||||
bypassChain.io.chainIn := controllerInternal.io.dataChainOut // for simplicity, doesn't visibly affect anything else
|
||||
require(allInstructions.size > 0, "Seriously? JTAG TAP with no instructions?")
|
||||
|
||||
val chainToIcode = allInstructions groupBy { case (icode, chain) => chain } map {
|
||||
case (chain, icodeToChain) => chain -> icodeToChain.keys
|
||||
}
|
||||
|
||||
val chainToSelect = chainToIcode map {
|
||||
case (chain, icodes) => {
|
||||
assume(icodes.size > 0)
|
||||
val icodeSelects = icodes map { controllerInternal.io.output.instruction === _.asUInt(irLength.W) }
|
||||
chain -> icodeSelects.reduceLeft(_||_)
|
||||
}
|
||||
}
|
||||
|
||||
def foldOutSelect(res: WhenContext, x: (Chain, Bool)): WhenContext = {
|
||||
val (chain, select) = x
|
||||
// Continue the WhenContext with if this chain is selected
|
||||
res.elsewhen(select) {
|
||||
controllerInternal.io.dataChainIn := chain.io.chainOut
|
||||
}
|
||||
}
|
||||
|
||||
val emptyWhen = when (false.B) { } // Empty WhenContext to start things off
|
||||
chainToSelect.toSeq.foldLeft(emptyWhen)(foldOutSelect).otherwise {
|
||||
controllerInternal.io.dataChainIn := bypassChain.io.chainOut
|
||||
}
|
||||
|
||||
def mapInSelect(x: (Chain, Bool)) {
|
||||
val (chain, select) = x
|
||||
when (select) {
|
||||
chain.io.chainIn := controllerInternal.io.dataChainOut
|
||||
} .otherwise {
|
||||
chain.io.chainIn := unusedChainOut
|
||||
}
|
||||
}
|
||||
|
||||
chainToSelect.map(mapInSelect)
|
||||
|
||||
val internalIo = Wire(new JtagBlockIO(irLength))
|
||||
|
||||
controllerInternal.io.jtag <> internalIo.jtag
|
||||
controllerInternal.io.control <> internalIo.control
|
||||
controllerInternal.io.output <> internalIo.output
|
||||
|
||||
internalIo
|
||||
}
|
||||
}
|
22
src/main/scala/jtag/JtagUtils.scala
Normal file
22
src/main/scala/jtag/JtagUtils.scala
Normal file
@ -0,0 +1,22 @@
|
||||
// See LICENSE.jtag for license details.
|
||||
|
||||
package jtag
|
||||
|
||||
import chisel3._
|
||||
import chisel3.util._
|
||||
|
||||
object JtagIdcode {
|
||||
/** Generates a JTAG IDCODE as a 32-bit integer, using the format in 12.1.1d.
|
||||
*/
|
||||
def apply(version: Int, partNumber: Int, mfrId: Int): BigInt = {
|
||||
require(version < (1 << 4), "version field must be 4 bits at most")
|
||||
require(partNumber < (1 << 16), "part number must be 16 bits at most")
|
||||
require(mfrId < (1 << 11), "manufacturer identity must be 11 bits at most")
|
||||
BigInt(version) << 28 | BigInt(partNumber) << 12 | BigInt(mfrId) << 1 | 1
|
||||
}
|
||||
|
||||
/** A dummy manufacturer ID, not to be used per 12.2.1b since bus masters may shift this out to
|
||||
* determine the end of a bus.
|
||||
*/
|
||||
def dummyMfrId: Int = 0x7f
|
||||
}
|
82
src/main/scala/jtag/Utils.scala
Normal file
82
src/main/scala/jtag/Utils.scala
Normal file
@ -0,0 +1,82 @@
|
||||
// See LICENSE.jtag for license details.
|
||||
|
||||
package jtag
|
||||
|
||||
import chisel3._
|
||||
import chisel3.util._
|
||||
|
||||
/** Bundle representing a tristate pin.
|
||||
*/
|
||||
class Tristate extends Bundle {
|
||||
val data = Bool()
|
||||
val driven = Bool() // active high, pin is hi-Z when driven is low
|
||||
}
|
||||
|
||||
class NegativeEdgeLatch[T <: Data](clock: Clock, dataType: T)
|
||||
extends Module(override_clock=Some(clock)) {
|
||||
class IoClass extends Bundle {
|
||||
val next = Input(dataType)
|
||||
val enable = Input(Bool())
|
||||
val output = Output(dataType)
|
||||
}
|
||||
val io = IO(new IoClass)
|
||||
|
||||
val reg = Reg(dataType)
|
||||
when (io.enable) {
|
||||
reg := io.next
|
||||
}
|
||||
io.output := reg
|
||||
}
|
||||
|
||||
/** Generates a register that updates on the falling edge of the input clock signal.
|
||||
*/
|
||||
object NegativeEdgeLatch {
|
||||
def apply[T <: Data](clock: Clock, next: T, enable: Bool=true.B, name: Option[String] = None): T = {
|
||||
// TODO better init passing once in-module multiclock support improves
|
||||
val latch_module = Module(new NegativeEdgeLatch((!clock.asUInt).asClock, next.cloneType))
|
||||
name.foreach(latch_module.suggestName(_))
|
||||
latch_module.io.next := next
|
||||
latch_module.io.enable := enable
|
||||
latch_module.io.output
|
||||
}
|
||||
}
|
||||
|
||||
/** A module that counts transitions on the input clock line, used as a basic sanity check and
|
||||
* debug indicator clock-crossing designs.
|
||||
*/
|
||||
class ClockedCounter(modClock: Clock, counts: BigInt, init: Option[BigInt])
|
||||
extends Module(override_clock=Some(modClock)) {
|
||||
require(counts > 0, "really?")
|
||||
|
||||
val width = log2Up(counts)
|
||||
class CountIO extends Bundle {
|
||||
val count = Output(UInt(width.W))
|
||||
}
|
||||
val io = IO(new CountIO)
|
||||
|
||||
val count = init match {
|
||||
case Some(init) => Reg(UInt(width.W), init=init.U)
|
||||
case None => Reg(UInt(width.W))
|
||||
}
|
||||
|
||||
when (count === (counts - 1).asUInt) {
|
||||
count := 0.U
|
||||
} .otherwise {
|
||||
count := count + 1.U
|
||||
}
|
||||
|
||||
io.count := count
|
||||
}
|
||||
|
||||
/** Count transitions on the input bit by specifying it as a clock to a counter.
|
||||
*/
|
||||
object ClockedCounter {
|
||||
def apply (data: Bool, counts: BigInt, init: BigInt): UInt = {
|
||||
val counter = Module(new ClockedCounter(data.asClock, counts, Some(init)))
|
||||
counter.io.count
|
||||
}
|
||||
def apply (data: Bool, counts: BigInt): UInt = {
|
||||
val counter = Module(new ClockedCounter(data.asClock, counts, None))
|
||||
counter.io.count
|
||||
}
|
||||
}
|
13
src/main/scala/jtag/package.scala
Normal file
13
src/main/scala/jtag/package.scala
Normal file
@ -0,0 +1,13 @@
|
||||
// See LICENSE.jtag for license details
|
||||
|
||||
import scala.language.implicitConversions
|
||||
|
||||
package object jtag {
|
||||
/** An implicit conversion to allow the instruction map for the TAP generator to be specified as
|
||||
* an Int instead of BigInt. Scala doesn't seem to want to automatically apply the built-in Int
|
||||
* to BigInt conversion when used as a Map key.
|
||||
*
|
||||
* This is limited to value types of Chain to limit application scope.
|
||||
*/
|
||||
implicit def instructionIntKeyToBigInt[V <: Chain](x: (Int, V)) = (BigInt(x._1), x._2)
|
||||
}
|
Loading…
Reference in New Issue
Block a user