//
//
//  Copyright (c) 2007. Actel Inc. All Rights Reserved.
//
//                  PROPRIETARY INFORMATION
//
//  This document is CONFIDENTIAL and PROPRIETARY and is for
//  the use of Actel Inc. personnel only, except to the 
//  extent that permission is expressly granted elsewhere.
//
//
//  Title:      iap.v
//  Created:    September 06, 2007
//  Author:     Ken Irving
//
//  Function: APB IAP Controller
//  New version from Ken December, 2 2009
//  
//  3/5/10 FJ: Modified the IAP_DR_LENGTH command to support 
//             8 bits of shift size.
/////////////////////////////////////////////////////////////
//
// 





module iap(TCK, TMS, TDI, TRSTB, TDO,

               MISO, MOSI, SCK, SS, CPHA, CPOL

			  ,APB_PWDATA
			  ,APB_PRDATA
	  		  ,APB_PADDR
			  ,APB_PWRITE
			  ,APB_PSELx
			  ,APB_PENABLE
			  ,APB_PREADY
			  ,APB_PRESETn
			  ,APB_PCLK,
              
              BUSY);

parameter SPISLAVES = 1;

localparam dwidth = 16;

output TCK;
output reg TMS;
output reg TDI;
output TRSTB;
input TDO;

// SPI

input CPHA, CPOL;

input MISO;
output reg MOSI;
output SCK;
output reg [SPISLAVES-1:0] SS;

output BUSY;

output [31:0] APB_PRDATA;

`ifdef APB3
input [31:0] APB_PADDR;
`else
input [23:0] APB_PADDR;
`endif

input APB_PSELx;
input APB_PWRITE;
input APB_PENABLE;
input [31:0] APB_PWDATA;
output APB_PREADY;
input APB_PRESETn;
input APB_PCLK;

// JTAG state encoding

localparam [3:0]    JTAG_TEST_LOGIC_RESET 	=  4'd15,
				    JTAG_RUN_TEST_IDLE    	=  4'd12,
					JTAG_SELECT_DR        	=  4'd7,
				 	JTAG_CAPTURE_DR			=  4'd6,
			      	JTAG_SHIFT_DR      		=  4'd2,
					JTAG_EXIT1_DR      		=	4'd1,
					JTAG_PAUSE_DR        	=  4'd3,
					JTAG_EXIT2_DR         	=  4'd0,
					JTAG_UPDATE_DR         	=  4'd5,
					JTAG_SELECT_IR          =  4'd4,
					JTAG_CAPTURE_IR         =  4'd14,
					JTAG_SHIFT_IR           =  4'd10,
					JTAG_EXIT1_IR           =  4'd9,
					JTAG_PAUSE_IR           =  4'd11,
					JTAG_EXIT2_IR           =  4'd8,
					JTAG_UPDATE_IR          =  4'd13 ;
                  

reg [3:0] currentState, targetState;

/*
 * Given a startState and endState derive the next TMS value needed to
 * reach the endState.
 *
 * For each possible startState we encode TMS values as a 16-bit vector,
 * where the vector index is a state encoding.  This effectively implements a
 * 2-D array.  Note that 2-D wire arrays are allowed in Verilog-2001 but they 
 * can't be initialized in a useful way.  Using a case statement seems to be
 * the most concise way of describing it.
 */



function tms;
input [3:0] startState, endState;
reg [15:0] edges;
begin

	case (startState)					   /*    FEDCBA9876543210 */

		JTAG_TEST_LOGIC_RESET: 	edges = 16'b1000000000000000;
		JTAG_RUN_TEST_IDLE:    	edges = 16'b1110111111111111;				
		JTAG_SELECT_DR:        	edges = 16'b1110111110010000;
  		JTAG_CAPTURE_DR:		edges = 16'b1111111111111011;
		JTAG_SHIFT_DR:      	edges = 16'b1111111111111011;
		JTAG_EXIT1_DR:      	edges = 16'b1111111111110000;
		JTAG_PAUSE_DR:         	edges = 16'b1111111111110111;
		JTAG_EXIT2_DR:		    edges = 16'b1111111111110000;
		JTAG_UPDATE_DR:        	edges = 16'b1110111111111111;
		JTAG_SELECT_IR:        	edges = 16'b1000000000000000;
		JTAG_CAPTURE_IR:        edges = 16'b1111101111111111;
		JTAG_SHIFT_IR:          edges = 16'b1111101111111111;
		JTAG_EXIT1_IR:          edges = 16'b1111000011111111;
		JTAG_PAUSE_IR:          edges = 16'b1111011111111111;					
		JTAG_EXIT2_IR:          edges = 16'b1111000011111111;  		
		JTAG_UPDATE_IR:         edges = 16'b1110111111111111;
          					
	endcase

	tms = edges[endState];
end
endfunction



reg [3:0] nextState;

always @(TMS or currentState)

begin
	case (currentState)
		JTAG_TEST_LOGIC_RESET:  nextState = TMS ? JTAG_TEST_LOGIC_RESET : JTAG_RUN_TEST_IDLE;
		JTAG_SELECT_DR	:       nextState = TMS ? JTAG_SELECT_IR : JTAG_CAPTURE_DR;
		JTAG_CAPTURE_DR,	
        JTAG_SHIFT_DR	:       nextState = TMS ? JTAG_EXIT1_DR : JTAG_SHIFT_DR;
		JTAG_EXIT1_DR	:       nextState = TMS ? JTAG_UPDATE_DR : JTAG_PAUSE_DR;
		JTAG_PAUSE_DR   :       nextState = TMS ? JTAG_EXIT2_DR : JTAG_PAUSE_DR;
		JTAG_EXIT2_DR	:       nextState = TMS ? JTAG_UPDATE_DR : JTAG_SHIFT_DR;
		JTAG_SELECT_IR	:       nextState = TMS ? JTAG_TEST_LOGIC_RESET :  JTAG_CAPTURE_IR;
		JTAG_CAPTURE_IR,
		JTAG_SHIFT_IR	:       nextState = TMS ? JTAG_EXIT1_IR : JTAG_SHIFT_IR;
		JTAG_EXIT1_IR   :       nextState = TMS ? JTAG_UPDATE_IR : JTAG_PAUSE_IR;
		JTAG_PAUSE_IR	:       nextState = TMS ? JTAG_EXIT2_IR : JTAG_PAUSE_IR;
		JTAG_EXIT2_IR 	:       nextState = TMS ? JTAG_UPDATE_IR : JTAG_SHIFT_IR;
	    JTAG_RUN_TEST_IDLE,
	    JTAG_UPDATE_DR,
	    JTAG_UPDATE_IR	:       nextState = TMS ? JTAG_SELECT_DR : JTAG_RUN_TEST_IDLE;
	endcase  

end




reg [7:0] tckCnt;		// needs to be able to count down from a min of 64


reg [dwidth-1:0] datareg;
reg [7:0] drLength;






// Register address map

localparam [5:0] 	IAP_IR              	= { 4'd0, 2'bx},
					IAP_DR2       			= { 4'd1, 2'bx},
					IAP_DR3       			= { 4'd2, 2'bx},
				    IAP_DR5       			= { 4'd3, 2'bx},
					IAP_DR26      			= { 4'd4, 2'bx},
					IAP_DR16     			= { 4'd5, 2'bx},
					IAP_DR        			= { 4'd6, 2'bx},
					IAP_DR_LENGTH   		= { 4'd7, 2'bx},
					IAP_TAP_NEW_STATE   	= { 4'd8, 2'bx},
					IAP_TAP_CONTROL  	  	= { 4'd9, 2'bx},

                    IAP_SPI_SS              = { 4'd10, 2'bx},
                    IAP_SPI_TRANSFER        = { 4'd11, 2'bx},
                    IAP_SPI_DATA            = { 4'd12, 2'bx};
					
												



// state machine encodings


localparam [2:0]      IDLE          = 3'b100,
 					  MOVE          = 3'b001,
					  SCAN          = 3'b010,
					  SPI_TRANSFER  = 3'b011,
                      SPI_COMMAND   = 3'b000;  

reg [2:0] state;

reg tckEn, sckEn;



assign TCK = APB_PCLK ? 1'b1 : !tckEn;

wire sck = APB_PCLK ? 1'b1 : !sckEn;  

assign SCK = CPOL ^ !sck;

/*
 *  Set up the state machine for a IR/DRSCAN operation
 */


task scan (input [7:0] nbits, input [1:0] s, input [3:0] shiftState);
begin
	datareg <= APB_PWDATA;
	tckCnt <= nbits;
	state <= s;
	targetState <= shiftState;
    tckEn <= 1'b1;
end
endtask



/*
 * Inject TDO at specified point in register
 */

function [dwidth-1:0] capture(input [dwidth-1:0] d, input [4:0] p);
begin

    capture = { TDO, d[dwidth-1:1] };
end
endfunction



wire busy = (state != IDLE);

assign BUSY = busy;


assign APB_PRDATA = datareg;


assign APB_PREADY = !busy;

// APB_PSELx && APB_PENABLE && !(APB_PWRITE && busy);


wire write = APB_PENABLE && APB_PWRITE && APB_PSELx;

wire read = APB_PENABLE && APB_PSELx && !APB_PWRITE;

assign TRSTB = APB_PRESETn;


always @(negedge TCK or negedge TRSTB)
begin

    if (!TRSTB) begin

        TMS <= 1'b1;
        TDI <= 1'b1;

    end else
        casex (state)

            MOVE:           TMS <= tms(currentState, targetState);

            SCAN,
            SPI_TRANSFER:   if (currentState != targetState)        
                               TMS <= tms(currentState, targetState);
                            else begin
                               TMS <= (tckCnt == 8'h01) || (tckCnt == 8'h00);        
							   TDI <= (state == SCAN) ? datareg[0] : MISO;
                            end

        endcase
end

always @(posedge TCK or negedge TRSTB)
begin
    if (!TRSTB)
        currentState <= JTAG_TEST_LOGIC_RESET;
    else
        currentState <= nextState;
end




always @(posedge APB_PCLK or negedge APB_PRESETn)
begin

	if (!APB_PRESETn) begin	

        tckEn <= 0;
        sckEn <= 0;

		state <= IDLE;

        SS <= ~ 'b0;
        drLength <= 8'h00;
        
	end else casex (state)

        
		IDLE:  begin

					if (write)  begin

					
						casex (APB_PADDR[5:0])

							IAP_TAP_NEW_STATE: begin
                                                   targetState <= APB_PWDATA[3:0];
													
														tckCnt <= {4'h0,APB_PWDATA[7:4]};			
														state <= MOVE;
                                                        tckEn <= 1;
													end

			
							IAP_IR: scan(8, SCAN, JTAG_SHIFT_IR);
						

							IAP_DR: scan( ((drLength > 16) ? 16 : drLength), SCAN, JTAG_SHIFT_DR);

							IAP_DR2:  scan(2, SCAN, JTAG_SHIFT_DR);

							IAP_DR3:  scan(3, SCAN, JTAG_SHIFT_DR);

							IAP_DR5:  scan(5, SCAN, JTAG_SHIFT_DR);
												
						  	IAP_DR16: scan(16, SCAN, JTAG_SHIFT_DR);
												

							IAP_DR_LENGTH: drLength <= APB_PWDATA[7:0];

                            IAP_SPI_TRANSFER: scan(APB_PWDATA[7:0], SPI_TRANSFER, JTAG_SHIFT_DR);

                            IAP_SPI_DATA:    begin       
                                                state <= SPI_COMMAND;
                                                datareg <= APB_PWDATA;
                                            end


                            IAP_SPI_SS  : SS <= APB_PWDATA[SPISLAVES-1:0];

                                             
						endcase
				
					end
				end



		MOVE:	  if (nextState == targetState) begin

                        if (tckCnt == 8'h00) begin
                             tckEn <= 1'b0;
                             state <= IDLE;
                        end else
                             tckCnt <= tckCnt - 8'h01;
					end


		SCAN,
        SPI_TRANSFER:	begin

                            if (nextState == targetState)
                                sckEn <= (state == SPI_TRANSFER);

				            if (currentState == targetState) begin   // shift

                                if (state == SCAN) begin
    							    datareg <= capture(datareg, 0);
								    drLength <= drLength - 8'h01;
                                end
 								if ((tckCnt == 8'h00) || (tckCnt == 8'h01)) begin

							       if (targetState == JTAG_SHIFT_DR)
									    targetState <= JTAG_PAUSE_DR;
								    else
									    targetState <= JTAG_PAUSE_IR;

    								state <= MOVE;
                                    sckEn <= 0;

								end else
                                    tckCnt <= tckCnt - 8'h01;
						    end
					    end


        SPI_COMMAND:  begin
                            MOSI <= datareg[0];
                            datareg <= {1'b0, datareg[dwidth-1:1] };
                            tckCnt <= tckCnt - 8'h01;

                            if (tckCnt == 8'h00) begin
                                state  <= IDLE;
                                sckEn <= 0;
                            end else
                                sckEn <= 1'b1;
                        end



	endcase
end


endmodule