From 8e6beb80beb66f62c23b14b39daf859a29e25fe5 Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Sun, 26 Mar 2017 18:03:21 -0700 Subject: [PATCH] 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 --- LICENSE.jtag | 26 +++ src/main/scala/jtag/JtagShifter.scala | 185 ++++++++++++++++ src/main/scala/jtag/JtagStateMachine.scala | 143 ++++++++++++ src/main/scala/jtag/JtagTap.scala | 241 +++++++++++++++++++++ src/main/scala/jtag/JtagUtils.scala | 22 ++ src/main/scala/jtag/Utils.scala | 82 +++++++ src/main/scala/jtag/package.scala | 13 ++ 7 files changed, 712 insertions(+) create mode 100644 LICENSE.jtag create mode 100644 src/main/scala/jtag/JtagShifter.scala create mode 100644 src/main/scala/jtag/JtagStateMachine.scala create mode 100644 src/main/scala/jtag/JtagTap.scala create mode 100644 src/main/scala/jtag/JtagUtils.scala create mode 100644 src/main/scala/jtag/Utils.scala create mode 100644 src/main/scala/jtag/package.scala diff --git a/LICENSE.jtag b/LICENSE.jtag new file mode 100644 index 00000000..42304239 --- /dev/null +++ b/LICENSE.jtag @@ -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. diff --git a/src/main/scala/jtag/JtagShifter.scala b/src/main/scala/jtag/JtagShifter.scala new file mode 100644 index 00000000..686818c1 --- /dev/null +++ b/src/main/scala/jtag/JtagShifter.scala @@ -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) +} diff --git a/src/main/scala/jtag/JtagStateMachine.scala b/src/main/scala/jtag/JtagStateMachine.scala new file mode 100644 index 00000000..da1606f3 --- /dev/null +++ b/src/main/scala/jtag/JtagStateMachine.scala @@ -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 +} diff --git a/src/main/scala/jtag/JtagTap.scala b/src/main/scala/jtag/JtagTap.scala new file mode 100644 index 00000000..53c1d1b1 --- /dev/null +++ b/src/main/scala/jtag/JtagTap.scala @@ -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 + } +} diff --git a/src/main/scala/jtag/JtagUtils.scala b/src/main/scala/jtag/JtagUtils.scala new file mode 100644 index 00000000..2bfb4606 --- /dev/null +++ b/src/main/scala/jtag/JtagUtils.scala @@ -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 +} diff --git a/src/main/scala/jtag/Utils.scala b/src/main/scala/jtag/Utils.scala new file mode 100644 index 00000000..f35a412a --- /dev/null +++ b/src/main/scala/jtag/Utils.scala @@ -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 + } +} diff --git a/src/main/scala/jtag/package.scala b/src/main/scala/jtag/package.scala new file mode 100644 index 00000000..12f47c01 --- /dev/null +++ b/src/main/scala/jtag/package.scala @@ -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) +}