diff --git a/README.md b/README.md
index dd3c247a..9a0eb390 100644
--- a/README.md
+++ b/README.md
@@ -9,6 +9,7 @@ BAR projects generally use these components with [rocket-chip](https://github.co
* SERDES <-> TileLink
* Custom serial interface for debug with simulator interface
* TileLink splitter, switcher
+* JTAG-to-memory-mapped bus (AXI4 & TileLink) master bridge
## Usage
Testchipip can be used in your project in one of two ways:
diff --git a/src/main/scala/Unittests.scala b/src/main/scala/Unittests.scala
index 3af97b52..76be0a21 100644
--- a/src/main/scala/Unittests.scala
+++ b/src/main/scala/Unittests.scala
@@ -2,6 +2,7 @@ package testchipip
import chisel3._
import chisel3.util._
+import chisel3.experimental._
import freechips.rocketchip.config.Parameters
import freechips.rocketchip.diplomacy._
import freechips.rocketchip.devices.tilelink.{DevNullParams, TLTestRAM, TLROM, TLError}
@@ -495,6 +496,57 @@ class NetworkXbarTest extends UnitTest {
io.finished := finished.reduce(_ && _)
}
+class Jtag2mmTest(implicit p: Parameters) extends LazyModule()(Parameters.empty) {
+
+ val beatBytes = 4
+ val irLength = 4
+ val initialInstruction = BigInt("0", 4)
+ val burstMaxNum = 8
+ val numOfTransfers = 10
+
+ val jtag2mm = LazyModule(new JTAGToMasterTL[TLClientPortParameters, TLManagerPortParameters, TLEdgeOut, TLEdgeIn, TLBundle](irLength, initialInstruction, beatBytes, burstMaxNum) {
+ override implicit val p: Parameters = Parameters.empty
+
+ def makeIO2(): TopModuleIO = {
+ val io2: TopModuleIO = IO(io.cloneType)
+ io2.suggestName("ioJTAG")
+ io2 <> io
+ io2
+ }
+
+ val ioJTAG = InModuleBody { makeIO2() }
+ })
+
+ val testram = LazyModule(new TLTestRAM(
+ address = AddressSet(0, 0xffff),
+ beatBytes = beatBytes))
+
+ lazy val fuzzer = Module(new JtagFuzzer(irLength, beatBytes, numOfTransfers))
+
+ testram.node := jtag2mm.node.get
+
+ InModuleBody {
+ jtag2mm.ioJTAG.jtag.TCK := fuzzer.io.TCK
+ jtag2mm.ioJTAG.jtag.TMS := fuzzer.io.TMS
+ jtag2mm.ioJTAG.jtag.TDI := fuzzer.io.TDI
+ fuzzer.io.TDO := jtag2mm.ioJTAG.jtag.TDO.data
+ }
+
+ lazy val module = new LazyModuleImp(this) {
+ val io = IO(new Bundle { val finished = Output(Bool()) })
+ io.finished := fuzzer.io.finished
+ }
+}
+
+class JTAG2mmTestWrapper(implicit p: Parameters) extends UnitTest {
+ val testReset = RegInit(true.B)
+ val test = Module(LazyModule(new Jtag2mmTest).module)
+ io.finished := test.io.finished
+ test.reset := testReset
+
+ when (testReset && io.start) { testReset := false.B }
+}
+
object TestChipUnitTests {
def apply(implicit p: Parameters): Seq[UnitTest] =
Seq(
@@ -506,6 +558,7 @@ object TestChipUnitTests {
Module(new SwitchTestWrapper),
Module(new StreamWidthAdapterTest),
Module(new NetworkXbarTest),
- Module(new TLRingNetworkTestWrapper)
+ Module(new TLRingNetworkTestWrapper),
+ Module(new JTAG2mmTestWrapper)
)
}
diff --git a/src/main/scala/jtag2mm/JtagFuzzer.scala b/src/main/scala/jtag2mm/JtagFuzzer.scala
new file mode 100644
index 00000000..a78af1df
--- /dev/null
+++ b/src/main/scala/jtag2mm/JtagFuzzer.scala
@@ -0,0 +1,359 @@
+package testchipip
+
+import chisel3._
+import chisel3.util._
+import chisel3.util.random.LFSR
+import chisel3.experimental._
+
+
+class InvertedJtagIO extends Bundle {
+ // TRST (4.6) is optional and not currently implemented.
+ val TCK = Output(Bool())
+ val TMS = Output(Bool())
+ val TDI = Output(Bool())
+ val TDO = Input(new Bool())
+}
+
+class InvertedJtagIOPlus extends InvertedJtagIO {
+ //val begin = Input(Bool())
+ val finished = Output(Bool())
+}
+
+class JtagFuzzer(irLength: Int, beatBytes: Int, numOfTransfers: Int) extends Module {
+
+
+ val io = IO(new InvertedJtagIOPlus)
+
+ object State extends ChiselEnum {
+ val sIdle, sTCK, sTMS, sTCKandTMS, sNone, sDataTCK, sDataTMS, sDataTCKandTMS, sDataNone = Value
+ }
+
+ val lfsrAddrReg = RegInit(UInt(32.W), 0.U)
+ lfsrAddrReg := LFSR(30) << 2
+ val lfsrDataReg = RegInit(UInt(32.W), 0.U)
+ lfsrDataReg := LFSR(32)
+
+ val dataBitCounter = RegInit(UInt(8.W), 0.U)
+
+ val idleCycleCounter = RegInit(UInt(4.W), 0.U)
+
+ val transferCounter = RegInit(UInt(4.W), 0.U)
+ //when(stateCounter === 187.U) {transferCounter := transferCounter + 1.U}
+
+ io.finished := Mux(transferCounter >= numOfTransfers.U, true.B, false.B)
+
+ val state = RegInit(State.sIdle)
+ val stateCounter = RegInit(UInt(10.W), 0.U)
+ when(state =/= RegNext(state)) {
+ stateCounter := stateCounter + 1.U
+ }
+
+ io.TCK := DontCare
+ io.TMS := DontCare
+ io.TDI := DontCare
+
+ switch(state) {
+ is(State.sIdle) {
+ //when(io.begin) {
+ when((idleCycleCounter === 10.U) && (transferCounter < numOfTransfers.U)){
+ state := State.sTMS
+ }
+ io.TCK := false.B
+ io.TMS := false.B
+ io.TDI := false.B
+ idleCycleCounter := idleCycleCounter + 1.U
+ stateCounter := 0.U
+ dataBitCounter := 0.U
+ }
+ is(State.sTMS) {
+ // jtag init
+ when((stateCounter === 0.U) || (stateCounter === 2.U) || (stateCounter === 4.U) || (stateCounter === 6.U) || (stateCounter === 8.U)) {
+ state := State.sTCKandTMS
+ }
+ // first instruction
+ .elsewhen((stateCounter === 12.U) || (stateCounter === 14.U) || (stateCounter === 28.U)){
+ state := State.sTCKandTMS
+ }
+ // first data
+ .elsewhen((stateCounter === 32.U) || (stateCounter === 70.U)){
+ state := State.sTCKandTMS
+ }
+ // second instruction
+ .elsewhen((stateCounter === 74.U) || (stateCounter === 76.U) || (stateCounter === 90.U)){
+ state := State.sTCKandTMS
+ }
+ // second data
+ .elsewhen((stateCounter === 94.U) || (stateCounter === 164.U)){
+ state := State.sTCKandTMS
+ }
+ // third instruction
+ .elsewhen((stateCounter === 168.U) || (stateCounter === 170.U) || (stateCounter === 184.U)){
+ state := State.sTCKandTMS
+ }
+ io.TCK := false.B
+ io.TMS := true.B
+ io.TDI := false.B
+ dataBitCounter := 0.U
+ idleCycleCounter := 0.U
+ }
+ is(State.sTCK) {
+ // endings
+ when((stateCounter === 11.U) || (stateCounter === 31.U) || (stateCounter === 73.U) || (stateCounter === 93.U) || (stateCounter === 167.U)) {
+ state := State.sTMS
+ }
+ // first instruction
+ .elsewhen(stateCounter === 17.U){
+ state := State.sNone
+ } .elsewhen(stateCounter === 19.U) {
+ state := State.sDataNone
+ }
+ // first data
+ .elsewhen(stateCounter === 35.U){
+ state := State.sNone
+ } .elsewhen(stateCounter === 37.U) {
+ state := State.sDataNone
+ }
+ // second instruction
+ .elsewhen(stateCounter === 79.U){
+ state := State.sNone
+ } .elsewhen(stateCounter === 81.U) {
+ state := State.sDataNone
+ }
+ // second data
+ .elsewhen(stateCounter === 97.U){
+ state := State.sNone
+ } .elsewhen(stateCounter === 99.U) {
+ state := State.sDataNone
+ }
+ // third instruction
+ .elsewhen(stateCounter === 173.U){
+ state := State.sNone
+ } .elsewhen(stateCounter === 175.U) {
+ state := State.sDataNone
+ }
+ // the end
+ .elsewhen(stateCounter === 187.U){
+ state := State.sIdle
+ transferCounter := transferCounter + 1.U
+ }
+ io.TCK := true.B
+ io.TMS := false.B
+ io.TDI := false.B
+ dataBitCounter := 0.U
+ }
+ is(State.sNone) {
+ // jtag init
+ when(stateCounter === 10.U) {
+ state := State.sTCK
+ }
+ // first instruction
+ .elsewhen((stateCounter === 16.U) || (stateCounter === 18.U) || (stateCounter === 30.U)){
+ state := State.sTCK
+ }
+ // first data
+ .elsewhen((stateCounter === 34.U) || (stateCounter === 36.U) || (stateCounter === 72.U)){
+ state := State.sTCK
+ }
+ // second instruction
+ .elsewhen((stateCounter === 78.U) || (stateCounter === 80.U) || (stateCounter === 92.U)){
+ state := State.sTCK
+ }
+ // second data
+ .elsewhen((stateCounter === 96.U) || (stateCounter === 98.U) || (stateCounter === 166.U)){
+ state := State.sTCK
+ }
+ // third instruction
+ .elsewhen((stateCounter === 172.U) || (stateCounter === 174.U) || (stateCounter === 186.U)){
+ state := State.sTCK
+ }
+ io.TCK := false.B
+ io.TMS := false.B
+ io.TDI := false.B
+ dataBitCounter := 0.U
+ }
+ is(State.sTCKandTMS) {
+ // jtag init
+ when((stateCounter === 1.U) || (stateCounter === 3.U) || (stateCounter === 5.U) || (stateCounter === 7.U)) {
+ state := State.sTMS
+ } .elsewhen (stateCounter === 9.U) {
+ state := State.sNone
+ }
+ // first instruction
+ .elsewhen((stateCounter === 13.U)){
+ state := State.sTMS
+ } .elsewhen((stateCounter === 15.U) || (stateCounter === 29.U)){
+ state := State.sNone
+ }
+ //first data
+ .elsewhen((stateCounter === 33.U) || (stateCounter === 71.U)){
+ state := State.sNone
+ }
+ // second instruction
+ .elsewhen((stateCounter === 75.U)){
+ state := State.sTMS
+ } .elsewhen((stateCounter === 77.U) || (stateCounter === 91.U)){
+ state := State.sNone
+ }
+ //second data
+ .elsewhen((stateCounter === 95.U) || (stateCounter === 165.U)){
+ state := State.sNone
+ }
+ // third instruction
+ .elsewhen((stateCounter === 169.U)){
+ state := State.sTMS
+ } .elsewhen((stateCounter === 171.U) || (stateCounter === 185.U)){
+ state := State.sNone
+ }
+ io.TCK := true.B
+ io.TMS := true.B
+ io.TDI := false.B
+ dataBitCounter := 0.U
+ }
+ is(State.sDataNone) {
+ // first instruction
+ when((stateCounter === 20.U) || (stateCounter === 22.U) || (stateCounter === 24.U)){
+ state := State.sDataTCK
+ }
+ // first data
+ .elsewhen((stateCounter >= 38.U) && (stateCounter <= 66.U)){
+ state := State.sDataTCK
+ }
+ // second instruction
+ .elsewhen((stateCounter === 82.U) || (stateCounter === 84.U) || (stateCounter === 86.U)){
+ state := State.sDataTCK
+ }
+ // second data
+ .elsewhen((stateCounter >= 100.U) && (stateCounter <= 160.U)){
+ state := State.sDataTCK
+ }
+ // third instruction
+ .elsewhen((stateCounter === 176.U) || (stateCounter === 178.U) || (stateCounter === 180.U)){
+ state := State.sDataTCK
+ }
+ when(((stateCounter >= 38.U) && (stateCounter <= 66.U)) || ((stateCounter >= 100.U) && (stateCounter <= 160.U))) {
+ dataBitCounter := dataBitCounter + 1.U
+ }
+ io.TCK := false.B
+ io.TMS := false.B
+ //io.TDI := Mux((stateCounter === 20.U) || (stateCounter === 22.U), true.B, false.B)
+ when((stateCounter === 22.U) || (stateCounter === 82.U) || (stateCounter === 84.U) || (stateCounter === 176.U)){
+ io.TDI := true.B
+ } .elsewhen((stateCounter >= 38.U) && (stateCounter <= 66.U)){
+ io.TDI := lfsrAddrReg(dataBitCounter)
+ } .elsewhen((stateCounter >= 100.U) && (stateCounter <= 160.U)){
+ io.TDI := lfsrDataReg(dataBitCounter)
+ } .otherwise {
+ io.TDI := false.B
+ }
+ }
+ is(State.sDataTCK) {
+ // first instruction
+ when((stateCounter === 21.U) || (stateCounter === 23.U)){
+ state := State.sDataNone
+ } .elsewhen(stateCounter === 25.U){
+ state := State.sDataTMS
+ }
+ // first data
+ .elsewhen((stateCounter >= 39.U) && (stateCounter <= 65.U)){
+ state := State.sDataNone
+ } .elsewhen(stateCounter === 67.U){
+ state := State.sDataTMS
+ }
+ // second instruction
+ .elsewhen((stateCounter === 83.U) || (stateCounter === 85.U)){
+ state := State.sDataNone
+ } .elsewhen(stateCounter === 87.U){
+ state := State.sDataTMS
+ }
+ // second data
+ .elsewhen((stateCounter >= 101.U) && (stateCounter <= 159.U)){
+ state := State.sDataNone
+ } .elsewhen(stateCounter === 161.U){
+ state := State.sDataTMS
+ }
+ // third instruction
+ .elsewhen((stateCounter === 177.U) || (stateCounter === 179.U)){
+ state := State.sDataNone
+ } .elsewhen(stateCounter === 181.U){
+ state := State.sDataTMS
+ }
+ io.TCK := true.B
+ io.TMS := false.B
+ //io.TDI := Mux((stateCounter === 21.U) || (stateCounter === 23.U), true.B, false.B)
+ when((stateCounter === 23.U) || (stateCounter === 83.U) || (stateCounter === 85.U) || (stateCounter === 177.U)){
+ io.TDI := true.B
+ } .elsewhen((stateCounter >= 39.U) && (stateCounter <= 67.U)){
+ io.TDI := lfsrAddrReg(dataBitCounter-1.U)
+ } .elsewhen((stateCounter >= 101.U) && (stateCounter <= 161.U)){
+ io.TDI := lfsrDataReg(dataBitCounter)
+ } .otherwise {
+ io.TDI := false.B
+ }
+ }
+ is(State.sDataTMS) {
+ // first instruction
+ when((stateCounter === 26.U)){
+ state := State.sDataTCKandTMS
+ }
+ // first data
+ .elsewhen((stateCounter === 68.U)){
+ state := State.sDataTCKandTMS
+ }
+ // second instruction
+ .elsewhen((stateCounter === 88.U)){
+ state := State.sDataTCKandTMS
+ }
+ // second data
+ .elsewhen((stateCounter === 162.U)){
+ state := State.sDataTCKandTMS
+ }
+ // third instruction
+ .elsewhen((stateCounter === 182.U)){
+ state := State.sDataTCKandTMS
+ }
+ io.TCK := false.B
+ io.TMS := true.B
+ //io.TDI := false.B
+ when(stateCounter === 68.U) {
+ io.TDI := lfsrAddrReg(31)
+ } .elsewhen(stateCounter === 162.U) {
+ io.TDI := lfsrDataReg(31)
+ } .otherwise {
+ io.TDI := false.B
+ }
+ }
+ is(State.sDataTCKandTMS) {
+ // first instruction
+ when((stateCounter === 27.U)){
+ state := State.sTMS
+ }
+ // first data
+ .elsewhen((stateCounter === 69.U)){
+ state := State.sTMS
+ }
+ // second instruction
+ .elsewhen((stateCounter === 89.U)){
+ state := State.sTMS
+ }
+ // second data
+ .elsewhen((stateCounter === 163.U)){
+ state := State.sTMS
+ }
+ // third instruction
+ .elsewhen((stateCounter === 183.U)){
+ state := State.sTMS
+ }
+ io.TCK := true.B
+ io.TMS := true.B
+ //io.TDI := false.B
+ when(stateCounter === 69.U) {
+ io.TDI := lfsrAddrReg(31)
+ } .elsewhen(stateCounter === 163.U) {
+ io.TDI := lfsrDataReg(31)
+ } .otherwise {
+ io.TDI := false.B
+ }
+ }
+ }
+
+}
diff --git a/src/main/scala/jtag2mm/JtagToMaster.scala b/src/main/scala/jtag2mm/JtagToMaster.scala
new file mode 100644
index 00000000..ae66bbbe
--- /dev/null
+++ b/src/main/scala/jtag2mm/JtagToMaster.scala
@@ -0,0 +1,811 @@
+package testchipip
+
+import chisel3._
+import chisel3.util._
+import chisel3.experimental._
+import chisel3.experimental.{withClockAndReset}
+
+import freechips.rocketchip.amba.axi4._
+import freechips.rocketchip.config._
+import freechips.rocketchip.diplomacy._
+import freechips.rocketchip.tilelink._
+
+
+class JtagToMasterControllerIO(irLength: Int, beatBytes: Int) extends JtagBlockIO(irLength) {
+ val dataOut = Output(UInt((beatBytes * 8).W))
+ val dataIn = Input(UInt((beatBytes * 8).W))
+ val validIn = Input(Bool())
+ val receivedIn = Output(Bool())
+ val receivedEnd = Output(Bool())
+}
+
+class TopModuleIO extends Bundle {
+ val jtag = new JtagIO
+ val asyncReset = Input(Bool())
+}
+
+class JtagController(irLength: Int, initialInstruction: BigInt, beatBytes: Int) extends Module {
+ require(irLength >= 3)
+
+ val io = IO(new JtagToMasterControllerIO(irLength, beatBytes))
+
+ val tdo = Wire(Bool())
+ tdo := DontCare
+ val tdo_driven = Wire(Bool())
+ tdo_driven := DontCare
+ io.jtag.TDO.data := NegativeEdgeLatch(clock, tdo)
+ io.jtag.TDO.driven := NegativeEdgeLatch(clock, tdo_driven)
+
+ val stateMachine = Module(new JtagStateMachine)
+ stateMachine.io.tms := io.jtag.TMS
+ val currState = stateMachine.io.currState
+ io.output.state := stateMachine.io.currState
+ stateMachine.io.asyncReset := io.control.fsmAsyncReset
+
+ val irShifter = Module(CaptureUpdateChain(UInt(irLength.W)))
+ irShifter.io.chainIn.shift := currState === JtagState.ShiftIR.U
+ irShifter.io.chainIn.data := io.jtag.TDI
+ irShifter.io.chainIn.capture := currState === JtagState.CaptureIR.U
+ irShifter.io.chainIn.update := currState === JtagState.UpdateIR.U
+ irShifter.io.capture.bits := "b01".U
+
+ val updateInstruction = Wire(Bool())
+ updateInstruction := DontCare
+
+ val nextActiveInstruction = Wire(UInt(irLength.W))
+ nextActiveInstruction := DontCare
+
+ val activeInstruction = NegativeEdgeLatch(clock, nextActiveInstruction, updateInstruction)
+
+ when(reset.asBool) {
+ nextActiveInstruction := initialInstruction.U(irLength.W)
+ updateInstruction := true.B
+ }.elsewhen(currState === JtagState.UpdateIR.U) {
+ nextActiveInstruction := irShifter.io.update.bits
+ updateInstruction := true.B
+ }.otherwise {
+ updateInstruction := false.B
+ }
+ io.output.instruction := activeInstruction
+
+ io.output.reset := currState === JtagState.TestLogicReset.U
+
+ val drShifter = Module(CaptureUpdateChain(UInt((beatBytes * 8).W)))
+ drShifter.io.chainIn.shift := currState === JtagState.ShiftDR.U
+ drShifter.io.chainIn.data := io.jtag.TDI
+ drShifter.io.chainIn.capture := currState === JtagState.CaptureDR.U
+ drShifter.io.chainIn.update := currState === JtagState.UpdateDR.U
+ drShifter.io.capture.bits := "b01".U
+
+ val updateData = Wire(Bool())
+ updateData := DontCare
+ val newData = Wire(UInt((beatBytes * 8).W))
+ newData := DontCare
+ val currentData = NegativeEdgeLatch(clock, newData, updateData)
+
+ when(reset.asBool) {
+ newData := 0.U
+ updateData := false.B
+ }.elsewhen(currState === JtagState.UpdateDR.U) {
+ newData := drShifter.io.update.bits
+ updateData := true.B
+ }.otherwise {
+ updateData := false.B
+ }
+
+ io.dataOut := currentData
+
+ val dataInReg = RegInit(UInt((beatBytes * 8).W), 0.U)
+ val indicator = RegInit(Bool(), false.B)
+ when(io.validIn) { dataInReg := io.dataIn }
+ val counterTDO = RegInit(UInt(7.W), 0.U)
+ when(RegNext(io.validIn)) { io.receivedIn := true.B }.otherwise { io.receivedIn := false.B }
+ when(RegNext(RegNext(indicator)) && !RegNext(indicator)) { io.receivedEnd := true.B }.otherwise {
+ io.receivedEnd := false.B
+ }
+ when(io.validIn && !RegNext(io.validIn)) { indicator := true.B }.elsewhen(counterTDO === (8 * beatBytes - 1).U) {
+ indicator := false.B
+ }
+ when(indicator) { counterTDO := counterTDO + 1.U }.otherwise { counterTDO := 0.U }
+
+ when(indicator) {
+ tdo := dataInReg(counterTDO)
+ tdo_driven := true.B
+ }.otherwise {
+ tdo := false.B
+ tdo_driven := false.B
+ }
+
+}
+
+object StateTL extends ChiselEnum {
+ val sIdle, sSetDataA, sSetReadAddress, sDataForward, sSetDataABurst, sIncrementWriteBurst, sSetReadAddressBurst,
+ sIncrementReadBurst, sDataForwardBurst, sDataForward2Burst = Value
+}
+
+class JTAGToMasterTL[D, U, E, O, B <: Data](irLength: Int, initialInstruction: BigInt, beatBytes: Int, burstMaxNum: Int)
+ extends LazyModule()(Parameters.empty) {
+
+ require(burstMaxNum <= 128)
+
+ val node = Some(
+ TLClientNode(
+ Seq(TLClientPortParameters(Seq(TLClientParameters(name = "JTAGToMasterOut", sourceId = IdRange(0, 4)))))
+ )
+ )
+
+ lazy val io = Wire(new TopModuleIO)
+
+ lazy val module = new LazyModuleImp(this) {
+
+ val (tl, edge) = node.get.out(0)
+
+ val jtagClk = Wire(Clock())
+ jtagClk := io.jtag.TCK.asClock
+
+ val syncReset = Wire(Bool())
+ val controller = withClockAndReset(jtagClk, syncReset) {
+ Module(new JtagController(irLength, initialInstruction, beatBytes))
+ }
+ syncReset := controller.io.output.reset
+ controller.io.jtag.TCK := io.jtag.TCK
+ controller.io.jtag.TMS := io.jtag.TMS
+ controller.io.jtag.TDI := io.jtag.TDI
+ io.jtag.TDO := controller.io.jtag.TDO
+ controller.io.control.fsmAsyncReset := io.asyncReset
+ controller.io.validIn := DontCare
+ controller.io.dataIn := DontCare
+
+ val state = RegInit(StateTL.sIdle)
+
+ val currentInstruction = RegInit(UInt(irLength.W), initialInstruction.U)
+ currentInstruction := controller.io.output.instruction
+
+ val dataValue = RegInit(UInt((beatBytes * 8).W), 0.U)
+ val addressValue = RegInit(UInt((beatBytes * 8).W), 0.U)
+
+ val burstTotalNumber = RegInit(UInt(8.W), 0.U)
+ val burstCurrentNumber = RegInit(UInt(8.W), 0.U)
+ val dataValueBurst = RegInit(VecInit(Seq.fill(burstMaxNum)(0.U((beatBytes * 8).W))))
+
+ when(currentInstruction === "b0011".U) {
+ dataValue := controller.io.dataOut
+ }
+
+ when(currentInstruction === "b0010".U) {
+ addressValue := controller.io.dataOut // >> (beatBytes * 4)
+ }
+
+ when(currentInstruction === "b1000".U) {
+ burstTotalNumber := controller.io.dataOut >> ((beatBytes - 1) * 8)
+ }
+
+ when(currentInstruction === "b1010".U) {
+ burstCurrentNumber := controller.io.dataOut >> ((beatBytes - 1) * 8)
+ }
+
+ when(currentInstruction === "b1011".U) {
+ dataValueBurst(burstCurrentNumber) := controller.io.dataOut
+ }
+
+ val shouldWrite = RegInit(Bool(), false.B)
+ when((currentInstruction === "b0001".U) && (RegNext(currentInstruction) =/= "b0001".U)) {
+ shouldWrite := true.B
+ }.elsewhen(state === StateTL.sSetDataA) {
+ shouldWrite := false.B
+ }
+
+ val shouldRead = RegInit(Bool(), false.B)
+ when((currentInstruction === "b0100".U) && (RegNext(currentInstruction) =/= "b0100".U)) {
+ shouldRead := true.B
+ }.elsewhen(state === StateTL.sSetReadAddress) {
+ shouldRead := false.B
+ }
+
+ val shouldWriteBurst = RegInit(Bool(), false.B)
+ when(
+ (currentInstruction === "b1001".U) && (RegNext(currentInstruction) =/= "b1001".U) && (burstTotalNumber > 0.U)
+ ) {
+ shouldWriteBurst := true.B
+ }.elsewhen(state === StateTL.sSetDataABurst) {
+ shouldWriteBurst := false.B
+ }
+
+ val shouldReadBurst = RegInit(Bool(), false.B)
+ when(
+ (currentInstruction === "b1100".U) && (RegNext(currentInstruction) =/= "b1100".U) && (burstTotalNumber > 0.U)
+ ) {
+ shouldReadBurst := true.B
+ }.elsewhen(state === StateTL.sSetReadAddressBurst) {
+ shouldReadBurst := false.B
+ }
+
+ val readData = RegInit(UInt((beatBytes * 8).W), 0.U)
+ val received = RegInit(Bool(), false.B)
+
+ val burstCounter = RegInit(UInt(8.W), 0.U)
+
+ val size = if (beatBytes == 4) 2 else 3
+
+ switch(state) {
+ is(StateTL.sIdle) {
+ when(shouldWrite) {
+ state := StateTL.sSetDataA
+ }.elsewhen(shouldRead) {
+ state := StateTL.sSetReadAddress
+ }.elsewhen(shouldWriteBurst) {
+ state := StateTL.sSetDataABurst
+ }.elsewhen(shouldReadBurst) {
+ state := StateTL.sSetReadAddressBurst
+ }
+
+ tl.d.ready := false.B
+ tl.a.valid := false.B
+
+ tl.a.bits.opcode := 0.U
+ tl.a.bits.param := 0.U
+ tl.a.bits.size := size.U
+ tl.a.bits.source := 0.U
+ tl.a.bits.address := 0.U
+ tl.a.bits.mask := 255.U
+ tl.a.bits.data := 0.U
+
+ controller.io.validIn := false.B
+ controller.io.dataIn := 0.U
+
+ burstCounter := 0.U
+ received := false.B
+ }
+ is(StateTL.sSetDataA) {
+ when(tl.d.valid) {
+ state := StateTL.sIdle
+ }
+
+ tl.d.ready := true.B
+ tl.a.valid := true.B
+
+ tl.a.bits.opcode := 0.U
+ tl.a.bits.param := 0.U
+ tl.a.bits.size := size.U
+ tl.a.bits.source := 0.U
+ tl.a.bits.address := addressValue
+ tl.a.bits.mask := 255.U
+ tl.a.bits.data := dataValue
+
+ controller.io.validIn := false.B
+ controller.io.dataIn := 0.U
+ }
+ is(StateTL.sSetReadAddress) {
+ when(tl.d.valid) {
+ state := StateTL.sDataForward
+ }
+
+ tl.d.ready := true.B
+ tl.a.valid := true.B
+
+ tl.a.bits.opcode := 4.U
+ tl.a.bits.param := 0.U
+ tl.a.bits.size := size.U
+ tl.a.bits.source := 0.U
+ tl.a.bits.address := addressValue
+ tl.a.bits.mask := 255.U
+ tl.a.bits.data := 0.U
+
+ readData := tl.d.bits.data
+
+ controller.io.validIn := false.B
+ controller.io.dataIn := 0.U
+ }
+ is(StateTL.sDataForward) {
+ when(controller.io.receivedIn) {
+ state := StateTL.sIdle
+ }
+
+ tl.d.ready := false.B
+ tl.a.valid := false.B
+
+ tl.a.bits.opcode := 0.U
+ tl.a.bits.param := 0.U
+ tl.a.bits.size := size.U
+ tl.a.bits.source := 0.U
+ tl.a.bits.address := 0.U
+ tl.a.bits.mask := 255.U
+ tl.a.bits.data := 0.U
+
+ controller.io.validIn := true.B
+ controller.io.dataIn := readData
+ }
+
+ is(StateTL.sSetDataABurst) {
+ when(tl.d.valid) {
+ state := StateTL.sIncrementWriteBurst
+ }
+
+ tl.d.ready := true.B
+ tl.a.valid := true.B
+
+ tl.a.bits.opcode := 0.U
+ tl.a.bits.param := 0.U
+ tl.a.bits.size := size.U
+ tl.a.bits.source := 0.U
+ tl.a.bits.address := addressValue + beatBytes.U * burstCounter
+ tl.a.bits.mask := 255.U
+ tl.a.bits.data := dataValueBurst(burstCounter)
+
+ controller.io.validIn := false.B
+ controller.io.dataIn := 0.U
+ }
+ is(StateTL.sIncrementWriteBurst) {
+ when(burstCounter < (burstTotalNumber - 1.U)) {
+ state := StateTL.sSetDataABurst
+ }.otherwise {
+ state := StateTL.sIdle
+ }
+ tl.d.ready := false.B
+ tl.a.valid := false.B
+
+ tl.a.bits.opcode := 0.U
+ tl.a.bits.param := 0.U
+ tl.a.bits.size := size.U
+ tl.a.bits.source := 0.U
+ tl.a.bits.address := 0.U
+ tl.a.bits.mask := 255.U
+ tl.a.bits.data := 0.U
+
+ controller.io.validIn := false.B
+ controller.io.dataIn := 0.U
+
+ burstCounter := burstCounter + 1.U
+ }
+ is(StateTL.sSetReadAddressBurst) {
+ when(tl.d.valid) {
+ state := StateTL.sIncrementReadBurst
+ }
+
+ tl.d.ready := true.B
+ tl.a.valid := true.B
+
+ tl.a.bits.opcode := 4.U
+ tl.a.bits.param := 0.U
+ tl.a.bits.size := size.U
+ tl.a.bits.source := 0.U
+ tl.a.bits.address := addressValue + beatBytes.U * burstCounter
+ tl.a.bits.mask := 255.U
+ tl.a.bits.data := 0.U
+
+ readData := tl.d.bits.data
+ received := false.B
+
+ controller.io.validIn := false.B
+ controller.io.dataIn := 0.U
+ }
+ is(StateTL.sIncrementReadBurst) {
+ state := StateTL.sDataForwardBurst
+
+ tl.d.ready := false.B
+ tl.a.valid := false.B
+
+ tl.a.bits.opcode := 4.U
+ tl.a.bits.param := 0.U
+ tl.a.bits.size := size.U
+ tl.a.bits.source := 0.U
+ tl.a.bits.address := 0.U
+ tl.a.bits.mask := 255.U
+ tl.a.bits.data := 0.U
+
+ //readData := tl.d.bits.data
+
+ controller.io.validIn := false.B
+ controller.io.dataIn := 0.U
+ burstCounter := burstCounter + 1.U
+ }
+ is(StateTL.sDataForwardBurst) {
+ when(controller.io.receivedIn) {
+ state := StateTL.sDataForward2Burst
+ }
+
+ tl.d.ready := false.B
+ tl.a.valid := false.B
+
+ tl.a.bits.opcode := 0.U
+ tl.a.bits.param := 0.U
+ tl.a.bits.size := size.U
+ tl.a.bits.source := 0.U
+ tl.a.bits.address := 0.U
+ tl.a.bits.mask := 255.U
+ tl.a.bits.data := 0.U
+
+ controller.io.validIn := true.B
+ controller.io.dataIn := readData
+ }
+ is(StateTL.sDataForward2Burst) {
+ when(!(controller.io.receivedEnd) && received && (burstCounter >= burstTotalNumber)) {
+ state := StateTL.sIdle
+ }.elsewhen(!(controller.io.receivedEnd) && received && (burstCounter < burstTotalNumber)) {
+ state := StateTL.sSetReadAddressBurst
+ }
+ when(controller.io.receivedEnd) { received := true.B }
+ controller.io.dataIn := readData
+ controller.io.validIn := false.B
+ }
+ }
+
+ }
+}
+
+object StateAXI4 extends ChiselEnum {
+ val sIdle, sSetDataAndAddress, sResetCounterW, sSetReadyB, sSetReadAddress, sResetCounterR, sSetReadyR,
+ sDataForward, sSetDataAndAddressBurst, sResetCounterWBurst, sSetReadyBBurst, sSetReadAddressBurst,
+ sResetCounterRBurst, sSetReadyRBurst, sDataForwardBurst, sDataForward2Burst = Value
+}
+
+class JTAGToMasterAXI4(irLength: Int, initialInstruction: BigInt, beatBytes: Int, address: AddressSet, burstMaxNum: Int)
+ extends LazyModule()(Parameters.empty) {
+
+ require(burstMaxNum <= 128)
+
+ val node = Some(AXI4MasterNode(Seq(AXI4MasterPortParameters(Seq(AXI4MasterParameters("ioAXI4"))))))
+
+ lazy val io = Wire(new TopModuleIO)
+
+ lazy val module = new LazyModuleImp(this) {
+
+ val (ioNode, _) = node.get.out(0)
+
+ val jtagClk = Wire(Clock())
+ jtagClk := io.jtag.TCK.asClock
+
+ val syncReset = Wire(Bool())
+ val controller = withClockAndReset(jtagClk, syncReset) {
+ Module(new JtagController(irLength, initialInstruction, beatBytes))
+ }
+ syncReset := controller.io.output.reset
+ controller.io.jtag.TCK := io.jtag.TCK
+ controller.io.jtag.TMS := io.jtag.TMS
+ controller.io.jtag.TDI := io.jtag.TDI
+ io.jtag.TDO := controller.io.jtag.TDO
+ controller.io.control.fsmAsyncReset := io.asyncReset
+ controller.io.validIn := DontCare
+ controller.io.dataIn := DontCare
+
+ val state = RegInit(StateAXI4.sIdle)
+
+ val currentInstruction = RegInit(UInt(irLength.W), initialInstruction.U)
+ currentInstruction := controller.io.output.instruction
+
+ val dataValue = RegInit(UInt((beatBytes * 8).W), 0.U)
+ val addressValue = RegInit(UInt((beatBytes * 8).W), 0.U)
+
+ val burstTotalNumber = RegInit(UInt(8.W), 0.U)
+ val burstCurrentNumber = RegInit(UInt(8.W), 0.U)
+ val dataValueBurst = RegInit(
+ VecInit(Seq.fill(burstMaxNum)(0.U((beatBytes * 8).W)))
+ )
+
+ when(currentInstruction === "b0011".U) {
+ dataValue := controller.io.dataOut
+ }
+
+ when(currentInstruction === "b0010".U) {
+ addressValue := controller.io.dataOut // >> (beatBytes * 4)
+ }
+
+ when(currentInstruction === "b1000".U) {
+ burstTotalNumber := controller.io.dataOut >> ((beatBytes - 1) * 8)
+ }
+
+ when(currentInstruction === "b1010".U) {
+ burstCurrentNumber := controller.io.dataOut >> ((beatBytes - 1) * 8)
+ }
+
+ when(currentInstruction === "b1011".U) {
+ dataValueBurst(burstCurrentNumber) := controller.io.dataOut
+ }
+
+ val shouldWrite = RegInit(Bool(), false.B)
+ when((currentInstruction === "b0001".U) && (RegNext(currentInstruction) =/= "b0001".U)) {
+ shouldWrite := true.B
+ }.elsewhen(state === StateAXI4.sSetDataAndAddress) {
+ shouldWrite := false.B
+ }
+
+ val shouldRead = RegInit(Bool(), false.B)
+ when((currentInstruction === "b0100".U) && (RegNext(currentInstruction) =/= "b0100".U)) {
+ shouldRead := true.B
+ }.elsewhen(state === StateAXI4.sSetReadAddress) {
+ shouldRead := false.B
+ }
+
+ val shouldWriteBurst = RegInit(Bool(), false.B)
+ when(
+ (currentInstruction === "b1001".U) && (RegNext(currentInstruction) =/= "b1001".U) && (burstTotalNumber > 0.U)
+ ) {
+ shouldWriteBurst := true.B
+ }.elsewhen(state === StateAXI4.sSetDataAndAddressBurst) {
+ shouldWriteBurst := false.B
+ }
+
+ val shouldReadBurst = RegInit(Bool(), false.B)
+ when(
+ (currentInstruction === "b1100".U) && (RegNext(currentInstruction) =/= "b1100".U) && (burstTotalNumber > 0.U)
+ ) {
+ shouldReadBurst := true.B
+ }.elsewhen(state === StateAXI4.sSetReadAddressBurst) {
+ shouldReadBurst := false.B
+ }
+
+ val readData = RegInit(UInt((beatBytes * 8).W), 0.U)
+ val received = RegInit(Bool(), false.B)
+
+ val dataSize = if (beatBytes == 4) 2 else 3
+
+ def maxWait = 500
+ val counter = RegInit(UInt(9.W), 0.U)
+
+ val burstCounter = RegInit(UInt(8.W), 0.U)
+
+ switch(state) {
+ is(StateAXI4.sIdle) {
+ when(shouldWrite) {
+ state := StateAXI4.sSetDataAndAddress
+ }.elsewhen(shouldRead) {
+ state := StateAXI4.sSetReadAddress
+ }.elsewhen(shouldWriteBurst) {
+ state := StateAXI4.sSetDataAndAddressBurst
+ }.elsewhen(shouldReadBurst) {
+ state := StateAXI4.sSetReadAddressBurst
+ }
+
+ ioNode.aw.valid := false.B
+ ioNode.w.valid := false.B
+ ioNode.b.ready := false.B
+
+ ioNode.aw.bits.addr := 0.U
+ ioNode.aw.bits.size := dataSize.U
+ ioNode.w.bits.data := 0.U
+ ioNode.w.bits.last := false.B
+ ioNode.w.bits.strb := 255.U
+
+ ioNode.ar.valid := false.B
+ ioNode.r.ready := false.B
+
+ controller.io.validIn := false.B
+ controller.io.dataIn := 0.U
+
+ counter := 0.U
+ burstCounter := 0.U
+ received := false.B
+ }
+ is(StateAXI4.sSetDataAndAddress) {
+ when(ioNode.w.ready && ioNode.aw.ready) {
+ state := StateAXI4.sResetCounterW
+ }.elsewhen(counter >= maxWait.U) {
+ state := StateAXI4.sIdle
+ }
+ ioNode.aw.valid := true.B
+ ioNode.w.valid := true.B
+ ioNode.b.ready := false.B
+
+ ioNode.aw.bits.addr := addressValue
+ ioNode.aw.bits.size := dataSize.U
+ ioNode.w.bits.data := dataValue
+ ioNode.w.bits.last := true.B
+ ioNode.w.bits.strb := 255.U
+
+ controller.io.validIn := false.B
+ controller.io.dataIn := 0.U
+
+ counter := counter + 1.U
+ }
+ is(StateAXI4.sResetCounterW) {
+ state := StateAXI4.sSetReadyB
+
+ ioNode.aw.valid := false.B
+ ioNode.w.valid := false.B
+ ioNode.b.ready := false.B
+
+ controller.io.validIn := false.B
+ controller.io.dataIn := 0.U
+
+ counter := 0.U
+ }
+ is(StateAXI4.sSetReadyB) {
+ when(ioNode.b.valid && (ioNode.b.bits.resp === 0.U) && (ioNode.b.bits.id === ioNode.aw.bits.id)) {
+ state := StateAXI4.sIdle
+ }.elsewhen(counter >= maxWait.U) {
+ state := StateAXI4.sIdle
+ }
+ ioNode.aw.valid := false.B
+ ioNode.w.valid := false.B
+ ioNode.b.ready := true.B
+
+ ioNode.aw.bits.addr := 0.U
+ ioNode.aw.bits.size := dataSize.U
+ ioNode.w.bits.data := 0.U
+ ioNode.w.bits.last := false.B
+ ioNode.w.bits.strb := 255.U
+
+ controller.io.validIn := false.B
+ controller.io.dataIn := 0.U
+
+ counter := counter + 1.U
+ }
+ is(StateAXI4.sSetReadAddress) {
+ when(ioNode.ar.ready) {
+ state := StateAXI4.sResetCounterR
+ }.elsewhen(counter >= maxWait.U) {
+ state := StateAXI4.sIdle
+ }
+ ioNode.ar.valid := true.B
+ ioNode.r.ready := false.B
+
+ ioNode.ar.bits.addr := addressValue
+ ioNode.ar.bits.size := dataSize.U
+
+ controller.io.validIn := false.B
+ controller.io.dataIn := 0.U
+
+ counter := counter + 1.U
+ }
+ is(StateAXI4.sResetCounterR) {
+ state := StateAXI4.sSetReadyR
+
+ ioNode.ar.valid := false.B
+ ioNode.r.ready := false.B
+
+ controller.io.validIn := false.B
+ controller.io.dataIn := 0.U
+
+ counter := 0.U
+ }
+ is(StateAXI4.sSetReadyR) {
+ when(ioNode.r.valid && (ioNode.r.bits.resp === 0.U) && (ioNode.r.bits.id === ioNode.ar.bits.id)) {
+ state := StateAXI4.sDataForward
+ }.elsewhen(counter >= maxWait.U) {
+ state := StateAXI4.sIdle
+ }
+ ioNode.ar.valid := false.B
+ ioNode.r.ready := true.B
+
+ readData := ioNode.r.bits.data
+
+ controller.io.validIn := false.B
+ controller.io.dataIn := 0.U
+
+ counter := counter + 1.U
+ }
+ is(StateAXI4.sDataForward) {
+ when(controller.io.receivedEnd) {
+ state := StateAXI4.sIdle
+ }
+ controller.io.dataIn := readData
+ controller.io.validIn := true.B
+ }
+
+ is(StateAXI4.sSetDataAndAddressBurst) {
+ when(ioNode.w.ready && ioNode.aw.ready) {
+ state := StateAXI4.sResetCounterWBurst
+ }.elsewhen(counter >= maxWait.U) {
+ state := StateAXI4.sIdle
+ }
+ ioNode.aw.valid := true.B
+ ioNode.w.valid := true.B
+ ioNode.b.ready := false.B
+
+ ioNode.aw.bits.addr := addressValue + burstCounter * beatBytes.U
+ ioNode.aw.bits.size := dataSize.U
+ ioNode.w.bits.data := dataValueBurst(burstCounter)
+ ioNode.w.bits.last := true.B
+ ioNode.w.bits.strb := 255.U
+
+ controller.io.validIn := false.B
+ controller.io.dataIn := 0.U
+
+ counter := counter + 1.U
+ }
+ is(StateAXI4.sResetCounterWBurst) {
+ state := StateAXI4.sSetReadyBBurst
+
+ ioNode.aw.valid := false.B
+ ioNode.w.valid := false.B
+ ioNode.b.ready := false.B
+
+ controller.io.validIn := false.B
+ controller.io.dataIn := 0.U
+
+ counter := 0.U
+ burstCounter := burstCounter + 1.U
+ }
+ is(StateAXI4.sSetReadyBBurst) {
+ when(
+ ioNode.b.valid && (ioNode.b.bits.resp === 0.U) && (ioNode.b.bits.id === ioNode.aw.bits.id) && (burstCounter >= burstTotalNumber)
+ ) {
+ state := StateAXI4.sIdle
+ }.elsewhen(counter >= maxWait.U) {
+ state := StateAXI4.sIdle
+ }.elsewhen(
+ ioNode.b.valid && (ioNode.b.bits.resp === 0.U) && (ioNode.b.bits.id === ioNode.aw.bits.id) && (burstCounter < burstTotalNumber)
+ ) {
+ state := StateAXI4.sSetDataAndAddressBurst
+ }
+ ioNode.aw.valid := false.B
+ ioNode.w.valid := false.B
+ ioNode.b.ready := true.B
+
+ ioNode.aw.bits.addr := 0.U
+ ioNode.aw.bits.size := dataSize.U
+ ioNode.w.bits.data := 0.U
+ ioNode.w.bits.last := false.B
+ ioNode.w.bits.strb := 255.U
+
+ controller.io.validIn := false.B
+ controller.io.dataIn := 0.U
+
+ counter := counter + 1.U
+ }
+ is(StateAXI4.sSetReadAddressBurst) {
+ when(ioNode.ar.ready) {
+ state := StateAXI4.sResetCounterRBurst
+ }.elsewhen(counter >= maxWait.U) {
+ state := StateAXI4.sIdle
+ }
+ ioNode.ar.valid := true.B
+ ioNode.r.ready := false.B
+
+ ioNode.ar.bits.addr := addressValue + burstCounter * beatBytes.U
+ ioNode.ar.bits.size := dataSize.U
+
+ controller.io.validIn := false.B
+ controller.io.dataIn := 0.U
+
+ counter := counter + 1.U
+ received := false.B
+ }
+ is(StateAXI4.sResetCounterRBurst) {
+ state := StateAXI4.sSetReadyRBurst
+
+ ioNode.ar.valid := false.B
+ ioNode.r.ready := false.B
+
+ controller.io.validIn := false.B
+ controller.io.dataIn := 0.U
+
+ counter := 0.U
+ burstCounter := burstCounter + 1.U
+ }
+ is(StateAXI4.sSetReadyRBurst) {
+ when(ioNode.r.valid && (ioNode.r.bits.resp === 0.U) && (ioNode.r.bits.id === ioNode.ar.bits.id)) {
+ state := StateAXI4.sDataForwardBurst
+ }.elsewhen(counter >= maxWait.U) {
+ state := StateAXI4.sIdle
+ }
+ ioNode.ar.valid := false.B
+ ioNode.r.ready := true.B
+
+ readData := ioNode.r.bits.data
+
+ controller.io.validIn := false.B
+ controller.io.dataIn := 0.U
+
+ counter := counter + 1.U
+ }
+ is(StateAXI4.sDataForwardBurst) {
+ when(controller.io.receivedIn) {
+ state := StateAXI4.sDataForward2Burst
+ }
+ controller.io.dataIn := readData
+ controller.io.validIn := true.B
+ }
+ is(StateAXI4.sDataForward2Burst) {
+ when(!(controller.io.receivedEnd) && received && (burstCounter >= burstTotalNumber)) {
+ state := StateAXI4.sIdle
+ }.elsewhen(!(controller.io.receivedEnd) && received && (burstCounter < burstTotalNumber)) {
+ state := StateAXI4.sSetReadAddressBurst
+ }
+ when(controller.io.receivedEnd) { received := true.B }
+ controller.io.dataIn := readData
+ controller.io.validIn := false.B
+ }
+ }
+ }
+}
diff --git a/src/main/scala/jtag2mm/README.md b/src/main/scala/jtag2mm/README.md
new file mode 100644
index 00000000..c72817aa
--- /dev/null
+++ b/src/main/scala/jtag2mm/README.md
@@ -0,0 +1,64 @@
+JTAG To Memory Master (JTAG2MM) Chisel Generator
+================================================
+
+## Overview
+This repository contains a generator of JTAG to memory-mapped bus master modules, written in [Chisel](http://www.chisel-lang.org) hardware design language. Generated modules can initiate AXI4/TileLink(TL) transactions and drive AXI4/TL memory mapped slave(s) through an interconnect bus.
+
+### JTAG To Memory Master
+JTAG To Memory Master block can be divided into two subparts: JTAG controller and AXI4/TL controller. Its simplified block diagram with input and output interfaces is given below.
+
+
+
+### JTAG Controller
+
+The main task of the JTAG controller is to collect signals from JTAG input serial interface and forward them to AXI4/TL controller in correct form through parallel port, as well as to acquired signals from parallel port convert to serial data and output them using JTAG output serial interface. JTAG serial interface consists of standard JTAG signals:
+* `TCK` - test clock input signal, used as clock signal for JTAG controller
+* `TMS` - test mode select input signal, used for state transition inside JTAG FSM
+* `TDI` - test data in input signal, used to acquire data sent to the module
+* `TDO_data` - test data out output data signal, used to send data to the output
+* `TDO_driven` - test data out output valid signal, used to indicate that data on the output is valid
+* `asyncReset` - asynchronous reset signal, used for entering reset state inside JTAG FSM
+
+JTAG finite state machine (FSM) is a standard JTAG FSM and it is used to ensure that the JTAG controller is working properly. Through the change of states of this FSM, it is possible to acquire instruction and data values from the JTAG serial input interface. `TCK` signal is used as a clock signal, state changing happens thanks to `TMS` signal and `TDI` signal is stored as data. JTAG FSM state changing diagram is shown below.
+
+
+
+JTAG controller is decsribed inside `src/main/scala/jtagToMaster.scala` file. JTAG FSM, JTAG IO bundles and some of the other subparts and submodules are modified versions that are taken from the repository: [chisel-jtag](https://github.com/ucb-art/chisel-jtag).
+
+### AXI4/TL Controller
+
+AXI4/TL controller is used to initiate transactions and drive signals through an interconnect bus. Unlike the JTAG controller, it is driven by the system clock signal. It receives instruction and data values from JTAG controller and acts correspondingly. It also has an FSM which ensures that write and read transactions are performed in accordance with the appropriate transfer protocol. FSM is represented in the figure below. FSM for AXI4 controller consists of eight states:
+* `sIdle` - reset state, stays in this state until write or read transaction is initiated
+* `sSetDataAndAddress` - state in which address is set on AW channel, data is set on W channel and valid signals are set on both AW and W channels. Stays in this state until ready signals are not valid on both W and AW channels or until a counter that ensures that FSM isn't stuck in this state counts out
+* `sResetCounterW` - state in which the mentioned counter is reset. Stays in this state for exactly one clock cycle
+* `sSetReadyB` - state in which ready signal is set on acknowledgement B channel. Stays in this state until B channel valid signal is not active or until a counter that ensures that FSM isn't stuck in this state counts out
+* `sSetReadAddress` - state in which address and valid signals are set on AR channel. Stays in this state until AR channel ready signal is not valid or until a counter that ensures that FSM isn't stuck in this state counts out
+* `sResetCounterR` - state in which the mentioned counter is reset. Stays in this state for exactly one clock cycle
+* `sSetReadyR` - state in which ready signal is set on R channel and data is read from the same channel. Stays in this state until R channel valid signal is not active or until a counter that ensures that FSM isn't stuck in this state counts out
+* `sDataForward` - state in which read data is forwarded to the JTAG controller, along with active validIn signal. Stays in this state until receivedIn signal is not active
+
+
+
+Same FSM could be applied for AXI4 burst transfers. Only difference is that after the completed single data transfer, FSM enters `sIdle` state only if the burst transfers counter has counted out. Otherwise, FSM enters `sSetDataAndAddress`/`sSetReadAddress` state to perform another transfer.
+
+TileLink FSM has different protocol signals involved, but works on the same principles as AXI4 FSM. AXI4 and TL controllers, as well as the appropriate FSM, are decsribed inside the `src/main/scala/jtagToMaster.scala` scala file.
+
+### User manual
+
+Total of four instructions are necessary for the JTAG2MM module to work properly. With additional instructions for performing burst data transfers, total of 9 instructions are defined. Instruction codes along with their descriptions are provided below:
+* `0x01` - write instruction, initiates the AXI4/TL FSM to begin writing acquired data to acquired address
+* `0x02` - address acquire instruction, accepts the serial data as the address for read/write instruction
+* `0x03` - data acquire instruction, accepts the serial data as the data for read/write instruction
+* `0x04` - read instruction, initiates the AXI4/TL FSM to begin reading data from the acquired address
+* `0x08` - number of burst transactions acquire instruction, accepts the serial data as the number of read/write instructions during one burst transfer cycle
+* `0x09` - burst write instruction, initiates the AXI4/TL FSM to begin acquired number of write transactions. Data is written to consecutive addresses
+* `0x0A` - data index number acquire instruction, accepts the serial data as the index number of data to be acquired for the burst read/write transfer
+* `0x0B` - indexed data acquire instruction, accepts the serial data as the data at the acquired index number for the burst read/write transfer
+* `0x0C` - burst read instruction, initiates the AXI4/TL FSM to begin acquired number of read transactions. Data is read from consecutive addresses
+
+User initiates one of defined instruction by driving the input JTAG signals with appropriate values. `TCK` signal should be driven continuously. Using `TMS` signal, JTAG FSM enters the state in which it accepts the serial data from `TDI` input as the instruction value. Address and data acquire instructions, as well as the number of burst transactions acquire, data index number acquire and indexed data acquire instructions, require data values besides address values, so after sending appropriate instruction code, by using `TMS` signal, user should enter the JTAG FSM state in which it accepts the serial data from `TDI` input as the data value. In these instructions, provided data is stored into appropriate registers, so that the write or read instruction can be performed.
+Before the write instruction, both address acquire and data acquire instructions must be performed. Before the read instruction, address acquire instruction must be performed. For burst write instruction, data for every single transaction must be acquired beforehand, as well as the total number of burst transactions for both burst write and burst read instructions. Current instruction and data values are being continuously sent from JTAG controller to AXI4/TL controller. When instruction value equals one of the acquire instructions code, obtained data value is stored in the appropriate register inside the AXI4/TL controller. When instruction value equals read, write, burst read or burst write instruction code, that's the indicator for the AXI4/TL controller to start (burst) read/write transaction on the interconnect bus. Two read/write/burst read/ burst write instructions of the same type cannot appear sequentially one right after another, there must be at least one other instruction between these two. After performing the read or burst read instruction, read data appear on the serial output JTAG `TDO` port.
+
+## Tests
+
+Several test examples and other additional information about the JTAG2MM generator can be found in the repository: [JTAG2MM](https://github.com/milovanovic/jtag2mm).
diff --git a/src/main/scala/jtag2mm/chisel-jtag/JtagIO.scala b/src/main/scala/jtag2mm/chisel-jtag/JtagIO.scala
new file mode 100644
index 00000000..8b15157f
--- /dev/null
+++ b/src/main/scala/jtag2mm/chisel-jtag/JtagIO.scala
@@ -0,0 +1,42 @@
+package testchipip
+
+import chisel3._
+
+/** JTAG signals, viewed from the device side.
+ */
+class JtagIO extends Bundle {
+ // TRST (4.6) is optional and not currently implemented.
+ val TCK = Input(Bool())
+ val TMS = Input(Bool())
+ val TDI = Input(Bool())
+ val TDO = Output(new Tristate())
+}
+
+/** 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 fsmAsyncReset = Input(Bool()) // TODO: asynchronous reset for FSM, used for TAP_POR*
+}
+
+/** Aggregate JTAG block IO.
+ */
+class JtagBlockIO(val irLength: Int) extends Bundle {
+ val jtag = new JtagIO
+ val control = new JtagControl
+ val output = new JtagOutput(irLength)
+}
+
+/** 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)
+}
diff --git a/src/main/scala/jtag2mm/chisel-jtag/JtagShifter.scala b/src/main/scala/jtag2mm/chisel-jtag/JtagShifter.scala
new file mode 100644
index 00000000..44074cb7
--- /dev/null
+++ b/src/main/scala/jtag2mm/chisel-jtag/JtagShifter.scala
@@ -0,0 +1,196 @@
+package testchipip
+
+import chisel3._
+import chisel3.experimental.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](private val 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
+}
+
+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)
+
+ if (updateWidth > 0) {
+ val updateBits = Cat(regs.reverse)(updateWidth - 1, 0)
+ io.update.bits := updateBits.asTypeOf(io.update.bits)
+ } else {
+ io.update.bits := 0.U
+ }
+
+ 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/jtag2mm/chisel-jtag/JtagStateMachine.scala b/src/main/scala/jtag2mm/chisel-jtag/JtagStateMachine.scala
new file mode 100644
index 00000000..51920c9b
--- /dev/null
+++ b/src/main/scala/jtag2mm/chisel-jtag/JtagStateMachine.scala
@@ -0,0 +1,151 @@
+package testchipip
+
+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 = log2Ceil(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 {
+ class StateMachineIO extends Bundle {
+ val tms = Input(Bool())
+ val currState = Output(JtagState.State.chiselType())
+
+ val asyncReset = Input(Bool()) // TODO: IMPLEMENT ME, make it actually async
+ }
+ val io = IO(new StateMachineIO)
+
+ // TMS is captured as a single signal, rather than fed directly into the next state logic.
+ // This increases the state computation delay at the beginning of a cycle (as opposed to near the
+ // end), but theoretically allows a cleaner capture.
+ val tms = RegNext(io.tms) // 4.3.1a captured on TCK rising edge, 6.1.2.1b assumed changes on TCK falling edge
+
+ withReset(io.asyncReset) {
+ val nextState = Wire(JtagState.State.chiselType())
+ nextState := DontCare //TODO: figure out what isn't getting connected
+ val lastState = RegNext(nextState, JtagState.TestLogicReset.U)
+
+ switch(lastState) {
+ is(JtagState.TestLogicReset.U) {
+ nextState := Mux(tms, JtagState.TestLogicReset.U, JtagState.RunTestIdle.U)
+ }
+ is(JtagState.RunTestIdle.U) {
+ nextState := Mux(tms, JtagState.SelectDRScan.U, JtagState.RunTestIdle.U)
+ }
+ is(JtagState.SelectDRScan.U) {
+ nextState := Mux(tms, JtagState.SelectIRScan.U, JtagState.CaptureDR.U)
+ }
+ is(JtagState.CaptureDR.U) {
+ nextState := Mux(tms, JtagState.Exit1DR.U, JtagState.ShiftDR.U)
+ }
+ is(JtagState.ShiftDR.U) {
+ nextState := Mux(tms, JtagState.Exit1DR.U, JtagState.ShiftDR.U)
+ }
+ is(JtagState.Exit1DR.U) {
+ nextState := Mux(tms, JtagState.UpdateDR.U, JtagState.PauseDR.U)
+ }
+ is(JtagState.PauseDR.U) {
+ nextState := Mux(tms, JtagState.Exit2DR.U, JtagState.PauseDR.U)
+ }
+ is(JtagState.Exit2DR.U) {
+ nextState := Mux(tms, JtagState.UpdateDR.U, JtagState.ShiftDR.U)
+ }
+ is(JtagState.UpdateDR.U) {
+ nextState := Mux(tms, JtagState.SelectDRScan.U, JtagState.RunTestIdle.U)
+ }
+ is(JtagState.SelectIRScan.U) {
+ nextState := Mux(tms, JtagState.TestLogicReset.U, JtagState.CaptureIR.U)
+ }
+ is(JtagState.CaptureIR.U) {
+ nextState := Mux(tms, JtagState.Exit1IR.U, JtagState.ShiftIR.U)
+ }
+ is(JtagState.ShiftIR.U) {
+ nextState := Mux(tms, JtagState.Exit1IR.U, JtagState.ShiftIR.U)
+ }
+ is(JtagState.Exit1IR.U) {
+ nextState := Mux(tms, JtagState.UpdateIR.U, JtagState.PauseIR.U)
+ }
+ is(JtagState.PauseIR.U) {
+ nextState := Mux(tms, JtagState.Exit2IR.U, JtagState.PauseIR.U)
+ }
+ is(JtagState.Exit2IR.U) {
+ nextState := Mux(tms, JtagState.UpdateIR.U, JtagState.ShiftIR.U)
+ }
+ is(JtagState.UpdateIR.U) {
+ nextState := Mux(tms, JtagState.SelectDRScan.U, JtagState.RunTestIdle.U)
+ }
+ }
+
+ io.currState := nextState
+ }
+}
diff --git a/src/main/scala/jtag2mm/chisel-jtag/Utils.scala b/src/main/scala/jtag2mm/chisel-jtag/Utils.scala
new file mode 100644
index 00000000..081a7c02
--- /dev/null
+++ b/src/main/scala/jtag2mm/chisel-jtag/Utils.scala
@@ -0,0 +1,84 @@
+package testchipip
+
+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](dataType: T) extends Module {
+ 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](modClock: Clock, next: T, enable: Bool = true.B): T = {
+ // TODO better init passing once in-module multiclock support improves
+
+ val latch_module = withClock((!(modClock.asUInt)).asClock) {
+ Module(new NegativeEdgeLatch(chiselTypeOf(next)))
+ }
+ 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(counts: BigInt, init: Option[BigInt]) extends Module {
+ require(counts > 0, "really?")
+
+ val width = log2Ceil(counts)
+ class CountIO extends Bundle {
+ val count = Output(UInt(width.W))
+ }
+ val io = IO(new CountIO)
+
+ val count = init match {
+ case Some(init) => RegInit(UInt(width.W), 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 = withClock(data.asClock) {
+ Module(new ClockedCounter(counts, Some(init)))
+ }
+ counter.io.count
+ }
+ def apply(data: Bool, counts: BigInt): UInt = {
+ val counter = withClock(data.asClock) {
+ Module(new ClockedCounter(counts, None))
+ }
+ counter.io.count
+ }
+}
diff --git a/src/main/scala/jtag2mm/doc/images/axi4_fsm.svg b/src/main/scala/jtag2mm/doc/images/axi4_fsm.svg
new file mode 100644
index 00000000..a2b90a38
--- /dev/null
+++ b/src/main/scala/jtag2mm/doc/images/axi4_fsm.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/src/main/scala/jtag2mm/doc/images/jtag2mm.svg b/src/main/scala/jtag2mm/doc/images/jtag2mm.svg
new file mode 100644
index 00000000..ce6c6213
--- /dev/null
+++ b/src/main/scala/jtag2mm/doc/images/jtag2mm.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/src/main/scala/jtag2mm/doc/images/jtag_fsm.svg b/src/main/scala/jtag2mm/doc/images/jtag_fsm.svg
new file mode 100644
index 00000000..428b9dd3
--- /dev/null
+++ b/src/main/scala/jtag2mm/doc/images/jtag_fsm.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file