首页 > 其他分享 >正点原子新起点V2开发板FPGA关于SDRAM代码解读

正点原子新起点V2开发板FPGA关于SDRAM代码解读

时间:2024-10-18 20:33:41浏览次数:9  
标签:SDRAM FPGA clk sdram rd V2 wr input

正点原子新起点V2开发板FPGA关于SDRAM代码解读

1. SDRAM 概述

SDRAM(Synchronous Dynamic Random Access Memory)是一种同步动态随机存储器,广泛用于FPGA项目中。通过SDRAM控制模块,可以实现数据读写、刷新等操作。本文对SDRAM的控制模块进行详细解读,分析代码中的命令控制、数据传输、状态管理等内容。

2.代码文件整体关系

以下是关于SDRAM模块的代码关系整理,便于对后续笔记有一个整体了解:

2.1. 顶层文件

  • sdram_test:整个SDRAM系统的最顶层文件,用于测试与集成。

2.2. 测试与错误提示模块

  • sdram_rw_test:用于写测试数据的模块,负责测试SDRAM的读写操作。
  • led_disp:LED显示模块,用于进行错误提示,显示SDRAM操作的状态或错误。

2.3. 整合SDRAM模块(6个文件)

  • sdram_top:SDRAM模块的顶层文件,负责协调和整合以下子模块。
  • sdram_fifo_ctrl:FIFO控制模块,管理FIFO的读写操作,确保数据正确传输至SDRAM。
  • sdram_controller:SDRAM控制器,负责管理对SDRAM的指令发送与数据操作。
  • sdram_para:SDRAM模块的参数列表,定义SDRAM的相关配置参数。
    • sdram_ctrl:状态控制模块,负责管理SDRAM的状态机。
    • sdram_cmd:命令控制模块,负责生成SDRAM的命令信号。
    • sdram_data:数据读写模块,处理SDRAM的读写操作与数据传输。

下面关系图可以帮助清晰理解各模块之间的层级与功能:

SDRAM程序代码关系结构图

图脚注提到的状态机可以在我的 SDRAM 学习笔记随笔中找到,下面再展示一次状态图方便阅读:

(初始化状态机)

image-20241016221943440

(工作状态机)

image-20241016221826356

3. 模块介绍

3.1. 顶层文件

顶层文件 sdram_rw_test.v 分析

//****************************************Copyright (c)***********************************//
//技术支持:www.openedv.com
//淘宝店铺:http://openedv.taobao.com 
//关注微信公众平台微信号:"正点原子",免费获取FPGA & STM32资料。
//版权所有,盗版必究。
//Copyright(C) 正点原子 2018-2028
//All rights reserved                               
//----------------------------------------------------------------------------------------
// File name:           sdram_rw_test
// Last modified Date:  2018/3/18 8:41:06
// Last Version:        V1.0
// Descriptions:        SDRAM读写测试顶层模块
//----------------------------------------------------------------------------------------
// Created by:          正点原子
// Created date:        2018/3/18 8:41:06
// Version:             V1.0
// Descriptions:        The original version
//
//----------------------------------------------------------------------------------------
//****************************************************************************************//

module sdram_rw_test(
    input         clk,                      //FPGA外部时钟,50M
    input         rst_n,                    //按键复位,低电平有效
    //SDRAM 芯片接口
    output        sdram_clk,                //SDRAM 芯片时钟
    output        sdram_cke,                //SDRAM 时钟有效
    output        sdram_cs_n,               //SDRAM 片选
    output        sdram_ras_n,              //SDRAM 行有效
    output        sdram_cas_n,              //SDRAM 列有效
    output        sdram_we_n,               //SDRAM 写有效
    output [ 1:0] sdram_ba,                 //SDRAM Bank地址
    output [12:0] sdram_addr,               //SDRAM 行/列地址
    inout  [15:0] sdram_data,               //SDRAM 数据
    output [ 1:0] sdram_dqm,                //SDRAM 数据掩码
    //LED
    output [ 1:0] led                       //状态指示灯
);
//parameter
parameter ADDR_MIN =  24'd0;                //最小地址
parameter ADDR_MAX =  24'd2048;             //最大地址
parameter LEN = 10'd512;                    //突发长度
    
//wire define
wire        clk_50m;                        //SDRAM 读写测试时钟
wire        clk_100m;                       //SDRAM 控制器时钟
wire        clk_100m_shift;                 //相位偏移时钟

wire        wr_en;                          //SDRAM 写端口:写使能
wire [15:0] wr_data;                        //SDRAM 写端口:写入的数据
wire        rd_en;                          //SDRAM 读端口:读使能
wire [15:0] rd_data;                        //SDRAM 读端口:读出的数据
wire        sdram_init_done;                //SDRAM 初始化完成信号

wire        locked;                         //PLL输出有效标志
wire        sys_rst_n;                      //系统复位信号
wire        error_flag;                     //读写测试错误标志

//*****************************************************
//**                    main code
//***************************************************** 

//待PLL输出稳定之后,停止系统复位
assign sys_rst_n = rst_n & locked;

//例化PLL, 产生各模块所需要的时钟
pll_clk u_pll_clk(
    .inclk0             (clk),
    .areset             (~rst_n),
    
    .c0                 (clk_50m),
    .c1                 (clk_100m),
    .c2                 (clk_100m_shift),
    .locked             (locked)
    );

//SDRAM测试模块,对SDRAM进行读写测试
sdram_test # (

    .WR_CNT             (ADDR_MAX),
    .RD_CNT             (ADDR_MAX)

)
u_sdram_test(
    .clk_50m            (clk_50m),
    .rst_n              (sys_rst_n),
    
    .wr_en              (wr_en),
    .wr_data            (wr_data),
    .rd_en              (rd_en),
    .rd_data            (rd_data),   
    
    .sdram_init_done    (sdram_init_done),    
    .error_flag         (error_flag)
    );

//利用LED灯指示SDRAM读写测试的结果
led_disp u_led_disp(
    .clk_50m            (clk_50m),
    .rst_n              (sys_rst_n),
    .sdram_init_done    (sdram_init_done),
    .error_flag         (error_flag),
    .led                (led)             
    );

//SDRAM 控制器顶层模块,封装成FIFO接口
//SDRAM 控制器地址组成: {bank_addr[1:0],row_addr[12:0],col_addr[8:0]}
sdram_top u_sdram_top(
	.ref_clk			(clk_100m),			//sdram	控制器参考时钟
	.out_clk			(clk_100m_shift),	//用于输出的相位偏移时钟
	.rst_n              (sys_rst_n),		//系统复位
    
    //用户写端口
	.wr_clk 			(clk_50m),		    //写端口FIFO: 写时钟
	.wr_en              (wr_en),            //写端口FIFO: 写使能
	.wr_data            (wr_data),          //写端口FIFO: 写数据
	.wr_min_addr        (ADDR_MIN),         //写SDRAM的起始地址
	.wr_max_addr        (ADDR_MAX),         //写SDRAM的结束地址
	.wr_len             (LEN),              //写SDRAM时的数据突发长度
	.wr_load			(~sys_rst_n),		//写端口复位: 复位写地址,清空写FIFO
   
    //用户读端口
	.rd_clk 			(clk_50m),			//读端口FIFO: 读时钟
    .rd_en              (rd_en),            //读端口FIFO: 读使能
	.rd_data	    	(rd_data),          //读端口FIFO: 读数据
	.rd_min_addr        (ADDR_MIN),         //读SDRAM的起始地址
	.rd_max_addr        (ADDR_MAX),	    	//读SDRAM的结束地址
	.rd_len 			(LEN),              //从SDRAM中读数据时的突发长度
	.rd_load			(~sys_rst_n),		//读端口复位: 复位读地址,清空读FIFO
	   
     //用户控制端口  
	.sdram_read_valid	(1'b1),             //SDRAM 读使能
	.sdram_init_done	(sdram_init_done),  //SDRAM 初始化完成标志
   
	//SDRAM 芯片接口
    .sdram_clk          (sdram_clk),        //SDRAM 芯片时钟
    .sdram_cke          (sdram_cke),        //SDRAM 时钟有效
    .sdram_cs_n         (sdram_cs_n),       //SDRAM 片选
    .sdram_ras_n        (sdram_ras_n),      //SDRAM 行有效
    .sdram_cas_n        (sdram_cas_n),      //SDRAM 列有效
    .sdram_we_n         (sdram_we_n),       //SDRAM 写有效
    .sdram_ba           (sdram_ba),         //SDRAM Bank地址
    .sdram_addr         (sdram_addr),       //SDRAM 行/列地址
    .sdram_data         (sdram_data),       //SDRAM 数据
    .sdram_dqm          (sdram_dqm)         //SDRAM 数据掩码
);

endmodule 

这个模块将SDRAM控制器和测试模块组合起来,并通过PLL生成所需的时钟信号,控制SDRAM的工作时序。

主要功能:

  1. PLL时钟生成:利用PLL生成多个时钟信号,包括SDRAM读写操作所需的clk_50m、SDRAM控制器时钟clk_100m,以及带有相位偏移的clk_100m_shift
  2. 测试模块:实例化sdram_test模块,并进行SDRAM的读写测试,测试结果通过LED显示。
  3. SDRAM控制器集成:通过实例化SDRAM_top模块,连接SDRAM芯片的接口,实现SDRAM的FIFO读写操作。
  4. 错误提示:实例化led_disp模块,通过LED指示读写是否出现错误。

关键逻辑:

  • 时钟和复位信号:模块利用PLL产生多种时钟,且通过locked信号控制系统复位sys_rst_n
  • SDRAM控制器与读写测试:SDRAM控制器通过FIFO接口连接SDRAM,提供了写操作与读操作的控制信号wr_enrd_en
  • LED状态显示:当SDRAM完成初始化和读写时,LED根据是否发生错误来显示状态。

3.2. 测试与错误提示模块

测试文件 sdram_test.v 分析

//****************************************Copyright (c)***********************************//
//技术支持:www.openedv.com
//淘宝店铺:http://openedv.taobao.com 
//关注微信公众平台微信号:"正点原子",免费获取FPGA & STM32资料。
//版权所有,盗版必究。
//Copyright(C) 正点原子 2018-2028
//All rights reserved                               
//----------------------------------------------------------------------------------------
// File name:           sdram_test
// Last modified Date:  2018/3/18 8:41:06
// Last Version:        V1.0
// Descriptions:        SDRAM读写测试: 向SDRAM中写入数据,然后将数据读出,并判断读出的数据是否正确
//----------------------------------------------------------------------------------------
// Created by:          正点原子
// Created date:        2018/3/18 8:41:06
// Version:             V1.0
// Descriptions:        The original version
//
//----------------------------------------------------------------------------------------
//****************************************************************************************//

module sdram_test(
    input             clk_50m,          //时钟
    input             rst_n,            //复位,低有效
    
    output reg        wr_en,            //SDRAM 写使能
    output reg [15:0] wr_data,          //SDRAM 写入的数据
    output reg        rd_en,            //SDRAM 读使能
    input      [15:0] rd_data,          //SDRAM 读出的数据
    
    input             sdram_init_done,  //SDRAM 初始化完成标志
    output reg        error_flag        //SDRAM 读写测试错误标志
);
//parameter
parameter WR_CNT = 24'd1024;
parameter RD_CNT = 24'd1024;
//reg define
reg        init_done_d0;                //寄存SDRAM初始化完成信号
reg        init_done_d1;                //寄存SDRAM初始化完成信号
reg [23:0] wr_cnt;                      //写操作计数器
reg [23:0] rd_cnt;                      //读操作计数器
reg        rd_valid;                    //读数据有效标志
   
//*****************************************************
//**                    main code
//***************************************************** 

//同步SDRAM初始化完成信号
always @(posedge clk_50m or negedge rst_n) begin
    if(!rst_n) begin
        init_done_d0 <= 1'b0;
        init_done_d1 <= 1'b0;
    end
    else begin
        init_done_d0 <= sdram_init_done;
        init_done_d1 <= init_done_d0;
    end
end            

//SDRAM初始化完成之后,写操作计数器开始计数
always @(posedge clk_50m or negedge rst_n) begin
    if(!rst_n) 
        wr_cnt <= 24'd0;  
    else if(init_done_d1 && (wr_cnt <= WR_CNT))
        wr_cnt <= wr_cnt + 1'b1;
    else
        wr_cnt <= wr_cnt;
end    

//SDRAM写端口FIFO的写使能、写数据(1~1024)
always @(posedge clk_50m or negedge rst_n) begin
    if(!rst_n) begin      
        wr_en   <= 1'b0;
        wr_data <= 16'd0;
    end
    else if((wr_cnt >= 24'd1) && (wr_cnt <= WR_CNT)) begin
            wr_en   <= 1'b1;            //写使能拉高
            wr_data <= wr_cnt;          //写入数据1~1024
        end    
    else begin
            wr_en   <= 1'b0;
            wr_data <= 16'd0;
        end                
end        

//写入数据完成后,开始读操作    
always @(posedge clk_50m or negedge rst_n) begin
    if(!rst_n) 
        rd_en <= 1'b0;
    else if(wr_cnt > WR_CNT)          //写数据完成
        rd_en <= 1'b1;                  //读使能拉高
end

//对读操作计数     
always @(posedge clk_50m or negedge rst_n) begin
    if(!rst_n) 
        rd_cnt <= 24'd0;
    else if(rd_en) begin
        if(rd_cnt < RD_CNT)
            rd_cnt <= rd_cnt + 1'b1;
        else
            rd_cnt <= 24'd1;
    end
end

//第一次读取的数据无效,后续读操作所读取的数据才有效
always @(posedge clk_50m or negedge rst_n) begin
    if(!rst_n) 
        rd_valid <= 1'b0;
    else if(rd_cnt == RD_CNT)         //等待第一次读操作结束
        rd_valid <= 1'b1;               //后续读取的数据有效
    else
        rd_valid <= rd_valid;
end            

//读数据有效时,若读取数据错误,给出标志信号
always @(posedge clk_50m or negedge rst_n) begin
    if(!rst_n)
        error_flag <= 1'b0; 
    else if(rd_valid && (rd_data != rd_cnt))
        error_flag <= 1'b1;             //若读取的数据错误,将错误标志位拉高 
    else
        error_flag <= error_flag;
end

endmodule 

该模块的主要功能是进行SDRAM的写操作和读操作的计数,并在操作完成后验证数据的一致性。如果数据读写不一致,则通过error_flag标志位报告错误。

主要功能:

  1. SDRAM初始化同步:通过两个寄存器init_done_d0init_done_d1实现SDRAM初始化信号的同步。
  2. 写操作:通过写操作计数器wr_cnt,从1到1024依次向SDRAM写入数据,控制信号wr_en和数据wr_data随计数器变化。
  3. 读操作:当写操作完成后,读操作计数器rd_cnt开始计数,并启用rd_en进行数据读取。
  4. 错误检测:通过比较读取的数据rd_datard_cnt判断是否出错,如果出错,error_flag被置1。

关键逻辑:

  • 写入过程:wr_cnt控制写数据的范围(1~1024),写使能信号wr_en与写数据同步。
  • 读出过程:当写操作结束后,开始读操作,rd_cnt用于记录读操作次数,并通过rd_valid标志区分有效数据。
  • 错误检测:通过比较读取到的rd_datard_cnt,如果不一致则置位error_flag

错误检测 led_disp 模块

//****************************************Copyright (c)***********************************//
//技术支持:www.openedv.com
//淘宝店铺:http://openedv.taobao.com 
//关注微信公众平台微信号:"正点原子",免费获取FPGA & STM32资料。
//版权所有,盗版必究。
//Copyright(C) 正点原子 2018-2028
//All rights reserved                               
//----------------------------------------------------------------------------------------
// File name:           led_disp
// Last modified Date:  2018/3/18 8:41:06
// Last Version:        V1.0
// Descriptions:        LED显示模块,利用LED灯指示SDRAM读写测试的结果
//----------------------------------------------------------------------------------------
// Created by:          正点原子
// Created date:        2018/3/18 8:41:06
// Version:             V1.0
// Descriptions:        The original version
//
//----------------------------------------------------------------------------------------
//****************************************************************************************//

module led_disp(
    input               clk_50m,        //系统时钟
    input               rst_n,          //系统复位
    input               sdram_init_done,//SDRAM初始化完成
    input               error_flag,     //错误标志信号
    output reg [1:0]    led             //LED灯             
    );
//parameter ddefine
parameter LED_CNT = 25'd25000000;
//reg define
reg [24:0] led_cnt;         //控制LED闪烁周期的计数器

//*****************************************************
//**                    main code
//***************************************************** 

//计数器对50MHz时钟计数,计数周期为0.5s
always @(posedge clk_50m or negedge rst_n) begin
    if(!rst_n)
        led_cnt <= 25'd0;
    else if(led_cnt < LED_CNT) 
        led_cnt <= led_cnt + 25'd1;
    else
        led_cnt <= 25'd0;
end

//利用LED灯不同的显示状态指示错误标志的高低
always @(posedge clk_50m or negedge rst_n) begin
    if(rst_n == 1'b0)
        led[0] <= 1'b0;
    else if(!sdram_init_done)begin
        if(led_cnt == LED_CNT) 
            led[0] <= ~led[0];  //SDRAM初始化失败时,LED灯每隔0.5s闪烁一次
        else
            led[0] <= led[0];
    end
   
    else
        led[0] <= 1'b1;         //错误标志为低时,LED灯常亮
end

always @(posedge clk_50m or negedge rst_n) begin
    if(rst_n == 1'b0)
        led[1] <= 1'b0;
    else if(error_flag) begin
        if(led_cnt == LED_CNT) 
            led[1] <= ~led[1];  //错误标志为高时,LED灯每隔0.5s闪烁一次
        else
            led[1] <= led[1];
    end 
    else
        led[1] <= 1'b1;         //错误标志为低时,LED灯常亮
end

endmodule 

led_disp 模块负责控制LED灯的状态,以指示SDRAM初始化是否成功以及系统中是否有错误:

  • 输入信号

    • clk_50m: 系统时钟,50MHz
    • rst_n: 系统复位信号,低电平有效
    • sdram_init_done: SDRAM初始化完成信号
    • error_flag: 错误标志信号
  • 功能

    • 当SDRAM初始化未完成时,LED[0]会以0.5秒的周期闪烁;若初始化成功,LED[0]常亮。
    • 当检测到错误标志error_flag时,LED[1]以0.5秒周期闪烁;若无错误,LED[1]常亮。

3.3. 整合SDRAM模块(6个文件)

3.3.1整合模块 sdram_top 顶层文件

//****************************************Copyright (c)***********************************//
//技术支持:www.openedv.com
//淘宝店铺:http://openedv.taobao.com 
//关注微信公众平台微信号:"正点原子",免费获取FPGA & STM32资料。
//版权所有,盗版必究。
//Copyright(C) 正点原子 2018-2028
//All rights reserved                               
//----------------------------------------------------------------------------------------
// File name:           sdram_test
// Last modified Date:  2018/3/18 8:41:06
// Last Version:        V1.0
// Descriptions:        SDRAM 控制器顶层模块
//----------------------------------------------------------------------------------------
// Created by:          正点原子
// Created date:        2018/3/18 8:41:06
// Version:             V1.0
// Descriptions:        The original version
//
//----------------------------------------------------------------------------------------
//****************************************************************************************//

module	sdram_top(
	input         ref_clk,                  //sdram 控制器参考时钟
	input         out_clk,                  //用于输出的相位偏移时钟
	input         rst_n,                    //系统复位
    
    //用户写端口			
	input         wr_clk,                   //写端口FIFO: 写时钟
	input         wr_en,                    //写端口FIFO: 写使能
	input  [15:0] wr_data,                  //写端口FIFO: 写数据
	input  [23:0] wr_min_addr,              //写SDRAM的起始地址
	input  [23:0] wr_max_addr,              //写SDRAM的结束地址
	input  [ 9:0] wr_len,                   //写SDRAM时的数据突发长度
	input         wr_load,                  //写端口复位: 复位写地址,清空写FIFO
    
    //用户读端口
	input         rd_clk,                   //读端口FIFO: 读时钟
	input         rd_en,                    //读端口FIFO: 读使能
	output [15:0] rd_data,                  //读端口FIFO: 读数据
	input  [23:0] rd_min_addr,              //读SDRAM的起始地址
	input  [23:0] rd_max_addr,              //读SDRAM的结束地址
	input  [ 9:0] rd_len,                   //从SDRAM中读数据时的突发长度
	input         rd_load,                  //读端口复位: 复位读地址,清空读FIFO
    
    //用户控制端口  
	input         sdram_read_valid,         //SDRAM 读使能
	output        sdram_init_done,          //SDRAM 初始化完成标志
    
	//SDRAM 芯片接口
	output        sdram_clk,                //SDRAM 芯片时钟
	output        sdram_cke,                //SDRAM 时钟有效
	output        sdram_cs_n,               //SDRAM 片选
	output        sdram_ras_n,              //SDRAM 行有效
	output        sdram_cas_n,              //SDRAM 列有效
	output        sdram_we_n,               //SDRAM 写有效
	output [ 1:0] sdram_ba,                 //SDRAM Bank地址
	output [12:0] sdram_addr,               //SDRAM 行/列地址
	inout  [15:0] sdram_data,               //SDRAM 数据
	output [ 1:0] sdram_dqm                 //SDRAM 数据掩码
    );

//wire define
wire        sdram_wr_req;                   //sdram 写请求
wire        sdram_wr_ack;                   //sdram 写响应
wire [23:0]	sdram_wr_addr;                  //sdram 写地址
wire [15:0]	sdram_din;                      //写入sdram中的数据

wire        sdram_rd_req;                   //sdram 读请求
wire        sdram_rd_ack;                   //sdram 读响应
wire [23:0]	sdram_rd_addr;                   //sdram 读地址
wire [15:0]	sdram_dout;                     //从sdram中读出的数据

//*****************************************************
//**                    main code
//***************************************************** 

assign	sdram_clk = out_clk;                //将相位偏移时钟输出给sdram芯片
assign	sdram_dqm = 2'b00;                  //读写过程中均不屏蔽数据线
			
//SDRAM 读写端口FIFO控制模块
sdram_fifo_ctrl u_sdram_fifo_ctrl(
	.clk_ref			(ref_clk),			//SDRAM控制器时钟
	.rst_n				(rst_n),			//系统复位

    //用户写端口
	.clk_write 			(wr_clk),    	    //写端口FIFO: 写时钟
	.wrf_wrreq			(wr_en),			//写端口FIFO: 写请求
	.wrf_din			(wr_data),		    //写端口FIFO: 写数据	
	.wr_min_addr	    (wr_min_addr),		//写SDRAM的起始地址
	.wr_max_addr		(wr_max_addr),		//写SDRAM的结束地址
	.wr_length			(wr_len),		    //写SDRAM时的数据突发长度
	.wr_load			(wr_load),			//写端口复位: 复位写地址,清空写FIFO    
    
    //用户读端口
	.clk_read			(rd_clk),     	    //读端口FIFO: 读时钟
	.rdf_rdreq			(rd_en),			//读端口FIFO: 读请求
	.rdf_dout			(rd_data),		    //读端口FIFO: 读数据
	.rd_min_addr		(rd_min_addr),	    //读SDRAM的起始地址
	.rd_max_addr		(rd_max_addr),		//读SDRAM的结束地址
	.rd_length			(rd_len),		    //从SDRAM中读数据时的突发长度
	.rd_load			(rd_load),			//读端口复位: 复位读地址,清空读FIFO
   
	//用户控制端口	
	.sdram_read_valid	(sdram_read_valid), //sdram 读使能
	.sdram_init_done	(sdram_init_done),	//sdram 初始化完成标志

    //SDRAM 控制器写端口
	.sdram_wr_req		(sdram_wr_req),		//sdram 写请求
	.sdram_wr_ack		(sdram_wr_ack),	    //sdram 写响应
	.sdram_wr_addr		(sdram_wr_addr),	//sdram 写地址
	.sdram_din			(sdram_din),		//写入sdram中的数据
    
    //SDRAM 控制器读端口
	.sdram_rd_req		(sdram_rd_req),		//sdram 读请求
	.sdram_rd_ack		(sdram_rd_ack),	    //sdram 读响应
	.sdram_rd_addr		(sdram_rd_addr),    //sdram 读地址
	.sdram_dout			(sdram_dout)		//从sdram中读出的数据
    );

//SDRAM控制器
sdram_controller u_sdram_controller(
	.clk				(ref_clk),			//sdram 控制器时钟
	.rst_n				(rst_n),			//系统复位
    
	//SDRAM 控制器写端口	
	.sdram_wr_req		(sdram_wr_req), 	//sdram 写请求
	.sdram_wr_ack		(sdram_wr_ack), 	//sdram 写响应
	.sdram_wr_addr		(sdram_wr_addr), 	//sdram 写地址
	.sdram_wr_burst		(wr_len),		    //写sdram时数据突发长度
	.sdram_din  		(sdram_din),    	//写入sdram中的数据
    
    //SDRAM 控制器读端口
	.sdram_rd_req		(sdram_rd_req), 	//sdram 读请求
	.sdram_rd_ack		(sdram_rd_ack),		//sdram 读响应
	.sdram_rd_addr		(sdram_rd_addr), 	//sdram 读地址
	.sdram_rd_burst		(rd_len),		    //读sdram时数据突发长度
	.sdram_dout		    (sdram_dout),   	//从sdram中读出的数据
    
	.sdram_init_done	(sdram_init_done),	//sdram 初始化完成标志

	//SDRAM 芯片接口
	.sdram_cke			(sdram_cke),		//SDRAM 时钟有效
	.sdram_cs_n			(sdram_cs_n),		//SDRAM 片选
	.sdram_ras_n		(sdram_ras_n),		//SDRAM 行有效	
	.sdram_cas_n		(sdram_cas_n),		//SDRAM 列有效
	.sdram_we_n			(sdram_we_n),		//SDRAM 写有效
	.sdram_ba			(sdram_ba),			//SDRAM Bank地址
	.sdram_addr			(sdram_addr),		//SDRAM 行/列地址
	.sdram_data			(sdram_data)		//SDRAM 数据	
    );
    
endmodule 

sdram_top 是整个SDRAM控制系统的顶层模块,它集成了SDRAM FIFO控制模块和SDRAM控制器模块:

  • 输入输出信号

    • 提供与用户的数据写入、读取控制信号以及SDRAM的接口信号。
    • 包含数据突发读写地址、数据长度等控制参数,完成SDRAM的整体控制。
  • 主要功能

    • 通过集成FIFO控制模块和SDRAM控制器模块,实现对SDRAM的读写操作,并通过sdram_init_done信号反馈初始化是否完成。
  • SDRAM 读写

    • 用户通过写时钟和读时钟(wr_clkrd_clk)与FIFO交互,进行SDRAM的读写操作。

3.3.2控制FIFO模块sdram_fifo_ctrl 文件

//****************************************Copyright (c)***********************************//
//技术支持:www.openedv.com
//淘宝店铺:http://openedv.taobao.com 
//关注微信公众平台微信号:"正点原子",免费获取FPGA & STM32资料。
//版权所有,盗版必究。
//Copyright(C) 正点原子 2018-2028
//All rights reserved                               
//----------------------------------------------------------------------------------------
// File name:           sdram_fifo_ctrl
// Last modified Date:  2018/3/18 8:41:06
// Last Version:        V1.0
// Descriptions:        SDRAM 读写端口FIFO控制模块
//----------------------------------------------------------------------------------------
// Created by:          正点原子
// Created date:        2018/3/18 8:41:06
// Version:             V1.0
// Descriptions:        The original version
//
//----------------------------------------------------------------------------------------
//****************************************************************************************//

module sdram_fifo_ctrl(
	input             clk_ref,		     //SDRAM控制器时钟
	input             rst_n,			 //系统复位 
                                         
    //用户写端口                         
	input             clk_write,		 //写端口FIFO: 写时钟 
	input             wrf_wrreq,		 //写端口FIFO: 写请求 
	input      [15:0] wrf_din,		     //写端口FIFO: 写数据	
	input      [23:0] wr_min_addr,	     //写SDRAM的起始地址
	input      [23:0] wr_max_addr,	     //写SDRAM的结束地址
 	input      [ 9:0] wr_length,		 //写SDRAM时的数据突发长度 
	input             wr_load,		     //写端口复位: 复位写地址,清空写FIFO 
                                         
    //用户读端口                         
	input             clk_read,		     //读端口FIFO: 读时钟
	input             rdf_rdreq,		 //读端口FIFO: 读请求 
	output     [15:0] rdf_dout,		     //读端口FIFO: 读数据
	input      [23:0] rd_min_addr,	     //读SDRAM的起始地址
	input      [23:0] rd_max_addr,	     //读SDRAM的结束地址
	input      [ 9:0] rd_length,		 //从SDRAM中读数据时的突发长度 
	input             rd_load,		     //读端口复位: 复位读地址,清空读FIFO
	                                     
	//用户控制端口	                     
	input             sdram_read_valid,  //SDRAM 读使能
	input             sdram_init_done,   //SDRAM 初始化完成标志
                                         
    //SDRAM 控制器写端口                 
	output reg		  sdram_wr_req,	     //sdram 写请求
	input             sdram_wr_ack,	     //sdram 写响应
	output reg [23:0] sdram_wr_addr,	 //sdram 写地址
	output	   [15:0] sdram_din,		 //写入SDRAM中的数据 
                                         
    //SDRAM 控制器读端口                 
	output reg        sdram_rd_req,	     //sdram 读请求
	input             sdram_rd_ack,	     //sdram 读响应
	output reg [23:0] sdram_rd_addr,     //sdram 读地址 
	input      [15:0] sdram_dout 		 //从SDRAM中读出的数据 
    );

//reg define
reg	       wr_ack_r1;                    //sdram写响应寄存器      
reg	       wr_ack_r2;                    
reg        rd_ack_r1;                    //sdram读响应寄存器      
reg	       rd_ack_r2;                    
reg	       wr_load_r1;                   //写端口复位寄存器      
reg        wr_load_r2;                   
reg	       rd_load_r1;                   //读端口复位寄存器      
reg        rd_load_r2;                   
reg        read_valid_r1;                //sdram读使能寄存器      
reg        read_valid_r2;                
                                         
//wire define                            
wire       write_done_flag;              //sdram_wr_ack 下降沿标志位      
wire       read_done_flag;               //sdram_rd_ack 下降沿标志位      
wire       wr_load_flag;                 //wr_load      上升沿标志位      
wire       rd_load_flag;                 //rd_load      上升沿标志位      
wire [10:0] wrf_use;                      //写端口FIFO中的数据量
wire [10:0] rdf_use;                      //读端口FIFO中的数据量

//*****************************************************
//**                    main code
//***************************************************** 

//检测下降沿
assign write_done_flag = wr_ack_r2   & ~wr_ack_r1;	
assign read_done_flag  = rd_ack_r2   & ~rd_ack_r1;

//检测上升沿
assign wr_load_flag    = ~wr_load_r2 & wr_load_r1;
assign rd_load_flag    = ~rd_load_r2 & rd_load_r1;

//寄存sdram写响应信号,用于捕获sdram_wr_ack下降沿
always @(posedge clk_ref or negedge rst_n) begin
	if(!rst_n) begin
		wr_ack_r1 <= 1'b0;
		wr_ack_r2 <= 1'b0;
    end
	else begin
		wr_ack_r1 <= sdram_wr_ack;
		wr_ack_r2 <= wr_ack_r1;		
    end
end	

//寄存sdram读响应信号,用于捕获sdram_rd_ack下降沿
always @(posedge clk_ref or negedge rst_n) begin
	if(!rst_n) begin
		rd_ack_r1 <= 1'b0;
		rd_ack_r2 <= 1'b0;
    end
	else begin
		rd_ack_r1 <= sdram_rd_ack;
		rd_ack_r2 <= rd_ack_r1;
    end
end	

//同步写端口复位信号,用于捕获wr_load上升沿
always @(posedge clk_ref or negedge rst_n) begin
	if(!rst_n) begin
		wr_load_r1 <= 1'b0;
		wr_load_r2 <= 1'b0;
    end
	else begin
		wr_load_r1 <= wr_load;
		wr_load_r2 <= wr_load_r1;
    end
end

//同步读端口复位信号,同时用于捕获rd_load上升沿
always @(posedge clk_ref or negedge rst_n) begin
	if(!rst_n) begin
		rd_load_r1 <= 1'b0;
		rd_load_r2 <= 1'b0;
    end
	else begin
		rd_load_r1 <= rd_load;
		rd_load_r2 <= rd_load_r1;
    end
end

//同步sdram读使能信号
always @(posedge clk_ref or negedge rst_n) begin
	if(!rst_n) begin
		read_valid_r1 <= 1'b0;
		read_valid_r2 <= 1'b0;
    end
	else begin
		read_valid_r1 <= sdram_read_valid;
		read_valid_r2 <= read_valid_r1;
    end
end

//sdram写地址产生模块
always @(posedge clk_ref or negedge rst_n) begin
	if(!rst_n)
		sdram_wr_addr <= wr_min_addr;	
    else if(wr_load_flag)                //检测到写端口复位信号时,写地址复位
		sdram_wr_addr <= wr_min_addr;	
	else if(write_done_flag) begin		 //若突发写SDRAM结束,更改写地址
                                         //若未到达写SDRAM的结束地址,则写地址累加
		if(sdram_wr_addr < wr_max_addr - wr_length)
			sdram_wr_addr <= sdram_wr_addr + wr_length;
            else                         //若已到达写SDRAM的结束地址,则回到写起始地址
            sdram_wr_addr <= wr_min_addr;
    end
end

//sdram读地址产生模块
always @(posedge clk_ref or negedge rst_n) begin
	if(!rst_n)
		sdram_rd_addr <= rd_min_addr;
	else if(rd_load_flag)				 //检测到读端口复位信号时,读地址复位
		sdram_rd_addr <= rd_min_addr;
	else if(read_done_flag) begin        //突发读SDRAM结束,更改读地址
                                         //若未到达读SDRAM的结束地址,则读地址累加
		if(sdram_rd_addr < rd_max_addr - rd_length)
			sdram_rd_addr <= sdram_rd_addr + rd_length;
		else                             //若已到达读SDRAM的结束地址,则回到读起始地址
            sdram_rd_addr <= rd_min_addr;
	end
end

//sdram 读写请求信号产生模块
always@(posedge clk_ref or negedge rst_n) begin
	if(!rst_n) begin
		sdram_wr_req <= 0;
		sdram_rd_req <= 0;
	end
	else if(sdram_init_done) begin       //SDRAM初始化完成后才能响应读写请求
                                         //优先执行写操作,防止写入SDRAM中的数据丢失
		if(wrf_use >= wr_length) begin   //若写端口FIFO中的数据量达到了写突发长度
			sdram_wr_req <= 1;		     //发出写sdarm请求
			sdram_rd_req <= 0;		     
		end
		else if((rdf_use < rd_length)    //若读端口FIFO中的数据量小于读突发长度,
                 && read_valid_r2) begin //同时sdram读使能信号为高
			sdram_wr_req <= 0;		     
			sdram_rd_req <= 1;		     //发出读sdarm请求
		end
		else begin
			sdram_wr_req <= 0;
			sdram_rd_req <= 0;
		end
	end
	else begin
		sdram_wr_req <= 0;
		sdram_rd_req <= 0;
	end
end

//例化写端口FIFO
wrfifo	u_wrfifo(
    //用户接口
	.wrclk		(clk_write),		     //写时钟
	.wrreq		(wrf_wrreq),		     //写请求
	.data		(wrf_din),			     //写数据
    
    //sdram接口
	.rdclk		(clk_ref),			     //读时钟
	.rdreq		(sdram_wr_ack),		     //读请求
	.q			(sdram_din),		     //读数据

	.rdusedw	(wrf_use),			     //FIFO中的数据量
	.aclr		(~rst_n | wr_load_flag)  //异步清零信号
    );	

//例化读端口FIFO
rdfifo	u_rdfifo(
	//sdram接口
	.wrclk		(clk_ref),       	     //写时钟
	.wrreq		(sdram_rd_ack),  	     //写请求
	.data		(sdram_dout),  		     //写数据
    
	//用户接口
	.rdclk		(clk_read),              //读时钟
	.rdreq		(rdf_rdreq),     	     //读请求
	.q			(rdf_dout),			     //读数据

	.wrusedw	(rdf_use),        	     //FIFO中的数据量
	.aclr		(~rst_n | rd_load_flag)  //异步清零信号   
    );
    
endmodule 

sdram_fifo_ctrl 模块负责管理SDRAM读写端口的FIFO控制逻辑:

  • 输入输出信号

    • 接收SDRAM写请求、写数据、写地址等信号,并与SDRAM控制器进行读写交互。
  • 功能

    • 检测SDRAM写、读请求并进行相应的地址控制和数据传输。
    • 通过内部寄存器和标志位检测写读操作的完成状态。
  • 信号控制

    • 检测写/读操作的响应信号,更新状态寄存器,保证写入和读取FIFO中的数据能按要求传输到SDRAM。

3.3.3sdram_controller 模块分析

//****************************************Copyright (c)***********************************//
//技术支持:www.openedv.com
//淘宝店铺:http://openedv.taobao.com 
//关注微信公众平台微信号:"正点原子",免费获取FPGA & STM32资料。
//版权所有,盗版必究。
//Copyright(C) 正点原子 2018-2028
//All rights reserved                               
//----------------------------------------------------------------------------------------
// File name:           sdram_controller
// Last modified Date:  2018/3/18 8:41:06
// Last Version:        V1.0
// Descriptions:        SDRAM 控制器
//----------------------------------------------------------------------------------------
// Created by:          正点原子
// Created date:        2018/3/18 8:41:06
// Version:             V1.0
// Descriptions:        The original version
//
//----------------------------------------------------------------------------------------
//****************************************************************************************//

module sdram_controller(
    input         clk,		        //SDRAM控制器时钟,100MHz
    input         rst_n,	        //系统复位信号,低电平有效
    
	//SDRAM 控制器写端口	
    input         sdram_wr_req,		//写SDRAM请求信号
    output        sdram_wr_ack,		//写SDRAM响应信号
    input  [23:0] sdram_wr_addr,	//SDRAM写操作的地址
    input  [ 9:0] sdram_wr_burst,   //写sdram时数据突发长度
    input  [15:0] sdram_din,	    //写入SDRAM的数据
    
	//SDRAM 控制器读端口	
    input         sdram_rd_req,		//读SDRAM请求信号
    output        sdram_rd_ack,		//读SDRAM响应信号
    input  [23:0] sdram_rd_addr,	//SDRAM写操作的地址
    input  [ 9:0] sdram_rd_burst,   //读sdram时数据突发长度
    output [15:0] sdram_dout,	    //从SDRAM读出的数据
    
    output	      sdram_init_done,  //SDRAM 初始化完成标志
                                     
	// FPGA与SDRAM硬件接口
    output        sdram_cke,		// SDRAM 时钟有效信号
    output        sdram_cs_n,		// SDRAM 片选信号
    output        sdram_ras_n,		// SDRAM 行地址选通脉冲
    output        sdram_cas_n,		// SDRAM 列地址选通脉冲
    output        sdram_we_n,		// SDRAM 写允许位
    output [ 1:0] sdram_ba,		    // SDRAM L-Bank地址线
    output [12:0] sdram_addr,	    // SDRAM 地址总线
    inout  [15:0] sdram_data		// SDRAM 数据总线
    );

//wire define
wire [4:0] init_state;	            // SDRAM初始化状态
wire [3:0] work_state;	            // SDRAM工作状态
wire [9:0] cnt_clk;		            // 延时计数器
wire       sdram_rd_wr;			    // SDRAM读/写控制信号,低电平为写,高电平为读

//*****************************************************
//**                    main code
//*****************************************************     

// SDRAM 状态控制模块                
sdram_ctrl u_sdram_ctrl(		    
    .clk                (clk),						
    .rst_n              (rst_n),

    .sdram_wr_req       (sdram_wr_req), 
    .sdram_rd_req       (sdram_rd_req),
    .sdram_wr_ack       (sdram_wr_ack),
    .sdram_rd_ack       (sdram_rd_ack),						
    .sdram_wr_burst     (sdram_wr_burst),
    .sdram_rd_burst     (sdram_rd_burst),
    .sdram_init_done    (sdram_init_done),
    
    .init_state         (init_state),
    .work_state         (work_state),
    .cnt_clk            (cnt_clk),
    .sdram_rd_wr        (sdram_rd_wr)
    );

// SDRAM 命令控制模块
sdram_cmd u_sdram_cmd(		        
    .clk                (clk),
    .rst_n              (rst_n),

    .sys_wraddr         (sdram_wr_addr),			
    .sys_rdaddr         (sdram_rd_addr),
    .sdram_wr_burst     (sdram_wr_burst),
    .sdram_rd_burst     (sdram_rd_burst),
    
    .init_state         (init_state),	
    .work_state         (work_state),
    .cnt_clk            (cnt_clk),
    .sdram_rd_wr        (sdram_rd_wr),
    
    .sdram_cke          (sdram_cke),		
    .sdram_cs_n         (sdram_cs_n),	
    .sdram_ras_n        (sdram_ras_n),	
    .sdram_cas_n        (sdram_cas_n),	
    .sdram_we_n         (sdram_we_n),	
    .sdram_ba           (sdram_ba),			
    .sdram_addr         (sdram_addr)
    );

// SDRAM 数据读写模块
sdram_data u_sdram_data(		
    .clk                (clk),
    .rst_n              (rst_n),
    
    .sdram_data_in      (sdram_din),
    .sdram_data_out     (sdram_dout),
    .work_state         (work_state),
    .cnt_clk            (cnt_clk),
    
    .sdram_data         (sdram_data)
    );

endmodule 

该模块是顶层模块,整合了多个子模块,管理FPGA与SDRAM之间的交互,提供了一些基本的读写请求信号和硬件接口信号。它的主要功能是:

  • 初始化 SDRAM:模块负责在系统启动时完成对SDRAM的初始化,包括预充电、自动刷新和模式寄存器设置。
  • 读写操作:在完成初始化后,模块通过内部状态机管理SDRAM的读写请求,并在突发读写期间与SDRAM通信。
  • 接口信号处理:包含SDRAM控制器的多种信号接口,如片选信号、地址线、数据总线等。
3.3.3.1 sdram_para 参数文件分析

// SDRAM 初始化过程各个状态
`define		I_NOP	        5'd0		                    //等待上电200us稳定期结束
`define		I_PRE 	        5'd1		                    //预充电状态
`define		I_TRP 	        5'd2		                    //等待预充电完成	      tRP
`define		I_AR 	        5'd3		                    //自动刷新            
`define		I_TRF	        5'd4		                    //等待自动刷新结束	  tRC
`define		I_MRS	        5'd5		                    //模式寄存器设置
`define		I_TRSC	        5'd6		                    //等待模式寄存器设置完成 tRSC
`define		I_DONE	        5'd7		                    //初始化完成

// SDRAM 工作过程各个状态
`define		W_IDLE		    4'd0                            //空闲
`define		W_ACTIVE	    4'd1                            //行有效
`define		W_TRCD		    4'd2                            //行有效等待
`define		W_READ		    4'd3                            //读操作
`define		W_CL		    4'd4                            //潜伏期
`define		W_RD		    4'd5                            //读数据
`define		W_WRITE		    4'd6                            //写操作
`define		W_WD		    4'd7                            //写数据
`define		W_TWR		    4'd8                            //写回
`define		W_PRE		    4'd9                            //预充电
`define		W_TRP		    4'd10                           //预充电等待
`define		W_AR		    4'd11                           //自动刷新
`define		W_TRFC		    4'd12                           //自动刷新等待
  
//延时参数
`define	    end_trp			cnt_clk	== TRP_CLK              //预充电有效周期结束
`define	    end_trfc		cnt_clk	== TRC_CLK              //自动刷新周期结束
`define	    end_trsc		cnt_clk	== TRSC_CLK             //模式寄存器设置时钟周期结束
`define	    end_trcd		cnt_clk	== TRCD_CLK-1           //行选通周期结束
`define     end_tcl			cnt_clk == TCL_CLK-1            //潜伏期结束
`define     end_rdburst		cnt_clk == sdram_rd_burst-3     //读突发终止
`define	    end_tread		cnt_clk	== sdram_rd_burst+2     //突发读结束     
`define     end_wrburst		cnt_clk == sdram_wr_burst-1     //写突发终止
`define	    end_twrite		cnt_clk	== sdram_wr_burst-1     //突发写结束
`define	    end_twr		    cnt_clk	== TWR_CLK	            //写回周期结束

//SDRAM控制信号命令
`define		CMD_INIT 	    5'b01111	                    // INITIATE
`define		CMD_NOP		    5'b10111	                    // NOP COMMAND
`define		CMD_ACTIVE	    5'b10011	                    // ACTIVE COMMAND
`define		CMD_READ	    5'b10101	                    // READ COMMADN
`define		CMD_WRITE	    5'b10100	                    // WRITE COMMAND
`define		CMD_B_STOP	    5'b10110	                    // BURST STOP
`define		CMD_PRGE	    5'b10010	                    // PRECHARGE
`define		CMD_A_REF	    5'b10001	                    // AOTO REFRESH
`define		CMD_LMR		    5'b10000	                    // LODE MODE REGISTER

  • 初始化状态
    定义了 SDRAM 初始化过程中的各个状态,如空闲、预充电、自动刷新、模式寄存器设置等。
  • 工作状态
    定义了 SDRAM 工作过程中的各个状态,如空闲、行有效、读操作、写操作、预充电等。
  • 延时参数
    定义了不同状态下的延时条件,确保 SDRAM 操作符合时序要求。
  • 命令定义
    包含了 SDRAM 的各种命令信号,如空操作、激活、读、写、突发停止、预充电、自动刷新、模式寄存器加载等。
3.3.3.2. sdram_ctrl 状态控制模块分析
//****************************************Copyright (c)***********************************//
//技术支持:www.openedv.com
//淘宝店铺:http://openedv.taobao.com 
//关注微信公众平台微信号:"正点原子",免费获取FPGA & STM32资料。
//版权所有,盗版必究。
//Copyright(C) 正点原子 2018-2028
//All rights reserved                               
//----------------------------------------------------------------------------------------
// File name:           sdram_ctrl
// Last modified Date:  2018/3/18 8:41:06
// Last Version:        V1.0
// Descriptions:        SDRAM 状态控制模块
//----------------------------------------------------------------------------------------
// Created by:          正点原子
// Created date:        2018/3/18 8:41:06
// Version:             V1.0
// Descriptions:        The original version
//
//----------------------------------------------------------------------------------------
//****************************************************************************************//

module sdram_ctrl(
    input            clk,			    //时钟
    input            rst_n,			    //复位信号,低电平有效
    
    input            sdram_wr_req,	    //写SDRAM请求信号
    input            sdram_rd_req,	    //读SDRAM请求信号
    output           sdram_wr_ack,	    //写SDRAM响应信号
    output           sdram_rd_ack,	    //读SDRAM响应信号
    input      [9:0] sdram_wr_burst,	//突发写SDRAM字节数(1-512个)
    input      [9:0] sdram_rd_burst,	//突发读SDRAM字节数(1-256个)	
    output           sdram_init_done,   //SDRAM系统初始化完毕信号

    output reg [4:0] init_state,	    //SDRAM初始化状态
    output reg [3:0] work_state,	    //SDRAM工作状态
    output reg [9:0] cnt_clk,	        //时钟计数器
    output reg       sdram_rd_wr 		//SDRAM读/写控制信号,低电平为写,高电平为读
    );

`include "sdram_para.v"		            //包含SDRAM参数定义模块
                                        
//parameter define   时钟个数,以100Mhz(10ns)为准                   
parameter  TRP_CLK	  = 10'd4;	        //预充电有效周期,至少15ns,
parameter  TRC_CLK	  = 10'd6;	        //自动刷新周期,至少60ns,
parameter  TRSC_CLK	  = 10'd6;	        //模式寄存器设置时钟周期,2个TCK周期
parameter  TRCD_CLK	  = 10'd2;	        //行选通周期,至少15ns,
parameter  TCL_CLK	  = 10'd3;	        //列潜伏期(cl),3个TCK周期
parameter  TWR_CLK	  = 10'd2;	        //写入校正,2个TCK周期
parameter  CNT_200US  = 15'd20_000;     //200us计数个数
parameter  CNT_REFRESH  = 11'd781;      //自动刷新计数器
                                        
//reg define                            
reg [14:0] cnt_200us;                   //SDRAM 上电稳定期200us计数器
reg [10:0] cnt_refresh;	                //刷新计数寄存器
reg        sdram_ref_req;		        //SDRAM 自动刷新请求信号
reg        cnt_rst_n;		            //延时计数器复位信号,低有效	
reg [ 3:0] init_ar_cnt;                 //初始化过程自动刷新计数器
                                        
//wire define                           
wire       done_200us;		            //上电后200us输入稳定期结束标志位
wire       sdram_ref_ack;		        //SDRAM自动刷新请求应答信号	

//*****************************************************
//**                    main code
//***************************************************** 

//SDRAM上电后200us稳定期结束后,将标志信号拉高
assign done_200us = (cnt_200us == CNT_200US);

//SDRAM初始化完成标志 
assign sdram_init_done = (init_state == `I_DONE);

//SDRAM 自动刷新应答信号
assign sdram_ref_ack = (work_state == `W_AR);

//写SDRAM响应信号
assign sdram_wr_ack = ((work_state == `W_TRCD) & ~sdram_rd_wr) | 
					  ( work_state == `W_WRITE)|
					  ((work_state == `W_WD) & (cnt_clk < sdram_wr_burst - 2'd2));
                      
//读SDRAM响应信号
assign sdram_rd_ack = (work_state == `W_RD) & 
					  (cnt_clk >= 10'd1) & (cnt_clk < sdram_rd_burst + 2'd1);
                      
//上电后计时200us,等待SDRAM状态稳定
always @ (posedge clk or negedge rst_n) begin
	if(!rst_n) 
        cnt_200us <= 15'd0;
	else if(cnt_200us < CNT_200US) 
        cnt_200us <= cnt_200us + 1'b1;
    else
        cnt_200us <= cnt_200us;
end
 
//刷新计数器循环计数7812ns (60ms内完成全部8192行刷新操作)
always @ (posedge clk or negedge rst_n)
	if(!rst_n) 
        cnt_refresh <= 11'd0;
	else if(cnt_refresh < CNT_REFRESH)      // 64ms/8192 =7812ns
        cnt_refresh <= cnt_refresh + 1'b1;	
	else 
        cnt_refresh <= 11'd0;	

//SDRAM 刷新请求
always @ (posedge clk or negedge rst_n)
	if(!rst_n) 
        sdram_ref_req <= 1'b0;
	else if(cnt_refresh == CNT_REFRESH - 1) 
        sdram_ref_req <= 1'b1;	        //刷新计数器计时达7812ns时产生刷新请求
	else if(sdram_ref_ack) 
        sdram_ref_req <= 1'b0;		    //收到刷新请求响应信号后取消刷新请求 

//延时计数器对时钟计数
always @ (posedge clk or negedge rst_n) 
	if(!rst_n) 
        cnt_clk <= 10'd0;
	else if(!cnt_rst_n)                 //在cnt_rst_n为低电平时延时计数器清零
        cnt_clk <= 10'd0;
	else 
        cnt_clk <= cnt_clk + 1'b1;
        
//初始化过程中对自动刷新操作计数
always @ (posedge clk or negedge rst_n) 
	if(!rst_n) 
        init_ar_cnt <= 4'd0;
	else if(init_state == `I_NOP) 
        init_ar_cnt <= 4'd0;
	else if(init_state == `I_AR)
        init_ar_cnt <= init_ar_cnt + 1'b1;
    else
        init_ar_cnt <= init_ar_cnt;
	
//SDRAM的初始化状态机
always @ (posedge clk or negedge rst_n) begin
	if(!rst_n) 
        init_state <= `I_NOP;
	else 
		case (init_state)
                                        //上电复位后200us结束则进入下一状态
            `I_NOP:  init_state <= done_200us  ? `I_PRE : `I_NOP;
                                        //预充电状态
			`I_PRE:  init_state <= `I_TRP;
                                        //预充电等待,TRP_CLK个时钟周期
			`I_TRP:  init_state <= (`end_trp)  ? `I_AR  : `I_TRP;
                                        //自动刷新
			`I_AR :  init_state <= `I_TRF;	
                                        //等待自动刷新结束,TRC_CLK个时钟周期
			`I_TRF:  init_state <= (`end_trfc) ? 
                                        //连续8次自动刷新操作
                                   ((init_ar_cnt == 4'd8) ? `I_MRS : `I_AR) : `I_TRF;
                                        //模式寄存器设置
			`I_MRS:	 init_state <= `I_TRSC;	
                                        //等待模式寄存器设置完成,TRSC_CLK个时钟周期
			`I_TRSC: init_state <= (`end_trsc) ? `I_DONE : `I_TRSC;
                                        //SDRAM的初始化设置完成标志
			`I_DONE: init_state <= `I_DONE;
			default: init_state <= `I_NOP;
		endcase
end

//SDRAM的工作状态机,工作包括读、写以及自动刷新操作
always @ (posedge clk or negedge rst_n) begin
	if(!rst_n) 
		work_state <= `W_IDLE;          //空闲状态
	else
		case(work_state)
                                        //定时自动刷新请求,跳转到自动刷新状态
            `W_IDLE: if(sdram_ref_req & sdram_init_done) begin
						 work_state <= `W_AR; 		
					     sdram_rd_wr <= 1'b1;
				     end 		        
                                        //写SDRAM请求,跳转到行有效状态
					 else if(sdram_wr_req & sdram_init_done) begin
						 work_state <= `W_ACTIVE;
						 sdram_rd_wr <= 1'b0;	
					 end                
                                        //读SDRAM请求,跳转到行有效状态
					 else if(sdram_rd_req && sdram_init_done) begin
						 work_state <= `W_ACTIVE;
						 sdram_rd_wr <= 1'b1;	
					 end                
                                        //无操作请求,保持空闲状态
					 else begin 
						 work_state <= `W_IDLE;
						 sdram_rd_wr <= 1'b1;
					 end
                     
            `W_ACTIVE:                  //行有效,跳转到行有效等待状态
                         work_state <= `W_TRCD;
            `W_TRCD: if(`end_trcd)      //行有效等待结束,判断当前是读还是写
						 if(sdram_rd_wr)//读:进入读操作状态
                             work_state <= `W_READ;
						 else           //写:进入写操作状态
                             work_state <= `W_WRITE;
					 else 
                         work_state <= `W_TRCD;
                         
            `W_READ:	                //读操作,跳转到潜伏期
                         work_state <= `W_CL;	
            `W_CL:		                //潜伏期:等待潜伏期结束,跳转到读数据状态
                         work_state <= (`end_tcl) ? `W_RD:`W_CL;	                                        
            `W_RD:		                //读数据:等待读数据结束,跳转到预充电状态
                         work_state <= (`end_tread) ? `W_PRE:`W_RD;
                         
            `W_WRITE:	                //写操作:跳转到写数据状态
                         work_state <= `W_WD;
            `W_WD:		                //写数据:等待写数据结束,跳转到写回周期状态
                         work_state <= (`end_twrite) ? `W_TWR:`W_WD;                         
            `W_TWR:	                    //写回周期:写回周期结束,跳转到预充电状态
                         work_state <= (`end_twr) ? `W_PRE:`W_TWR;
                         
            `W_PRE:		                //预充电:跳转到预充电等待状态
                         work_state <= `W_TRP;
            `W_TRP:	                   //预充电等待:预充电等待结束,进入空闲状态
                         work_state <= (`end_trp) ? `W_IDLE:`W_TRP;
                         
            `W_AR:		                //自动刷新操作,跳转到自动刷新等待
                         work_state <= `W_TRFC;             
            `W_TRFC:	                //自动刷新等待:自动刷新等待结束,进入空闲状态
                         work_state <= (`end_trfc) ? `W_IDLE:`W_TRFC;
            default: 	 work_state <= `W_IDLE;
		endcase
end

//计数器控制逻辑
always @ (*) begin
	case (init_state)
        `I_NOP:	 cnt_rst_n <= 1'b0;     //延时计数器清零(cnt_rst_n低电平复位)
                                        
        `I_PRE:	 cnt_rst_n <= 1'b1;     //预充电:延时计数器启动(cnt_rst_n高电平启动)
                                        //等待预充电延时计数结束后,清零计数器
        `I_TRP:	 cnt_rst_n <= (`end_trp) ? 1'b0 : 1'b1;
                                        //自动刷新:延时计数器启动
        `I_AR:
                 cnt_rst_n <= 1'b1;
                                        //等待自动刷新延时计数结束后,清零计数器
        `I_TRF:
                 cnt_rst_n <= (`end_trfc) ? 1'b0 : 1'b1;	
                                        
        `I_MRS:  cnt_rst_n <= 1'b1;	    //模式寄存器设置:延时计数器启动
                                        //等待模式寄存器设置延时计数结束后,清零计数器
        `I_TRSC: cnt_rst_n <= (`end_trsc) ? 1'b0:1'b1;
                                        
        `I_DONE: begin                  //初始化完成后,判断工作状态
		    case (work_state)
				`W_IDLE:	cnt_rst_n <= 1'b0;
                                        //行有效:延时计数器启动
				`W_ACTIVE: 	cnt_rst_n <= 1'b1;
                                        //行有效延时计数结束后,清零计数器
				`W_TRCD:	cnt_rst_n <= (`end_trcd)   ? 1'b0 : 1'b1;
                                        //潜伏期延时计数结束后,清零计数器
				`W_CL:		cnt_rst_n <= (`end_tcl)    ? 1'b0 : 1'b1;
                                        //读数据延时计数结束后,清零计数器
				`W_RD:		cnt_rst_n <= (`end_tread)  ? 1'b0 : 1'b1;
                                        //写数据延时计数结束后,清零计数器
				`W_WD:		cnt_rst_n <= (`end_twrite) ? 1'b0 : 1'b1;
                                        //写回周期延时计数结束后,清零计数器
				`W_TWR:	    cnt_rst_n <= (`end_twr)    ? 1'b0 : 1'b1;
                                        //预充电等待延时计数结束后,清零计数器
				`W_TRP:	cnt_rst_n <= (`end_trp) ? 1'b0 : 1'b1;
                                        //自动刷新等待延时计数结束后,清零计数器
				`W_TRFC:	cnt_rst_n <= (`end_trfc)   ? 1'b0 : 1'b1;
				default:    cnt_rst_n <= 1'b0;
		    endcase
        end
		default: cnt_rst_n <= 1'b0;
	endcase
end
 
endmodule 

SDRAM_Ctrl 模块是SDRAM_Controller模块的核心之一,负责对SDRAM的读写请求进行控制和状态管理。

1. 初始化状态机 (init_state)

初始化状态机用于控制 SDRAM 的初始化过程,在上电后200微秒稳定期结束后,执行预充电、自动刷新、模式寄存器设置等操作,最后进入工作状态。

  • 状态 I_NOP:这是默认状态,等待上电后的200微秒计时器完成 (done_200us) 后,进入 I_PRE 状态。
  • 状态 I_PRE:执行预充电操作后,进入 I_TRP 状态等待预充电完成。
  • 状态 I_TRP:等待预充电周期 (TRP_CLK) 结束,当预充电结束后,进入 I_AR 状态,开始自动刷新操作。
  • 状态 I_AR:执行自动刷新操作,并在完成刷新后检查是否已进行8次刷新(初始化要求)。如果满足条件,进入 I_MRS 状态设置模式寄存器。
  • 状态 I_MRS:设置 SDRAM 的模式寄存器,定义工作模式,然后进入 I_TRSC 状态等待模式寄存器设置完成。
  • 状态 I_TRSC:等待模式寄存器设置完成 (TRSC_CLK),完成后进入 I_DONE 状态,标志初始化结束。
  • 状态 I_DONE:表示 SDRAM 初始化完成,SDRAM 进入工作状态,系统可以进行读写操作。
2. 工作状态机 (work_state)

工作状态机负责控制 SDRAM 的读写和自动刷新操作。它根据外部请求和当前状态,管理工作流程。

  • 状态 W_IDLE:这是空闲状态。当检测到自动刷新请求、写请求或读请求时,工作状态机会相应跳转:

    • 若收到 sdram_ref_req 刷新请求,跳转到 W_AR 状态执行自动刷新操作。
    • 若收到写请求 (sdram_wr_req),跳转到 W_ACTIVE 状态,并设置 sdram_rd_wr 为低电平(写操作)。
    • 若收到读请求 (sdram_rd_req),同样跳转到 W_ACTIVE 状态,但设置 sdram_rd_wr 为高电平(读操作)。
  • 状态 W_ACTIVE:行有效状态,进入行选通等待状态 W_TRCD

  • 状态 W_TRCD:行选通等待状态,根据 sdram_rd_wr 信号,决定跳转到读操作状态 (W_READ) 或写操作状态 (W_WRITE)。

  • 读操作流程

    • W_READ:进入潜伏期状态 W_CL
    • W_CL:等待潜伏期结束后进入读数据状态 W_RD
    • W_RD:读取数据,完成后进入预充电状态 W_PRE
  • 写操作流程

    • W_WRITE:进入写数据状态 W_WD
    • W_WD:写入数据,完成后进入写回周期 W_TWR
    • W_TWR:完成写回周期后,进入预充电状态 W_PRE
  • 自动刷新操作

    • W_AR:执行自动刷新,等待 W_TRFC 状态完成刷新,最后返回空闲状态 W_IDLE
3. 计数器控制逻辑

计数器用于控制不同操作中的延迟和时序管理,根据状态机的不同状态执行计数。

  • cnt_200us:上电后的200微秒稳定期计时器。当 cnt_200us 计数到 CNT_200USdone_200us 置高,表示200微秒结束,可以进入下一状态。

  • cnt_refresh:刷新计时器,每 64ms 完成 8192 行的刷新操作(计时器为7812ns)。当 cnt_refresh 达到 CNT_REFRESH 时,产生自动刷新请求 sdram_ref_req

  • cnt_clk:用于计数不同状态下的延时。在不同状态如预充电 (W_PRE)、行选通等待 (W_TRCD)、潜伏期 (W_CL)、读数据 (W_RD)、写数据 (W_WD) 等,会通过 cnt_rst_n 控制计数器复位或启动。

    例如,在 I_PRE 状态下,计数器启动计数预充电时钟周期 (TRP_CLK),等待 end_trp 信号结束后,计数器复位并进入下一状态。

3.3.3.3. sdram_cmd 命令控制模块分析
//****************************************Copyright (c)***********************************//
//技术支持:www.openedv.com
//淘宝店铺:http://openedv.taobao.com 
//关注微信公众平台微信号:"正点原子",免费获取FPGA & STM32资料。
//版权所有,盗版必究。
//Copyright(C) 正点原子 2018-2028
//All rights reserved                               
//----------------------------------------------------------------------------------------
// File name:           sdram_cmd
// Last modified Date:  2018/3/18 8:41:06
// Last Version:        V1.0
// Descriptions:        SDRAM 命令控制模块
//----------------------------------------------------------------------------------------
// Created by:          正点原子
// Created date:        2018/3/18 8:41:06
// Version:             V1.0
// Descriptions:        The original version
//
//----------------------------------------------------------------------------------------
//****************************************************************************************//

module sdram_cmd(
    input             clk,			    //系统时钟
    input             rst_n,			//低电平复位信号

    input      [23:0] sys_wraddr,		//写SDRAM时地址
    input      [23:0] sys_rdaddr,		//读SDRAM时地址
    input      [ 9:0] sdram_wr_burst,	//突发写SDRAM字节数
    input      [ 9:0] sdram_rd_burst,	//突发读SDRAM字节数
    
    input      [ 4:0] init_state,		//SDRAM初始化状态
    input      [ 3:0] work_state, 		//SDRAM工作状态
    input      [ 9:0] cnt_clk,		    //延时计数器	
    input             sdram_rd_wr,	    //SDRAM读/写控制信号,低电平为写
    
    output            sdram_cke,		//SDRAM时钟有效信号
    output            sdram_cs_n,		//SDRAM片选信号
    output            sdram_ras_n,	    //SDRAM行地址选通脉冲
    output            sdram_cas_n,	    //SDRAM列地址选通脉冲
    output            sdram_we_n,		//SDRAM写允许位
    output reg [ 1:0] sdram_ba,		    //SDRAM的L-Bank地址线
    output reg [12:0] sdram_addr	    //SDRAM地址总线
);

`include "sdram_para.v"		            //包含SDRAM参数定义模块

//parameter define
parameter BURST_LENGTH = 3'b111;        //突发长度,这里设置为页突发
parameter ADDRESSING_MODE = 1'b0;       //突发传输方式,这里设置为顺序
parameter CAS_LATENCY = 3'b011;         //CAS潜伏期(CL)设置,这里设置为3,
parameter WRITE_MODE = 1'b0;            //读写方式 A9=0,突发读&突发写

//reg define
reg  [ 4:0] sdram_cmd_r;	            //SDRAM操作指令

//wire define
wire [23:0] sys_addr;		            //SDRAM读写地址	

//*****************************************************
//**                    main code
//***************************************************** 

//SDRAM 控制信号线赋值
assign {sdram_cke,sdram_cs_n,sdram_ras_n,sdram_cas_n,sdram_we_n} = sdram_cmd_r;

//SDRAM 读/写地址总线控制
assign sys_addr = sdram_rd_wr ? sys_rdaddr : sys_wraddr;
	
//SDRAM 操作指令控制
always @ (posedge clk or negedge rst_n) begin
	if(!rst_n) begin
			sdram_cmd_r <= `CMD_INIT;
			sdram_ba    <= 2'b11;
			sdram_addr  <= 13'h1fff;
	end
	else
		case(init_state)
                                        //初始化过程中,以下状态不执行任何指令
            `I_NOP,`I_TRP,`I_TRF,`I_TRSC: begin
                    sdram_cmd_r <= `CMD_NOP;
                    sdram_ba    <= 2'b11;
                    sdram_addr  <= 13'h1fff;	
                end
            `I_PRE: begin               //预充电指令
                    sdram_cmd_r <= `CMD_PRGE;
                    sdram_ba    <= 2'b11;
                    sdram_addr  <= 13'h1fff;
                end 
            `I_AR: begin
                                        //自动刷新指令
                    sdram_cmd_r <= `CMD_A_REF;
                    sdram_ba    <= 2'b11;
                    sdram_addr  <= 13'h1fff;						
                end 			 	
            `I_MRS: begin	            //模式寄存器设置指令
                    sdram_cmd_r <= `CMD_LMR;
                    sdram_ba    <= 2'b00;
                    sdram_addr  <= {        //利用地址线设置模式寄存器,可根据实际需要进行修改
                        3'b000,		        //预留
                        WRITE_MODE,		    //读写方式 A9=0,突发读&突发写
                        2'b00,		        //默认,{A8,A7}=00
                        CAS_LATENCY,		//CAS潜伏期设置,这里设置为3,{A6,A5,A4}=011
                        ADDRESSING_MODE,    //突发传输方式,这里设置为顺序,A3=0
                        BURST_LENGTH        //突发长度,这里设置为页突发,
					};
                end	
            `I_DONE:                    //SDRAM初始化完成
					case(work_state)    //以下工作状态不执行任何指令
                        `W_IDLE,`W_TRCD,`W_CL,`W_TWR,`W_TRP,`W_TRFC: begin
                                sdram_cmd_r <= `CMD_NOP;
                                sdram_ba    <= 2'b11;
                                sdram_addr  <= 13'h1fff;
                            end
                        `W_ACTIVE: begin//行有效指令
                                sdram_cmd_r <= `CMD_ACTIVE;
                                sdram_ba    <= sys_addr[23:22];
                                sdram_addr  <= sys_addr[21:9];
                            end
                        `W_READ: begin  //读操作指令
                                sdram_cmd_r <= `CMD_READ;
                                sdram_ba    <= sys_addr[23:22];
                                sdram_addr  <= {4'b0000,sys_addr[8:0]};
                            end
                        `W_RD: begin    //突发传输终止指令
                                if(`end_rdburst) 
                                    sdram_cmd_r <= `CMD_B_STOP;
                                else begin
                                    sdram_cmd_r <= `CMD_NOP;
                                    sdram_ba    <= 2'b11;
                                    sdram_addr  <= 13'h1fff;
                                end
                            end								
                        `W_WRITE: begin //写操作指令
                                sdram_cmd_r <= `CMD_WRITE;
                                sdram_ba    <= sys_addr[23:22];
                                sdram_addr  <= {4'b0000,sys_addr[8:0]};
                            end		
                        `W_WD: begin    //突发传输终止指令
                                if(`end_wrburst) 
                                    sdram_cmd_r <= `CMD_B_STOP;
                                else begin
                                    sdram_cmd_r <= `CMD_NOP;
                                    sdram_ba    <= 2'b11;
                                    sdram_addr  <= 13'h1fff;
                                end
                            end
                        `W_PRE:begin    //预充电指令
                                sdram_cmd_r <= `CMD_PRGE;
                                sdram_ba    <= sys_addr[23:22];
                                sdram_addr  <= 13'h0000;
                            end				
                        `W_AR: begin    //自动刷新指令
                                sdram_cmd_r <= `CMD_A_REF;
                                sdram_ba    <= 2'b11;
                                sdram_addr  <= 13'h1fff;
                            end
                        default: begin
                                sdram_cmd_r <= `CMD_NOP;
                                sdram_ba    <= 2'b11;
                                sdram_addr  <= 13'h1fff;
                            end
					endcase
            default: begin
                    sdram_cmd_r <= `CMD_NOP;
                    sdram_ba    <= 2'b11;
                    sdram_addr  <= 13'h1fff;
                end
		endcase
end

endmodule 

这个 sdram_cmd 模块主要负责根据系统状态生成控制 SDRAM 的命令信号。通过时钟信号驱动,模块根据初始化状态 (init_state) 和工作状态 (work_state) 来决定何时发送命令,如行激活、读写、预充电等操作。下面详细分析其工作机制:

1. 接口信号
  • 输入信号:

    • clkrst_n: 系统时钟和复位信号,用于时序控制。
    • sys_wraddrsys_rdaddr: 分别为系统写地址和读地址,用于读写操作时指定SDRAM的地址。
    • sdram_wr_burstsdram_rd_burst: 突发写和读时的字节数,用于控制读写操作中的突发传输。
    • init_statework_state: 初始化和工作状态控制信号,用于决定当前 SDRAM 的操作。
    • cnt_clk: 延时计数器,用于特定时序的延时控制。
    • sdram_rd_wr: SDRAM的读写控制信号,低电平表示写操作,高电平表示读操作。
  • 输出信号:

    • sdram_cke, sdram_cs_n, sdram_ras_n, sdram_cas_n, sdram_we_n: 这些信号分别用于控制SDRAM的时钟有效、片选、行地址选通、列地址选通以及写使能。
    • sdram_ba: SDRAM的L-Bank地址信号,用于指定SDRAM的Bank选择。
    • sdram_addr: SDRAM的地址信号总线,用于读写操作时指定SDRAM内存位置。
2. 主要逻辑部分

(1) SDRAM 命令控制

  • 模块包含了五个主要命令信号 (sdram_ckesdram_cs_nsdram_ras_nsdram_cas_nsdram_we_n),这些信号通过 sdram_cmd_r 寄存器赋值控制。具体命令定义在 sdram_para.v 中,通过状态机切换来控制命令的发送。
  • 地址控制方面,通过 sys_addr 来区分读地址 (sys_rdaddr) 或写地址 (sys_wraddr),并且在不同的工作状态下使用不同的地址传输。

(2) 状态机控制

  • 初始化状态 (init_state):

    • 在初始化过程中,根据不同的状态 (I_NOP, I_TRP, I_TRF, I_TRSC, I_PRE, I_AR, I_MRS 等),模块发送不同的命令。例如:
      • I_NOP 状态下发送 CMD_NOP(无操作指令)。
      • I_PRE 状态发送预充电命令 CMD_PRGE
      • I_MRS 状态发送模式寄存器设置命令 CMD_LMR,并通过 sdram_addr 配置CAS潜伏期、突发长度等参数。
  • 工作状态 (work_state):

    • 初始化完成后,进入工作状态。根据不同的状态执行不同操作:
      • W_IDLE, W_TRCD, W_CL 等空闲状态发送 CMD_NOP
      • W_ACTIVE 发送行激活命令 CMD_ACTIVE,并根据地址 sys_addr 设置 sdram_basdram_addr
      • W_READW_WRITE 分别发送读/写命令 CMD_READCMD_WRITE,对应地址根据当前 sys_addr 分配。
      • W_RDW_WD 状态下检查突发传输结束后发送突发终止命令 CMD_B_STOP
      • W_PRE 发送预充电命令 CMD_PRGE

(3) SDRAM命令生成逻辑

每个时钟周期通过 always @ (posedge clk or negedge rst_n) 块进行更新。状态机的各个状态通过 case 语句进行区分,状态机的切换和相应操作指令的发出主要依赖 init_statework_state。对于特定状态,生成相应的命令指令并设置相应的地址与控制信号。

3. 参数设置

模块中通过 parameter 设置了几个重要的 SDRAM 工作参数:

  • BURST_LENGTH: 页突发长度,设置为 3'b111
  • CAS_LATENCY: CAS潜伏期设置为3。
  • WRITE_MODE: 设置为突发读和突发写。

这些参数的设置影响了SDRAM读写操作的延迟和传输模式,可以根据应用需求进行修改。

3.3.3.4. sdram_data数据读写模块分析
//****************************************Copyright (c)***********************************//
//技术支持:www.openedv.com
//淘宝店铺:http://openedv.taobao.com 
//关注微信公众平台微信号:"正点原子",免费获取FPGA & STM32资料。
//版权所有,盗版必究。
//Copyright(C) 正点原子 2018-2028
//All rights reserved                               
//----------------------------------------------------------------------------------------
// File name:           sdram_data
// Last modified Date:  2018/3/18 8:41:06
// Last Version:        V1.0
// Descriptions:        SDRAM 数据读写模块
//----------------------------------------------------------------------------------------
// Created by:          正点原子
// Created date:        2018/3/18 8:41:06
// Version:             V1.0
// Descriptions:        The original version
//
//----------------------------------------------------------------------------------------
//****************************************************************************************//

module sdram_data(
    input             clk,              //系统时钟
    input             rst_n,            //低电平复位信号

    input   [15:0]    sdram_data_in,    //写入SDRAM中的数据
    output  [15:0]    sdram_data_out,   //从SDRAM中读取的数据
    input   [ 3:0]    work_state,       //SDRAM工作状态寄存器
    input   [ 9:0]    cnt_clk,          //时钟计数
    
    inout   [15:0]    sdram_data        //SDRAM数据总线
    );

`include "sdram_para.v"                 //包含SDRAM参数定义模块

//reg define
reg        sdram_out_en;                //SDRAM数据总线输出使能
reg [15:0] sdram_din_r;                 //寄存写入SDRAM中的数据
reg [15:0] sdram_dout_r;                //寄存从SDRAM中读取的数据

//*****************************************************
//**                    main code
//***************************************************** 

//SDRAM 双向数据线作为输入时保持高阻态
assign sdram_data = sdram_out_en ? sdram_din_r : 16'hzzzz;

//输出SDRAM中读取的数据
assign sdram_data_out = sdram_dout_r;

//SDRAM 数据总线输出使能
always @ (posedge clk or negedge rst_n) begin 
    if(!rst_n) 
       sdram_out_en <= 1'b0;
   else if((work_state == `W_WRITE) | (work_state == `W_WD)) 
       sdram_out_en <= 1'b1;            //向SDRAM中写数据时,输出使能拉高
   else 
       sdram_out_en <= 1'b0;
end

//将待写入数据送到SDRAM数据总线上
always @ (posedge clk or negedge rst_n) begin
    if(!rst_n) 
        sdram_din_r <= 16'd0;
    else if((work_state == `W_WRITE) | (work_state == `W_WD))
        sdram_din_r <= sdram_data_in;	//寄存写入SDRAM中的数据
end

//读数据时,寄存SDRAM数据线上的数据
always @ (posedge clk or negedge rst_n) begin
    if(!rst_n) 
        sdram_dout_r <= 16'd0;
    else if(work_state == `W_RD) 
        sdram_dout_r <= sdram_data;	    //寄存从SDRAM中读取的数据
end

endmodule 
  • 功能:处理 SDRAM 数据线上的双向数据传输。
  • 主要信号
    • 输入
      • sdram_data_in:需要写入 SDRAM 的数据。
      • work_state:当前的工作状态。
      • cnt_clk:时钟计数器,用于同步操作。
    • 输出
      • sdram_data_out:从 SDRAM 中读取的数据。
      • sdram_data:SDRAM 数据总线(inout)。
  • 数据总线控制
    • 当处于写操作或写数据状态时,sdram_out_en 使能数据输出,sdram_data 总线作为输出;在其他状态下,保持高阻态。
    • work_state 中判断当前是读操作或写操作,分别将数据写入或从 SDRAM 中读取。

4. 疑问与总结

其实这里在读代码的过程中我发现了关于地址线的一些问题,问题如下图中红字所述,在sdram_rw_test.v文件我们可以看到最大地址和数据传递关系:

image-20241018195916810

image-20241018200034463

sdram_cmd.v文件中

image-20241018203436658

image-20241018203502619

以上是我的疑惑,请求大佬帮忙解答!

本篇博客详细分析了SDRAM代码中各个模块的作用和功能,包括命令控制模块、数据控制模块以及时序控制机制。通过这些模块的协同工作,实现了SDRAM的初始化、读写操作。

声明:本博客笔记中的SDRAM代码来自正点原子,整理用于自己学习与经验交流。

标签:SDRAM,FPGA,clk,sdram,rd,V2,wr,input
From: https://www.cnblogs.com/LilMonsterOvO/p/18475001

相关文章

  • Stratix® 10 SX SoC FPGA结构图— 1SX040HH3F35E2LG 1SX040HH3F35E3XG 1SX040HH3F35I
    Stratix®10SXSoCFPGA结构图英特尔®Stratix®10SoCFPGA结合了四核ARM*Cortex*-A53MPCore*硬处理器系统与革命性的英特尔®Hyperflex™FPGA架构,为嵌入式应用提供了必要的嵌入式性能、功效、密度和系统集成。针对数百万个逻辑元件(LE)FPGA设计优化的全新引擎......
  • 统信uos v20国产信创系统运行Windows的exe程序-后续
    本次尝试使用wine软件来进行exe程序的安装,因为第一次使用迁移助手来做,发现每次都要重新安装组件,要等待很长时间,不适合实时调试,所以采用了当前这种方式。1、通过应用商店安装wine软件,我这边版本是:4.1.0.02、执行命令开启串口权限给指定用户sudousermod-a-Gdialoutlenov......
  • 基于FPGA的以太网设计(四)
    1.ARP协议简介ARP(Address ResolutionProtocol),即地址解析协议,是根据IP地址(逻辑地址)获取MAC地址的一种TCP/IP协议。在以太网通信中,数据是以“帧”的格式进行传输的,帧格式里面包含目的主机的MAC地址。源主机的应用程序知道目的主机的IP地址,却不知道目的主机的MAC地址。而目的......
  • 折腾 Buffalo LS-WXL/R1 (开启SMBv2)
    无聊刷咸鱼看到一个只要50的NAS还带电源,没忍住爱捡垃圾的手买了下来。成色也还可以。根据卖家的提醒,需要下载一个初始引导软件「BUFFALONASNavigator2」,还提供了官方下载地址:http://www.buffalo-asia.com/support/downloads/?lang=zh-cn点进去后就跳转到了http://b......
  • ModuleNotFoundError: No module named 'cv2'
    前言运行 python3req.py 文件时遇到:ModuleNotFoundError:Nomodulenamed'cv2'原因是:环境中缺少 cv2 的包,所以会出现 Nomodulenamed'cv2’ 的问题。cv2 的包名并不叫 cv2 ,所以使用 pipinstallcv2 不能安装。cv2 的包名叫 opencv-python ,使用以下命令即......
  • 会声会影2023永久激活旗舰版免费下载v26.2.0.311免费中文版(附Crack补丁)
    软件介绍会声会影2023永久激活版是一款刚刚最新推出的多功能视频剪辑软件,这款软件不仅完美继承了之前多个版本当中的强大功能。而且我们还可以通过会声会影2023永久激活版来体验到标题动态选项、标题特效等多个全新的功能,让你可以更加快速地进行视频编辑。会声会影2023永久激......
  • Parallels Desktop 20(Mac虚拟机) v20.1.0 for Mac 2024最新破解版免费下载附带PD 20
    ParallelsDesktop20 是一款目前功能最强大灵活度最高的虚拟机软件,可运行数千种Windows应用程序,如MicrosoftOffice、InternetExplorer、Access、Quicken、QuickBooks、VisualStudio,甚至支持对图像要求较高的游戏和CAD项目,而不影响任何性能且无需重启。ParallelsDes......
  • HttpUtility.UrlEncode和Uri.EscapeDataString的区别V2024
    HttpUtility.UrlEncode和Uri.EscapeDataString的区别 先上代码: usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;usingSystem.Web;namespaceConsoleUrlEncode{classProgram{......
  • 界面组件DevExpress WPF v24.1亮点 - 支持全新的字体图标图像
    DevExpressWPF拥有120+个控件和库,将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpressWPF能创建有着强大互动功能的XAML基础应用程序,这些应用程序专注于当代客户的需求和构建未来新一代支持触摸的解决方案。DevExpressWPF控件日前正式发布了今年一个重大版......
  • 基于FPGA的16PSK调制解调系统,包含testbench,高斯信道模块,误码率统计模块,可以设置不
    1.算法仿真效果VIVADO2019.2仿真结果如下(完整代码运行后无水印): 设置SNR=30db      设置SNR=20db:     系统RTL结构图如下:   2.算法涉及理论知识概要       十六进制相位移键控(16PSK,16-PhaseShiftKeying)是一种数字调制技术,它通......