//注意:下降沿写数据
module SD_Read

(

	//输入                    
	rst_n,

	SD_in,             //读SD数据

	//内部输入

	SD_25M,           //SPI时钟25M  
	//按键内部输入

	write_ctl,        //按键控制写启动(注意要在初始化完毕后)//低位1代表启动写,高位1代表读


	//输出到Top

	SD_out,          	//往SD写

	SD_CS,             //片选

	SD_tasnfer_r,      //切换发送线路控制权

  
	SD_R_reg           //随便对应

);


//---------------------------------------------------------------------------

//--	外部端口声明

//---------------------------------------------------------------------------

	input      SD_25M;    

	input      SD_in;        //读

	input      rst_n;

	input      write_ctl;//低位1代表启动写,高位1代表读


	output	reg[7:0]  SD_R_reg;         //稳定的读到的数据  
	output	reg  	SD_tasnfer_r;  
	output	reg  	SD_out;

	output	reg  	SD_CS;

//---------------------------------------------------------------------------

//--	内部端口声明/*synthesis keep*/

//---------------------------------------------------------------------------

	reg    	SD_tasnfer_r_n;  
	reg    	SD_in_n;

	reg    	SD_out_n;        //写的下位时刻寄存器

	reg  [7:0]  SD_R;            //读寄存器(变化过程)

	reg  [7:0]  SD_R_n;  

	reg  [7:0]  SD_R_reg_n;  
	reg    	SD_CS_n;        //写的下位时刻寄存器  
	reg  [3:0]  fsm_s;

	reg  [3:0]  fsm_s_n;  
	reg  [9:0]  time_cnt;  //计时器脉冲数

	reg  [9:0]  time_cnt_n;  //下一时刻的计时器脉冲式

	reg  [5:0]  bit_cnt;  	//记录写或者读到第几个bit了,最多64个,实际48个就行

	reg  [5:0]  bit_cnt_n;

	reg  [9:0]  byte_cnt;       //每一个字节的索引,最多512个,

	reg  [9:0]  byte_cnt_n;


  
	//--------------------------------------------------------------------

	//--定义参数

	//-------------------------------------------------------------------  

	//**状态机参数

	parameter idle=4'b0000;             //状态为idle

	parameter send_cmd17=4'b0001;        //状态为发送CMD17

	parameter wait_00=4'b0010;          //状态为等待CMD17应答8'h00

	parameter wait_fe=4'b0011;          //状态为等待接收到0xfe

	parameter read_data=4'b0100;        	//接收数据512bytes数据

	parameter read_end=4'b0101;    //状态为等待8个时钟后,返回idle态  
	parameter Erro_end=4'b0110;         //状态为写数据失败,等待8个时钟,返回idle态


	//**命令8'h00,8'h00,8'h21,8'h00

//	parameter CMD17={8'h51,8'h00,8'h00,8'h50,8'h00,8'hff};	//block读命令CMD17的字节序列

	parameter CMD17={8'h51,8'h00,8'h00,8'h21,8'h00,8'hff};	//block读命令CMD17的字节序列,地址是8448


//	parameter SD_Add_R=32'd8448;              //要读取的地址    debug:字节的顺序

	//--------------------------------------------------------------------

	//--逻辑实现

	//-------------------------------------------------------------------    

	//**************SD_tasnfer_r切换发送线路控制权***************  

	//时序电路

	always @(negedge SD_25M or negedge rst_n)

  begin

  	if(!rst_n)

    SD_tasnfer_r<=1'b0;

  	else

    SD_tasnfer_r<=SD_tasnfer_r_n;  

  end

  

	//组合电路

	always @(*)        
  begin  
  	if(write_ctl)                //来一次采集到就保持控制权

    SD_tasnfer_r_n=1'b1;

  	else if(fsm_s==read_end)        //读成功后,交还控制权

    SD_tasnfer_r_n=1'b0;

  	else                              //保持

    SD_tasnfer_r_n=SD_tasnfer_r;

  end  

  
  

	//**************状态机***************  
	//时序电路,----------有限状态机状态切换-------------------  
	always @(negedge SD_25M or negedge rst_n)

  begin

  	if(!rst_n)

    fsm_s<=idle;           //默认是空闲状态

  	else

    fsm_s<=fsm_s_n;  

  end

  

	//组合电路  ---有限状态机核心

	always@(*)                            //状态机内部仅仅执行状态的跳转,不进行状态的内部操作

  begin  
    case(fsm_s)            
    	idle:                      //空闲态,判断是否有写命令

      if(write_ctl)           //仅仅一个上升沿,1时进入读

      	fsm_s_n=send_cmd17;      //进入下一态

      else

      	fsm_s_n=fsm_s;          	//保持当前状态  

    	send_cmd17:                    //状态为发送CMD17

      if(bit_cnt==6'd48)         //发送完6个字节的控制指令,进入下一态

      	fsm_s_n=wait_00;      //进入下一态

      else

      	fsm_s_n=fsm_s;          	//保持当前状态

    	wait_00:                    //状态为等待CMD17应答8'h00

      if(time_cnt>=10'd8&&SD_R==8'h00)    //至少发送了1个8位的同步时钟且判断此时是否接收到了0x00

      	fsm_s_n=wait_fe;      //进入下一态

      else if(time_cnt>=10'd32)    //32个bits的时间还没有收到0x00则响应失败

      	fsm_s_n=Erro_end;      //进入下一态

      else

      	fsm_s_n=fsm_s;          	//保持当前状态  
    	wait_fe:                    //状态为等待0xfe          //要等很久

      if(time_cnt>=10'd8&&SD_in==0)    //至少发送了1个8位的同步时钟且判断此时是否接收到了0xfe

      	fsm_s_n=read_data;      //进入下一态

      else

      	fsm_s_n=fsm_s;          	//保持当前状态          
    	read_data:                    	//状态为接收512字节

      if(byte_cnt==10'd512)    //等待接收了512个字节

      	fsm_s_n=read_end;      //进入下一态

      else

      	fsm_s_n=fsm_s;          	//保持当前状态        
      

    	read_end:                   //状态为等待8个时钟后,返回idle态  
      if(time_cnt==8)          
      	fsm_s_n=idle;      //进入下一态

      else

      	fsm_s_n=fsm_s;          	//保持当前状态          
    	Erro_end:                    //状态为写数据失败,等待8个时钟,返回idle态

      if(time_cnt==8)      
      	fsm_s_n=send_cmd17;      //重新发送

      else

      	fsm_s_n=fsm_s;          	//保持当前状态        


    	default:fsm_s_n=idle;      //默认在空闲态

    endcase

  end  

	//**************SD状态时钟计数器***************  

	//时序电路

	always @(negedge SD_25M or negedge rst_n)

  begin

  	if(!rst_n)

    time_cnt<=10'b0;

  	else

    time_cnt<=time_cnt_n;  

  end

  

	//组合电路

	always @(*)        
  begin  
  	//每次进入新的态后或者加满了,计数器清零

  	if(fsm_s!=fsm_s_n||fsm_s==10'd1023)    

    time_cnt_n=10'b0;

  	else  
    time_cnt_n=time_cnt+1'b1;

  end  


	//**************片选SD_CS***************    

	//时序电路

	always @(negedge SD_25M or negedge rst_n)

  begin

  	if(!rst_n)

    SD_CS<=1'b1;        //默认为1,不选中SD卡

  	else

    SD_CS<=SD_CS_n;  

  end

  

	//组合电路

	always @(*)        
  begin  
  	//只需判断第一个变换点就行,其他保持

  	//idle态、read_end态(else保持)...时,拉高,不选中

  	if(fsm_s==idle||fsm_s==read_end)              
    SD_CS_n=1'b1;

  	else if(fsm_s==send_cmd17)    //发送CMD17态拉低,紧接的几个都保持低

    SD_CS_n=1'b0;               //拉低,选中

  	else                         //否则保持

    SD_CS_n=SD_CS;

  end  
  

	//**************bit_cnt,计数每次收发的bit数***************  

	//时序电路

	always @(negedge SD_25M or negedge rst_n)

  begin

  	if(!rst_n)

    bit_cnt<=1'b0;

  	else

    bit_cnt<=bit_cnt_n;  

  end

  

	//组合电路

	always @(*)        
  begin  
  	//状态改变,或者数据接收态,bit_cnt等于7就清零

  	if(fsm_s!=fsm_s_n||(fsm_s==read_data&&bit_cnt==6'd7))    
    bit_cnt_n=1'b0;    

  	else      

    bit_cnt_n=bit_cnt+1'b1;       //记录字节的位数

  end  
  
	//**************byte_cnt,计数接收了多少字节了***************  

	//时序电路

	always @(negedge SD_25M or negedge rst_n)

  begin

  	if(!rst_n)

    byte_cnt<=10'b0;

  	else

    byte_cnt<=byte_cnt_n;  

  end

  

	//组合电路

	always @(*)        
  begin  
  	//每发送完一个字节,数据加1,直到发送512字节数据态时

  	if(fsm_s==read_data&&bit_cnt==6'd7&&byte_cnt<10'd512)  

    byte_cnt_n=byte_cnt+1'b1;          //记录每个状态的脉冲数

  	else if(fsm_s==wait_fe||byte_cnt==10'd512)   //在read_data的前一个状态清零

    byte_cnt_n=1'b0;          

  	else      //保持

    byte_cnt_n=byte_cnt;    

  end  
  

	//**************向SD卡发送数据端SD_out***************    

	//时序电路

	always @(negedge SD_25M or negedge rst_n)

  begin

  	if(!rst_n)

    SD_out<=1'b1;        //默认为1

  	else

    SD_out<=SD_out_n;  

  end

  

	//组合电路

	always @(*)        
  begin  

  	//连续的态只用判断第一个,其他保持

  	//除了发送CMD17外,保持高

  	if(fsm_s==idle||fsm_s==wait_00)    

    SD_out_n=1'b1;

  	else if(fsm_s==send_cmd17&&bit_cnt<6'd48)     //发送CMD17态

    SD_out_n=CMD17[6'd47-bit_cnt];            //先发最高位

  	else                         //否则保持

    SD_out_n=SD_out;

  end  

	//**************向SD卡接收数据寄存器SD_R***************  

	//时序电路

	always @(posedge SD_25M )     //只有此处是上升沿,接收数据的上升沿

  begin

  	SD_in_n=SD_in;

  end

  
	//时序电路

	always @(negedge SD_25M or negedge rst_n)     //只有此处是上升沿,接收数据的上升沿

  begin

  	if(!rst_n)

    SD_R<=8'd0;        //默认接收的数据为0

  	else

    SD_R<=SD_R_n;  

  end

  

	//组合电路

	always @(*)        
  begin

  	if(fsm_s==wait_00||fsm_s==wait_fe||fsm_s==read_data)              //在读数据态时

    SD_R_n={SD_R[6:0],SD_in_n};

  	else                         //否则保持

    SD_R_n=SD_R;

  end  
  

  

	//**************向SD卡接收到的*稳定*数据寄存器SD_R_reg***************  

	//时序电路

	always @(negedge SD_25M or negedge rst_n)     //只有此处是上升沿,接收数据的上升沿

  begin

  	if(!rst_n)

    SD_R_reg<=8'h2A;        //默认接收的数据为"*"

  	else

    SD_R_reg<=SD_R_reg_n;  

  end

  

	//组合电路

	always @(*)        
  begin

  	if(fsm_s==read_data&&bit_cnt==6'd7)              //在读数据态时

    SD_R_reg_n={SD_R[6:0],SD_in_n};

  	else                         //否则保持

    SD_R_reg_n=SD_R_reg;

  end  
  
endmodule