// See LICENSE.SiFive for license details.

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

   
