目录

银杏科技有限公司旗下技术文档发布平台
技术支持电话0379-69926675-801
技术支持邮件Gingko@vip.163.com
版本 日期 作者 修改内容
V1.0 2020-02-15 gingko 初次建立

实验二十六:SDRAM读写测试实验

一、 实验目的与意义

  1. 了解SDRAM的工作原理。
  2. 了解SDRAM的驱动方法。
  3. 掌握QuartusII 集成开发环境使用方法。

二、 实验设备及平台

  1. iCore3 双核心板。点击购买
  2. Blaster(或相同功能)仿真器和USB线缆。点击购买
  3. Micro USB线缆。
  4. 装有QuarusII 13.1开发环境的计算机。

三、 实验原理

1、 SDRAM介绍

2、SDRAM的工作原理

(1) SDRAM的初始化

(2) SDRAM的读写操作

(3) SDRAM的刷新

四、 代码讲解

1、State_Ctrl模块

//--------------------Init_FSM-------------------------------// 
    reg [4:0] cnt_clk_i = 5'd0;//初始化时钟计数器
    reg [4:0] Init_State_r = 5'd0;//初始化状态寄存器
 
    always @(posedge SDRAM_CLK or negedge RST_n)
        if (!RST_n)
            begin
                cnt_clk_i <= 5'd0;
                Init_State_r <= `Init_NOP;//上电复位后处于NOP状态
            end
        else
            case (Init_State)
                `Init_NOP: begin
                    cnt_clk_i <= 5'd0;
                    if (done_200us)//200us稳定期完成
                        Init_State_r <= `Init_PCH;//进入所有Bank预充电状态
                    else
                        Init_State_r <= `Init_NOP;
                end
                `Init_PCH: begin
                    if (cnt_clk_i == 5'd9)//预充电占用10个周期
                        begin
                            Init_State_r <= `Init_AR1;//进入8次自动刷新状态
                            cnt_clk_i <= 5'd0;
                        end
                    else
                        begin
                            Init_State_r <= `Init_PCH;
                            cnt_clk_i <= cnt_clk_i + 1'd1;
                        end
                end
                `Init_AR1: begin    //第一次自动刷新
                    if (cnt_clk_i == 5'd9)//自动刷新占用10个周期
                        begin
                            Init_State_r <= `Init_AR2;
                            cnt_clk_i <= 5'd0;
                        end
                    else
                        begin
                            Init_State_r <= `Init_AR1;
                            cnt_clk_i <= cnt_clk_i + 1'd1;
                        end
                end
                `Init_AR2: begin    //第二次自动刷新
                    if (cnt_clk_i == 5'd9)
                        begin
                            Init_State_r <= `Init_AR3;
                            cnt_clk_i <= 5'd0;
                        end
                    else
                        begin
                            Init_State_r <= `Init_AR2;
                            cnt_clk_i <= cnt_clk_i + 1'd1;
                        end
                end
                `Init_AR3: begin    //第三次
                    if (cnt_clk_i == 5'd9)
                        begin
                            Init_State_r <= `Init_AR4;
                            cnt_clk_i <= 5'd0;
                        end
                    else
                        begin
                            Init_State_r <= `Init_AR3;
                            cnt_clk_i <= cnt_clk_i + 1'd1;
                        end
                end
                `Init_AR4: begin    //第四次
                    if (cnt_clk_i == 5'd9)
                        begin
                            Init_State_r <= `Init_AR5;
                            cnt_clk_i <= 5'd0;
                        end
                    else
                        begin
                            Init_State_r <= `Init_AR4;
                            cnt_clk_i <= cnt_clk_i + 1'd1;
                        end
                end
                `Init_AR5: begin    //第五次
                    if (cnt_clk_i == 5'd9)
                        begin
                            Init_State_r <= `Init_AR6;
                            cnt_clk_i <= 5'd0;
                        end
                    else
                        begin
                            Init_State_r <= `Init_AR5;
                            cnt_clk_i <= cnt_clk_i + 1'd1;
                        end
                end
                `Init_AR6: begin    //第六次
                    if (cnt_clk_i == 5'd9)
                        begin
                            Init_State_r <= `Init_AR7;
                            cnt_clk_i <= 5'd0;
                        end
                    else
                        begin
                            Init_State_r <= `Init_AR6;
                            cnt_clk_i <= cnt_clk_i + 1'd1;
                        end
                end
                `Init_AR7: begin    //第七次
                    if (cnt_clk_i == 5'd9)
                        begin
                            Init_State_r <= `Init_AR8;
                            cnt_clk_i <= 5'd0;
                        end
                    else
                        begin
                            Init_State_r <= `Init_AR7;
                            cnt_clk_i <= cnt_clk_i + 1'd1;
                        end
                end
                `Init_AR8: begin    //第八次
                    if (cnt_clk_i == 5'd9)
                        begin
                            Init_State_r <= `Init_MRS;
                            cnt_clk_i <= 5'd0;
                        end
                    else
                        begin
                            Init_State_r <= `Init_AR8;
                            cnt_clk_i <= cnt_clk_i + 1'd1;
                        end
                end
                `Init_MRS: begin    //模式寄存器设置状态
                    if (cnt_clk_i == 5'd4)  //占用5个周期
                        begin
                            Init_State_r <= `Init_DONE; //初始化完成
                            cnt_clk_i <= 5'd0;
                        end
                    else
                        begin
                            Init_State_r <= `Init_MRS;
                            cnt_clk_i <= cnt_clk_i + 1'd1;
                        end
                end
                `Init_DONE: begin //初始化完成
                    Init_State_r <= `Init_DONE;
                end
                default: Init_State_r <= `Init_NOP;
            endcase
//--------------------Work_FSM-------------------------------//	
	reg [2:0] Work_State_r = 3'd0;//工作状态寄存器
	reg [8:0] cnt_clk_w = 9'd0;//工作状态时钟计数器
	reg wr_sig;
	always @(posedge SDRAM_CLK or negedge RST_n)
		if (!RST_n)
			Work_State_r <= `Work_IDLE;//上电复位处于IDLE状态
		else
			case (Work_State_r)
				`Work_IDLE: begin
					cnt_clk_w <= 9'd0;
					if (SD_Init_Done && SD_RD_Req)        //初始化完成且
						Work_State_r <= `Work_ACT_RD; //有读请求,进入读ACT状态
					else if (SD_Init_Done && SD_WR_Req)   //有写请求,进入写ACT状态
						Work_State_r <= `Work_ACT_WR;
					else if (SD_Init_Done && REF_Req)    //初始化完成且有刷新请求
						Work_State_r <= `Work_AR;    //进入刷新状态
					else Work_State_r <= `Work_IDLE;
				end
				`Work_ACT_RD: begin
					if (cnt_clk_w == 9'd3)//ACT占用3个周期
						begin
							cnt_clk_w <= 9'd0;        //时钟计数器清零
							Work_State_r <= `Work_RD; //根据读写信号
						end				  //决定是进入WR状态还是RD状态
					else
						begin
							cnt_clk_w <= cnt_clk_w + 1'd1; //自加1
							Work_State_r <= `Work_ACT_RD;
						end
				end
 
				`Work_ACT_WR: begin
					if (cnt_clk_w == 9'd2)              //ACT占用3个周期
						begin
							cnt_clk_w <= 9'd0;  //时钟计数器清零
							Work_State_r <= `Work_WR;  //根据读写信号
						end			           //决定是进入WR状态还是RD状态
					else
						begin
							cnt_clk_w <= cnt_clk_w + 1'd1;//自加1
							Work_State_r <= `Work_ACT_WR;
						end
				end
 
				`Work_WR: begin//写状态
					if (cnt_clk_w == 9'd258)    //写状态占用258个周期,发送写命令滞后占用1个周期
						begin		    //写操作占用256个周期,写入最后一个数据的同时
						cnt_clk_w <= 9'd0;  //发生BST(突发停止)命令,然后预充电占用1个周期
						Work_State_r <= `Work_IDLE;
						end
					else
						begin
							cnt_clk_w <= cnt_clk_w + 1'd1;
							Work_State_r <= `Work_WR;
						end
				end
				`Work_RD: begin     //读状态
					if (cnt_clk_w == 9'd5)	//读状态占用260个周期,发送读命令滞后占用1个周期
						begin		//CL延时占用3个周期,读操作占用256个周期
						cnt_clk_w <= 9'd0;  //BST和预充电命令在CL延时的3个周期内发送,不额外占用时间
						Work_State_r <= `Work_IDLE;//读完成后进入空闲状态
						end
					else
						begin
							cnt_clk_w <= cnt_clk_w + 1'd1;
							Work_State_r <= `Work_RD;
						end
				end
				`Work_AR: begin                 //自动刷新状态
					if (cnt_clk_w == 9'd9)  //占用10个周期
						begin
							cnt_clk_w <= 9'd0;
							Work_State_r <= `Work_IDLE;//刷新完成后进入空闲状态
						end
					else
						begin
							cnt_clk_w <= cnt_clk_w + 1'd1;
							Work_State_r <= `Work_AR;
						end
				end
				default: begin
					cnt_clk_w <= 9'd0;
					Work_State_r <= `Work_IDLE;
				end
			endcase

2、CMD_Ctrl模块

always @(posedge SDRAM_CLK or negedge RST_n)
		if (!RST_n)
			begin
				SD_BA_r <= 2'b11;
				SD_AB_r <= 12'hFFF;
				SD_CMD <= `CMD_Init;//上电复位后,发送命令Init
			end
		else
			case (Init_Stste)
				`Init_NOP: begin
					SD_BA_r <= 2'b11;
					SD_AB_r <= 12'hFFF;
					SD_CMD <= `CMD_NOP;       //空闲状态发送NOP命令
				end
				`Init_PCH: begin
					if (Init_Cnt_CLK == 5'd0) //进入预充电状态后第一个上升沿发送命令
						begin		  //即为之前注释的发送命令滞后1个周期
							SD_BA_r <= 2'b11;
							SD_AB_r <= 12'hFFF;
							SD_CMD <= `CMD_PCH;  //发送PCH命令
						end
					else SD_CMD <= `CMD_NOP;
				end
				`Init_AR1,`Init_AR2,`Init_AR3,`Init_AR4,
				`Init_AR5,`Init_AR6,`Init_AR7,`Init_AR8: begin
					if (Init_Cnt_CLK == 5'd0)
						begin
							SD_BA_r <= 2'b11;
							SD_AB_r <= 12'hFFF;
							SD_CMD <= `CMD_REF;  //刷新状态发送刷新命令
						end
					else
						SD_CMD <= `CMD_NOP;
				end
				`Init_MRS: begin//模式寄存器设置状态
					if (Init_Cnt_CLK == 5'd0)//同PCH状态
						begin
						SD_BA_r <= 2'b00;     //必须为00
						SD_AB_r <= {2'b00,    //必须为00
							1'b1,   //写模式,0为突发读突发写,1为突发读单次写
							2'b00,  //必须为0
							3'b011, //CL值,010为2,011为3
							1'b0,   //访问模式,0为顺序模式,1为交错模式
							3'b000};//BL值,000为1,001为2,010为4,011为8,111为全页
							SD_CMD <= `CMD_MRS; //进入MRS状态后,滞后1个周期发送MRS命令
						end
					else SD_CMD <= `CMD_NOP;
				end

3、Data_Generator模块。

always @(posedge SDRAM_CLK or negedge RST_n)
		if (!RST_n)
			begin
				i <= 4'd0;
				SD_WR_Req_r <= 1'd0;
				WR_AB <= 20'd0;
				RD_AB <= 20'd0;
				Sys_Data_out_r <= 16'd0;
				BANK <= 2'd0;
				bank <= 2'd0;
				data_in <= 20'd0;
				data_out <= 20'd0;
				led1 <= 1'd1;
				led2 <= 1'd1;
				led3 <= 1'd1;
				p <= 20'd0;
				q <= 4'd0;
				counter <= 28'd0;
			end
		else case(i)
			4'd0:
				begin
					if ((!SD_Busy) && (cnt >= 24'hfffff))
						begin
						BANK <= bank;
						WR_AB <= data_in;
						Sys_Data_out_r <= data_in[15:0];
						SD_WR_Req_r <= 1'd1;            //发出写请求,同时发出写地址
						led1 <= 1'd0;
						i <= 4'd1;
						end
					else i <= 4'd0;
				end
			4'd1:
				begin
					if (SD_WR_Ack)    //等待SDRAM控制模块发出写应答信号
						begin
						if (data_in == 20'hfffff)
							begin
								if(bank == 2'd3)
									begin
									data_in <= 20'd0;
									SD_WR_Req_r <= 1'd0;//写请求信号撤销
									bank <= 2'd0;
									i <= 4'd2;
									end
								else 
									begin
									data_in <= 20'd0;
									SD_WR_Req_r <= 1'd0;//写请求信号撤销
									bank <= bank + 1'd1;
									i <= 4'd0;
									end	
								end
							else
								begin
								SD_WR_Req_r <= 1'd0;//写请求信号撤销
								i <= 4'd0;
								data_in <= data_in + 1'd1;
								end
						end
					else i <= 4'd1;
				end
			4'd2:
				begin
					if((!SD_Busy) && (cnt >= 24'hfffff))//等待SDRAM控制模块不忙
						begin
							SD_RD_Req_r <= 1'd1;//发出读请求信号,同时发出读地址
							RD_AB <= data_out;
							BANK <= bank;
							i <= 4'd3;
						end
					else
						i <= 4'd2;
				end
			4'd3:
				begin
					if(SD_RD_Ack)	//等待SDRAM控制模块发出读应答信号
						begin
						SD_RD_Req_r <= 1'd0;	//读请求信号撤销
						i <= 4'd4;
						end
					else
						i <= 4'd3;
				end
			4'd4:
				begin
					if (q == 4'd7)
						begin
							q <= 4'd0;
							i <= 4'd5;
						end
					else
						begin
							i <= 4'd4;
							q <= q + 1'd1;
						end
					end
			4'd5:
				begin
					if (SDRAM_Data_out == data_out[15:0])//判断数据是否正确
						begin
							if (data_out == 20'hfffff)
								begin
									if (bank == 2'd3)
										begin
											data_out <= 20'd0;
											i <= 4'd7;
										end
									else 
										begin
											data_out <= 20'd0;
											bank <= bank + 1'd1;
											i <= 4'd2;
										end	
								end
							else 
								begin
									data_out <= data_out + 1'd1;
									i <= 4'd2;
								end
						end
					else i <= 4'd8;
				end
			4'd7:		//结束
				begin
					if(counter == 28'h4000000)
						begin
							led1 <= 1'd0;
							led2 <= 1'd1;
							led3 <= 1'd1;
						end
					else if(counter == 28'h8000000)
						begin
							led1 <= 1'd1;
							led2 <= 1'd0;
							led3 <= 1'd1;
						end
					else if(counter == 28'hC000000)
						begin
							led1 <= 1'd1;
							led2 <= 1'd1;
							led3 <= 1'd0;		
						end
					if(counter == 28'hC000000)
						begin
							counter <= 28'd0;
						end
					else 
						begin
							counter <= counter + 1'd1;
						end	
				end
			4'd8:		//报错
				begin
					if (p == 20'd1000000)
						begin
							p <= 20'd0;
							i <= 4'd8;
						end
					else
						begin
							p <= p + 1'd1;
							led1 <= 1'd0;
							led3 <= 1'd0;
							led2 <= 1'd0;
						end
				end
			default: i <= 4'd0;
		endcase

五、 实验步骤

  1. 将USB-Blaster与iCore3的JTGA调试口相连;
  2. 把iCore3通过Micro USB线与计算机连接,为iCore3供电;
  3. 按照实验原理编写程序及调试代码;
  4. 烧写FPGA程序到iCore3上;
  5. 观察实验现象;

六、 实验现象

观察iCore3开发板,可以看到FPGA_LED灯红绿蓝三种颜色依次循环闪烁,表示实验验证成功。