1
0
Fork 0

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:
Megan Wachs 2017-03-26 18:03:21 -07:00 committed by GitHub
parent 0e2b780089
commit 8e6beb80be
7 changed files with 712 additions and 0 deletions

26
LICENSE.jtag Normal file
View 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.

View 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)
}

View 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
}

View 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
}
}

View 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
}

View 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
}
}

View 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)
}