/* -------------------------------------------------------------------------------
 Title      : AXI Master Interface
----------------------------------------------------------------------------------
 File       : AXI_IF.v
 Author     : Corporate Applications Engineering
 Company    : Microsemi Corporation
 Device     : SmartFusion2
 Standard   : Verilog
----------------------------------------------------------------------------------
 Description: The AXI master (AXI_IF) block consists of AXI read channel, AXI 
 write channel, write throughput counter, read throughput counter and a 512x64 
 LSRAM. AXI master performs the read or write operation based on the inputs 
 signals from command decoder. During the write operation, the AXI master reads 
 the content from LSRAM and writes into DDR3 memory and measures the write 
 throughput. During the read operation, the AXI master read the content from DDR3 
 memory and writes into LSRAM and measures the read throughput. The write 
 throughput counter counts the AXI clocks between AWVALID of first data and WLAST 
 of last data. Similarly read throughput counter counts the AXI clocks between 
 ARVALID of first data and RLAST of last data. After each write or read operation, 
 the AXI master sends the throughput count value and an eSRAM address starting 
 from 0x 20008104 to AHB master. 
----------------------------------------------------------------------------------
 Copyright (c) 2014   Microsemi Corporation
                      All rights reserved.
----------------------------------------------------------------------------------
 Revisions  : V1.0
----------------------------------------------------------------------------------*/

module AXI_IF(

	input 				CLK,
	input 				RESETn,
	input 				write_start,
	input 				read_start,
	input   wire        AHB_BUSY,
	input       [9:0]   w_xfer_size,
    input       [9:0]   r_xfer_size,
	input		[3:0]	WLEN,               
	input       [3:0]   RLEN,              

	output	reg			AXI_BUSY,	
	output  reg         WRITE_AHB,
	output  reg  [31:0] AHB_ADDR,
	output  reg  [31:0] AHB_DATA,
	output	reg			done,
	output	reg			read_done,

  	/* Write Address Channel (WAC) */
	output		[3:0]   AWID, 
	output reg [31:0]   AWADDR, 
  	output  	[3:0]   AWLEN, 
  	output reg 	[2:0]   AWSIZE, 
  	output  	[1:0]   AWLOCK,  
  	output reg 	[1:0]   AWBURST, 
  	output reg          AWVALID_ext,  
  	input               AWREADY,  

  	/*  Write Data Channel (RDC) */
  	output  [3:0]       WID, 
  	output reg [7:0]    WSTRB,
  	output reg          WLAST,
  	output reg          WVALID_ext,
  	output reg [63:0]   WDATA,  
  	input               WREADY, 
	
  	/* Write Response Channel (WRC) */
 	input [3:0]         BID,
	input [1:0]         BRESP,  
	input               BVALID,	
	output              BREADY,

    /* Read Address Channel (RAC) */
	output [3:0]        ARID, 
	output reg [31:0]   ARADDR, 
	output     [3:0]    ARLEN, 
  	output reg [1:0]    ARSIZE, 
  	output     [1:0]    ARLOCK, 
  	output reg [1:0]    ARBURST, 
  	output reg          ARVALID_ext, 
  	input               ARREADY, 

    /* Read Data Channel (RDC) */
  	input [3:0]         RID,
  	input [63:0]        RDATA, 
  	input [1:0]         RRESP,
  	input               RLAST,
  	input               RVALID,
  	output reg          RREADY);

	`define WITHOUT_WRITE_RESPONSE /* Comment this line to define With Write Response state    */
    `define READ_ADDRESS_QUEUING   /* Comment this line to define Without Read Address Queuing */

reg          AWVALID, ARVALID, WVALID;

/* AXI FSM States */
reg [2:0] axi_fsm_current_state;
reg [2:0] axi_fsm_read_state;
reg [2:0] axi_fsm_read1_state;
reg [1:0] ahb_state;
reg [1:0] wt_state;
reg [1:0] rt_state;
parameter [2:0] Idle_0 = 3'b000, Idle_1 = 3'b001, Write_0 = 3'b010, Write_1 = 3'b011, Read_0 = 3'b010, Read_1 = 3'b011, Read_2 = 3'b100, Bresp_0 = 3'b100, Write_2 = 3'b101;

reg   [31:0] AWADDR_int;
reg   [31:0] ARADDR_int;
reg   [8:0] WDATA_int;
reg   [3:0]  burst_cnt;
reg   [13:0] w_clk_cnt;
reg   [13:0] r_clk_cnt;
reg   [8:0] rdata_cnt;
reg   [8:0] wburst_cnt;
reg   [8:0] rburst_cnt;
reg   [63:0] Rdata_mem [0:511] /* synthesis syn_ramstyle="lsram" */; 

reg   [3:0]  w_loop;
reg   [3:0]  r_loop;
reg   w_start;
reg   r_start;
reg [1:0] w_loop_state;
reg [1:0] r_loop_state;

reg [9:0]   w_xfer_size_i;
reg [9:0]   r_xfer_size_i;

assign AWLEN  = WLEN;
assign ARLEN  = RLEN;
assign AWLOCK = 2'b00;
assign ARLOCK = 2'b00;
assign AWID   = 4'b0000;
assign WID    = 4'b0000;
assign ARID   = 4'b0000;
assign BREADY = 1'b1;

wire w_loop_0;
wire r_loop_0;
wire write_idle1;
wire write_idle2;
wire wt_0;
wire wt_1;
wire rt_0;
wire rt_1;
wire ahb0;
wire ahb1;
wire ahb2;
wire read_idle;
wire read_read0;
wire [19:0] LAST_ARADDR;
wire [31:0] ARADDR_RANGE;
wire read_read1;
wire read1_idle;

assign w_loop_0     = ( write_start == 1'b1 && w_xfer_size_i > 10'h00) ? 1'b1 : 1'b0;
assign r_loop_0     = ( read_start == 1'b1 && r_xfer_size_i > 10'h00) ? 1'b1 : 1'b0;
assign write_idle1  = ( w_start == 1'b1 && wburst_cnt < w_xfer_size_i) ? 1'b1 : 1'b0;
assign write_idle2  = ( w_loop == 4'b0111 && wburst_cnt == w_xfer_size_i ) ? 1'b1 : 1'b0;
assign wt_0         = ( w_start == 1'b1 && AWVALID == 1'b1 ) ? 1'b1 : 1'b0;
assign wt_1         = ( wburst_cnt < w_xfer_size_i - 1)  ? 1'b1 : 1'b0;
assign rt_0         = ( r_start == 1'b1 && rburst_cnt < r_xfer_size_i && r_loop < 4'b1000) ? 1'b1 : 1'b0;
assign rt_1         = ( rburst_cnt < r_xfer_size_i - 1) ? 1'b1 : 1'b0;
assign ahb0         = ( w_start == 1'b1 && wburst_cnt == w_xfer_size_i && AHB_BUSY == 1'b0) ? 1'b1 : 1'b0;
assign ahb1         = ( r_start == 1'b1 && rburst_cnt == r_xfer_size_i && AHB_BUSY == 1'b0) ? 1'b1 : 1'b0;
assign ahb2         = ( (w_loop == 4'b1000) || (r_loop == 4'b1000)) ? 1'b1 : 1'b0;
assign read_idle    = ( r_start == 1'b1 && r_loop < 4'b1000 ) ? 1'b1 : 1'b0;
assign read_read0   = ( r_loop == 4'b0111 && rburst_cnt == r_xfer_size_i ) ? 1'b1 : 1'b0;
assign LAST_ARADDR  = (((r_xfer_size_i << 3) - 1) << 7);
assign ARADDR_RANGE = { 12'h000 , LAST_ARADDR };
assign read_read1   = ( ARADDR < ARADDR_RANGE ) ? 1'b1 : 1'b0; 
assign read1_idle   = ( r_start == 1'b1 && rburst_cnt < r_xfer_size_i) ? 1'b1 : 1'b0;

`ifndef WITHOUT_WRITE_RESPONSE
	`define WITH_WRITE_RESPONSE
`else
	`define WITHOUT_WRITE_RESPONSE
`endif

/* Write Signal generation. Write signal asserts till loop count reaches 8 */
always@(posedge CLK, negedge RESETn)
begin	
  if( RESETn == 1'b0 )
    begin	
		w_start       <= 0;
		w_xfer_size_i <= 10'h00;
		w_loop_state  <= 2'b00;
	end
  else
	begin
	  case (w_loop_state)
		2'b00:
		begin
	      if ( w_loop_0 == 1'b1 )
		    begin
				w_start       <= 1;
		        w_loop_state  <= 2'b01;
		    end
	      else
		    begin
				w_start       <= 0;
			    w_xfer_size_i <= w_xfer_size;
		        w_loop_state  <= 2'b00;
		    end
        end
		2'b01:
		begin
		   if ( w_loop < 4'b1000 ) 
		     begin
		        w_loop_state  <= 2'b01;
		     end
	      else
		    begin
				w_start       <= 0;
		        w_loop_state  <= 2'b00;
		    end
        end
	   endcase
	end
end

/* Read Signal generation. Read signal asserts till loop count reaches 8 */
always@(posedge CLK, negedge RESETn)
begin	
  if( RESETn == 1'b0 )
    begin	
		r_start       <= 0;
	    r_xfer_size_i <= 10'h00;
		r_loop_state  <= 2'b00;
	end
  else
	begin
	  case (r_loop_state)
		2'b00:
		begin
	      if ( r_loop_0 == 1'b1 )
		    begin
				r_start       <= 1;
		        r_loop_state  <= 2'b01;
		    end
	      else
		    begin
				r_start       <= 0;
				r_xfer_size_i <= r_xfer_size;
		        r_loop_state  <= 2'b00;
		    end
        end
		2'b01:
		begin
		   if ( r_loop < 4'b1000 ) 
		     begin
				r_start       <= 1;
		        r_loop_state  <= 2'b01;
		     end
	      else
		    begin
				r_start       <= 0;
		        r_loop_state  <= 2'b00;
		    end
        end
	   endcase
	end
end

/*Write address, Write data and Write response channels */
always@(posedge CLK, negedge RESETn)
begin	
	if(RESETn == 1'b0 )
		begin
		 AWADDR_int 	<= 32'h00000000;
		 WDATA_int		<= 11'h000;		 		 
		 AWSIZE 		<= 3'b000;		 
		 AWBURST 		<= 2'b00;		
		 WSTRB  		<= 8'h00;		 
		 AXI_BUSY		<= 1'b1;
		 AWVALID 		<= 1'b0;
		 WVALID 		<= 1'b0;
		 WLAST  		<= 1'b0;
		 wburst_cnt		<= 0;
		 axi_fsm_current_state   <=  Idle_1;
		end
	else
		begin
			case (axi_fsm_current_state)
			Idle_1:
			  begin
				if ( write_idle1 == 1'b1 )
				 begin
					AWSIZE 					<= 3'b011;
					AWBURST 				<= 2'b01;
					AWADDR 					<= AWADDR_int;
					AWVALID 				<= 1'b1;
					burst_cnt 				<= 1;
					AXI_BUSY				<= 1'b1;
					axi_fsm_current_state   <= Write_0;
				 end
				else
				 begin
				     axi_fsm_current_state   <= Idle_1;
                     wburst_cnt	             <= 0; 
		             if ( write_idle2 == 1'b1 ) 
		               begin
						 AWADDR_int <= 32'h00000000;
                         WDATA_int <= 11'h000;
		               end
				 end
			  end
			Write_0:
			  begin
				if (AWREADY == 1'b1)
				  begin
					AWVALID 				<= 1'b0;
					WSTRB  					<= 8'hFF;
					WVALID 					<= 1'b1;
					WDATA_int 				<= WDATA_int+1;
                    axi_fsm_current_state   <= Write_1;
				  end
				 else
				  begin
					axi_fsm_current_state   <= Write_0;
				  end
			end
			Write_1:
			  begin
				if (WREADY == 1'b1)
				  begin					
					burst_cnt <= burst_cnt+1;
					if(burst_cnt == WLEN)
					  begin
						WLAST  					<= 1'b1;
						axi_fsm_current_state   <= Bresp_0;					
					  end
					else
					  begin
					  	WDATA_int 				<= WDATA_int+1;
						axi_fsm_current_state   <= Write_1;	
					  end
				  end
			  end
			Bresp_0:
			  begin
				if (WREADY == 1'b1)
				  begin	
					WVALID 					<= 1'b0;
					WLAST  					<= 1'b0;
					
`ifdef WITHOUT_WRITE_RESPONSE
$display("WITHOUT_WRITE_RESPONSE");

					wburst_cnt		        <= wburst_cnt + 1'b1;
					AXI_BUSY		        <= 1'b0;
					AWADDR_int 		        <= AWADDR_int + 128;
					WDATA_int               <= WDATA_int+1;		 		
					axi_fsm_current_state   <= Idle_1;
					
`endif

				  end
				  
`ifdef WITH_WRITE_RESPONSE
$display("WITH_WRITE_RESPONSE");

				if (BVALID == 1'b1)
				  begin				
					wburst_cnt		        <= wburst_cnt + 1'b1;
					AXI_BUSY		        <= 1'b0;
					AWADDR_int 		        <= AWADDR_int + 128;
					WDATA_int               <= WDATA_int+1;		 		
					axi_fsm_current_state   <= Idle_1;
				  end

`endif
				else
				  begin
					axi_fsm_current_state   <=  Bresp_0;
				  end
			end		
			endcase
	end
end

/* LSRAM */
always@(posedge CLK)
begin		
		if (RVALID == 1'b1 )
		Rdata_mem[rdata_cnt] <= RDATA;			
		else
		WDATA <= Rdata_mem[WDATA_int];
end 

/* Write throughput counter. The write throughput counter counts the AXI clocks between 
AWVALID of first data and WLAST of last data. */
always@(posedge CLK, negedge RESETn)
begin	
  if( RESETn == 1'b0 )
    begin	
		w_clk_cnt <= 0;
		wt_state  <= 2'b00;
	end
  else
	begin
	  case (wt_state)
		2'b00:
		begin
	      if ( wt_0 == 1'b1 )
		    begin
				w_clk_cnt <= w_clk_cnt + 1;
		        wt_state  <= 2'b01;
		    end
	      else
		    begin
		      if ( wburst_cnt == 9'b000000000)
		        begin
				   w_clk_cnt <= 0; 	 
		        end
		        wt_state  <= 2'b00;
		    end
        end
		2'b01:
		begin
		   if ( wt_1 == 1'b1 )
		     begin
				w_clk_cnt <= w_clk_cnt + 1;
		        wt_state  <= 2'b01;
		     end
	      else
		    begin
				w_clk_cnt <= w_clk_cnt;
		        wt_state  <= 2'b10;
		    end
        end
		2'b10:
		begin
		   if (WLAST == 1'b1 && WREADY == 1'b1)
		     begin
				w_clk_cnt <= w_clk_cnt;
		        wt_state  <= 2'b00;
		     end
	      else
		    begin
				w_clk_cnt <= w_clk_cnt + 1;
		        wt_state  <= 2'b10;
		    end
        end
	   endcase
	end
end

/* Read throughput counter. Read throughput counter counts the AXI clocks between 
ARVALID of first data and RLAST of last data. */
always@(posedge CLK, negedge RESETn)
begin	
  if( RESETn == 1'b0 )
    begin	
		r_clk_cnt <= 0;
		rt_state  <= 2'b00;
	end
  else
	begin
	  case (rt_state)
		2'b00:
		begin
	      if ( rt_0 == 1'b1 )
		    begin
				r_clk_cnt <= r_clk_cnt + 1;
		        rt_state  <= 2'b01;
		    end
	      else
		    begin
				r_clk_cnt <= 0; 
		        rt_state  <= 2'b00;
		    end
        end
		2'b01:
		begin
		   if ( rt_1 == 1'b1 )
		     begin
				r_clk_cnt <= r_clk_cnt + 1;
		        rt_state  <= 2'b01;
		     end
	      else
		    begin
				r_clk_cnt <= r_clk_cnt;
		        rt_state  <= 2'b10;
		    end
        end
		2'b10:
		begin
		   if (RLAST == 1'b1 && RREADY == 1'b1)
		     begin
				r_clk_cnt <= r_clk_cnt;
		        rt_state  <= 2'b00;
		     end
	      else
		    begin
				r_clk_cnt <= r_clk_cnt + 1;
		        rt_state  <= 2'b10;
		    end
        end
	   endcase
	end
end

/* AXI master sends the throughput count value and an eSRAM address starting from 
0x 20008104 to AHB master */
always@(posedge CLK, negedge RESETn)
begin	
	if(RESETn == 1'b0 )
	  begin   
		done		<= 1'b0;
		WRITE_AHB	<= 1'b0;
		AHB_DATA    <= 32'h00000000;
		AHB_ADDR    <= 32'h20008100;		 		 
		ahb_state   <= 2'b00;
        w_loop      <= 4'b0000;
        r_loop      <= 4'b0000;
	  end
	else
	   begin
		 case (ahb_state)
			2'b00:
			begin
			  if ( ahb0 == 1'b1 )
				begin
					WRITE_AHB	<= 1'b1;
					AHB_DATA    <= w_clk_cnt;
					ahb_state   <= 2'b01;  
                    w_loop      <= w_loop + 1;
		            AHB_ADDR    <= AHB_ADDR + 4;
				end
			  else if ( ahb1 == 1'b1 )
				begin
					WRITE_AHB	<= 1'b1;
					AHB_DATA    <= r_clk_cnt;
					ahb_state   <= 2'b01;
                    r_loop      <= r_loop + 1;
		            AHB_ADDR    <= AHB_ADDR + 4;
				end
			  else
				begin
					ahb_state   <=  2'b00;
				end
			end
			2'b01:
			begin
				done		<= 1'b1;
				ahb_state   <= 2'b00;
				WRITE_AHB	<= 1'b0;
	            if( ahb2 == 1'b1 ) 
                  begin
        			w_loop      <= 4'b0000;
        			r_loop      <= 4'b0000;
		            AHB_ADDR    <= 32'h20008100;
                  end
			end
		  endcase
		end
end

`ifdef READ_ADDRESS_QUEUING

/* Read address channel */
always@(posedge CLK, negedge RESETn)
begin	
  if(RESETn == 1'b0 )
	begin
	  ARADDR 	 	<= 32'h00000000; 		 
	  ARSIZE 		<= 3'b000;		 
	  ARBURST 		<= 2'b00;	
	  ARVALID 		<= 1'b0;	
	  read_done     <=  0;	 		 
	  axi_fsm_read_state   <=  Idle_1;
	end
	else
		begin 
		  case (axi_fsm_read_state)
			Idle_1:
			  begin
				if ( read_idle == 1'b1 )
				  begin						
					read_done               <= 0;
					ARSIZE 					<= 3'b011;
					ARBURST 				<= 2'b01;
					ARADDR   			    <= ARADDR;
					ARVALID 				<= 1'b1;
					axi_fsm_read_state      <= Read_0;
					end
				else
				  begin
					read_done            <= 1;
					axi_fsm_read_state   <= Idle_1;
					ARADDR 	 	         <= 32'h00000000;	
                    rdata_cnt <= 11'h000;
				  end
			  end
			Read_0:
			begin
			  if (ARREADY == 1'b1)
				begin
				    if ( read_read0 == 1'b1 ) 
		               begin
					 	 axi_fsm_read_state   <= Idle_1;
						 ARADDR 	 	      <= 32'h00000000;
		               end
                    else if (read_read1 == 1'b1 )
                       begin
						 ARADDR   			 <= ARADDR + 128;
					     axi_fsm_read_state  <= Read_0;
					   end
					else
					begin
					    ARVALID 		     <= 1'b0;
					end
				end
			  else
				begin
					axi_fsm_read_state   <= Read_0;
				end
			end		
		  endcase
	end
end

/* Read data channel */
always@(posedge CLK, negedge RESETn)
begin	
  if(RESETn == 1'b0 )
	begin
	  RREADY		<= 1'b0; 		 
	  rdata_cnt		<=	0;
	  rburst_cnt	<=  0;
	  axi_fsm_read1_state   <=  Idle_1;
	end
	else
		begin 
		  case (axi_fsm_read1_state)
			Idle_1:
			  begin
				if ( read1_idle == 1'b1 )
				  begin						
					axi_fsm_read1_state      <= Read_1;
					end
				else
				  begin
					axi_fsm_read1_state   <= Idle_1;
                    rburst_cnt	          <= 0;
				  end
			  end
			Read_1:
			begin
			  if (RLAST == 1'b0)
				begin
					RREADY				 <= 1'b1;
					axi_fsm_read1_state   <= Read_2;
				end
			  else
				begin
					axi_fsm_read1_state   <= Read_1;
				end
			end
			Read_2:
			begin
			  if (RVALID == 1'b1)
				begin
					rdata_cnt <= rdata_cnt + 1;
				end
			if (RLAST == 1'b1 && RVALID == 1'b1)
			    begin					
					rburst_cnt           <= rburst_cnt + 1;
					RREADY				 <= 1'b0;		
					axi_fsm_read1_state  <= Idle_1;
				end				
			  else
				begin
					axi_fsm_read1_state   <= Read_2;
				end
			end			
		  endcase
	end
end

`else

//Read address and Read data channels
always@(posedge CLK, negedge RESETn)
begin	
  if(RESETn == 1'b0 )
	begin
	  ARADDR 	 	<= 32'h00000000;		 
	  ARSIZE 		<= 3'b000;		 
	  ARBURST 		<= 2'b00;	
	  RREADY		<= 1'b0;
	  ARVALID 		<= 1'b0;	
	  read_done     <=  0;	  
	  rdata_cnt		<=	0;
	  rburst_cnt	<=  0;
	  axi_fsm_read_state   <=  Idle_1;
	end
	else
		begin 
		  case (axi_fsm_read_state)
			Idle_1:
			  begin
				if ( read1_idle == 1'b1 )
				  begin			
                    read_done               <= 0;				  
					ARSIZE 					<= 3'b011;
					ARBURST 				<= 2'b01;
					ARADDR 					<= ARADDR;
					ARVALID 				<= 1'b1;
					axi_fsm_read_state      <= Read_0;
					end
				else
				  begin
					axi_fsm_read_state   <= Idle_1;
                    rburst_cnt	         <= 0;
		            if ( read_read0 == 1'b1 ) 
		              begin
	  					ARADDR 	   <= 32'h00000000;
						read_done  <= 1;
		              end
				  end
			  end
			Read_0:
			begin
			  if (ARREADY == 1'b1)
				begin
					ARVALID 			 <= 1'b0;
					axi_fsm_read_state   <= Read_1;
				end
			  else
				begin
					axi_fsm_read_state   <= Read_0;
				end
			end
			Read_1:
			begin
			  if (RLAST == 1'b0)
				begin
					RREADY				 <= 1'b1;
					axi_fsm_read_state   <= Read_2;
				end
			  else
				begin
					axi_fsm_read_state   <= Read_1;
				end
			end
			Read_2:
			begin
			  if (RVALID == 1'b1)
				begin
					rdata_cnt <= rdata_cnt + 1;
				end
			  if (RLAST == 1'b1)
			    begin					
					rburst_cnt           <= rburst_cnt + 1;
					RREADY				 <= 1'b0;
					ARADDR   			 <= ARADDR + 128;				
					axi_fsm_read_state   <= Idle_1;
				end				
			  else
				begin
					axi_fsm_read_state   <= Read_2;
				end
			end			
		  endcase
	end
end

`endif

always@(negedge CLK, negedge RESETn)
begin	
  if( RESETn == 1'b0 )
    begin	
		AWVALID_ext <= 1'b0;
		WVALID_ext  <= 1'b0;
        ARVALID_ext <= 1'b0;
	end
  else
	begin
	  	AWVALID_ext <= AWVALID && AWREADY;
		WVALID_ext  <= WVALID && WREADY;
        ARVALID_ext <= ARVALID && ARREADY;
    end
end


endmodule
