设计目标:
1.sd卡初始化完成
2.往sd卡内的某一扇区写入数据
3.将对应扇区中的数据进行读取
4.比较写入数据与读取数据是否一致
sd卡顶层:
sd卡采用spi模式,有片选cs、时钟clk、命令线mosi、数据线miso四个引脚
例化sd初始化,写入,读取模块。初始化,由四个灯的亮灭查看对应状态是否完成
由于sd卡初始化频率不能超过400Khz,对时钟进行分频。
module sd_top(
input clk,
input clr,
input miso,
output sd_clk,
output reg mosi,
output reg sd_cs,
output sd_init_done_flag,//灯1
output wirte_done,//灯2
output read_done,//灯3
output data_right_flag//灯4
//初始化测试端口
/*output [8:0]c_state,
output [47:0]r_d,
output [3:0]r_dcnt */
//写测试端口
/*
output [5:0]send_cmd_cnt,
output [8:0]send_cmd_cnt2,//256
output [5:0]cs ,
output [2:0]r_cnt,
output [47:0]rece_data,
output [15:0]rece_data1, */
//读测试端口
/*
output [3:0]cs_read,
output [47:0]rece_fb,
output [3:0]re_cnt,
output [5:0]rece_data_cnt,
output [8:0]rece_data_cnt2,
output [15:0]data */
);reg [31:0]cnt;//分频产数250 50000000
reg div_clk;reg init_miso,wirte_miso,read_miso;//初始化
wire mosi_init,sd_cs_init;
//写
wire mosi_wirte,sd_cs_wirte;
//读
wire mosi_read,sd_cs_read;always@(*) beginif(sd_init_done_flag&&!wirte_done) begin //写过程mosi<=mosi_wirte;sd_cs<=sd_cs_wirte;wirte_miso<=miso;endelse if(sd_init_done_flag&&wirte_done) begin //读过程mosi<=mosi_read;sd_cs<=sd_cs_read;read_miso<=miso;endelse beginmosi<=mosi_init;sd_cs<=sd_cs_init;init_miso<=miso;end
end//分频
always@(posedge clk or negedge clr) beginif(!clr) begincnt<=0;div_clk<=0;end else beginif(cnt==125) begin //125div_clk=~div_clk;cnt<=0;endelse begincnt<=cnt+1;endend
end sd u1(.clk(div_clk),.clr(clr),//sd卡接口.miso(init_miso),.sd_clk(sd_clk),.mosi(mosi_init),.sd_cs(sd_cs_init),.sd_init_done_flag(sd_init_done_flag)//测试端口/*.c_state(c_state),.r_d(r_d),.r_dcnt(r_dcnt)*/); sd_wirte u2(.clk(div_clk),.clr(clr),.sd_init_done_flag(sd_init_done_flag),//sd卡端口.miso(wirte_miso),.mosi(mosi_wirte),.sd_cs(sd_cs_wirte),.wirte_done(wirte_done)//测试端口/* .send_cmd_cnt(send_cmd_cnt),.send_cmd_cnt2(send_cmd_cnt2),//256.cs(cs),.r_cnt(r_cnt),.rece_data(rece_data),.rece_data1(rece_data1)*/); sd_read u3(
.clk(div_clk),
.clr(clr),
.miso(read_miso),
.wirte_done(wirte_done),
.mosi(mosi_read),
.sd_cs(sd_cs_read),
.read_done(read_done),
.data_right_flag(data_right_flag)
//测试端口
/*
.cs(cs_read),
.rece_fb(rece_fb),
.re_cnt(re_cnt),
.rece_data_cnt(rece_data_cnt),
.rece_data_cnt2(rece_data_cnt2),
.data(data) */
);
endmodule
sd卡初始化
sd卡spi模式中时钟设置为:空闲时为高电平,下降沿更新数据,上升沿采集数据,所以状态机3发送命令为clk的上升沿,接收反馈过程块为sd_clk的下降沿。
module sd(
input clk,//初始化时钟设置为200khz
input clr,
//sd卡接口
input miso,//反馈接收端口
output sd_clk,
output reg mosi,//命令输入
output reg sd_cs,//片选信号
output reg sd_init_done_flag,
//测试端口
output [8:0]c_state,
output [47:0]r_d,//接收数据
output [3:0]r_dcnt
);parameter
//初始化相关状态
wait_cnt_process =9'b00000_0000,//等待上电过程
send_cmd0 =9'b00000_0001,//发送cmd0
rece_cmd0_fb =9'b00000_0010,
send_cmd8 =9'b00000_0100,//发送cmd8
rece_cmd8_fb =9'b00000_1000,
send_cmd55 =9'b00001_0000,//发送cmd55
rece_cmd55_fb =9'b00010_0000,
send_cmd41 =9'b00100_0000,//f发送cmd41
rece_cmd41_fb =9'b01000_0000,
sd_init_done =9'b1_0000_0000,cmd0={8'h40,8'h00,8'h00,8'h00,8'h00,8'h95},
cmd8={8'h48,8'h00,8'h00,8'h01,8'haa,8'h87},
cmd55={8'h77,8'h00,8'h00,8'h00,8'h00,8'hff},
cmd41={8'h69,8'h40,8'h00,8'h00,8'h00,8'hff};
//状态
reg [8:0]cs,ns;//等待同步时钟
reg [12:0]wait_cnt;//初始化时钟计数器
reg wait_cnt_comp;//计数组件
reg wait_done_flag;//上电同步完成标志//接收反馈
reg rece_comp;//接收组件
reg [47:0]rece_data;
reg [47:0]rece_cnt;
reg [47:0]rece_cnt1;//额外等待
reg rece_done_flag1;//额外等待
reg rece_done_flag;
reg [3:0]rdcnt;//发送命令
reg [6:0]cmd_send_cnt;//发送命令计数器assign sd_clk=~clk;
assign c_state=cs;
assign r_d=rece_data;
assign r_dcnt=rdcnt;//初始化等待74个以上的同步时钟
always@(posedge clk or negedge clr) beginif(!clr) beginwait_cnt<=0;wait_done_flag<=0;wait_cnt_comp<=0;endelse beginif((wait_cnt_comp==0)&&(wait_cnt==9'd5000)) begin //500wait_cnt<=0; wait_done_flag<=1;wait_cnt_comp<=1;endelse beginwait_cnt<=wait_cnt+1;wait_done_flag<=0;endend
end//状态机1-1
always@(posedge clk or negedge clr) beginif(!clr) cs<=wait_cnt_process;else cs<=ns;
end//状态机1-2
always@(*) begincase(cs) wait_cnt_process:beginif(wait_cnt_comp) ns<=send_cmd0;else ns<=wait_cnt_process;endsend_cmd0:beginif(cmd_send_cnt==47) ns<=rece_cmd0_fb;else ns<=send_cmd0;endrece_cmd0_fb:beginif(rece_done_flag) beginif(rece_data[47:40]==1) ns<=send_cmd8;else ns<=rece_cmd0_fb;endelse ns<=rece_cmd0_fb;endsend_cmd8:beginif(cmd_send_cnt==47) ns<=rece_cmd8_fb;else ns<=send_cmd8;endrece_cmd8_fb:beginif(rece_done_flag) beginif(rece_data[19:12]==8'h1a) ns<=send_cmd55;else ns<=send_cmd8;endelse ns<=rece_cmd8_fb;endsend_cmd55:beginif(cmd_send_cnt==47) ns<=rece_cmd55_fb;else ns<=send_cmd55;endrece_cmd55_fb:beginif(rece_done_flag) beginif(rece_data[47:36]==12'h01f) ns<=send_cmd41;else ns<=send_cmd55;endelse ns<=rece_cmd55_fb;endsend_cmd41:beginif(cmd_send_cnt==47) ns<=rece_cmd41_fb;else ns<=send_cmd41;endrece_cmd41_fb:beginif(rece_done_flag) beginif(rece_data[47:40]==0) ns<=sd_init_done;else ns<=send_cmd55;endelse ns<=rece_cmd41_fb;endsd_init_done:beginns<=sd_init_done;enddefault:beginns<=wait_cnt_process;endendcase
end//状态机1-3
always@(posedge clk or negedge clr) beginif(!clr) beginsd_cs<=1;mosi<=1;cmd_send_cnt<=0;sd_init_done_flag<=0;endelse begincase(cs) wait_cnt_process:beginsd_cs<=1;mosi<=1;endsend_cmd0:beginsd_cs<=0;if(cmd_send_cnt==7'd47) begincmd_send_cnt<=0;mosi<=cmd0[0];endelse begincmd_send_cnt<=cmd_send_cnt+1;mosi<=cmd0[7'd47-cmd_send_cnt];endendrece_cmd0_fb:beginmosi<=1;if(rece_done_flag) sd_cs<=1;else sd_cs<=0;endsend_cmd8:beginsd_cs<=0;if(cmd_send_cnt==7'd47) begincmd_send_cnt<=0;mosi<=cmd8[0];endelse begincmd_send_cnt<=cmd_send_cnt+1;mosi<=cmd8[7'd47-cmd_send_cnt];endendrece_cmd8_fb:beginmosi<=1;if(rece_done_flag) sd_cs<=1;else sd_cs<=0;endsend_cmd55:beginsd_cs<=0;if(cmd_send_cnt==7'd47) begincmd_send_cnt<=0;mosi<=cmd55[0];endelse begincmd_send_cnt<=cmd_send_cnt+1;mosi<=cmd55[7'd47-cmd_send_cnt];endendrece_cmd55_fb:beginmosi<=1;if(rece_done_flag) sd_cs<=1;else sd_cs<=0;end9'b00100_0000:beginsd_cs<=0;if(cmd_send_cnt==7'd47) begincmd_send_cnt<=0;mosi<=cmd41[0];endelse begincmd_send_cnt<=cmd_send_cnt+1;mosi<=cmd41[7'd47-cmd_send_cnt];endend9'b01000_0000:beginmosi<=1;if(rece_done_flag) sd_cs<=1;else sd_cs<=0;endsd_init_done:beginmosi<=1;sd_cs<=1;sd_init_done_flag<=1;enddefault:beginmosi<=1;sd_cs<=1;endendcase
end
end//接收反馈
always@(posedge sd_clk or negedge clr) begin //下降沿采集数据if(!clr) beginrece_comp<=1;rece_data<=0;rece_cnt<=0;rece_done_flag<=0;rdcnt<=0;endelse beginif(!miso&&rece_comp) beginrece_data<={rece_data[46:0],miso}; rece_comp<=0;rece_done_flag<=0;rdcnt<=rdcnt+1;endelse beginif(!rece_comp) beginif(rece_cnt==46) beginrece_cnt<=0;rece_comp<=1;rece_done_flag<=1;rece_data<={rece_data[46:0],miso};endelse beginrece_cnt<=rece_cnt+1;rece_data<={rece_data[46:0],miso};rece_done_flag<=0;endend else rece_done_flag<=0;endend
endendmodule
sd卡写入
由于sd卡spi模式不需要crc校验,所以命令24最后一个字节与发送数据的最后两个字节为f
sd卡默认写入数据读取量为512字节,本次以两字节为单位写入16'h5550,写入地址为6000,一共需要写入512次
在写入完成后,当miso端口为高电平一段时间后,即写入完成
module sd_wirte(
input clk,
input clr,
input sd_init_done_flag,
//sd卡端口
input miso,//反馈
output reg mosi,//命令输入
output reg sd_cs,
output reg wirte_done,
//测试端口
output reg [5:0]send_cmd_cnt,
output reg [8:0]send_cmd_cnt2,//255
output reg [5:0]cs,ns,
output reg [2:0]r_cnt,
output reg [47:0]rece_data,
output reg [15:0]rece_data1
);parameter cmd24={8'h58,32'd6000,8'hff},
start_byte=8'hfe,
data=16'h5550,//两字节
end_byte=16'b1111_1111_1111_1111,
//状态
send_cmd24=6'b000001,
rece_cmd24_fb=6'b000010,
send_data1=6'b000100,//发送数据头
send_data2=6'b001000,//发送数据
send_data3=6'b010000,//发送crc校验spi下不管
rece_fb=6'b100000;reg [5:0]rece_cnt;
reg rece_done_flag;
reg rece_comp;reg [5:0]rece_cnt1;reg rece_comp1; reg [47:0]fb_data;//发送完数据后的检测
reg [3:0]fb_cnt;
reg fb_flag;
reg fb_comp;assign clk_180=~clk;//状态机1
always@(posedge clk or negedge clr) beginif(!clr) cs<=send_cmd24;else cs<=ns;
end//状态机2
always@(*) beginns<=send_cmd24;case(cs) send_cmd24:beginif(send_cmd_cnt==47) ns<=rece_cmd24_fb;else ns<=send_cmd24;endrece_cmd24_fb:beginif(rece_done_flag) beginif(rece_data[47:40]==0) beginns<=send_data1;endelse beginns<=send_cmd24;endendelse beginns<=rece_cmd24_fb;endendsend_data1:beginif(send_cmd_cnt==7) ns<=send_data2;else ns<=send_data1;endsend_data2:beginif(send_cmd_cnt2==256) ns<=send_data3; //256else ns<=send_data2;endsend_data3:beginif(send_cmd_cnt==15) ns<=rece_fb;else ns<=send_data3;endrece_fb:beginns<=rece_fb;enddefault:beginns<=send_cmd24;endendcase
end//发送命令以及控制相关端口
always@(posedge clk or negedge clr) beginif(!clr) beginsend_cmd_cnt<=0; send_cmd_cnt2<=0;sd_cs<=1;mosi<=1;fb_flag<=0;wirte_done<=0;endelse begincase(cs) send_cmd24:beginif(sd_init_done_flag) beginsd_cs<=0;if(send_cmd_cnt==47) beginmosi<=cmd24[0];send_cmd_cnt<=0;end else beginsend_cmd_cnt<=send_cmd_cnt+1;mosi<=cmd24[47-send_cmd_cnt];end endelse beginsd_cs<=1;mosi<=1;endendrece_cmd24_fb:beginmosi<=1; endsend_data1:beginif(send_cmd_cnt==7) beginsend_cmd_cnt<=0;mosi<=start_byte[7-send_cmd_cnt];endelse beginsend_cmd_cnt<=send_cmd_cnt+1;mosi<=start_byte[7-send_cmd_cnt];endendsend_data2:beginif(send_cmd_cnt==15) beginsend_cmd_cnt<=0;mosi<=data[15-send_cmd_cnt];send_cmd_cnt2<=send_cmd_cnt2+1;endelse beginsend_cmd_cnt<=send_cmd_cnt+1;mosi<=data[15-send_cmd_cnt];endendsend_data3:beginif(send_cmd_cnt==15) beginsend_cmd_cnt<=0;mosi<=end_byte[15-send_cmd_cnt];endelse beginsend_cmd_cnt<=send_cmd_cnt+1;mosi<=end_byte[15-send_cmd_cnt];endendrece_fb:beginmosi<=1;fb_flag<=1;if(fb_data==48'hffff_ffff_ffff) beginfb_flag<=0;wirte_done<=1;sd_cs<=1;end else beginfb_flag<=1;wirte_done<=0;end enddefault:beginmosi<=1;sd_cs<=1;endendcaseend
end//接收数据
always@(posedge clk_180 or negedge clr) beginif(!clr) beginrece_cnt<=0;rece_data<=0;rece_comp<=1; r_cnt<=0;rece_done_flag<=0;endelse beginif(rece_comp&&miso==0) beginrece_comp<=0;rece_data<={rece_data[46:0],miso};endelse beginif(rece_comp==0) beginif(rece_cnt==46) beginrece_data<={rece_data[46:0],miso};rece_cnt<=0;rece_comp<=1;r_cnt<=r_cnt+1; rece_done_flag<=1;endelse beginrece_cnt<=rece_cnt+1;rece_data<={rece_data[46:0],miso};endendelse rece_done_flag<=0;endend
end//检测完成数据发送后状态是否稳定
always@(posedge clk_180 or negedge clr) beginif(!clr) beginfb_data<=0;fb_cnt<=0;fb_comp<=1;endelse beginif(fb_flag&&fb_comp) beginfb_comp<=0; end else if(!fb_comp) beginif(fb_cnt==47) beginfb_cnt<=0;fb_data<={fb_data[46:0],miso};endelse beginfb_cnt<=fb_cnt+1;fb_data<={fb_data[46:0],miso}; endendend
end//接收反馈数据
always@(posedge clk_180 or negedge clr) beginif(!clr) beginrece_cnt1<=0;rece_data1<=0;rece_comp1<=1; endelse beginif(rece_comp1&&miso==0) beginrece_comp1<=0;rece_data1<={rece_data1[14:0],miso};endelse beginif(rece_comp1==0) beginif(rece_cnt1==14) beginrece_data1<={rece_data1[14:0],miso};rece_cnt1<=0;rece_comp1<=1; endelse beginrece_cnt1<=rece_cnt1+1;rece_data1<={rece_data1[14:0],miso};endendendend
endendmodule
sd卡读取
以两字节为单位接收数据,读取地址为6000,共需接收256次,每接收两字节数据检测是否为16‘后5550
module sd_read(
input clk,
input clr,
input miso,
input wirte_done,
output reg mosi,
output reg sd_cs,
output reg read_done,
output reg data_right_flag,
//测试端口
output reg [3:0]cs,ns,
output reg [47:0]rece_fb,
output reg [3:0]re_cnt,
output reg [5:0]rece_data_cnt,
output reg [8:0]rece_data_cnt2,//255
output reg [15:0]data
);parameter
w_t=0,
send_cmd17=4'b0001,
rece_cmd17_fb=4'b0010,
rece_data=4'b0100,
rece_data_done=4'b1000,
cmd17={8'h51,32'd6000,8'hff};wire clk_180;reg [5:0]wait_cnt;
reg wait_flag;reg [5:0]send_cmd_cnt;reg [5:0]rece_cnt;
reg rece_comp;
reg rece_done_flag;reg rece_data_flag;
reg rece_data_flag1;assign clk_180=~clk;//等待
always@(posedge clk or negedge clr) beginif(!clr) beginwait_cnt=0; wait_flag=0;endelse beginif(wirte_done) beginif(wait_cnt==6'd48) beginwait_cnt<=0;wait_flag<=1;endelse beginwait_cnt<=wait_cnt+1;wait_flag<=0;endendelse beginwait_flag<=0;endend
end//状态机1
always@(posedge clk or negedge clr) beginif(!clr) cs<=0;else cs<=ns;
end//状态机2
always@(*) begincase(cs) w_t:beginif(wait_flag) beginns<=send_cmd17;endelse beginns<=w_t;endendsend_cmd17:beginif(send_cmd_cnt==47) ns<=rece_cmd17_fb;else ns<=send_cmd17;endrece_cmd17_fb:beginif(rece_done_flag) beginif(rece_fb[47:40]==0) beginns<=rece_data;endelse beginns<=send_cmd17;endendelse beginns<=rece_cmd17_fb;endendrece_data:beginif(rece_data_cnt2==256) beginns<=rece_data_done;endelse ns<=rece_data;endrece_data_done:beginns<=rece_data_done;enddefault:beginns<=4'b0000;endendcase
end//状态机3
always@(posedge clk or negedge clr) beginif(!clr) beginsd_cs<=1;mosi<=1;send_cmd_cnt<=0;rece_data_flag<=0;read_done<=0;endelse begincase(cs)w_t:beginsd_cs<=1;mosi<=1;endsend_cmd17:beginsd_cs<=0;if(send_cmd_cnt==47) beginmosi<=cmd17[0];send_cmd_cnt<=0;end else beginsend_cmd_cnt<=send_cmd_cnt+1;mosi<=cmd17[47-send_cmd_cnt];end endrece_cmd17_fb:beginmosi<=1; endrece_data:beginrece_data_flag<=1;endrece_data_done:beginrece_data_flag<=0;read_done<=1;enddefault:beginsd_cs<=1;mosi<=1; endendcaseend
end//接收反馈
always@(posedge clk_180 or negedge clr) beginif(!clr) beginrece_cnt<=0;rece_fb<=0;rece_comp<=1; re_cnt<=0;rece_done_flag<=0;endelse beginif(rece_comp&&miso==0) beginrece_comp<=0;rece_fb<={rece_fb[46:0],miso};endelse beginif(rece_comp==0) beginif(rece_cnt==46) beginrece_fb<={rece_fb[46:0],miso};rece_cnt<=0;rece_comp<=1;re_cnt<=re_cnt+1; rece_done_flag<=1;endelse beginrece_cnt<=rece_cnt+1;rece_fb<={rece_fb[46:0],miso};endendelse rece_done_flag<=0;endend
end//接收数据
always@(posedge clk_180 or negedge clr) beginif(!clr) beginrece_data_cnt<=0;rece_data_cnt2<=0;rece_data_flag1<=0;data<=0;endelse beginif(rece_data_flag&&!miso&&!rece_data_flag1) beginrece_data_flag1<=1;endelse if(rece_data_flag1&&rece_data_flag) beginif(rece_data_cnt==15) beginrece_data_cnt<=0;rece_data_cnt2<=rece_data_cnt2+1;data<={data[14:0],miso};endelse beginrece_data_cnt<=rece_data_cnt+1;data<={data[14:0],miso};endendend
end//数据检测
always@(posedge clk or negedge clr) beginif(!clr) begindata_right_flag<=0;endelse beginif(rece_data_cnt==0) beginif(data==16'h5550) data_right_flag<=1;else begindata_right_flag<=0;endendend
endendmodule
RTL视图
管脚分配
SignalTrap
图为读取的SignalTrap调试图,成功读取出正确的写入数据16’h5550
下载验证
分别对应初始化、写入数据、读取数据、所读数据与写入数据一致成功
修改比较数据为16‘h5555或修改读取地址为32’d7000,则对应第四盏灯都不点亮
cmd17={8'h51,32'd7000,8'hff};
...
always@(posedge clk or negedge clr) beginif(!clr) begindata_right_flag<=0;endelse beginif(rece_data_cnt==0) beginif(data==16'h5555) data_right_flag<=1;else begindata_right_flag<=0;endendend
end