2017-03-27 03:03:21 +02:00
// See LICENSE.jtag for license details.
2017-07-07 19:48:16 +02:00
package freechips.rocketchip.jtag
2017-03-27 03:03:21 +02:00
2017-10-31 23:33:41 +01:00
import scala.collection.SortedMap
2017-12-19 06:02:31 +01:00
// !!! See Issue #1160.
// import chisel3._
import Chisel._
import chisel3.core. { Input , Output }
2017-03-27 03:03:21 +02:00
import chisel3.util._
2018-01-17 22:59:05 +01:00
import chisel3.experimental.withReset
2017-10-02 20:05:45 +02:00
import freechips.rocketchip.config.Parameters
2017-03-27 03:03:21 +02:00
/* * JTAG signals, viewed from the master side
*/
class JTAGIO ( hasTRSTn : Boolean = false ) extends Bundle {
val TRSTn = if ( hasTRSTn ) Some ( Output ( Bool ( ) ) ) else None
2017-07-07 23:16:39 +02:00
val TCK = Output ( Clock ( ) )
2017-03-27 03:03:21 +02:00
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.
*/
2017-04-14 01:12:22 +02:00
class JtagBlockIO ( irLength : Int , hasIdcode : Boolean = true ) extends Bundle {
2017-03-27 03:03:21 +02:00
val jtag = Flipped ( new JTAGIO ( ) )
val control = new JtagControl
val output = new JtagOutput ( irLength )
2017-04-14 01:12:22 +02:00
val idcode = if ( hasIdcode ) Some ( Input ( new JTAGIdcodeBundle ( ) ) ) else None
2017-03-27 03:03:21 +02:00
2017-04-14 01:12:22 +02:00
override def cloneType = new JtagBlockIO ( irLength , hasIdcode ) . asInstanceOf [ this . type ]
2017-03-27 03:03:21 +02:00
}
/* * Internal controller block IO with data shift outputs.
*/
2017-04-14 01:12:22 +02:00
class JtagControllerIO ( irLength : Int ) extends JtagBlockIO ( irLength , false ) {
2017-03-27 03:03:21 +02:00
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
*/
2017-10-02 20:05:45 +02:00
class JtagTapController ( irLength : Int , initialInstruction : BigInt ) ( implicit val p : Parameters ) extends Module {
2017-03-27 03:03:21 +02:00
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 ( ) )
2018-01-17 22:59:05 +01:00
io . jtag . TDO . data : = NegEdgeReg ( 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 : = NegEdgeReg ( clock , tdo_driven , name = Some ( "tdoeReg" ) )
2017-03-27 03:03:21 +02:00
//
// JTAG state machine
//
2018-01-17 22:59:05 +01:00
val currState = Wire ( JtagState . State . chiselType )
2017-03-27 03:03:21 +02:00
// 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." )
2018-01-17 22:59:05 +01:00
withReset ( io . control . jtag_reset ) {
val stateMachine = Module ( new JtagStateMachine )
stateMachine . suggestName ( "stateMachine" )
stateMachine . io . tms : = io . jtag . TMS
currState : = stateMachine . io . currState
io . output . state : = stateMachine . io . currState
}
2017-03-27 03:03:21 +02:00
//
// 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 ) )
2018-01-17 22:59:05 +01:00
val activeInstruction = NegEdgeReg ( clock , nextActiveInstruction , updateInstruction , name = Some ( "irReg" ) ) // 7.2.1d active instruction output latches on TCK falling edge
2017-03-27 03:03:21 +02:00
2017-09-06 18:49:47 +02:00
when ( reset . toBool ) {
2017-03-27 03:03:21 +02:00
nextActiveInstruction : = initialInstruction . U ( irLength . W )
updateInstruction : = true . B
} . elsewhen ( currState === JtagState . UpdateIR . U ) {
nextActiveInstruction : = irChain . io . update . bits
updateInstruction : = true . B
} . otherwise {
2017-12-19 06:02:31 +01:00
//!!! Needed when using chisel3._ (See #1160)
// nextActiveInstruction := DontCare
2017-03-27 03:03:21 +02:00
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 {
2017-12-19 06:02:31 +01:00
//!!! Needed when using chisel3._ (See #1160)
//tdo := DontCare
2017-03-27 03:03:21 +02:00
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
2017-04-14 01:12:22 +02:00
* @param idcode optional idcode instruction . idcode UInt will come from outside this core .
2017-03-27 03:03:21 +02:00
*
* @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 . 1 b TMS must appear high when undriven
* - 4.3 . 1 c ( rec ) minimize load presented by TMS
* - 4.4 . 1 b TDI must appear high when undriven
* - 4.5 . 1 b TDO must be inactive except when shifting data ( undriven ? 6.1 . 2 )
* - 6.1 . 3.1 b 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
*/
2017-10-02 20:05:45 +02:00
def apply ( irLength : Int , instructions : Map [ BigInt , Chain ] , icode : Option [ BigInt ] = None ) ( implicit p : Parameters ) : JtagBlockIO = {
2017-04-14 01:12:22 +02:00
val internalIo = Wire ( new JtagBlockIO ( irLength , icode . isDefined ) )
2017-03-27 03:03:21 +02:00
// Create IDCODE chain if needed
2017-04-14 01:12:22 +02:00
val allInstructions = icode match {
case ( Some ( icode ) ) => {
2017-03-27 03:03:21 +02:00
require ( ! ( instructions contains icode ) , "instructions may not contain IDCODE" )
2017-04-14 01:12:22 +02:00
val idcodeChain = Module ( CaptureChain ( new JTAGIdcodeBundle ( ) ) )
idcodeChain . suggestName ( "idcodeChain" )
val i = internalIo . idcode . get . asUInt ( )
assert ( i % 2. U === 1. U , "LSB must be set in IDCODE, see 12.1.1d" )
assert ( ( ( i >> 1 ) & ( ( 1. U << 11 ) - 1. U ) ) =/= JtagIdcode . dummyMfrId . U ,
"IDCODE must not have 0b00001111111 as manufacturer identity, see 12.2.1b" )
idcodeChain . io . capture . bits : = internalIo . idcode . get
2017-03-27 03:03:21 +02:00
instructions + ( icode -> idcodeChain )
2017-04-14 01:12:22 +02:00
}
case None => instructions
2017-03-27 03:03:21 +02:00
}
val bypassIcode = ( BigInt ( 1 ) << irLength ) - 1 // required BYPASS instruction
2017-04-14 01:12:22 +02:00
val initialInstruction = icode . getOrElse ( bypassIcode ) // 7.2.1e load IDCODE or BYPASS instruction after entry into TestLogicReset
2017-03-27 03:03:21 +02:00
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?" )
2017-10-31 23:33:41 +01:00
// Need to ensure that this mapping is ordered to produce deterministic verilog,
// and neither Map nor groupBy are deterministic.
// Therefore, we first sort by IDCODE, then sort the groups by the first IDCODE in each group.
val chainToIcode = ( SortedMap ( allInstructions . toList : _ * ) . groupBy { case ( icode , chain ) => chain } map {
2017-03-27 03:03:21 +02:00
case ( chain , icodeToChain ) => chain -> icodeToChain . keys
2017-10-31 23:33:41 +01:00
} ) . toList . sortBy ( _ . _2 . head )
2017-03-27 03:03:21 +02:00
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 )
controllerInternal . io . jtag <> internalIo . jtag
2017-12-19 06:02:31 +01:00
internalIo . control <> controllerInternal . io . control
internalIo . output <> controllerInternal . io . output
2017-03-27 03:03:21 +02:00
internalIo
}
2017-04-14 01:12:22 +02:00
2017-03-27 03:03:21 +02:00
}