module DebugTransportModuleJtag ( //JTAG Interface jtag_TDI, jtag_TDO, jtag_TCK, jtag_TMS, jtag_TRST, jtag_DRV_TDO, dtm_req_valid, dtm_req_ready, dtm_req_bits, dtm_resp_valid, dtm_resp_ready, dtm_resp_bits ); //-------------------------------------------------------- // Parameter Declarations parameter DEBUG_DATA_BITS = 34; parameter DEBUG_ADDR_BITS = 5; // Spec allows values are 5-7 parameter DEBUG_OP_BITS = 2; // OP and RESP are the same size. parameter JTAG_VERSION = 4'h1; parameter JTAG_PART_NUM = 16'h0E31; // E31 parameter JTAG_MANUF_ID = 11'h489; // As Assigned by JEDEC // Number of cycles which must remain in IDLE // The software should handle even if the // answer is actually higher than this, or // the software may choose to ignore it entirely // and just check for busy. parameter DBUS_IDLE_CYCLES = 3'h5; localparam IR_BITS = 5; localparam DEBUG_VERSION = 0; // JTAG State Machine localparam TEST_LOGIC_RESET = 4'h0; localparam RUN_TEST_IDLE = 4'h1; localparam SELECT_DR = 4'h2; localparam CAPTURE_DR = 4'h3; localparam SHIFT_DR = 4'h4; localparam EXIT1_DR = 4'h5; localparam PAUSE_DR = 4'h6; localparam EXIT2_DR = 4'h7; localparam UPDATE_DR = 4'h8; localparam SELECT_IR = 4'h9; localparam CAPTURE_IR = 4'hA; localparam SHIFT_IR = 4'hB; localparam EXIT1_IR = 4'hC; localparam PAUSE_IR = 4'hD; localparam EXIT2_IR = 4'hE; localparam UPDATE_IR = 4'hF; //RISCV DTM Registers (see RISC-V Debug Specification) // All others are treated as 'BYPASS'. localparam REG_BYPASS = 5'b11111; localparam REG_IDCODE = 5'b00001; localparam REG_DEBUG_ACCESS = 5'b10001; localparam REG_DTM_INFO = 5'b10000; localparam DBUS_REG_BITS = DEBUG_OP_BITS + DEBUG_ADDR_BITS + DEBUG_DATA_BITS; localparam DBUS_REQ_BITS = DEBUG_OP_BITS + DEBUG_ADDR_BITS + DEBUG_DATA_BITS; localparam DBUS_RESP_BITS = DEBUG_OP_BITS + DEBUG_DATA_BITS; localparam SHIFT_REG_BITS = DBUS_REG_BITS; //-------------------------------------------------------- // I/O Declarations //JTAG SIDE input jtag_TDI; output reg jtag_TDO; input jtag_TCK; input jtag_TMS; input jtag_TRST; // To allow tri-state outside of this block. output reg jtag_DRV_TDO; // RISC-V Core Side output dtm_req_valid; input dtm_req_ready; output [DBUS_REQ_BITS - 1 :0] dtm_req_bits; input dtm_resp_valid; output dtm_resp_ready; input [DBUS_RESP_BITS - 1 : 0] dtm_resp_bits; //-------------------------------------------------------- // Reg and Wire Declarations reg [IR_BITS -1 : 0 ] irReg; wire [31:0] idcode; wire [31:0] dtminfo; reg [DBUS_REG_BITS - 1 : 0] dbusReg; reg dbusValidReg; reg [3:0] jtagStateReg; reg [SHIFT_REG_BITS -1 : 0] shiftReg; reg doDbusWriteReg; reg doDbusReadReg; reg busyReg; reg stickyBusyReg; reg stickyNonzeroRespReg; reg skipOpReg; // Skip op because we're busy. reg downgradeOpReg; // Downgrade op because prev. op failed. wire busy; wire nonzeroResp; wire [SHIFT_REG_BITS -1 : 0] busyResponse; wire [SHIFT_REG_BITS -1 : 0] nonbusyResponse; //-------------------------------------------------------- // Combo Logic assign idcode = {JTAG_VERSION, JTAG_PART_NUM, JTAG_MANUF_ID, 1'h1}; wire [3:0] debugAddrBits = DEBUG_ADDR_BITS[3:0]; wire [3:0] debugVersion = DEBUG_VERSION[3:0]; wire [1:0] dbusStatus; wire [2:0] dbusIdleCycles; wire dbusReset; assign dbusIdleCycles = DBUS_IDLE_CYCLES; assign dbusStatus = {stickyNonzeroRespReg, stickyNonzeroRespReg | stickyBusyReg}; assign dbusReset = shiftReg[16]; assign dtminfo = {15'b0, 1'b0, // dbusreset goes here but is write-only 3'b0, dbusIdleCycles, dbusStatus, debugAddrBits, debugVersion}; //busy, dtm_resp* is only valid during CAPTURE_DR, // so these signals should only be used at that time. // This assumes there is only one transaction in flight at a time. assign busy = (busyReg & ~dtm_resp_valid); // stickyBusyReg; // This is needed especially for the first request. assign nonzeroResp = (dtm_resp_valid ? |{dtm_resp_bits[DEBUG_OP_BITS-1:0]} : 1'b0); //stickyNonzeroRespReg; // Interface to DM. // Note that this means dtm_resp_bits must only be used during CAPTURE_DR. assign dtm_resp_ready = (jtagStateReg == CAPTURE_DR) && (irReg == REG_DEBUG_ACCESS) && dtm_resp_valid; assign dtm_req_valid = dbusValidReg; assign dtm_req_bits = dbusReg; assign busyResponse = {{(DEBUG_ADDR_BITS + DEBUG_DATA_BITS){1'b0}}, {(DEBUG_OP_BITS){1'b1}}}; // Generalizing 'busy' to 'all-1' assign nonbusyResponse = {dbusReg[(DEBUG_DATA_BITS + DEBUG_OP_BITS) +: DEBUG_ADDR_BITS] , // retain address bits from Req. dtm_resp_bits[DEBUG_OP_BITS +: DEBUG_DATA_BITS] , // data dtm_resp_bits[0 +: DEBUG_OP_BITS] // response }; //-------------------------------------------------------- // Sequential Logic // JTAG STATE MACHINE always @(posedge jtag_TCK or posedge jtag_TRST) begin if (jtag_TRST) begin jtagStateReg <= TEST_LOGIC_RESET; end else begin case (jtagStateReg) TEST_LOGIC_RESET : jtagStateReg <= jtag_TMS ? TEST_LOGIC_RESET : RUN_TEST_IDLE; RUN_TEST_IDLE : jtagStateReg <= jtag_TMS ? SELECT_DR : RUN_TEST_IDLE; SELECT_DR : jtagStateReg <= jtag_TMS ? SELECT_IR : CAPTURE_DR; CAPTURE_DR : jtagStateReg <= jtag_TMS ? EXIT1_DR : SHIFT_DR; SHIFT_DR : jtagStateReg <= jtag_TMS ? EXIT1_DR : SHIFT_DR; EXIT1_DR : jtagStateReg <= jtag_TMS ? UPDATE_DR : PAUSE_DR; PAUSE_DR : jtagStateReg <= jtag_TMS ? EXIT2_DR : PAUSE_DR; EXIT2_DR : jtagStateReg <= jtag_TMS ? UPDATE_DR : SHIFT_DR; UPDATE_DR : jtagStateReg <= jtag_TMS ? SELECT_DR : RUN_TEST_IDLE; SELECT_IR : jtagStateReg <= jtag_TMS ? TEST_LOGIC_RESET : CAPTURE_IR; CAPTURE_IR : jtagStateReg <= jtag_TMS ? EXIT1_IR : SHIFT_IR; SHIFT_IR : jtagStateReg <= jtag_TMS ? EXIT1_IR : SHIFT_IR; EXIT1_IR : jtagStateReg <= jtag_TMS ? UPDATE_IR : PAUSE_IR; PAUSE_IR : jtagStateReg <= jtag_TMS ? EXIT2_IR : PAUSE_IR; EXIT2_IR : jtagStateReg <= jtag_TMS ? UPDATE_IR : SHIFT_IR; UPDATE_IR : jtagStateReg <= jtag_TMS ? SELECT_DR : RUN_TEST_IDLE; endcase // case (jtagStateReg) end // else: !if(jtag_TRST) end // always @ (posedge jtag_TCK or posedge jtag_TRST) // SHIFT REG always @(posedge jtag_TCK) begin case (jtagStateReg) CAPTURE_IR : shiftReg <= {{(SHIFT_REG_BITS-1){1'b0}}, 1'b1}; //JTAG spec only says must end with 'b01. SHIFT_IR : shiftReg <= {{(SHIFT_REG_BITS-IR_BITS){1'b0}}, jtag_TDI, shiftReg[IR_BITS-1 : 1]}; CAPTURE_DR : case (irReg) REG_BYPASS : shiftReg <= {(SHIFT_REG_BITS){1'b0}}; REG_IDCODE : shiftReg <= {{(SHIFT_REG_BITS-32){1'b0}}, idcode}; REG_DTM_INFO : shiftReg <= {{(SHIFT_REG_BITS-32){1'b0}}, dtminfo}; REG_DEBUG_ACCESS : shiftReg <= busy ? busyResponse : nonbusyResponse; default : //BYPASS shiftReg <= {(SHIFT_REG_BITS){1'b0}}; endcase SHIFT_DR : case (irReg) REG_BYPASS : shiftReg <= {{(SHIFT_REG_BITS- 1){1'b0}}, jtag_TDI}; REG_IDCODE : shiftReg <= {{(SHIFT_REG_BITS-32){1'b0}}, jtag_TDI, shiftReg[31:1]}; REG_DTM_INFO : shiftReg <= {{(SHIFT_REG_BITS-32){1'b0}}, jtag_TDI, shiftReg[31:1]}; REG_DEBUG_ACCESS : shiftReg <= {jtag_TDI, shiftReg[SHIFT_REG_BITS -1 : 1 ]}; default: // BYPASS shiftReg <= {{(SHIFT_REG_BITS- 1){1'b0}} , jtag_TDI}; endcase // case (irReg) endcase // case (jtagStateReg) end // IR always @(negedge jtag_TCK or posedge jtag_TRST) begin if (jtag_TRST) begin irReg <= REG_IDCODE; end else if (jtagStateReg == TEST_LOGIC_RESET) begin irReg <= REG_IDCODE; end else if (jtagStateReg == UPDATE_IR) begin irReg <= shiftReg[IR_BITS-1:0]; end end // Busy. We become busy when we first try to send a request. // We stop being busy when we accept a response. // This means that busyReg will still be set when we check it, // so the logic for checking busy looks ahead. always @(posedge jtag_TCK or posedge jtag_TRST) begin if (jtag_TRST) begin busyReg <= 1'b0; end else if (dtm_req_valid) begin //UPDATE_DR onwards busyReg <= 1'b1; end else if (dtm_resp_valid & dtm_resp_ready) begin //only in CAPTURE_DR busyReg <= 1'b0; end end // always @ (posedge jtag_TCK or posedge jtag_TRST) // Downgrade/Skip. We make the decision to downgrade or skip // during every CAPTURE_DR, and use the result in UPDATE_DR. always @(posedge jtag_TCK or posedge jtag_TRST) begin if (jtag_TRST) begin skipOpReg <= 1'b0; downgradeOpReg <= 1'b0; stickyBusyReg <= 1'b0; stickyNonzeroRespReg <= 1'b0; end else if (irReg == REG_DEBUG_ACCESS) begin case(jtagStateReg) CAPTURE_DR: begin skipOpReg <= busy; downgradeOpReg <= (~busy & nonzeroResp); stickyBusyReg <= busy; stickyNonzeroRespReg <= nonzeroResp; end UPDATE_DR: begin skipOpReg <= 1'b0; downgradeOpReg <= 1'b0; end endcase // case (jtagStateReg) end else if (irReg == REG_DTM_INFO) begin case(jtagStateReg) UPDATE_DR: begin if (dbusReset) begin stickyNonzeroRespReg <= 1'b0; stickyBusyReg <= 1'b0; end end endcase // case (jtagStateReg) end end // always @ (posedge jtag_TCK or posedge jtag_TRST) //dbusReg, dbusValidReg. always @(posedge jtag_TCK or posedge jtag_TRST) begin if (jtag_TRST) begin dbusReg <= {(DBUS_REG_BITS) {1'b0}}; dbusValidReg <= 1'b0; end else if (jtagStateReg == UPDATE_DR) begin if (irReg == REG_DEBUG_ACCESS) begin if (skipOpReg) begin // do nothing. end else if (downgradeOpReg) begin dbusReg <= {(DBUS_REG_BITS){1'b0}}; // NOP has encoding 2'b00. dbusValidReg <= 1'b1; end else begin dbusReg <= shiftReg[DBUS_REG_BITS-1:0]; dbusValidReg <= 1'b1; end end end else if (dtm_req_ready) begin dbusValidReg <= 1'b0; end end // always @ (negedge jtag_TCK or posedge jtag_TRST) //TDO always @(negedge jtag_TCK or posedge jtag_TRST) begin if (jtag_TRST) begin jtag_TDO <= 1'b0; jtag_DRV_TDO <= 1'b0; end else if (jtagStateReg == SHIFT_IR) begin jtag_TDO <= shiftReg[0]; jtag_DRV_TDO <= 1'b1; end else if (jtagStateReg == SHIFT_DR) begin jtag_TDO <= shiftReg[0]; jtag_DRV_TDO <= 1'b1; end else begin jtag_TDO <= 1'b0; jtag_DRV_TDO <= 1'b0; end end // always @ (negedge jtag_TCK or posedge jtag_TRST) endmodule