Created
January 15, 2018 16:59
-
-
Save ahappyforest/41c41a5bb0e3ef67b817fd4d58d1d95f to your computer and use it in GitHub Desktop.
DebugTransportModuleJtag.v
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
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 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; | |
wire [3:0] debugVersion = DEBUG_VERSION; | |
assign dtminfo = {24'b0, 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; | |
// This is needed especially for the first request. | |
assign nonzeroResp = dtm_resp_valid ? |{dtm_resp_bits[DEBUG_OP_BITS-1:0]} : 1'b0; | |
// 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; | |
end else if (irReg == REG_DEBUG_ACCESS) begin | |
case(jtagStateReg) | |
CAPTURE_DR: begin | |
skipOpReg <= busy; | |
downgradeOpReg <= (~busy & nonzeroResp); | |
end | |
UPDATE_DR: begin | |
skipOpReg <= 1'b0; | |
downgradeOpReg <= 1'b0; | |
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment