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状态;
信号描述
signals | I/O | Width | from | to | declaration |
---|---|---|---|---|---|
in_sd_clk | input | 1 | sd_clk | 时钟信号 | |
hrst_n | input | 1 | AHB bus | 异步复位 | |
in_soft_reset | input | 1 | 软件复位 | ||
in_longresponse | input | 1 | sd_if | device有命令长回复信号 | |
in_response | input | 1 | sd_if | device有命令回复信号 | |
in_command_ready | input | 1 | sd_if | 命令有效,状态机启动信号 | |
in_sd_data | input | [3:0] | sd card | 数据输入端口 | |
in_sd_cmd | input | 1 | sd card | 命令输入端口 | |
current_state | output | [2:0] | sd_if sd_cmd_send | ||
sd_cmd_receive | 状态机标志位 | ||||
has_send_bit | output | [5:0] | sd_cmd_send | 已发送命令bit位的大小 | |
has_receive_bit | output | [7:0] | sd_cmd_receive | 已接收命令bit位的大小 | |
end_command | output | 1 | sd_if | 命令结束信号 | |
end_command_and_response | output | 1 | sd_if | 命令结束且反馈接收完成信号 | |
response_timeout | output | 1 | sd_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校验数据,并送入命令线输出。
信号描述
signals | I/O | Width | from | to | declaration |
---|---|---|---|---|---|
in_sd_clk | input | 1 | sd_clk | 时钟信号 | |
hrst_n | input | 1 | AHB bus | 异步复位 | |
in_soft_reset | input | 1 | 软件复位 | ||
in_current_state | input | [2:0] | sd_cmd_fsm | cmd控制状态机标志位 | |
in_command_index | input | [5:0] | sd_if | 待发送的命令 | |
in_command_argument | input | [31:0] | sd_if | 待发送伴随命令的argument | |
in_has_send_bit | input | [5:0] | sd_cmd_fsm | 已发送的命令bit数 | |
in_high_speed_clk | input | 1 | sd_if | ||
in_TestMode | input | 1 | 测试模式 | ||
out_sd_cmd | output | 1 | sd card | 命令输出端口 | |
out_cmd_dir | output | 1 | 命令线方向控制端口 |
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),软件可查。
信号描述
signals | I/O | Width | from | to | declaration |
---|---|---|---|---|---|
in_sd_clk | input | 1 | sd_clk | 时钟信号 | |
hrst_n | input | 1 | AHB bus | ||
in_soft_reset | input | 1 | 软件复位 | ||
in_current_state | input | [2:0] | sd_cmd_fsm | cmd控制状态机标志位 | |
in_serial_cmd | input | 1 | sd card | 命令输入端口 | |
in_has_receieve_bit | input | [7:0] | 已接收的命令bit数 | ||
in_longresponse | input | 1 | device有命令长回复信号(这个模块有工作就说明有response,不是长就是短) | ||
out_cmd_receive_crc_error | output | 1 | 接收命令出错信号 | ||
response0 | output | [31:0] | sd_if | 响应信号寄存器0 | |
response1 | output | [31:0] | sd_if | 响应信号寄存器1 | |
response2 | output | [31:0] | sd_if | 响应信号寄存器2 | |
response3 | output | [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