SD-Host 命令控制模块

sd_cmd_fsm模块:命令操作控制模块;
sd_cmd_send_shift_register:根据cmd_state对命令发送进行控制;
sd_cmd_receice_shift_register:根据cmd_state对命令相应的接收进行控制;

sd_cmd_fsm

命令发送及命令响应接收控制模块,根据输入的控制信号,进行状态机的跳转进行判定,以控制sd_cmd_send_shift_register和sd_cmd_receive_shift_register模块进行符合SD协议的数据处理过程。

状态转化图:

在这里插入图片描述
STATE_STOP :空闲状态,软件配置新的命令时,使in_command_ready位有效(开启状态机),状态机进入STATE_WAIT_SEND状态;
STATE_WAIT_SEND:命令发送等待状态,当in_sd_data[0]低有效时(start bit),跳入STATE_SEND状态,否则保持;
STATE_SEND:进行命令数据的发送,在所有48bit数据发送结束后,进行in_response信号的判断,如果高有效则进入STATE_WAIT_RECEIVE状态,装备接收device返回的响应。否则进入STATE_STOP状态;
STATE_WAIT_RECEIVE:如果接收响应的时间超过64 个 sd_clk 周期,则硬件跳入STATE_STOP,并给出timeout信号(响应超时),否则跳入STATE_RECEIVE状态;
STATE_RECEIVE:响应接收状态,结束后,返回STATE_STOP状态;

信号描述

signalsI/OWidthfromtodeclaration
in_sd_clkinput1sd_clk时钟信号
hrst_ninput1AHB bus异步复位
in_soft_resetinput1软件复位
in_longresponseinput1sd_ifdevice有命令长回复信号
in_responseinput1sd_ifdevice有命令回复信号
in_command_readyinput1sd_if命令有效,状态机启动信号
in_sd_datainput[3:0]sd card数据输入端口
in_sd_cmdinput1sd card命令输入端口
current_stateoutput[2:0]sd_if sd_cmd_send
sd_cmd_receive状态机标志位
has_send_bitoutput[5:0]sd_cmd_send已发送命令bit位的大小
has_receive_bitoutput[7:0]sd_cmd_receive已接收命令bit位的大小
end_commandoutput1sd_if命令结束信号
end_command_and_responseoutput1sd_if命令结束且反馈接收完成信号
response_timeoutoutput1sd_if响应超时信号

代码实现:

module sd_cmd_fsm(
    in_sd_clk,
    in_soft_reset,
    hrst_n,
    in_longresponse,
    in_response,
    in_command_ready,
    in_sd_dat,
    in_sd_cmd,
    current_state,
    has_send_bit,
    has_receive_bit,
    end_command,
    end_command_and_response,
    response_timeout

);
input       in_sd_clk;
input       in_soft_reset;  
input       hrst_n;
input       in_longresponse;
input       in_response;
input       in_command_ready;
input [3:0] in_sd_dat;
input       in_sd_cmd;

output [2:0]    current_state;
output [5:0]    has_send_bit;
output [7:0]    has_receive_bit;
output          end_command;
output          end_command_and_response;
output          response_timeout;

reg [2:0]   current_state;
reg [2:0]   next_state;
reg         send_bit_counter_en;
reg [5:0]   has_send_bit;
reg         receive_bit_counter_en;
reg [7:0]   has_receive_bit;
reg         resp_time_counter_en;
reg [6:0]   resp_time;
reg         end_command;
reg         end_command_and_response;

wire [7:0]  need_to_receive_bit;
wire        in_command_ready;

assign need_to_receive_bit = in_longresponse ? 8'd136 : 8'd47;  // Use to compare with has received bits

// FSM1 
always @(posedge in_sd_clk or negedge hrst_n) begin
    if(!hrst_n)
        current_state <= `CMD_STATE_STOP;
    else if (!in_soft_reset)
        current_state <= `CMD_STATE_STOP;
    else
        current_state <= next_state;
end

//FSM2
always @(*) begin
    case (current_state)
        `CMD_STATE_STOP:
            if (in_command_ready)
                next_state = `CMD_STATE_WAIT_SEND;
            else
                next_state = current_state;

        `CMD_STATE_WAIT_SEND:
            if (in_sd_dat[0])
                next_state = `CMD_STATE_SEND;
            else
                next_state = current_state;

        `CMD_STATE_SEND:
            if (has_send_bit != 47)
                next_state = `CMD_STATE_SEND;
            else if (!in_response)
                next_state = `CMD_STATE_STOP;
            else
                next_state = `CMD_STATE_WAIT_RECEIVE;
        
        `CMD_SEND_WAITE_RECEIVE:
            if(resp_time == 7'b0111111)
                next_state = `CMD_STATE_STOP;
            else if (!in_sd_cmd)
                next_state = `CMD_STATE_RECEIVE;
            else
                next_state = `CMD_SEND_WAITE_RECEIVE;

        `CMD_STATE_RECEIVE:
            if(has_receive_bit == (need_to_receive_bit - 1))
                next_state = `CMD_STATE_STOP;
            else 
                next_state = `CMD_STATE_RECEIVE;

        default: next_state = `CMD_STATE_STOP;
    endcase
end
//FSM3
always @(*) begin
    case (current_state)
        `CMD_STATE_STOP:
        begin
            has_send_bit = 1'b0;
            receive_bit_counter_en = 1'b0;
            resp_time_counter_en = 1'b0;
            end_command_and_response = 1'b0;
            end_command = 1'b0;
            response_timeout =1'b0;
        end

        `CMD_STATE_WAIT_SEND:
        begin
            has_send_bit = 1'b0;
            receive_bit_counter_en = 1'b0;
            resp_time_counter_en = 1'b0;
            end_command_and_response = 1'b0;
            end_command = 1'b0;
            response_timeout =1'b0;
        end

        `CMD_STATE_SEND:
        begin
            send_bit_counter_en = 1'b1;

            receive_bit_counter_en = 1'b0;
            resp_time_counter_en = 1'b0;
            end_command_and_response = 1'b0;
            end_command = 1'b0;
            response_timeout =1'b0;
        end
        
        `CMD_SEND_WAITE_RECEIVE:
        begin
            end_command = 1'b1;
            send_bit_counter_en = 1'b0;

            receive_bit_counter_en = 1'b0;
            resp_time_counter_en = 1'b0;
            end_command_and_response = 1'b0;
            response_timeout =1'b0;

            if(resp_time == 7'b0111111)
                response_timeout = 1'b1;
            else if (!in_sd_cmd)
                resp_time_counter_en = 1'b0;
            else
                resp_time_counter_en = 1'b1;
        end

        `CMD_STATE_RECEIVE:
        begin
            send_bit_counter_en = 1'b0;
            resp_time_counter_en = 1'b0;
            end_command = 1'b1;
            end_command_and_response = 1'b0;
            response_timeout = 1'b0;
            if(has_receive_bit == (need_to_receive_bit - 1))
                begin
                    end_command_and_response = 1'b1;
                    receive_bit_counter_en = 1'b0;
                end
            else
                receive_bit_counter_en = 1'b1;
        end

        default: 
        begin
            has_send_bit = 1'b0;
            receive_bit_counter_en = 1'b0;
            resp_time_counter_en = 1'b0;
            end_command_and_response = 1'b0;
            end_command = 1'b0;
            response_timeout =1'b0;
        end
        
    endcase
end

//-----------------------------------------------
//Has sent bits counter
//-----------------------------------------------
always @(posedge in_sd_clk or negedge hrst_n) begin
    if(!hrst_n)
        has_send_bit <= 6'b0;
    else if(!in_soft_reset)
        has_send_bit <= 6'b0;
    else
    begin
        if(has_send_bit == 47)
            has_send_bit <= 6'b0;
        else if (send_bit_counter_en == 1'b1)
            has_send_bit <= has_send_bit + 1;
    end
end

//-------------------------------------------------
//has received bits counter
//-------------------------------------------------
always @(posedge in_sd_clk or negedge hrst_n) begin
    if(!hrst_n)
        has_receive_bit <= 8'b0;
    else if (!in_soft_reset)
        has_receive_bit <= 8'b0;
    else begin
        if(has_receive_bit == (need_to_receive_bit - 1))
            has_receive_bit <= 8'b0;
        else if (receive_bit_counter_en == 1'b1)
            has_receive_bit <= has_receive_bit + 1;
    end
end

//-------------------------------------------------------
//Command response time counter
//------------------------------------------------------

always @(posedge in_sd_clk or negedge hrst_n) begin
    if(!hrst_n)
        resp_time <= 7'b0;
    else if (!in_soft_reset)
        resp_time <= 7'b0;
    else if( current_state == `CMD_STATE_RECEIVE)
            resp_time <= 7'b0;
    else if (resp_time == 63)
        resp_time <= 7'b0;
    else if (resp_time_counter_en == 1'b1)
        resp_time <= resp_time + 1;
end

endmodule

sd_cmd_send_shift_register

功能描述:

1、从配置寄存器中接收命令,转换成串行数据送入CMD输入端,如下图所示:在数据发送状态讲数据转换成串行输出,根据SD卡的时钟模式,选择偏移半个周期的数据或者原始数据。
在这里插入图片描述
2、命令线的方向控制:默认输入状态,只有在发送命令状态时(STATE_SEND)输出;
3、产生CRC校验数据,并送入命令线输出。

信号描述

signalsI/OWidthfromtodeclaration
in_sd_clkinput1sd_clk时钟信号
hrst_ninput1AHB bus异步复位
in_soft_resetinput1软件复位
in_current_stateinput[2:0]sd_cmd_fsmcmd控制状态机标志位
in_command_indexinput[5:0]sd_if待发送的命令
in_command_argumentinput[31:0]sd_if待发送伴随命令的argument
in_has_send_bitinput[5:0]sd_cmd_fsm已发送的命令bit数
in_high_speed_clkinput1sd_if
in_TestModeinput1测试模式
out_sd_cmdoutput1sd card命令输出端口
out_cmd_diroutput1命令线方向控制端口

verilog 实现

module sd_cmd_send_shift_regidter (
    in_sd_clk,              //clock for sd card
    hrst_n,                 //ahb signal
    in_soft_reset,          //software reset
    in_current_state,       //current state od command fsm
    in_command_index,       //command index
    in_command_argument,    //command argument
    in_has_send_bit,        //has sent command bits counter      
    in_high_speed_clk,
    out_sd_cmd,             //output command for sd card
    out_cmd_dir             //command direction  0:receive  1:send

);

input   in_sd_clk;
input   hrst_n;
input   in_soft_reset;
input [2:0]  in_current_state;
input [5:0]   in_command_index;
input [31:0]  in_command_argument;
input [5:0]  in_has_send_bit;
input   in_high_speed_clk;

output   out_sd_cmd;
output   out_cmd_dir;

reg [7:0]   shift_r;
reg [6:0]   crc_reg;
reg [0:7]   cmd_for_send;
reg cmd_dir_neg;
reg cmd_dir_pos;
reg sd_cmd_pos;
reg sd_cmd_neg;

//---------------------------------------------------
// Beginning if main code
//---------------------------------------------------
// Overview iof the command send shift register operation
//the following key signals are used in the command send shift register
//out_cmd_dir is the command direction
//out_sd_cmd is the output command to sd card
//crc_reg is the generated command crc register
//----------------------------------------------------
// Determine the command direction  0:receive 1:send
//----------------------------------------------------

always @(posedge in_sd_clk or negedge hrst_n) begin
    if(!hrst_n)
        cmd_dir_pos <= 1'b0;
	else if (in_current_state == `CMD_STATE_SEND)
		cmd_dir_pos <= 1'b1;
	else if (in_current_state == `CMD_STATE_STOP)
		cmd_dir_pos <= 1'b0;
	else if (in_current_state == `CMD_STATE_WAIT_RECEIVE)
		cmd_dir_pos <= 1'b0;
end

always @(negedge in_sd_clk or negedge hrst_n) begin
    if(!hrst_n)
        cmd_dir_neg <= 1'b0;
    else
        cmd_dir_neg <= cmd_dir_pos;
end
// cmd_dir_neg 比 cmd_dir_pos delay 半个周期

assign out_cmd_dir = in_high_speed_clk ? cmd_dir_pos : cmd_dir_neg;

//------------------------------------------------------------
// Divide the command payload in to 5 8bit parts
// -----------------------------------------------------------
always @(*) begin
    cmd_for_send = 8'b0;
//这里先写了cmd_for_send=0,等下就不用else了,有什么优点来着??
//下面的case不包含所有的情况,
    if (in_current_state == `CMD_STATE_SEND)
        begin
            case(in_has_send_bit)
                6'b0,6'b1,6'b10,6'b11,6'b100,6'b101,6'b110,6'b111:
				//{start bit,transmit bit,in_command_index}  差7bit crc  1bit end bit
                    cmd_for_send = {1'b0,1'b1,in_command_index};
                6'b001000,6'b001001,6'b001010,6'b001011,6'b001100,6'b001101,6'b001110,6'b001111:
                    cmd_for_send = {in_command_argument[31:24]};
                6'b010000,6'b010001,6'b010010,6'b010011,6'b010100,6'b010101,6'b010110,6'b010111:
                    cmd_for_send = {in_command_argument[23:16]};
                6'b011000,6'b011001,6'b011010,6'b011011,6'b011100,6'b011101,6'b011110,6'b011111:
                    cmd_for_send = {in_command_argument[15:8]};
                6'b100000,6'b100001,6'b100010,6'b100011,6'b100100,6'b100101,6'b100110,6'b100111:
                    cmd_for_send = {in_command_argument[7:0]};
            endcase
        end
end

//------------------------------------------------------------
// Send command
//------------------------------------------------------------

always @(posedge in_sd_clk or negedge hrst_n) begin
    if(!hrst_n)
        begin
            sd_cmd_pos <= 1'b1;
            shift_r <= 8'b0;
        end
    else if(!in_soft_reset)
        begin
            sd_cmd_pos <= 1'b1;
            shift_r <= 8'b0;
        end
    else
        begin
            if(in_current_state == `CMD_STATE_SEND)
                begin
				//每8bit的开始进到case判断是第几个8bit,拿数据
				//不是开始,则每个clk移位并行转串行
				//这里最后可以不补0,把shift_r改小1bit
                    if(in_has_send_bit[2:0] == 3'b0)
                        begin
                            case(in_has_send_bit)
                                6'b000000:
                                    {sd_cmd_pos,shift_r} <= {1'b0,1'b1,in_command_index,1'b0};
                                6'b001000:
                                    {sd_cmd_pos,shift_r} <= {in_command_argument[31:24],1'b0};
                                6'b010000:
                                    {sd_cmd_pos,shift_r} <= {in_command_argument[23:16],1'b0};
                                6'b011000:
                                    {sd_cmd_pos,shift_r} <= {in_command_argument[15:8],1'b0};
                                6'b100000:
                                    {sd_cmd_pos,shift_r} <= {in_command_argument[7:0],1'b0};
								6'b101000:
                                    {sd_cmd_pos,shift_r} <= {crc_reg,1'b1,1'b0};
                            endcase
                        end
                    else
                        {sd_cmd_pos,shift_r} <= {shift_r,1'b0};
                end
        end
end

always @(negedge in_sd_clk or negedge hrst_n) begin
    if (!hrst_n)
        sd_cmd_neg <= 1'b0;
    else
        sd_cmd_neg <= sd_cmd_pos;
end

assign out_sd_cmd = in_high_speed_clk ? sd_cmd_pos : sd_cmd_neg;
//让接收方更好的满足时序,pos建立时间不满足,那么移动半个周期可能就满足了
//-------------------------------------------------------------
// Genetate the command crc for sending
// ------------------------------------------------------------

always @(posedge in_sd_clk or negedge hrst_n) begin
    if(!hrst_n)
        crc_reg <= 7'b0;
    else if (!in_soft_reset)
        crc_reg <= 7'b0;
    else
        if((in_current_state == `CMD_STATE_SEND) &&
            (in_has_send_bit >= 6'b0)            &&
            (in_has_send_bit < 6'd40))
        begin
            crc_reg[0] <= cmd_for_send[in_has_send_bit[2:0]]^crc_reg[6];
            crc_reg[1] <= crc_reg[0];
            crc_reg[2] <= crc_reg[1];
            crc_reg[3] <= cmd_for_send[in_has_send_bit[2:0]]^crc_reg[6]^crc_reg[2];
            crc_reg[4] <= crc_reg[3];
            crc_reg[5] <= crc_reg[4];
            crc_reg[6] <= crc_reg[5];
        end
        else
            crc_reg <= 7'b0;
end

endmodule

sd_cmd_receive_shift_register

功能描述:

1、接收卡返回的数据(命令的响应),转成并行数据,送入response0~3寄存器,供软件可读(写到sd_if的寄存器中);
2、接收CRC数据,并和内部产生的CRC数据进行对比,判断数据接收是否出错(out_cmd_receive_crc_error),软件可查。

信号描述

signalsI/OWidthfromtodeclaration
in_sd_clkinput1sd_clk时钟信号
hrst_ninput1AHB bus
in_soft_resetinput1软件复位
in_current_stateinput[2:0]sd_cmd_fsmcmd控制状态机标志位
in_serial_cmdinput1sd card命令输入端口
in_has_receieve_bitinput[7:0]已接收的命令bit数
in_longresponseinput1device有命令长回复信号(这个模块有工作就说明有response,不是长就是短)
out_cmd_receive_crc_erroroutput1接收命令出错信号
response0output[31:0]sd_if响应信号寄存器0
response1output[31:0]sd_if响应信号寄存器1
response2output[31:0]sd_if响应信号寄存器2
response3output[31:0]sd_if响应信号寄存器3

verilog 实现

module sd_cmd_receive_shift_register(
    in_sd_clk           ,
    hrst_n              ,
    in_soft_reset       ,
    in_current_state    ,
    in_serial_cmd       ,
    in_has_receive_bit  ,
    in_long_response    ,
    out_cmd_receive_crc_error,
    response0   ,
    response1   ,
    response2   ,
    response3
);
input       in_sd_clk                  ;
input       hrst_n                     ;
input       in_soft_reset              ;
input [2:0] in_current_state           ;
input       in_serial_cmd              ;
input [7:0] in_has_receive_bit         ;
input       in_long_response           ;

output       out_cmd_receive_crc_error  ;
output       response0  ; 
output       response1  ; 
output       response2  ; 
output       response3  ;

reg [31:0]  response0;
reg [31:0]  response1;
reg [31:0]  response2;
reg [31:0]  response3;
reg [6:0]   receive_crc_reg;
reg [6:0]   generate_crc_reg;
reg         out_cmd_receive_crc_error;

always @(posedge in_sd_clk or negedge hrst_n) begin
    if(!hrst_n)
        begin
            response0 <= 32'b0; 
            response1 <= 32'b0; 
            response2 <= 32'b0; 
            response3 <= 32'b0;
        end
    else if (!in_soft_reset)
        begin
            response0 <= 32'b0; 
            response1 <= 32'b0; 
            response2 <= 32'b0; 
            response3 <= 32'b0;
        end
    else if (in_current_state == `CMD_STATE_SEND)
        begin
            response0 <= 32'b0; 
            response1 <= 32'b0; 
            response2 <= 32'b0; 
            response3 <= 32'b0;
        end
    else if (in_current_state == `CMD_STATE_RECEIVE)
        begin
            if(in_long_response)
                begin
				//这里不是38:longresponse是120bit,下面32*3  剩高位的24bit,response3最高8bit=0,剩下24bit读入
                    if((in_has_receive_bit >= 7) && (in_has_receive_bit <=30))
                        response3 <= {response3[30:0],in_serial_cmd};
                    else if((in_has_receive_bit >= 31) && (in_has_receive_bit <= 62))
                        response2 <= {response2[30:0],in_serial_cmd};
                    else if((in_has_receive_bit >= 63) && (in_has_receive_bit <= 94))
                        response1 <= {response1[30:0],in_serial_cmd};
                    else if((in_has_receive_bit >= 95) && (in_has_receive_bit <= 126))
                        response0 <= {response0[30:0],in_serial_cmd};
                end
				//从第7bit开始:从46开始计数的,start bit没被记进去
            else if((in_has_receive_bit >= 7) && (in_has_receive_bit <= 38))
                response0 <= {response0[30:0],in_serial_cmd};
        end
end

//--------------------------------------------
// Receive command crc
// --------------------------------------------

always @(posedge in_sd_clk or negedge hrst_n) begin
    if(!hrst_n)
        receive_cec_reg <= 7'b0;
    else if (!in_soft_reset)
        receive_cec_reg <= 7'b0;
    else if (in_current_state == `CMD_STATE_SEND)
        receive_cec_reg <= 7'b0;
    else if (in_current_state == `CMD_STATE_RECEIVE)
        begin
            if (in_long_response)
                begin
                    if((in_has_receive_bit >= 127) && (in_has_receive_bit <= 133))
                        receive_cec_reg <= {receive_cec_reg[5:0],in_serial_cmd};
                end
            else begin
                 if((in_has_receive_bit >= 39) && (in_has_receive_bit <= 45))
                     receive_cec_reg <= {receive_cec_reg[5:0],in_serial_cmd};
             end
         end
end

//-----------------------------------------------
// Gernerate command crc
// ----------------------------------------------

always @(posedge in_sd_clk or negedge hrst_n) begin
    if(!hrst_n)
        receive_cec_reg <= 7'b0;
    else if (!in_soft_reset)
        receive_crc_reg <= 7'b0;
    else if(in_current_state == `CMD_STATE_SEND)
        receive_crc_reg <= 7'b0;
    else if (in_current_state == `CMD_STATE_RECEIVE)
        begin
            if(in_long_response)
                begin
                    if((in_has_receive_bit >= 7) && (in_has_receive_bit <=126))
                        begin
                            generate_crc_reg[0] <= in_serial_cmd^generate_crc_reg[6];
                            generate_crc_reg[1] <= generate_crc_reg[0];
                            generate_crc_reg[2] <= generate_crc_reg[1];
                            generate_crc_reg[3] <= in_serial_cmd^generate_crc_reg[6]^generate_crc_reg[2];
                            generate_crc_reg[4] <= generate_crc_reg[3];
                            generate_crc_reg[5] <= generate_crc_reg[4];
                            generate_crc_reg[6] <= generate_crc_reg[5];
                        end
                end
            else
                begin
                    if((in_has_receive_bit >= 0) && (in_has_receive_bit <=38))
                        begin
                            generate_crc_reg[0] <= in_serial_cmd^generate_crc_reg[6];
                            generate_crc_reg[1] <= generate_crc_reg[0];
                            generate_crc_reg[2] <= generate_crc_reg[1];
                            generate_crc_reg[3] <= in_serial_cmd^generate_crc_reg[6]^generate_crc_reg[2];
                            generate_crc_reg[4] <= generate_crc_reg[3];
                            generate_crc_reg[5] <= generate_crc_reg[4];
                            generate_crc_reg[6] <= generate_crc_reg[5];
                        end
                end
        end
end

//----------------------------------------------------
// Compare generate command crc with received command crc
// -------------------------------------------------------

always @(posedge in_sd_clk or negedge hrst_n) begin
    if (!hrst_n)
        out_cmd_receive_crc_error <= 1'b0;
    else if (!in_soft_reset)
        out_cmd_receive_crc_error <= 1'b0;
    else if (in_current_state == `CMD_STATE_SEND)
        out_cmd_receive_crc_error <= 1'b0;
    else if (in_current_state == `CMD_STATE_RECEIVE)
        begin
            if (in_long_response)
                begin 
                    if(in_has_receive_bit == 134)
                        out_cmd_receive_crc_error <= !(generate_crc_reg == receive_crc_reg);
                end
            else if (in_has_receive_bit == 46)
                out_cmd_receive_crc_error <= !(generate_crc_reg == receive_crc_reg);
        end
end

endmodule

  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值