///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------
//-- Title      : SPI Flash to MDDR
//-------------------------------------------------------------------------------
//-- File       : SPI_to_MDDR.v
//-- Author     : Corporate Applications Engineering
//-- Company    : Microsemi Corporation
//-- Device     : SmartFusion2
//-- Standard   : Verilog
//-------------------------------------------------------------------------------
//-- Description: This code implementes the logic to read the application image from    
//--              SPI flash of size "FILE_SIZE". It reads 1K bytes and writes to MDDR 
//-- 			  through AXI master interface. The default file size is 128KB.
//-------------------------------------------------------------------------------
//-- Copyright (c) 2013   Microsemi Corporation
//--                      All rights reserved.
//-------------------------------------------------------------------------------
//-- Revisions  : V1.0
//--              V1.1 modified for winbond flash on SF2090 Eval kit
//-------------------------------------------------------------------------------
/////////////////////////////////////////////////////////////////////////////////////////////////// 


module SPI_to_MDDR(
// Generic Signals
input      wire           CLK,
input      wire           RESETn,

//Interface to Logic
input       wire   [31:0] DATAIN,
input       wire          BUSY,
output      reg           READ,
output      reg           WRITE,
output      reg   [31:0]  ADDR,
output      reg   [31:0]  DATAOUT,

output      reg	   [63:0] AXI_DATA,
output		reg			  AXI_WRITE,
input					  AXI_BUSY,

input					  START_CODE_SHADOW,
output      reg           M3_RESETn
);


reg [23:0] block_address;
reg [7:0]  read_byte[1027:0];
reg [7:0]  CMD;

reg [2:0]  reg_count;
reg [2:0]  CMD_count;
reg [11:0] nbytes;
reg [11:0] nblocks;

reg [3:0] SPI_current_state;
parameter [3:0] Idle = 4'b0000, Reg_init = 4'b0001, Write_cmd = 4'b0010,Read_statusTx =4'b0011, 
                Read_statusTx2 =4'b0100, Read_status = 4'b0101, Read_status2 = 4'b0110, Read =4'b0111, 
                Read2 =4'b1000, Read3 =4'b1001, write_ddr =4'b1010, next_block =4'b1011, 
                stop_st =4'b1100, SPI_disable =4'b1101, final_st =4'b1111;

parameter [11:0] block_length = 12'h404;//read cmd + 3 byte address + 1024 bytes
parameter [11:0] File_size = 128;
wire      [31:0] SPI_Reg[3:0];
wire      [31:0] SPI_Reg_add[3:0];

assign	SPI_Reg[0] = 32'h23040403; 		//control reg
assign	SPI_Reg[1] = 32'h8;				//txrxdf size
assign	SPI_Reg[2] = 32'h5;				//clk_gen reg
assign	SPI_Reg[3] = 32'h1;				//slave select reg

assign	SPI_Reg_add[0] = 32'h40001000;	//control reg address
assign	SPI_Reg_add[1] = 32'h40001004;	//txrxdf reg address 
assign	SPI_Reg_add[2] = 32'h40001018;	//clk_gen reg address
assign	SPI_Reg_add[3] = 32'h4000101C;	//slave select reg address


always@(posedge CLK, negedge RESETn)
begin

	if(RESETn  == 1'b0)
	    begin	     
	     WRITE              <=  1'b0;	     
	     READ               <=  1'b0;
         AXI_DATA           <=  0;
         DATAOUT            <=  0;
         AXI_WRITE          <=  0;
         ADDR               <=  0;
		 reg_count			<=  3'b000;
		 CMD_count			<=  3'b000;
		 nbytes				<=  4'b000;
		 nblocks			<=  12'h000;
		 block_address		<=  32'h0;		 
		 M3_RESETn			<=  1'b0;		 
	     SPI_current_state  <=  Idle;       
	    end

	else
    	begin
	        case (SPI_current_state)	      

//	Initializing SPI Flash

	        Idle: 
	        begin
	           if ( BUSY  == 1'b0 && START_CODE_SHADOW == 1'b1)
	           begin						
			   M3_RESETn			    	<=  1'b0;
			   WRITE                    	<=  1'b1;						
			   ADDR		       				<=  SPI_Reg_add[reg_count];
			   DATAOUT						<=  SPI_Reg[reg_count];
			   reg_count					<=  reg_count + 1'b1;			   
	           SPI_current_state        	<=  Reg_init;                       
	           end	                
	           else
	           begin
			   M3_RESETn					<=  1'b1;
	           SPI_current_state        	<=  Idle;
	           end
	        end

	        Reg_init: 
	        begin
				if ( reg_count == 3'b100 )
                begin						
				SPI_current_state   		<=  Read_statusTx;
				end
				else
				begin
					if ( BUSY  == 1'b0 )
	            	begin
					WRITE                   <=  1'b1;
			    	ADDR		       		<=  SPI_Reg_add[reg_count];
			    	DATAOUT					<=  SPI_Reg[reg_count];						
					reg_count				<=  reg_count + 1'b1;
					SPI_current_state   	<=  Reg_init;						
					end
					else
					begin
					SPI_current_state   	<=  Reg_init;                    
	            	end
				end
			end
			Read_statusTx:
			begin
				if ( BUSY  == 1'b0 )
	            begin
				WRITE                       <=  1'b0;
 				READ               			<=  1'b1;
				ADDR       					<=  32'h40001008;//status				
				SPI_current_state   	    <=  Read_statusTx2; 
    	        end
				else
				begin
				SPI_current_state   		<=  Read_statusTx;                  
	            end
			end
			Read_statusTx2:
			begin
				if ( BUSY  == 1'b0 )
	            begin
				WRITE                       <=  1'b0;
 				READ               			<=  1'b0;
				SPI_current_state   	    <=  Write_cmd; 
    	        end
				else
				begin
				SPI_current_state   		<=  Read_statusTx2;                   
	            end
			end			
//Initiating SPI read 
			Write_cmd:
			begin
				if ( BUSY  == 1'b0 )
	            begin
					if ( DATAIN[8]  == 1'b0 )
	            	begin 					
					WRITE                   <=  1'b1;
					ADDR       				<=  32'h40001014;//txd
					DATAOUT					<=  CMD;
					SPI_current_state   	<=  Read_status; 
    	        	end
					else
					begin
					SPI_current_state   	<=  Read_status;                   
	            	end				 
    	        end
				else
				begin
				SPI_current_state   		<=  Write_cmd;                   
	            end
			end
			Read_status:
			begin
				if ( BUSY  == 1'b0 )
	            begin
				WRITE                       <=  1'b0;
 				READ               			<=  1'b1;
				ADDR       					<=  32'h40001008;//status
				if (CMD_count < 3'b100)
				begin
				CMD_count					<=  CMD_count + 1'b1;
				end
				SPI_current_state   	    <=  Read_status2; 
    	        end
				else
				begin
				SPI_current_state   		<=  Read_status;                  
	            end
			end
			Read_status2:
			begin
				if ( BUSY  == 1'b0 )
	            begin
				WRITE                       <=  1'b0;
 				READ               			<=  1'b0;
				SPI_current_state   	    <=  Read; 
    	        end
				else
				begin
				SPI_current_state   		<=  Read_status2;                   
	            end
			end
//Reading 1K block of data
			Read:
			begin
				if ( BUSY  == 1'b0 )
	            begin
					if ( DATAIN[6]  == 1'b0 )					
	            	begin
 					READ               		<=  1'b1;
					ADDR       				<=  32'h40001010;//rxd
					SPI_current_state   	<=  Read2; 
    	        	end
					else
					begin
					SPI_current_state   	<=  Read_statusTx;                    
	            	end
				end
				else
				begin
				SPI_current_state   		<=  Read;
				end
			end
			Read2:
			begin
				if ( BUSY  == 1'b0 )
	            begin
				WRITE                       <=  1'b0;
 				READ               			<=  1'b0;
				SPI_current_state   		<=  Read3;
				end	
				else
				begin
				SPI_current_state   		<=  Read2;                    
	            end
			end
			Read3:
			begin
				if ( BUSY  == 1'b0 )
				begin
				read_byte[nbytes]			<=  DATAIN[7:0];
					if (nbytes >= block_length)
					begin
					WRITE                   <=  1'b1;
					ADDR					<=  SPI_Reg_add[0];
					DATAOUT					<=  32'h0;					//for disabling SPI
					SPI_current_state   	<=  SPI_disable;
					nbytes					<=  4'h4;
					end
					else
					begin
					SPI_current_state   	<=  Read_statusTx;
					nbytes					<=  nbytes + 1'b1;
					end				
				end
				else
				begin
				SPI_current_state   		<=  Read3;	
				end
			end
			SPI_disable:
			begin
				if ( BUSY  == 1'b0 )
				begin
			 	READ               			<=  1'b0;
				WRITE                       <=  1'b0;
				SPI_current_state   		<=  write_ddr;
				end
				else
				begin
				SPI_current_state   		<=  SPI_disable;
				end
			end

//Writing 1K block of data to MDDR through AXI master interface

			write_ddr:
			begin
				if ( AXI_BUSY == 1'b0 )
				begin
				AXI_WRITE					<=  1'b1;
				AXI_DATA[7:0]			    <=  read_byte[nbytes];
				AXI_DATA[15:8]			    <=  read_byte[nbytes + 1];
				AXI_DATA[23:16]			    <=  read_byte[nbytes + 2];
				AXI_DATA[31:24]			    <=  read_byte[nbytes + 3];
				AXI_DATA[39:32]			    <=  read_byte[nbytes + 4];
				AXI_DATA[47:40]			    <=  read_byte[nbytes + 5];
				AXI_DATA[55:48]			    <=  read_byte[nbytes + 6];
				AXI_DATA[63:56]			    <=  read_byte[nbytes + 7];				
					if (nbytes >= block_length-8)
					begin
					SPI_current_state   	<=  next_block;
					end
					else
					begin						
					nbytes					<=  nbytes + 4'h8;
					SPI_current_state   	<=  write_ddr;
					end
				end
				else
				begin
				SPI_current_state   		<=  write_ddr;                    
	            end				
			end 		

//Initiating next block transfer if Image is not completely copied	

			next_block:
			begin
				if (AXI_BUSY == 1'b0)
				begin
				AXI_WRITE					<=  1'b0;		 		
					if (File_size > nblocks )
					begin
					SPI_current_state   	<=  Idle;
		 			reg_count				<=  3'b000;
		 			CMD_count				<=  3'b000;
		 			nbytes					<=  4'b000;
					block_address			<=  block_address + 1024;
					nblocks					<=  nblocks + 1;
					end
					else
					begin
					WRITE                   <=  1'b1;
					ADDR					<=  32'h40038008;
					DATAOUT					<=  32'h1;//remapping code to ddr
					SPI_current_state   	<=  stop_st;
					end
				end
				else
				begin
				SPI_current_state   		<=  next_block;
				end
			end
			stop_st:
			begin
				if (BUSY == 1'b0 )
				begin
				SPI_current_state   		<=  final_st;
				end
				else
				begin
				SPI_current_state   		<=  stop_st;
				end
			end
			final_st:
			begin
				if (BUSY == 1'b0 )
				begin
 				READ               			<=  1'b0;
				WRITE                       <=  1'b0;
		 		M3_RESETn					<=  1'b1;
				SPI_current_state   		<=  final_st;
				end
				else
				begin
				SPI_current_state   		<=  final_st;
				end
			end
     		endcase
		end     
end

//Generates commands to SPI Flash

always@(posedge CLK, negedge RESETn)
begin
	if(RESETn  == 1'b0)
	begin
		CMD					<=	8'h00;
	end
	else
	begin
		if( CMD_count == 3'b000)
		begin
			CMD				<=	8'h03; //read arry opcode
		end
		else
		begin
			if( CMD_count == 3'b001)
			begin
				CMD			<=	block_address[23:16];
			end
			else
			begin
				if( CMD_count == 3'b010)
				begin
				CMD			<=	block_address[15:8];
				end
				else
				begin
				CMD	<=	8'h00;
				end
			end		
		end
	end
end

endmodule

