diff --git a/README.md b/README.md index 7d2e47b2..0e9cc48a 100644 --- a/README.md +++ b/README.md @@ -188,6 +188,7 @@ Abstractions are built on top of `DspBlock`, for example a chain of blocks is a 2) A diplomatic implementation of AXI4-Stream 3) Some DMA (stream <-> mem) blocks 4) Simulation models for chisel-testers for memory interfaces (AHB, APB, AXI4, TileLink) and AXI4-Stream. +5) JTAG-to-memory-mapped bus (AXI4 & TileLink) master bridge ---------- diff --git a/doc/rocket/JTAG2MM.md b/doc/rocket/JTAG2MM.md new file mode 100644 index 00000000..c99cd135 --- /dev/null +++ b/doc/rocket/JTAG2MM.md @@ -0,0 +1,71 @@ +JTAG To Memory Master (JTAG2MM) Chisel Generator +======================================================== + +## Overview +In this document, a generator of JTAG to memory-mapped bus master modules, written in [Chisel](http://www.chisel-lang.org) hardware design language, is described. 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 shown below. + +![JTAG To Memory Master block diagram](./images/jtag2mm.svg) + +### 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 FSM state changing diagram](./images/jtag_fsm.svg) + +JTAG controller is decsribed inside the `rocket/src/main/scala/jtag2mm/JtagToMaster.scala` 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 + +![AXI4 FSM state changing diagram](./images/axi4_fsm.svg) + +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 `rocket/src/main/scala/jtag2mm/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 tests were used to verify the correctness of the module. Some of them are defined inside the `rocket/src/test/scala/jtag2mm` directory. The rest can be found in the repository: [JTAG2MM](https://github.com/milovanovic/jtag2mm). In order to verify the module, a simple streaming multiplexer module is defined for both TileLink and AXI4 interfaces. Moreover, JTAG Fuzzer module that initiate arbitrary number of TL/AXI4 write transactions when connected to JTAG2MM module is defined. These transactions' data and addresses are pseudo-random. JTAG Fuzzer module can be of great value for additional testing of JTAG2MM modules. + +Following tests are defined in this repository: +* `Jtag2AXI4MultiplexerTester.scala` - test used to verify the AXI4 variant of the JTAG2MM module working with AXI4Multiplexer module with memory mapped control registers connected to the AXI4 bus. +* `Jtag2TLMultiplexerTester.scala` - test used to verify the TL variant of the JTAG2MM module working with TLMultiplexer module with memory mapped control registers connected to the TL bus. +* `JtagFuzzerTester.scala` - test used to verify the correctness of the output signals of the JTAG Fuzzer module. + + diff --git a/doc/rocket/images/axi4_fsm.svg b/doc/rocket/images/axi4_fsm.svg new file mode 100644 index 00000000..a2b90a38 --- /dev/null +++ b/doc/rocket/images/axi4_fsm.svg @@ -0,0 +1,3 @@ + + +
sIdle
sIdle
sSetReadyB
sSetReadyB
sResetCounterW
sResetCounterW
sSetReadAddress
sSetReadAddress
sResetCounterR
sResetCounterR
sSetData
AndAdress
sSetData...
sSetReadyR
sSetReadyR
sDataForward
sDataForward
reset
reset
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/doc/rocket/images/jtag2mm.svg b/doc/rocket/images/jtag2mm.svg new file mode 100644 index 00000000..ce6c6213 --- /dev/null +++ b/doc/rocket/images/jtag2mm.svg @@ -0,0 +1,3 @@ + + +
TCK
TCK
TMS
TMS
TDI
TDI
TDO_data
TDO_data
TDO_driven
TDO_driven
async_reset
async_reset
JTAG Controller
JTAG Controller
AXI4/TL Controller
AXI4/TL Controller
dataOut
dataOut
instruction
instruction
dataIn
dataIn
receivedIn
receivedIn
validIn
validIn
AXI4 / TL
AXI4 / TL
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/doc/rocket/images/jtag_fsm.svg b/doc/rocket/images/jtag_fsm.svg new file mode 100644 index 00000000..428b9dd3 --- /dev/null +++ b/doc/rocket/images/jtag_fsm.svg @@ -0,0 +1,3 @@ + + +
TestLogicReset
TestLogicReset
RunTestIdle
RunTestIdle
SelectDRScan
SelectDRScan
CaptureDR
CaptureDR
ShiftDR
ShiftDR
Exit1DR
Exit1DR
PauseDR
PauseDR
Exit2DR
Exit2DR
UpdateDR
UpdateDR
SelectIRScan
SelectIRScan
CaptureIR
CaptureIR
ShiftIR
ShiftIR
Exit1IR
Exit1IR
PauseIR
PauseIR
Exit2IR
Exit2IR
UpdateIR
UpdateIR
1
1
0
0
0
0
0
0
0
0
0
0
0
0
1
1
1
1
1
1
0
0
0
0
1
1
0
0
1
1
1
1
1
1
1
1
0
0
0
0
0
0
1
1
1
1
1
1
0
0
1
1
0
0
1
1
1
1
1
1
0
0
1
1
0
0
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/rocket/src/main/scala/jtag2mm/JtagFuzzer.scala b/rocket/src/main/scala/jtag2mm/JtagFuzzer.scala new file mode 100644 index 00000000..01ac508e --- /dev/null +++ b/rocket/src/main/scala/jtag2mm/JtagFuzzer.scala @@ -0,0 +1,352 @@ +// SPDX-License-Identifier: Apache-2.0 + +package freechips.rocketchip.jtag2mm + +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 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(16.W), 0.U) + lfsrAddrReg := LFSR(14) << 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) + + 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((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 + 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 + 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 + when(stateCounter === 68.U) { + io.TDI := lfsrAddrReg(15) + } .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 + when(stateCounter === 69.U) { + io.TDI := lfsrAddrReg(15) + } .elsewhen(stateCounter === 163.U) { + io.TDI := lfsrDataReg(31) + } .otherwise { + io.TDI := false.B + } + } + } +} diff --git a/rocket/src/main/scala/jtag2mm/JtagToMaster.scala b/rocket/src/main/scala/jtag2mm/JtagToMaster.scala new file mode 100644 index 00000000..106fe61c --- /dev/null +++ b/rocket/src/main/scala/jtag2mm/JtagToMaster.scala @@ -0,0 +1,916 @@ +// SPDX-License-Identifier: Apache-2.0 + +package freechips.rocketchip.jtag2mm + +import chisel3._ +import chisel3.util._ +import chisel3.experimental._ +//import chisel3.experimental.{withClockAndReset} + +import freechips.rocketchip.amba.axi4._ +import freechips.rocketchip.amba.axi4stream._ +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 * 4).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 + val mask = if (beatBytes == 4) 15 else 255 + + 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 := mask.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 := mask.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 := mask.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 := mask.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 := mask.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 := mask.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 := mask.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 := mask.U + tl.a.bits.data := 0.U + + 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 := mask.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 + } + } + + } +} + +class TLJTAGToMasterBlock( + irLength: Int = 4, + initialInstruction: BigInt = BigInt("0", 2), + beatBytes: Int = 4, + addresses: AddressSet, + burstMaxNum: Int = 8 +)( + implicit p: Parameters) + extends JTAGToMasterTL[TLClientPortParameters, TLManagerPortParameters, TLEdgeOut, TLEdgeIn, TLBundle]( + irLength, + initialInstruction, + beatBytes, + burstMaxNum + ) { + + require(burstMaxNum <= 128) + + val devname = "TLJTAGToMasterBlock" + val devcompat = Seq("jtagToMaster", "radardsp") + val device = new SimpleDevice(devname, devcompat) { + override def describe(resources: ResourceBindings): Description = { + val Description(name, mapping) = super.describe(resources) + Description(name, mapping) + } + } + + def makeIO2(): TopModuleIO = { + val io2: TopModuleIO = IO(io.cloneType) + io2.suggestName("ioJTAG") + io2 <> io + io2 + } + + val managerParams = TLManagerParameters( + address = Seq(addresses), + resources = device.reg, + regionType = RegionType.UNCACHED, + executable = true, + supportsArithmetic = TransferSizes(1, beatBytes), + supportsLogical = TransferSizes(1, beatBytes), + supportsGet = TransferSizes(1, beatBytes), + supportsPutFull = TransferSizes(1, beatBytes), + supportsPutPartial = TransferSizes(1, beatBytes), + supportsHint = TransferSizes(1, beatBytes), + fifoId = Some(0) + ) + + val ioStreamNode = BundleBridgeSink[TLBundle]() + ioStreamNode := TLToBundleBridge(managerParams, beatBytes) := node.get + val ioTL = InModuleBody { ioStreamNode.makeIO() } + val ioJTAG = InModuleBody { makeIO2() } + +} + +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 * 4).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))) + ) //(0.asTypeOf(UInt((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 + } + } + } +} + +class AXI4JTAGToMasterBlock( + irLength: Int = 4, + initialInstruction: BigInt = BigInt("0", 2), + beatBytes: Int = 4, + addresses: AddressSet, + burstMaxNum: Int = 8 +)( + implicit p: Parameters) + extends JTAGToMasterAXI4(irLength, initialInstruction, beatBytes, addresses, burstMaxNum) { + require(burstMaxNum <= 128) + + def makeIO2(): TopModuleIO = { + val io2: TopModuleIO = IO(io.cloneType) + io2.suggestName("ioJTAG") + io2 <> io + io2 + } + + val slaveParams = AXI4SlaveParameters( + address = Seq(addresses), + regionType = RegionType.UNCACHED, + executable = true, + supportsWrite = TransferSizes(1, beatBytes), + supportsRead = TransferSizes(1, beatBytes) + ) + + val ioNode = BundleBridgeSink[AXI4Bundle]() + ioNode := + AXI4ToBundleBridge(AXI4SlavePortParameters(Seq(slaveParams), beatBytes)) := node.get + val ioAXI4 = InModuleBody { ioNode.makeIO() } + + val ioJTAG = InModuleBody { makeIO2() } + +} + +/*object JTAGToMasterDspBlockTL extends App { + implicit val p: Parameters = Parameters.empty + val jtagModule = LazyModule(new TLJTAGToMasterBlock(3, BigInt("0", 2), 4, AddressSet(0x00000, 0x3fff), 8)) + + chisel3.Driver.execute(args, () => jtagModule.module) +} + +object JTAGToMasterDspBlockAXI4 extends App { + implicit val p: Parameters = Parameters.empty + val jtagModule = LazyModule(new AXI4JTAGToMasterBlock(3, BigInt("0", 2), 4, AddressSet(0x00000, 0x3fff), 8)) + + chisel3.Driver.execute(args, () => jtagModule.module) +}*/ diff --git a/rocket/src/main/scala/jtag2mm/TestMultiplexer.scala b/rocket/src/main/scala/jtag2mm/TestMultiplexer.scala new file mode 100644 index 00000000..0978d9b7 --- /dev/null +++ b/rocket/src/main/scala/jtag2mm/TestMultiplexer.scala @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: Apache-2.0 + +package freechips.rocketchip.jtag2mm + +import chisel3._ +import chisel3.util._ +import chisel3.experimental._ +//import chisel3.experimental.{withClockAndReset} + +import dsptools._ +import dsptools.numbers._ + +import dspblocks._ +import freechips.rocketchip.amba.axi4._ +import freechips.rocketchip.amba.axi4stream._ +import freechips.rocketchip.config._ +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.regmapper._ +import freechips.rocketchip.tilelink._ + + +abstract class TestMultiplexer[D, U, EO, EI, B <: Data]()(implicit p: Parameters) + extends DspBlock[D, U, EO, EI, B] with HasCSR { + + val streamNode = AXI4StreamMasterNode(Seq(AXI4StreamMasterPortParameters(Seq(AXI4StreamMasterParameters("out", n = 8))))) + + lazy val module = new LazyModuleImp(this) { + val (out, _) = streamNode.out.unzip + + val a = RegInit(UInt(64.W), 0.U) + val b = RegInit(UInt(64.W), 0.U) + val select = RegInit(Bool(), true.B) + + regmap(0x00 -> Seq(RegField(64, a)), 0x08 -> Seq(RegField(64, b)), 0x10 -> Seq(RegField(1, select))) + + out.head.bits.data := Mux(select, a, b) + } +} + +class TLMultiplexer()(implicit p: Parameters) + extends TestMultiplexer[TLClientPortParameters, TLManagerPortParameters, TLEdgeOut, TLEdgeIn, TLBundle]() + with TLBasicBlock + +class Jtag2TLMultiplexer( + irLength: Int, + initialInstruction: BigInt, + beatBytes: Int, + jtagAddresses: AddressSet, + maxBurstNum: Int) + extends LazyModule()(Parameters.empty) { + + val multiplexerModule = LazyModule(new TLMultiplexer() { + + def standaloneParams = TLBundleParameters( + addressBits = 16, + dataBits = 64, + sourceBits = 16, + sinkBits = 16, + sizeBits = 3, + aUserBits = 0, + dUserBits = 0, + hasBCE = false + ) + + val clientParams = TLClientParameters( + name = "BundleBridgeToTL", + sourceId = IdRange(0, 1), + nodePath = Seq(), + requestFifo = false, + visibility = Seq(AddressSet(0, ~0)), + supportsProbe = TransferSizes(1, beatBytes), + supportsArithmetic = TransferSizes(1, beatBytes), + supportsLogical = TransferSizes(1, beatBytes), + supportsGet = TransferSizes(1, beatBytes), + supportsPutFull = TransferSizes(1, beatBytes), + supportsPutPartial = TransferSizes(1, beatBytes), + supportsHint = TransferSizes(1, beatBytes), + userBits = Nil + ) + + val ioMem = mem.map { m => + { + val ioMemNode = BundleBridgeSource(() => TLBundle(standaloneParams)) + m := BundleBridgeToTL(TLClientPortParameters(Seq(clientParams))) := ioMemNode + val ioMem = InModuleBody { ioMemNode.makeIO() } + ioMem + } + } + + val ioStreamNode = BundleBridgeSink[AXI4StreamBundle]() + ioStreamNode := + AXI4StreamToBundleBridge(AXI4StreamSlaveParameters()) := streamNode + val outStream = InModuleBody { ioStreamNode.makeIO() } + }) + + val jtagModule = LazyModule( + new TLJTAGToMasterBlock(irLength, initialInstruction, beatBytes, jtagAddresses, maxBurstNum) + ) + + InModuleBody { multiplexerModule.ioMem.get <> jtagModule.ioTL } + + def makeIO1(): AXI4StreamBundle = { + val io2: AXI4StreamBundle = IO(multiplexerModule.outStream.cloneType) + io2.suggestName("outStream") + io2 <> multiplexerModule.outStream + io2 + } + def makeIO2(): TopModuleIO = { + val io2: TopModuleIO = IO(jtagModule.ioJTAG.cloneType) + io2.suggestName("ioJTAG") + io2 <> jtagModule.ioJTAG + io2 + } + + val outStream = InModuleBody { makeIO1() } + val ioJTAG = InModuleBody { makeIO2() } + + lazy val module = new LazyModuleImp(this) +} + +class AXI4Multiplexer()(implicit p: Parameters) + extends TestMultiplexer[AXI4MasterPortParameters, AXI4SlavePortParameters, AXI4EdgeParameters, AXI4EdgeParameters, AXI4Bundle] + with AXI4BasicBlock + +class Jtag2AXI4Multiplexer( + irLength: Int, + initialInstruction: BigInt, + beatBytes: Int, + jtagAddresses: AddressSet, + maxBurstNum: Int) + extends LazyModule()(Parameters.empty) { + + val multiplexerModule = LazyModule(new AXI4Multiplexer() { + + def standaloneParams = AXI4BundleParameters(addrBits = 8, dataBits = beatBytes*8, idBits = 1) + val ioMem = mem.map { + m => { + val ioMemNode = BundleBridgeSource(() => AXI4Bundle(standaloneParams)) + m := BundleBridgeToAXI4(AXI4MasterPortParameters(Seq(AXI4MasterParameters("bundleBridgeToAXI4")))) := ioMemNode + val ioMem = InModuleBody { ioMemNode.makeIO() } + ioMem + } + } + + val ioStreamNode = BundleBridgeSink[AXI4StreamBundle]() + ioStreamNode := + AXI4StreamToBundleBridge(AXI4StreamSlaveParameters()) := streamNode + val outStream = InModuleBody { ioStreamNode.makeIO() } + }) + + val jtagModule = LazyModule( + new AXI4JTAGToMasterBlock(irLength, initialInstruction, beatBytes, jtagAddresses, maxBurstNum) + ) + + InModuleBody { multiplexerModule.ioMem.get <> jtagModule.ioAXI4 } + + def makeIO1(): AXI4StreamBundle = { + val io2: AXI4StreamBundle = IO(multiplexerModule.outStream.cloneType) + io2.suggestName("outStream") + io2 <> multiplexerModule.outStream + io2 + } + def makeIO2(): TopModuleIO = { + val io2: TopModuleIO = IO(jtagModule.ioJTAG.cloneType) + io2.suggestName("ioJTAG") + io2 <> jtagModule.ioJTAG + io2 + } + + val outStream = InModuleBody { makeIO1() } + val ioJTAG = InModuleBody { makeIO2() } + + lazy val module = new LazyModuleImp(this) +} + +/*object JTAGToTLMultiplexerApp extends App { + + val irLength = 4 + val initialInstruction = BigInt("0", 2) + val addresses = AddressSet(0x00000, 0x3fff) + val beatBytes = 8 + val maxBurstNum = 8 + + implicit val p: Parameters = Parameters.empty + val appModule = LazyModule( + new Jtag2TLMultiplexer(irLength, initialInstruction, beatBytes, addresses, maxBurstNum) + ) + + chisel3.Driver.execute(args, () => appModule.module) +} + +object JTAGToAXI4MultiplexerApp extends App { + + val irLength = 4 + val initialInstruction = BigInt("0", 2) + val addresses = AddressSet(0x00000, 0x3fff) + val beatBytes = 8 + val maxBurstNum = 8 + + implicit val p: Parameters = Parameters.empty + val appModule = LazyModule( + new Jtag2AXI4Multiplexer(irLength, initialInstruction, beatBytes, addresses, maxBurstNum) + ) + + chisel3.Driver.execute(args, () => appModule.module) +}*/ diff --git a/rocket/src/main/scala/jtag2mm/chisel-jtag/JtagIO.scala b/rocket/src/main/scala/jtag2mm/chisel-jtag/JtagIO.scala new file mode 100644 index 00000000..3abd4b1c --- /dev/null +++ b/rocket/src/main/scala/jtag2mm/chisel-jtag/JtagIO.scala @@ -0,0 +1,46 @@ +// See ./LICENSE for license details. + +package freechips.rocketchip.jtag2mm + +import chisel3._ + +// This code was taken from https://github.com/ucb-art/chisel-jtag/blob/master/src/main/scala/jtag/jtagTap.scala and adjusted to our design needs + +/** 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/rocket/src/main/scala/jtag2mm/chisel-jtag/JtagShifter.scala b/rocket/src/main/scala/jtag2mm/chisel-jtag/JtagShifter.scala new file mode 100644 index 00000000..c8022744 --- /dev/null +++ b/rocket/src/main/scala/jtag2mm/chisel-jtag/JtagShifter.scala @@ -0,0 +1,200 @@ +// See ./LICENSE for license details. + +package freechips.rocketchip.jtag2mm + +import chisel3._ +import chisel3.experimental.DataMirror +import chisel3.internal.firrtl.KnownWidth +import chisel3.util._ + +// This code was taken from https://github.com/ucb-art/chisel-jtag/blob/master/src/main/scala/jtag/jtagShifter.scala and adjusted to our design needs + +/** 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/rocket/src/main/scala/jtag2mm/chisel-jtag/JtagStateMachine.scala b/rocket/src/main/scala/jtag2mm/chisel-jtag/JtagStateMachine.scala new file mode 100644 index 00000000..9fc9c620 --- /dev/null +++ b/rocket/src/main/scala/jtag2mm/chisel-jtag/JtagStateMachine.scala @@ -0,0 +1,155 @@ +// See ./LICENSE for license details. + +package freechips.rocketchip.jtag2mm + +import chisel3._ +import chisel3.util._ + +// This code was taken from https://github.com/ucb-art/chisel-jtag/blob/master/src/main/scala/jtag/jtagStateMachine.scala and adjusted to our design needs + +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/rocket/src/main/scala/jtag2mm/chisel-jtag/LICENSE b/rocket/src/main/scala/jtag2mm/chisel-jtag/LICENSE new file mode 100644 index 00000000..42304239 --- /dev/null +++ b/rocket/src/main/scala/jtag2mm/chisel-jtag/LICENSE @@ -0,0 +1,26 @@ +chisel-jtag license terms + +Copyright (c) 2016 The Regents of the University of +California (Regents). All Rights Reserved. Redistribution and use in +source and binary forms, with or without modification, are permitted +provided that the following conditions are met: + * Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + two paragraphs of disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + two paragraphs of disclaimer in the documentation and/or other materials + provided with the distribution. + * Neither the name of the Regents nor the names of its contributors + may be used to endorse or promote products derived from this + software without specific prior written permission. +IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, +SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF +REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF +ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION +TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR +MODIFICATIONS. diff --git a/rocket/src/main/scala/jtag2mm/chisel-jtag/Utils.scala b/rocket/src/main/scala/jtag2mm/chisel-jtag/Utils.scala new file mode 100644 index 00000000..4a7137e0 --- /dev/null +++ b/rocket/src/main/scala/jtag2mm/chisel-jtag/Utils.scala @@ -0,0 +1,88 @@ +// See ./LICENSE for license details. + +package freechips.rocketchip.jtag2mm + +import chisel3._ +import chisel3.util._ + +// This code was taken from https://github.com/ucb-art/chisel-jtag/blob/master/src/main/scala/jtag/Utils.scala and adjusted to our design needs + +/** 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/rocket/src/test/scala/amba/axi4/DMATests.scala b/rocket/src/test/scala/amba/axi4/DMATests.scala index 198d353b..e9cafde8 100644 --- a/rocket/src/test/scala/amba/axi4/DMATests.scala +++ b/rocket/src/test/scala/amba/axi4/DMATests.scala @@ -8,7 +8,8 @@ import freechips.rocketchip.diplomacy.{AddressSet, LazyModule, LazyModuleImp, La import freechips.rocketchip.subsystem.{PeripheryBus, PeripheryBusParams} import freechips.rocketchip.system.BaseConfig import freechips.rocketchip.tilelink.{TLBundleParameters, TLFragmenter, TLIdentityNode, TLToAXI4} -import org.scalatest.{FlatSpec, Matchers} +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers class StreamingAXI4DMAWithMemoryTester(dut: StreamingAXI4DMAWithMemory with AXI4StandaloneBlock, silentFail: Boolean = false) extends PeekPokeTester(dut.module) @@ -181,7 +182,7 @@ class DMASimplifierTester(dut: DMASimplifier) extends PeekPokeTester(dut) { } } -class DmaSpec extends FlatSpec with Matchers { +class DmaSpec extends AnyFlatSpec with Matchers { implicit val p: Parameters = (new BaseConfig).toInstance behavior of "DMASimplifier" diff --git a/rocket/src/test/scala/amba/axi4stream/AXI4StreamSpec.scala b/rocket/src/test/scala/amba/axi4stream/AXI4StreamSpec.scala index b90e0625..07c4610c 100644 --- a/rocket/src/test/scala/amba/axi4stream/AXI4StreamSpec.scala +++ b/rocket/src/test/scala/amba/axi4stream/AXI4StreamSpec.scala @@ -9,7 +9,8 @@ import freechips.rocketchip.amba.axi4stream import freechips.rocketchip.config.Parameters import freechips.rocketchip.diplomacy._ import freechips.rocketchip.tilelink._ -import org.scalatest.{FlatSpec, Matchers} +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers class TestModule(val inP: AXI4StreamBundleParameters, outP: AXI4StreamSlaveParameters, @@ -166,7 +167,7 @@ object StreamMuxTester { } } -class AXI4StreamSpec extends FlatSpec with Matchers { +class AXI4StreamSpec extends AnyFlatSpec with Matchers { behavior of "AXI4 Stream Nodes" it should "work with fuzzer and identity" in { val inP = AXI4StreamBundleParameters(n = 2) diff --git a/rocket/src/test/scala/craft/ShiftRegisterMemSpec.scala b/rocket/src/test/scala/craft/ShiftRegisterMemSpec.scala index 91ad1aa4..5bfc4b9a 100644 --- a/rocket/src/test/scala/craft/ShiftRegisterMemSpec.scala +++ b/rocket/src/test/scala/craft/ShiftRegisterMemSpec.scala @@ -4,7 +4,8 @@ package craft import chisel3._ import dsptools.DspTester -import org.scalatest.{FlatSpec, Matchers} +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers class SRMemModule( dut: (UInt, Bool) => UInt ) extends Module { val io = IO(new Bundle { @@ -30,7 +31,7 @@ class SRMemTester( dut: SRMemModule, input: Seq[(Int, Boolean)], expected_output } //noinspection RedundantDefaultArgument,RedundantDefaultArgument,RedundantDefaultArgument,RedundantDefaultArgument -class ShiftRegisterMemSpec extends FlatSpec with Matchers { +class ShiftRegisterMemSpec extends AnyFlatSpec with Matchers { behavior of "ShiftRegisterMem" val testVector: Seq[(Int, Boolean)] = Seq( diff --git a/rocket/src/test/scala/dspblocks/DspBlockSpec.scala b/rocket/src/test/scala/dspblocks/DspBlockSpec.scala index 0b6632b9..b25efccd 100644 --- a/rocket/src/test/scala/dspblocks/DspBlockSpec.scala +++ b/rocket/src/test/scala/dspblocks/DspBlockSpec.scala @@ -5,9 +5,10 @@ package dspblocks import freechips.rocketchip.config.Parameters import freechips.rocketchip.diplomacy._ import freechips.rocketchip.system.BaseConfig -import org.scalatest.{FlatSpec, Matchers} +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers -class DspBlockSpec extends FlatSpec with Matchers { +class DspBlockSpec extends AnyFlatSpec with Matchers { implicit val p: Parameters = (new BaseConfig).toInstance behavior of "Passthrough" diff --git a/rocket/src/test/scala/dspblocks/DspRegisterSpec.scala b/rocket/src/test/scala/dspblocks/DspRegisterSpec.scala index e34512b5..361e5c12 100644 --- a/rocket/src/test/scala/dspblocks/DspRegisterSpec.scala +++ b/rocket/src/test/scala/dspblocks/DspRegisterSpec.scala @@ -7,8 +7,8 @@ import freechips.rocketchip.amba.axi4._ import freechips.rocketchip.amba.axi4stream._ import freechips.rocketchip.config.Parameters import freechips.rocketchip.diplomacy._ -import org.scalatest.{FlatSpec, Matchers} -import DoubleToBigIntRand._ +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers class DspRegisterTestModule( val inP: AXI4StreamBundleParameters, @@ -74,7 +74,7 @@ class DspRegisterTestModuleTester(c: DspRegisterTestModule, println(s"${axiReadWord(0)} is the veclen") } -class DspRegisterSpec extends FlatSpec with Matchers { +class DspRegisterSpec extends AnyFlatSpec with Matchers { behavior of "AXI4DspRegister" it should "be able to read and write" ignore { diff --git a/rocket/src/test/scala/jtag2mm/Jtag2AXI4MultiplexerTester.scala b/rocket/src/test/scala/jtag2mm/Jtag2AXI4MultiplexerTester.scala new file mode 100644 index 00000000..f0ccfffe --- /dev/null +++ b/rocket/src/test/scala/jtag2mm/Jtag2AXI4MultiplexerTester.scala @@ -0,0 +1,243 @@ +// SPDX-License-Identifier: Apache-2.0 + +package freechips.rocketchip.jtag2mm + +import dsptools._ +import freechips.rocketchip.config._ +import freechips.rocketchip.diplomacy._ +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +//class Jtag2AXI4MultiplexerTester(dut: Jtag2AXI4Multiplexer) extends PeekPokeTester(dut.module) { +class Jtag2AXI4MultiplexerTester(dut: Jtag2AXI4Multiplexer) extends DspTester(dut.module) { + def jtagReset(stepSize: Int = 1) { + var i = 0 + while (i < 5) { + poke(dut.ioJTAG.jtag.TCK, 0) + poke(dut.ioJTAG.jtag.TMS, 1) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + i += 1 + } + } + + def jtagSend( + data: BigInt, + dataLength: Int, + dataNotInstruction: Boolean = true, + stateResetNotIdle: Boolean = true, + stepSize: Int = 1 + ) { + + if (stateResetNotIdle) { + poke(dut.ioJTAG.jtag.TCK, 0) + poke(dut.ioJTAG.jtag.TMS, 0) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + } + + poke(dut.ioJTAG.jtag.TCK, 0) + poke(dut.ioJTAG.jtag.TMS, 1) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + + if (!dataNotInstruction) { + poke(dut.ioJTAG.jtag.TCK, 0) + poke(dut.ioJTAG.jtag.TMS, 1) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + } + + poke(dut.ioJTAG.jtag.TCK, 0) + poke(dut.ioJTAG.jtag.TMS, 0) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 0) + poke(dut.ioJTAG.jtag.TMS, 0) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + + var i = 0 + while (i < dataLength - 1) { + poke(dut.ioJTAG.jtag.TCK, 0) + poke(dut.ioJTAG.jtag.TMS, 0) + poke(dut.ioJTAG.jtag.TDI, data.testBit(i)) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + i += 1 + } + + poke(dut.ioJTAG.jtag.TCK, 0) + poke(dut.ioJTAG.jtag.TMS, 1) // 0 + poke(dut.ioJTAG.jtag.TDI, data.testBit(i)) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + + poke(dut.ioJTAG.jtag.TCK, 0) + poke(dut.ioJTAG.jtag.TMS, 1) + poke(dut.ioJTAG.jtag.TDI, 0) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + + poke(dut.ioJTAG.jtag.TCK, 0) + poke(dut.ioJTAG.jtag.TMS, 0) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + } + + val stepSize = 5 + + step(5) + poke(dut.outStream.ready, 1) + step(1) + peek(dut.outStream.bits.data) + expect(dut.outStream.bits.data, 0) + + updatableDspVerbose.withValue(false) { + jtagReset(stepSize) + + // write value 0x08 to address 0x00 + jtagSend(BigInt("0010", 2), 4, false, true, stepSize) + jtagSend(BigInt("0" * 32, 2), 32, true, false, stepSize) + jtagSend(BigInt("0011", 2), 4, false, false, stepSize) + jtagSend(BigInt("0" * 56 ++ "00001000", 2), 64, true, false, stepSize) + jtagSend(BigInt("0001", 2), 4, false, false, stepSize) + } + + step(100) + peek(dut.outStream.bits.data) + expect(dut.outStream.bits.data, 8) + + poke(dut.outStream.ready, 0) + + /*jtagReset(stepSize) + jtagSend(BigInt("0010", 2), 4, false, true, stepSize) + jtagSend(BigInt("0"*32, 2), 32, true, false, stepSize) + jtagSend(BigInt("0011", 2), 4, false, false, stepSize) + jtagSend(BigInt("0"*56 ++ "00011000", 2), 64, true, false, stepSize) + jtagSend(BigInt("0001", 2), 4, false, false, stepSize)*/ + + updatableDspVerbose.withValue(false) { + + // set start address for burst write to 0x08 and number of burst transactions to 2 + jtagSend(BigInt("0010", 2), 4, false, false, stepSize) + jtagSend(BigInt("0" * 24 ++ "00001000", 2), 32, true, false, stepSize) + jtagSend(BigInt("1000", 2), 4, false, false, stepSize) + jtagSend(BigInt("0" * 6 ++ "10", 2), 8, true, false, stepSize) + + // set data value for the first burst write transaction to 0x18 + jtagSend(BigInt("1010", 2), 4, false, false, stepSize) + jtagSend(BigInt("0" * 6 ++ "00", 2), 8, true, false, stepSize) + jtagSend(BigInt("1011", 2), 4, false, false, stepSize) + jtagSend(BigInt("0" * 56 ++ "00011000", 2), 64, true, false, stepSize) + + // set data value for the second burst write transaction to 0x00 + jtagSend(BigInt("1010", 2), 4, false, false, stepSize) + jtagSend(BigInt("0" * 6 ++ "01", 2), 8, true, false, stepSize) + jtagSend(BigInt("1011", 2), 4, false, false, stepSize) + jtagSend(BigInt("0" * 56 ++ "00000000", 2), 64, true, false, stepSize) + + // initiate burst write transactions + jtagSend(BigInt("1001", 2), 4, false, false, stepSize) + + poke(dut.ioJTAG.jtag.TCK, 0) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 0) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 0) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 0) + step(stepSize) + + jtagSend(BigInt("0100", 2), 4, false, false, stepSize) + + var i = 0 + while (i < 32) { + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 0) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 0) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 0) + step(stepSize) + i += 1 + } + } + + poke(dut.outStream.ready, 1) + step(1) + step(1) + peek(dut.outStream.bits.data) + expect(dut.outStream.bits.data, 24) + + updatableDspVerbose.withValue(false) { + + // set start address for burst read to 0x00 and number of burst transactions to 3 + jtagSend(BigInt("0010", 2), 4, false, false, stepSize) + jtagSend(BigInt("0" * 32, 2), 32, true, false, stepSize) + jtagSend(BigInt("1000", 2), 4, false, false, stepSize) + jtagSend(BigInt("0" * 6 ++ "11", 2), 8, true, false, stepSize) + + // initiate burst read transactions + jtagSend(BigInt("1100", 2), 4, false, false, stepSize) + var i = 0 + while (i < 32 * 4) { + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 0) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 0) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 0) + step(stepSize) + i += 1 + } + } + + step(300) +} + +class Jtag2AXI4MultiplexerSpec extends AnyFlatSpec with Matchers { + implicit val p: Parameters = Parameters.empty + + val irLength = 4 + val initialInstruction = BigInt("0", 2) + val addresses = AddressSet(0x00000, 0xffff) + val beatBytes = 8 + val maxBurstNum = 8 + + it should "Test JTAG To AXI4 Multiplexer" in { + val lazyDut = + LazyModule(new Jtag2AXI4Multiplexer(irLength, initialInstruction, beatBytes, addresses, maxBurstNum) {}) + + //chisel3.iotesters.Driver.execute(Array("-tiwv", "-tbn", "verilator", "-tivsuv"), () => lazyDut.module) { c => + chisel3.iotesters.Driver.execute(Array("-tbn", "verilator"), () => lazyDut.module) { c => + new Jtag2AXI4MultiplexerTester(lazyDut) + } should be(true) + } +} diff --git a/rocket/src/test/scala/jtag2mm/Jtag2TLMultiplexerTester.scala b/rocket/src/test/scala/jtag2mm/Jtag2TLMultiplexerTester.scala new file mode 100644 index 00000000..c1e74f36 --- /dev/null +++ b/rocket/src/test/scala/jtag2mm/Jtag2TLMultiplexerTester.scala @@ -0,0 +1,257 @@ +// SPDX-License-Identifier: Apache-2.0 + +package freechips.rocketchip.jtag2mm + +import chisel3._ +import chisel3.util._ +import chisel3.experimental._ + +import dsptools._ +import dsptools.numbers._ + +import dspblocks._ +import freechips.rocketchip.amba.axi4._ +import freechips.rocketchip.amba.axi4stream._ +import freechips.rocketchip.config._ +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.regmapper._ +import freechips.rocketchip.tilelink._ + +import chisel3.iotesters.Driver +import chisel3.iotesters.PeekPokeTester +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +//class Jtag2TLMultiplexerTester(dut: Jtag2TLMultiplexer) extends PeekPokeTester(dut.module) { +class Jtag2TLMultiplexerTester(dut: Jtag2TLMultiplexer) extends DspTester(dut.module) { + def jtagReset(stepSize: Int = 1) { + var i = 0 + while (i < 5) { + poke(dut.ioJTAG.jtag.TCK, 0) + poke(dut.ioJTAG.jtag.TMS, 1) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + i += 1 + } + } + + def jtagSend( + data: BigInt, + dataLength: Int, + dataNotInstruction: Boolean = true, + stateResetNotIdle: Boolean = true, + stepSize: Int = 1 + ) { + + if (stateResetNotIdle) { + poke(dut.ioJTAG.jtag.TCK, 0) + poke(dut.ioJTAG.jtag.TMS, 0) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + } + + poke(dut.ioJTAG.jtag.TCK, 0) + poke(dut.ioJTAG.jtag.TMS, 1) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + + if (!dataNotInstruction) { + poke(dut.ioJTAG.jtag.TCK, 0) + poke(dut.ioJTAG.jtag.TMS, 1) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + } + + poke(dut.ioJTAG.jtag.TCK, 0) + poke(dut.ioJTAG.jtag.TMS, 0) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 0) + poke(dut.ioJTAG.jtag.TMS, 0) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + + var i = 0 + while (i < dataLength - 1) { + poke(dut.ioJTAG.jtag.TCK, 0) + poke(dut.ioJTAG.jtag.TMS, 0) + poke(dut.ioJTAG.jtag.TDI, data.testBit(i)) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + i += 1 + } + + poke(dut.ioJTAG.jtag.TCK, 0) + poke(dut.ioJTAG.jtag.TMS, 1) // 0 + poke(dut.ioJTAG.jtag.TDI, data.testBit(i)) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + + poke(dut.ioJTAG.jtag.TCK, 0) + poke(dut.ioJTAG.jtag.TMS, 1) + poke(dut.ioJTAG.jtag.TDI, 0) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + + poke(dut.ioJTAG.jtag.TCK, 0) + poke(dut.ioJTAG.jtag.TMS, 0) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + } + + val stepSize = 5 + + step(5) + poke(dut.outStream.ready, 1) + step(1) + peek(dut.outStream.bits.data) + expect(dut.outStream.bits.data, 0) + + updatableDspVerbose.withValue(false) { + jtagReset(stepSize) + + // write value 0x08 to address 0x00 + jtagSend(BigInt("0010", 2), 4, false, true, stepSize) + jtagSend(BigInt("0" * 32, 2), 32, true, false, stepSize) + jtagSend(BigInt("0011", 2), 4, false, false, stepSize) + jtagSend(BigInt("0" * 56 ++ "00001000", 2), 64, true, false, stepSize) + jtagSend(BigInt("0001", 2), 4, false, false, stepSize) + } + + step(100) + peek(dut.outStream.bits.data) + expect(dut.outStream.bits.data, 8) + + poke(dut.outStream.ready, 0) + + /*jtagReset(stepSize) + jtagSend(BigInt("0010", 2), 4, false, true, stepSize) + jtagSend(BigInt("0"*32, 2), 32, true, false, stepSize) + jtagSend(BigInt("0011", 2), 4, false, false, stepSize) + jtagSend(BigInt("0"*56 ++ "00011000", 2), 64, true, false, stepSize) + jtagSend(BigInt("0001", 2), 4, false, false, stepSize)*/ + + updatableDspVerbose.withValue(false) { + + // set start address for burst write to 0x08 and number of burst transactions to 2 + jtagSend(BigInt("0010", 2), 4, false, false, stepSize) + jtagSend(BigInt("0" * 24 ++ "00001000", 2), 32, true, false, stepSize) + jtagSend(BigInt("1000", 2), 4, false, false, stepSize) + jtagSend(BigInt("0" * 6 ++ "10", 2), 8, true, false, stepSize) + + // set data value for the first burst write transaction to 0x18 + jtagSend(BigInt("1010", 2), 4, false, false, stepSize) + jtagSend(BigInt("0" * 6 ++ "00", 2), 8, true, false, stepSize) + jtagSend(BigInt("1011", 2), 4, false, false, stepSize) + jtagSend(BigInt("0" * 56 ++ "00011000", 2), 64, true, false, stepSize) + + // set data value for the second burst write transaction to 0x00 + jtagSend(BigInt("1010", 2), 4, false, false, stepSize) + jtagSend(BigInt("0" * 6 ++ "01", 2), 8, true, false, stepSize) + jtagSend(BigInt("1011", 2), 4, false, false, stepSize) + jtagSend(BigInt("0" * 56 ++ "00000000", 2), 64, true, false, stepSize) + + // initiate burst write transactions + jtagSend(BigInt("1001", 2), 4, false, false, stepSize) + + poke(dut.ioJTAG.jtag.TCK, 0) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 0) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 0) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 0) + step(stepSize) + + jtagSend(BigInt("0100", 2), 4, false, false, stepSize) + + var i = 0 + while (i < 32) { + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 0) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 0) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 0) + step(stepSize) + i += 1 + } + } + + poke(dut.outStream.ready, 1) + step(1) + peek(dut.outStream.bits.data) + expect(dut.outStream.bits.data, 24) + + updatableDspVerbose.withValue(false) { + + // set start address for burst read to 0x00 and number of burst transactions to 3 + jtagSend(BigInt("0010", 2), 4, false, false, stepSize) + jtagSend(BigInt("0" * 32, 2), 32, true, false, stepSize) + jtagSend(BigInt("1000", 2), 4, false, false, stepSize) + jtagSend(BigInt("0" * 6 ++ "11", 2), 8, true, false, stepSize) + + // initiate burst read transactions + jtagSend(BigInt("1100", 2), 4, false, false, stepSize) + + var i = 0 + while (i < 32 * 4) { + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 0) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 0) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 1) + step(stepSize) + poke(dut.ioJTAG.jtag.TCK, 0) + step(stepSize) + i += 1 + } + } + + step(300) +} + +class Jtag2TLMultiplexerSpec extends AnyFlatSpec with Matchers { + implicit val p: Parameters = Parameters.empty + + val irLength = 4 + val initialInstruction = BigInt("0", 2) + val addresses = AddressSet(0x00000, 0xffff) + val beatBytes = 8 + val maxBurstNum = 8 + + it should "Test JTAG To TL Multiplexer" in { + val lazyDut = + LazyModule(new Jtag2TLMultiplexer(irLength, initialInstruction, beatBytes, addresses, maxBurstNum) {}) + + //chisel3.iotesters.Driver.execute(Array("-tiwv", "-tbn", "verilator", "-tivsuv"), () => lazyDut.module) { c => + chisel3.iotesters.Driver.execute(Array("-tbn", "verilator"), () => lazyDut.module) { c => + new Jtag2TLMultiplexerTester(lazyDut) + } should be(true) + } +} diff --git a/rocket/src/test/scala/jtag2mm/JtagFuzzerTester.scala b/rocket/src/test/scala/jtag2mm/JtagFuzzerTester.scala new file mode 100644 index 00000000..e4b67d1e --- /dev/null +++ b/rocket/src/test/scala/jtag2mm/JtagFuzzerTester.scala @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: Apache-2.0 + +package freechips.rocketchip.jtag2mm + +import chisel3._ +import chisel3.util._ +import chisel3.util.random.LFSR +import chisel3.experimental._ +import chisel3.iotesters.Driver +import chisel3.iotesters.PeekPokeTester +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import dsptools.DspTester + +class JtagFuzzerTester(dut: JtagFuzzer) extends DspTester(dut) { + + step(10) + step(5) + step(2500) +} + + +class JtagFuzzerSpec extends AnyFlatSpec with Matchers { + + def dut(irLength: Int, beatBytes: Int, numOfTransfers: Int): () => JtagFuzzer = () => { + new JtagFuzzer(irLength, beatBytes, numOfTransfers) + } + + val beatBytes = 4 + val irLength = 4 + val numOfTransfers = 10 + + it should "Test JTAG Fuzzer" in { + + //chisel3.iotesters.Driver.execute(Array("-tiwv", "-tbn", "verilator", "-tivsuv"), () => dut) { c => + chisel3.iotesters.Driver.execute(Array("-tbn", "verilator"), dut(irLength, beatBytes, numOfTransfers)) { c => + new JtagFuzzerTester(c) + } should be(true) + } +} diff --git a/rocket/src/test/scala/tester/MemMasterSpec.scala b/rocket/src/test/scala/tester/MemMasterSpec.scala index 3892a249..3e2dd03c 100644 --- a/rocket/src/test/scala/tester/MemMasterSpec.scala +++ b/rocket/src/test/scala/tester/MemMasterSpec.scala @@ -9,7 +9,8 @@ import freechips.rocketchip.diplomacy._ import freechips.rocketchip.interrupts._ import freechips.rocketchip.regmapper._ import freechips.rocketchip.tilelink._ -import org.scalatest.{FlatSpec, Matchers} +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers trait RegmapExample extends HasRegMap { val r0 = RegInit(0.U(64.W)) @@ -97,7 +98,7 @@ class APBRegmapExample extends APBRegisterRouter(0, beatBytes = 8, interrupts = } } -class MemMasterSpec extends FlatSpec with Matchers { +class MemMasterSpec extends AnyFlatSpec with Matchers { abstract class RegmapExampleTester[M <: MultiIOModule](c: M) extends PeekPokeTester(c) with MemMasterModel { memReadWord(0x00) should be (0) memReadWord(0x08) should be (1)