|
【国产Tang Primer 25K测评】一次失败的尝试中出现的问题-SD卡+SDRAM通过HDMI显示
[复制链接]
- 最近初步看懂了nestang25k的例程,依旧没下手,看见里面出现了SD卡和SDRAM的内容,于是我就去野火的例程里搜出来了一个最接近的例程想移植看看,区别在于野火使用的是DDR3 SDRAM,但是tang25k使用的SDRAM型号貌似是DDR1 SDRAM,这里没有DDR3的话只能进行更改,然后我就想起来了tang25k的活动页介绍里有提到DDR3
emmmm,真的有DDR3吗?而且也并没有参考例程存在,难道SDRAM模块也可以当作DDR3用?感觉上是写错了,或者需要自行设计一个DDR3模块。
- 延续上面的思路,没有DDR3,就用普通的SDRAM应该也一样,这里考虑使用nestang25k例程中给出的sdram控制器或者高云研制的sdram的ip进行替换。
- 然后就是SD卡,nestang25k例程里用到的SD卡是SDIO模式,野火的SD卡用的是SPI模式,这里我想使用野火的SPI模式,因为人家例程都是设计好的,尽量减少更改也能简单些。
- 开始写代码:
- 碰到的第一个问题是 按键问题,PLL的复位按键时灵时不灵让我很恼火,想研究一下到底为什么,花了半天时间,我发现了问题,在引脚绑定中,要根据按键实际的物理接线方式,对引脚约束为上拉下拉,否则按键失灵(一直忽略引脚约束这个问题是因为我一直认为只要物理结构上设置了上拉下拉就不用引脚约束了,实际是错误的)。
以下是我测试按键的代码:
module led_top(
input CLK_0,
input s1,
input reset,//系统复位,高电平有效
input button1,
input button2,
input button3,
input button4,
output IO_voltage,
output led1,
output led2,
output led3,
output led4,
output led5,
output led6,
output led7
);
//parameter Clock_frequency = 50_000_000;// 50MHZ
parameter count_value = 25_000_000;// count
reg[31:0] count_value_reg;
reg count_value_flag;
wire CLK_1;
always@(posedge CLK_1)begin
if(count_value_reg <=count_value)begin
count_value_reg <=count_value_reg+1'b1;
count_value_flag<=1'b0;
end
else begin
count_value_reg<=32'b0;
count_value_flag <=1'b1;//convert
end
end
reg IO_voltage_reg=1'b0;
always@(posedge CLK_1)begin
if(count_value_flag)
IO_voltage_reg<=~IO_voltage_reg;
else
IO_voltage_reg<=IO_voltage_reg;
end
assign IO_voltage = IO_voltage_reg;
wire lock_o;
board_input board_input_u1(
.rst(s1),
.clk(CLK_1),
.p8_i(button1),
.to_o({led6,led5})
);
wire CLK_25M;
wire CLK_8M388;
assign led7=button2;//不按的时候为1,按下去为0,因为led模块设计原因,led=1,输出为0,led为0,输出为1
Gowin_PLL your_instance_name(
.lock(lock_o), //output lock,
.clkout0(CLK_1), //output clkout0,50MHz,相偏180
.clkout1(CLK_25M), //output clkout1,25MHZ
.clkin(CLK_0), //input clkin,50MHz
.clkout2(CLK_8M388), //output clkout2
// .reset(1'b0) //input reset,没有进入复位状态,有输出;
// .reset(1'b1) //input reset,没有进入复位状态,没输出;
.reset(reset) //input reset,没有进入复位状态,初始取反为1,按前两行得出结论应该没有输出,实际上却有输出
);
endmodule
module board_input(
rst,//复位
clk,//第八个按键,用来制作选择高中低
p8_i,
to_o//输出第八位的数据
);
//
parameter s1=2'b01,//1
s2=2'b10,//2
s3=2'b11;//3
//input rst;
input rst;
input clk;
input p8_i;
output reg [1:0] to_o;
always@(posedge clk or posedge rst)
begin
if(rst)begin
to_o<=s3;
end
else if(~p8_i)
begin
to_o<=s1;
end
else if(p8_i)
begin
to_o<=s2;
end
else;
end
endmodule
以下为引脚约束:一定要设置引脚上拉下拉
IO_LOC "led7" E11;
IO_PORT "led7" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "led6" A10;
IO_PORT "led6" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "led5" A11;
IO_PORT "led5" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "led4" L11;
IO_PORT "led4" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "led3" K11;
IO_PORT "led3" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "led2" K5;
IO_PORT "led2" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "led1" L5;
IO_PORT "led1" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "IO_voltage" E10;
IO_PORT "IO_voltage" PULL_MODE=DOWN DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "button4" G11;
IO_PORT "button4" PULL_MODE=UP DRIVE=OFF BANK_VCCIO=3.3;
IO_LOC "button3" D11;
IO_PORT "button3" PULL_MODE=UP DRIVE=OFF BANK_VCCIO=3.3;
IO_LOC "button2" B5510;
IO_PORT "button2" PULL_MODE=UP DRIVE=OFF BANK_VCCIO=3.3;
IO_LOC "button1" C10;
IO_PORT "button1" PULL_MODE=UP DRIVE=OFF BANK_VCCIO=3.3;
IO_LOC "reset" H10;
IO_PORT "reset" PULL_MODE=DOWN DRIVE=OFF BANK_VCCIO=3.3;
IO_LOC "CLK_0" E2;
IO_PORT "CLK_0" PULL_MODE=UP DRIVE=OFF BANK_VCCIO=3.3;
IO_LOC "s1" H11;
IO_PORT "s1" PULL_MODE=DOWN DRIVE=OFF BANK_VCCIO=3.3;
- 发现的第二个问题就是 PMOD模块中的LED模块,起因是我在调试按键的时候,led灯的亮灭总是跟我想的反着来,搞得我糊涂,查看了一下原理图,发现原理图中FPGA引脚对应的是LED灯的阴极,也就是LED灯亮的时候,FPGA引脚输出低电平;所以为了方便,我推荐用 语句 LED=~FPGA_Pin;的方式进行输出。
- 第三个问题依旧是在调试按键的时候发现的,PMOD中的按键模块,我在进行对其的引脚绑定的时候,似乎总是会出错,一开始我以为是因为官方画板的时候把丝印放错了,后来发现是我看反了,在对PMOD端口和PMOD模块进行绑定的时候,应该按如下的视角进行匹配:
- SD卡控制模块的失效:首先体现了在了初始化模块的时候就已经失效了,表现在初始化信号没有反应,偶尔初始化模块有反应了,SD卡的读写模块也依旧没反应。排查了一下发现居然卡在了一个cnt计数的寄存器always里,看了很久的逻辑也没整明白为什么会卡在那里。
偶然发现,当我对SD卡控制模块的复位接口进行胡乱接,或者在逻辑卡住的位置的代码段里加上一个根本没有用处的寄存器flag并赋值,然后外接到顶层模块接到LED显示,就会出现SD卡的初始化模块莫名其妙可以运行了,当然整体依旧不行。于是我开始怀疑起是不是我的SPI引脚约束有问题,一顿改之后依旧没有解决问题。按道理来说,SD卡控制模块已经是经过多次验证过的模块不会出问题,结合我上面讲到的偶尔初始化莫名成功的现象,有可能25k这种板上的SD卡PMOD模块不支持SPI模式?或者是在使用SPI模式的时候,50MHz的时钟,通道串扰会更加严重,PMOD模块设计上有问题?另外,官方的SD卡模块似乎并没有设计上拉电阻,这会不会有问题呢?
以下是SD卡模块模块,读写模块,初始化模块代码,看不出什么问题
`timescale 1ns/1ns
////////////////////////////////////////////////////////////////////////
// Author : EmbedFire
//Create Date : 2019/09/03
// Module Name : sd_ctrl
// Project Name : sd_vga_pic
// Target Devices: Xilinx XC6SLX16
// Tool Versions : ISE 14.7
// Description : SD卡控制顶层模块
//
////////////////////////////////////////////////////////////////////////
module sd_ctrl
(
input wire sys_clk , //输入工作时钟,频率50MHz
input wire sys_clk_shift , //输入工作时钟,频率50MHz,相位偏移90度
input wire sys_rst_n , //输入复位信号,低电平有效
//SD卡接口
input wire sd_miso , //主输入从输出信号
output wire sd_clk , //SD卡时钟信号
output reg sd_cs_n , //片选信号
output reg sd_mosi , //主输出从输入信号
//写SD卡接口
input wire wr_en , //数据写使能信号
input wire [31:0] wr_addr , //写数据扇区地址
input wire [15:0] wr_data , //写数据
output wire wr_busy , //写操作忙信号
output wire wr_req , //写数据请求信号
//读SD卡接口
input wire rd_en , //数据读使能信号
input wire [31:0] rd_addr , //读数据扇区地址
output wire rd_busy , //读操作忙信号
output wire rd_data_en , //读数据标志信号
output wire [15:0] rd_data , //读数据
output wire init_end //SD卡初始化完成信号
);
//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//wire define
wire init_cs_n ; //初始化阶段片选信号
wire init_mosi ; //初始化阶段主输出从输入信号
wire wr_cs_n ; //写数据阶段片选信号
wire wr_mosi ; //写数据阶段主输出从输入信号
wire rd_cs_n ; //读数据阶段片选信号
wire rd_mosi ; //读数据阶段主输出从输入信号
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//sd_clk:SD卡时钟信号
assign sd_clk = sys_clk_shift;
//SD卡接口信号选择
always@(*)
if(init_end == 1'b0)
begin
sd_cs_n <= init_cs_n;
sd_mosi <= init_mosi;
end
else if(wr_busy == 1'b1)
begin
sd_cs_n <= wr_cs_n;
sd_mosi <= wr_mosi;
end
else if(rd_busy == 1'b1)
begin
sd_cs_n <= rd_cs_n;
sd_mosi <= rd_mosi;
end
else
begin
sd_cs_n <= 1'b1;
sd_mosi <= 1'b1;
end
//********************************************************************//
//************************** Instantiation ***************************//
//********************************************************************//
//------------- sd_init_inst -------------
sd_init sd_init_inst
(
.sys_clk (sys_clk ), //输入工作时钟,频率50MHz
.sys_clk_shift (sys_clk_shift ), //输入工作时钟,频率50MHz,相位偏移90度
.sys_rst_n (sys_rst_n ), //输入复位信号,低电平有效
.miso (sd_miso ), //主输入从输出信号
.cs_n (init_cs_n ), //输出片选信号
.mosi (init_mosi ), //主输出从输入信号
.init_end (init_end ) //初始化完成信号
);
//------------- sd_write_inst -------------
sd_write sd_write_inst
(
.sys_clk (sys_clk ), //输入工作时钟,频率50MHz
.sys_clk_shift (sys_clk_shift ), //输入工作时钟,频率50MHz,相位偏移90度
.sys_rst_n (sys_rst_n ), //输入复位信号,低电平有效
.miso (sd_miso ), //主输入从输出信号
.wr_en (wr_en && init_end ), //数据写使能信号
.wr_addr (wr_addr ), //写数据扇区地址
.wr_data (wr_data ), //写数据
.cs_n (wr_cs_n ), //输出片选信号
.mosi (wr_mosi ), //主输出从输入信号
.wr_busy (wr_busy ), //写操作忙信号
.wr_req (wr_req ) //写数据请求信号
);
//------------- sd_read_inst -------------
sd_read sd_read_inst
(
.sys_clk (sys_clk ), //输入工作时钟,频率50MHz
.sys_clk_shift (sys_clk_shift ), //输入工作时钟,频率50MHz,相位偏移90度
.sys_rst_n (sys_rst_n ), //输入复位信号,低电平有效
.miso (sd_miso ), //主输入从输出信号
.rd_en (rd_en & init_end ), //数据读使能信号
.rd_addr (rd_addr ), //读数据扇区地址
.rd_busy (rd_busy ), //读操作忙信号
.rd_data_en (rd_data_en ), //读数据标志信号
.rd_data (rd_data ), //读数据
.cs_n (rd_cs_n ), //片选信号
.mosi (rd_mosi ) //主输出从输入信号
);
endmodule
module sd_init
(
input wire sys_clk , //输入工作时钟,频率50MHz
input wire sys_clk_shift , //输入工作时钟,频率50MHz,相位偏移90度
input wire sys_rst_n , //输入复位信号,低电平有效
input wire miso , //主输入从输出信号
output reg cs_n , //输出片选信号
output reg mosi , //主输出从输入信号
output reg init_end //初始化完成信号
);
//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//parameter define
parameter 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}, //应用指令告知指令
ACMD41 = {8'h69,8'h40,8'h00,8'h00,8'h00,8'hff}; //应用指令
parameter CNT_WAIT_MAX = 8'd100; //上电后同步过程等待时钟计数最大值
parameter IDLE = 4'b0000, //初始状态
SEND_CMD0 = 4'b0001, //CMD0发送状态
CMD0_ACK = 4'b0011, //CMD0响应状态
SEND_CMD8 = 4'b0010, //CMD8发送状态
CMD8_ACK = 4'b0110, //CMD8响应状态
SEND_CMD55 = 4'b0111, //CMD55发送状态
CMD55_ACK = 4'b0101, //CMD55响应状态
SEND_ACMD41 = 4'b0100, //ACMD41发送状态
ACMD41_ACK = 4'b1100, //ACMD41响应状态
INIT_END = 4'b1101; //初始化完成状态
//reg define
reg [7:0] cnt_wait ; //上电同步时钟计数器
reg [3:0] state ; //状态机状态
reg [7:0] cnt_cmd_bit ; //指令比特计数器
reg miso_dly ; //主输入从输出信号打一拍
reg ack_en ; //响应使能信号
reg [39:0] ack_data ; //响应数据
reg [7:0] cnt_ack_bit ; //响应数据字节计数
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//cnt_wait:上电同步时钟计数器
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_wait <= 8'd0;
else if(cnt_wait >= CNT_WAIT_MAX)
cnt_wait <= CNT_WAIT_MAX;
else
cnt_wait <= cnt_wait + 1'b1;
//state:状态机状态跳转
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
state <= IDLE;
else
case(state)
IDLE:
if(cnt_wait == CNT_WAIT_MAX)
state <= SEND_CMD0;
else
state <= state;
SEND_CMD0:
if(cnt_cmd_bit == 8'd48)
state <= CMD0_ACK;
else
state <= state;
CMD0_ACK:
if(cnt_ack_bit == 8'd48)
if(ack_data[39:32] == 8'h01)
state <= SEND_CMD8;
else
state <= SEND_CMD0;
else
state <= state;
SEND_CMD8:
if(cnt_cmd_bit == 8'd48)
state <= CMD8_ACK;
else
state <= state;
CMD8_ACK:
if(cnt_ack_bit == 8'd48)
if(ack_data[11:8] == 4'b0001)
state <= SEND_CMD55;
else
state <= SEND_CMD8;
else
state <= state;
SEND_CMD55:
if(cnt_cmd_bit == 8'd48)
state <= CMD55_ACK;
else
state <= state;
CMD55_ACK:
if(cnt_ack_bit == 8'd48)
if(ack_data[39:32] == 8'h01)
state <= SEND_ACMD41;
else
state <= SEND_CMD55;
else
state <= state;
SEND_ACMD41:
if(cnt_cmd_bit == 8'd48)
state <= ACMD41_ACK;
else
state <= state;
ACMD41_ACK:
if(cnt_ack_bit == 8'd48)
if(ack_data[39:32] == 8'h00)
state <= INIT_END;
else
state <= SEND_CMD55;
else
state <= state;
INIT_END:
state <= state;
default:
state <= IDLE;
endcase
//cs_n,mosi,init_end,cnt_cmd_bit
//片选信号,主输出从输入信号,初始化结束信号,指令比特计数器
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
begin
cs_n <= 1'b1;
mosi <= 1'b1;
init_end <= 1'b0;
cnt_cmd_bit <= 8'd0;
end
else
case(state)
IDLE:
begin
cs_n <= 1'b1;
mosi <= 1'b1;
init_end <= 1'b0;
cnt_cmd_bit <= 8'd0;
end
SEND_CMD0:
if(cnt_cmd_bit == 8'd48)
cnt_cmd_bit <= 8'd0;
else
begin
cs_n <= 1'b0;
mosi <= CMD0[8'd47 - cnt_cmd_bit];
init_end <= 1'b0;
cnt_cmd_bit <= cnt_cmd_bit + 8'd1;
end
CMD0_ACK:
if(cnt_ack_bit == 8'd47)
cs_n <= 1'b1;
else
cs_n <= 1'b0;
SEND_CMD8:
if(cnt_cmd_bit == 8'd48)
cnt_cmd_bit <= 8'd0;
else
begin
cs_n <= 1'b0;
mosi <= CMD8[8'd47 - cnt_cmd_bit];
init_end <= 1'b0;
cnt_cmd_bit <= cnt_cmd_bit + 8'd1;
end
CMD8_ACK:
if(cnt_ack_bit == 8'd47)
cs_n <= 1'b1;
else
cs_n <= 1'b0;
SEND_CMD55:
if(cnt_cmd_bit == 8'd48)
cnt_cmd_bit <= 8'd0;
else
begin
cs_n <= 1'b0;
mosi <= CMD55[8'd47 - cnt_cmd_bit];
init_end <= 1'b0;
cnt_cmd_bit <= cnt_cmd_bit + 8'd1;
end
CMD55_ACK:
if(cnt_ack_bit == 8'd47)
cs_n <= 1'b1;
else
cs_n <= 1'b0;
SEND_ACMD41:
if(cnt_cmd_bit == 8'd48)
cnt_cmd_bit <= 8'd0;
else
begin
cs_n <= 1'b0;
mosi <= ACMD41[8'd47 - cnt_cmd_bit];
init_end <= 1'b0;
cnt_cmd_bit <= cnt_cmd_bit + 8'd1;
end
ACMD41_ACK:
if(cnt_ack_bit < 8'd47)
cs_n <= 1'b0;
else
cs_n <= 1'b1;
INIT_END:
begin
cs_n <= 1'b1;
mosi <= 1'b1;
init_end <= 1'b1;
end
default:
begin
cs_n <= 1'b1;
mosi <= 1'b1;
end
endcase
//miso_dly:主输入从输出信号打一拍
always@(posedge sys_clk_shift or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
miso_dly <= 1'b0;
else
miso_dly <= miso;
//ack_en:响应使能信号
always@(posedge sys_clk_shift or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
ack_en <= 1'b0;
else if(cnt_ack_bit == 8'd47)
ack_en <= 1'b0;
else if((miso == 1'b0) && (miso_dly == 1'b1) && (cnt_ack_bit == 8'd0))
ack_en <= 1'b1;
else
ack_en <= ack_en;
//ack_data:响应数据
//cnt_ack_bit:响应数据字节计数
always@(posedge sys_clk_shift or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
begin
ack_data <= 8'b0;
cnt_ack_bit <= 8'd0;
end
else if(ack_en == 1'b1)
begin
cnt_ack_bit <= cnt_ack_bit + 8'd1;
if(cnt_ack_bit < 8'd40)
ack_data <= {ack_data[38:0],miso_dly};
else
ack_data <= ack_data;
end
else
cnt_ack_bit <= 8'd0;
endmodule
module sd_read
(
input wire sys_clk , //输入工作时钟,频率50MHz
input wire sys_clk_shift , //输入工作时钟,频率50MHz,相位偏移90度
input wire sys_rst_n , //输入复位信号,低电平有效
input wire miso , //主输入从输出信号
input wire rd_en , //数据读使能信号
input wire [31:0] rd_addr , //读数据扇区地址
output wire rd_busy , //读操作忙信号
output reg rd_data_en , //读数据标志信号
output reg [15:0] rd_data , //读数据
output reg cs_n , //片选信号
output reg mosi //主输出从输入信号
);
//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//parameter define
parameter IDLE = 3'b000 , //初始状态
SEND_CMD17 = 3'b001 , //读命令CMD17发送状态
CMD17_ACK = 3'b011 , //CMD17响应状态
RD_DATA = 3'b010 , //读数据状态
RD_END = 3'b110 ; //读结束状态
parameter DATA_NUM = 12'd256 ; //待读取数据字节数
//wire define
wire [47:0] cmd_rd ; //数据读指令
//reg define
reg [2:0] state ; //状态机状态
reg [7:0] cnt_cmd_bit ; //指令比特计数器
reg ack_en ; //响应使能信号
reg [7:0] ack_data ; //响应数据
reg [7:0] cnt_ack_bit ; //响应数据字节计数
reg [11:0] cnt_data_num; //读出数据个数计数
reg [3:0] cnt_data_bit; //读数据比特计数器
reg [2:0] cnt_end ; //结束状态时钟计数
reg miso_dly ; //主输入从输出信号打一拍
reg [15:0] rd_data_reg ; //读出数据寄存
reg [15:0] byte_head ; //读数据字节头
reg byte_head_en; //读数据字节头使能
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//rd_busy:读操作忙信号
assign rd_busy = (state != IDLE) ? 1'b1 : 1'b0;
//cmd_rd:数据读指令
assign cmd_rd = {8'h51,rd_addr,8'hff};
//miso_dly:主输入从输出信号打一拍
always@(posedge sys_clk_shift or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
miso_dly <= 1'b0;
else
miso_dly <= miso;
//ack_en:响应使能信号
always@(posedge sys_clk_shift or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
ack_en <= 1'b0;
else if(cnt_ack_bit == 8'd15)
ack_en <= 1'b0;
else if((state == CMD17_ACK) && (miso == 1'b0)
&& (miso_dly == 1'b1) && (cnt_ack_bit == 8'd0))
ack_en <= 1'b1;
else
ack_en <= ack_en;
//ack_data:响应数据
//cnt_ack_bit:响应数据字节计数
always@(posedge sys_clk_shift or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
begin
ack_data <= 8'b0;
cnt_ack_bit <= 8'd0;
end
else if(ack_en == 1'b1)
begin
cnt_ack_bit <= cnt_ack_bit + 8'd1;
if(cnt_ack_bit < 8'd8)
ack_data <= {ack_data[6:0],miso_dly};
else
ack_data <= ack_data;
end
else
cnt_ack_bit <= 8'd0;
//state:状态机状态跳转
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
state <= IDLE;
else
case(state)
IDLE:
if(rd_en == 1'b1)
state <= SEND_CMD17;
else
state <= state;
SEND_CMD17:
if(cnt_cmd_bit == 8'd47)
state <= CMD17_ACK;
else
state <= state;
CMD17_ACK:
if(cnt_ack_bit == 8'd15)
if(ack_data == 8'h00)
state <= RD_DATA;
else
state <= SEND_CMD17;
else
state <= state;
RD_DATA:
if((cnt_data_num == (DATA_NUM + 1'b1))
&& (cnt_data_bit == 4'd15))
state <= RD_END;
else
state <= state;
RD_END:
if(cnt_end == 3'd7)
state <= IDLE;
else
state <= state;
default:state <= IDLE;
endcase
//cs_n:输出片选信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cs_n <= 1'b1;
else if(cnt_end == 3'd7)
cs_n <= 1'b1;
else if(rd_en == 1'b1)
cs_n <= 1'b0;
else
cs_n <= cs_n;
//cnt_cmd_bit:指令比特计数器
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_cmd_bit <= 8'd0;
else if(state == SEND_CMD17)
cnt_cmd_bit <= cnt_cmd_bit + 8'd1;
else
cnt_cmd_bit <= 8'd0;
//mosi:主输出从输入信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
mosi <= 1'b1;
else if(state == SEND_CMD17)
mosi <= cmd_rd[8'd47 - cnt_cmd_bit];
else
mosi <= 1'b1;
//byte_head:读数据字节头
always@(posedge sys_clk_shift or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
byte_head <= 16'b0;
else if(byte_head_en == 1'b0)
byte_head <= 16'b0;
else if(byte_head_en == 1'b1)
byte_head <= {byte_head[14:0],miso};
else
byte_head <= byte_head;
//byte_head_en:读数据字节头使能
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
byte_head_en <= 1'b0;
else if(byte_head == 16'hfffe)
byte_head_en <= 1'b0;
else if((state == RD_DATA) && (cnt_data_num == 12'd0)
&& (cnt_data_bit == 4'd0))
byte_head_en <= 1'b1;
else
byte_head_en <= byte_head_en;
//cnt_data_bit:读数据比特计数器
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_data_bit <= 4'd0;
else if((state == RD_DATA) && (cnt_data_num >= 12'd1))
cnt_data_bit <= cnt_data_bit + 4'd1;
else
cnt_data_bit <= 4'd0;
//cnt_data_num:读出数据个数计数
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_data_num <= 12'd0;
else if(state == RD_DATA)
if((cnt_data_bit == 4'd15) || (byte_head == 16'hfffe))
cnt_data_num <= cnt_data_num + 12'd1;
else
cnt_data_num <= cnt_data_num;
else
cnt_data_num <= 12'd0;
//rd_data_reg:读出数据寄存
always@(posedge sys_clk_shift or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rd_data_reg <= 16'd0;
else if((state == RD_DATA) && (cnt_data_num >= 12'd1)
&& (cnt_data_num <= DATA_NUM))
rd_data_reg <= {rd_data_reg[14:0],miso};
else
rd_data_reg <= 16'd0;
//rd_data_en:读数据标志信号
//rd_data:读数据
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
begin
rd_data_en <= 1'b0;
rd_data <= 16'd0;
end
else if(state == RD_DATA)
begin
if((cnt_data_bit == 4'd15) && (cnt_data_num <= DATA_NUM))
begin
rd_data_en <= 1'b1;
rd_data <= rd_data_reg;
end
else
begin
rd_data_en <= 1'b0;
rd_data <= rd_data;
end
end
else
begin
rd_data_en <= 1'b0;
rd_data <= 16'd0;
end
//cnt_end:结束状态时钟计数
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_end <= 3'd0;
else if(state == RD_END)
cnt_end <= cnt_end + 3'd1;
else
cnt_end <= 3'd0;
endmodule
module sd_write
(
input wire sys_clk , //输入工作时钟,频率50MHz
input wire sys_clk_shift , //输入工作时钟,频率50MHz,相位偏移90度
input wire sys_rst_n , //输入复位信号,低电平有效
input wire miso , //主输入从输出信号
input wire wr_en , //数据写使能信号
input wire [31:0] wr_addr , //写数据扇区地址
input wire [15:0] wr_data , //写数据
output reg cs_n , //输出片选信号
output reg mosi , //主输出从输入信号
output wire wr_busy , //写操作忙信号
output wire wr_req //写数据请求信号
);
//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//parameter define
parameter IDLE = 3'b000 , //初始状态
SEND_CMD24 = 3'b001 , //写命令CMD24发送状态
CMD24_ACK = 3'b011 , //CMD24响应状态
WR_DATA = 3'b010 , //写数据状态
WR_BUSY = 3'b110 , //SD卡写忙状态
WR_END = 3'b111 ; //写结束状态
parameter DATA_NUM = 12'd256 ; //待写入数据字节数
parameter BYTE_HEAD = 16'hfffe; //写数据字节头
//wire define
wire [47:0] cmd_wr ; //数据写指令
//reg define
reg [2:0] state ; //状态机状态
reg [7:0] cnt_cmd_bit ; //指令比特计数器
reg ack_en ; //响应使能信号
reg [7:0] ack_data ; //响应数据
reg [7:0] cnt_ack_bit ; //响应数据字节计数
reg [11:0] cnt_data_num; //写入数据个数计数
reg [3:0] cnt_data_bit; //写数据比特计数器
reg [7:0] busy_data ; //忙状态数据
reg [2:0] cnt_end ; //结束状态时钟计数
reg miso_dly ; //主输入从输出信号打一拍
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//wr_busy:写操作忙信号
assign wr_busy = (state != IDLE) ? 1'b1 : 1'b0;
//wr_req:写数据请求信号
assign wr_req = ((cnt_data_num <= DATA_NUM - 1'b1) && (cnt_data_bit == 4'd15))
? 1'b1 : 1'b0;
//cmd_wr:数据写指令
assign cmd_wr = {8'h58,wr_addr,8'hff};
//miso_dly:主输入从输出信号打一拍
always@(posedge sys_clk_shift or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
miso_dly <= 1'b0;
else
miso_dly <= miso;
//ack_en:响应使能信号
always@(posedge sys_clk_shift or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
ack_en <= 1'b0;
else if(cnt_ack_bit == 8'd15)
ack_en <= 1'b0;
else if((state == CMD24_ACK) && (miso == 1'b0)
&& (miso_dly == 1'b1) && (cnt_ack_bit == 8'd0))
ack_en <= 1'b1;
else
ack_en <= ack_en;
//ack_data:响应数据
//cnt_ack_bit:响应数据字节计数
always@(posedge sys_clk_shift or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
begin
ack_data <= 8'b0;
cnt_ack_bit <= 8'd0;
end
else if(ack_en == 1'b1)
begin
cnt_ack_bit <= cnt_ack_bit + 8'd1;
if(cnt_ack_bit < 8'd8)
ack_data <= {ack_data[6:0],miso_dly};
else
ack_data <= ack_data;
end
else
cnt_ack_bit <= 8'd0;
//busy_data:忙状态数据
always@(posedge sys_clk_shift or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
busy_data <= 8'd0;
else if(state == WR_BUSY)
busy_data <= {busy_data[6:0],miso};
else
busy_data <= 8'd0;
//state:状态机状态跳转
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
state <= IDLE;
else
case(state)
IDLE:
if(wr_en == 1'b1)
state <= SEND_CMD24;
else
state <= state;
SEND_CMD24:
if(cnt_cmd_bit == 8'd47)
state <= CMD24_ACK;
else
state <= state;
CMD24_ACK:
if(cnt_ack_bit == 8'd15)
if(ack_data == 8'h00)
state <= WR_DATA;
else
state <= SEND_CMD24;
else
state <= state;
WR_DATA:
if((cnt_data_num == (DATA_NUM + 1'b1))
&& (cnt_data_bit == 4'd15))
state <= WR_BUSY;
else
state <= state;
WR_BUSY:
if(busy_data == 8'hff)
state <= WR_END;
else
state <= state;
WR_END:
if(cnt_end == 3'd7)
state <= IDLE;
else
state <= state;
default:state <= IDLE;
endcase
//cs_n:输出片选信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cs_n <= 1'b1;
else if(cnt_end == 3'd7)
cs_n <= 1'b1;
else if(wr_en == 1'b1)
cs_n <= 1'b0;
else
cs_n <= cs_n;
//cnt_cmd_bit:指令比特计数器
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_cmd_bit <= 8'd0;
else if(state == SEND_CMD24)
cnt_cmd_bit <= cnt_cmd_bit + 8'd1;
else
cnt_cmd_bit <= 8'd0;
//mosi:主输出从输入信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
mosi <= 1'b1;
else if(state == SEND_CMD24)
mosi <= cmd_wr[8'd47 - cnt_cmd_bit];
else if(state == WR_DATA)
if(cnt_data_num == 12'd0)
mosi <= BYTE_HEAD[15 - cnt_data_bit];
else if((cnt_data_num >= 12'd1) && (cnt_data_num <= DATA_NUM))
mosi <= wr_data[15 - cnt_data_bit];
else
mosi <= 1'b1;
else
mosi <= 1'b1;
//cnt_data_bit:写数据比特计数器
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_data_bit <= 4'd0;
else if(state == WR_DATA)
cnt_data_bit <= cnt_data_bit + 4'd1;
else
cnt_data_bit <= 4'd0;
//cnt_data_num:写入数据个数计数
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_data_num <= 12'd0;
else if(state == WR_DATA)
if(cnt_data_bit == 4'd15)
cnt_data_num <= cnt_data_num + 12'd1;
else
cnt_data_num <= cnt_data_num;
else
cnt_data_num <= 12'd0;
//cnt_end:结束状态时钟计数
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_end <= 3'd0;
else if(state == WR_END)
cnt_end <= cnt_end + 3'd1;
else
cnt_end <= 3'd0;
endmodule
- SDRAM控制器:我使用了官方的IP,但是依旧需要对IP的输入信号等进行处理,这就需要额外模块了,我试着按官方的参考设计写了一下,可耻扑街了
这里贴一下官方例程网址:Gowin SDRAM Controller (gowinsemi.com.cn),包含了例程和文档,文档里面有引脚定义,和如何操作的详细解释(我也是写到这里才发现的,等会继续看看),我不明白的是用户信号里的sdrc_data_len数据长度的含义,很是迷惑,数据长度是指写入的数据量还是写入的数据bit位数还是10进制bit位数?以及I_sdrc_addr地址线的使用,我们存进一张680*480的图片数据,如何对正确找到地址?希望文档后面的内容能解答疑惑。
还有一个初始化教程设置:2020-10-02_高云sdram-CSDN博客
这里就不贴我写得一坨屑的代码了,有源再来。
- 关于高云的FIFO IP的设置:如下设置了一个读写16位宽,256bit的fifo
- 后面还会更新,打算把SD卡模块和SDRAM分别验证完后再组合一起。
|
|