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

news/2024/10/18 20:41:05/文章来源:https://www.cnblogs.com/LilMonsterOvO/p/18475001

正点原子新起点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外部时钟,50Minput         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 数据掩码//LEDoutput [ 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) beginif(!rst_n) begininit_done_d0 <= 1'b0;init_done_d1 <= 1'b0;endelse begininit_done_d0 <= sdram_init_done;init_done_d1 <= init_done_d0;end
end            //SDRAM初始化完成之后,写操作计数器开始计数
always @(posedge clk_50m or negedge rst_n) beginif(!rst_n) wr_cnt <= 24'd0;  else if(init_done_d1 && (wr_cnt <= WR_CNT))wr_cnt <= wr_cnt + 1'b1;elsewr_cnt <= wr_cnt;
end    //SDRAM写端口FIFO的写使能、写数据(1~1024)
always @(posedge clk_50m or negedge rst_n) beginif(!rst_n) begin      wr_en   <= 1'b0;wr_data <= 16'd0;endelse if((wr_cnt >= 24'd1) && (wr_cnt <= WR_CNT)) beginwr_en   <= 1'b1;            //写使能拉高wr_data <= wr_cnt;          //写入数据1~1024end    else beginwr_en   <= 1'b0;wr_data <= 16'd0;end                
end        //写入数据完成后,开始读操作    
always @(posedge clk_50m or negedge rst_n) beginif(!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) beginif(!rst_n) rd_cnt <= 24'd0;else if(rd_en) beginif(rd_cnt < RD_CNT)rd_cnt <= rd_cnt + 1'b1;elserd_cnt <= 24'd1;end
end//第一次读取的数据无效,后续读操作所读取的数据才有效
always @(posedge clk_50m or negedge rst_n) beginif(!rst_n) rd_valid <= 1'b0;else if(rd_cnt == RD_CNT)         //等待第一次读操作结束rd_valid <= 1'b1;               //后续读取的数据有效elserd_valid <= rd_valid;
end            //读数据有效时,若读取数据错误,给出标志信号
always @(posedge clk_50m or negedge rst_n) beginif(!rst_n)error_flag <= 1'b0; else if(rd_valid && (rd_data != rd_cnt))error_flag <= 1'b1;             //若读取的数据错误,将错误标志位拉高 elseerror_flag <= error_flag;
endendmodule 

该模块的主要功能是进行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) beginif(!rst_n)led_cnt <= 25'd0;else if(led_cnt < LED_CNT) led_cnt <= led_cnt + 25'd1;elseled_cnt <= 25'd0;
end//利用LED灯不同的显示状态指示错误标志的高低
always @(posedge clk_50m or negedge rst_n) beginif(rst_n == 1'b0)led[0] <= 1'b0;else if(!sdram_init_done)beginif(led_cnt == LED_CNT) led[0] <= ~led[0];  //SDRAM初始化失败时,LED灯每隔0.5s闪烁一次elseled[0] <= led[0];endelseled[0] <= 1'b1;         //错误标志为低时,LED灯常亮
endalways @(posedge clk_50m or negedge rst_n) beginif(rst_n == 1'b0)led[1] <= 1'b0;else if(error_flag) beginif(led_cnt == LED_CNT) led[1] <= ~led[1];  //错误标志为高时,LED灯每隔0.5s闪烁一次elseled[1] <= led[1];end elseled[1] <= 1'b1;         //错误标志为低时,LED灯常亮
endendmodule 

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) beginif(!rst_n) beginwr_ack_r1 <= 1'b0;wr_ack_r2 <= 1'b0;endelse beginwr_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) beginif(!rst_n) beginrd_ack_r1 <= 1'b0;rd_ack_r2 <= 1'b0;endelse beginrd_ack_r1 <= sdram_rd_ack;rd_ack_r2 <= rd_ack_r1;end
end	//同步写端口复位信号,用于捕获wr_load上升沿
always @(posedge clk_ref or negedge rst_n) beginif(!rst_n) beginwr_load_r1 <= 1'b0;wr_load_r2 <= 1'b0;endelse beginwr_load_r1 <= wr_load;wr_load_r2 <= wr_load_r1;end
end//同步读端口复位信号,同时用于捕获rd_load上升沿
always @(posedge clk_ref or negedge rst_n) beginif(!rst_n) beginrd_load_r1 <= 1'b0;rd_load_r2 <= 1'b0;endelse beginrd_load_r1 <= rd_load;rd_load_r2 <= rd_load_r1;end
end//同步sdram读使能信号
always @(posedge clk_ref or negedge rst_n) beginif(!rst_n) beginread_valid_r1 <= 1'b0;read_valid_r2 <= 1'b0;endelse beginread_valid_r1 <= sdram_read_valid;read_valid_r2 <= read_valid_r1;end
end//sdram写地址产生模块
always @(posedge clk_ref or negedge rst_n) beginif(!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) beginif(!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) beginif(!rst_n) beginsdram_wr_req <= 0;sdram_rd_req <= 0;endelse if(sdram_init_done) begin       //SDRAM初始化完成后才能响应读写请求//优先执行写操作,防止写入SDRAM中的数据丢失if(wrf_use >= wr_length) begin   //若写端口FIFO中的数据量达到了写突发长度sdram_wr_req <= 1;		     //发出写sdarm请求sdram_rd_req <= 0;		     endelse if((rdf_use < rd_length)    //若读端口FIFO中的数据量小于读突发长度,&& read_valid_r2) begin //同时sdram读使能信号为高sdram_wr_req <= 0;		     sdram_rd_req <= 1;		     //发出读sdarm请求endelse beginsdram_wr_req <= 0;sdram_rd_req <= 0;endendelse beginsdram_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控制器时钟,100MHzinput         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) beginif(!rst_n) cnt_200us <= 15'd0;else if(cnt_200us < CNT_200US) cnt_200us <= cnt_200us + 1'b1;elsecnt_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 =7812nscnt_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;elseinit_ar_cnt <= init_ar_cnt;//SDRAM的初始化状态机
always @ (posedge clk or negedge rst_n) beginif(!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) beginif(!rst_n) work_state <= `W_IDLE;          //空闲状态elsecase(work_state)//定时自动刷新请求,跳转到自动刷新状态`W_IDLE: if(sdram_ref_req & sdram_init_done) beginwork_state <= `W_AR; 		sdram_rd_wr <= 1'b1;end 		        //写SDRAM请求,跳转到行有效状态else if(sdram_wr_req & sdram_init_done) beginwork_state <= `W_ACTIVE;sdram_rd_wr <= 1'b0;	end                //读SDRAM请求,跳转到行有效状态else if(sdram_rd_req && sdram_init_done) beginwork_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 @ (*) begincase (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;endcaseenddefault: cnt_rst_n <= 1'b0;endcase
endendmodule 

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) beginif(!rst_n) beginsdram_cmd_r <= `CMD_INIT;sdram_ba    <= 2'b11;sdram_addr  <= 13'h1fff;endelsecase(init_state)//初始化过程中,以下状态不执行任何指令`I_NOP,`I_TRP,`I_TRF,`I_TRSC: beginsdram_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}=00CAS_LATENCY,		//CAS潜伏期设置,这里设置为3,{A6,A5,A4}=011ADDRESSING_MODE,    //突发传输方式,这里设置为顺序,A3=0BURST_LENGTH        //突发长度,这里设置为页突发,};end	`I_DONE:                    //SDRAM初始化完成case(work_state)    //以下工作状态不执行任何指令`W_IDLE,`W_TRCD,`W_CL,`W_TWR,`W_TRP,`W_TRFC: beginsdram_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 beginsdram_cmd_r <= `CMD_NOP;sdram_ba    <= 2'b11;sdram_addr  <= 13'h1fff;endend								`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 beginsdram_cmd_r <= `CMD_NOP;sdram_ba    <= 2'b11;sdram_addr  <= 13'h1fff;endend`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;enddefault: beginsdram_cmd_r <= `CMD_NOP;sdram_ba    <= 2'b11;sdram_addr  <= 13'h1fff;endendcasedefault: beginsdram_cmd_r <= `CMD_NOP;sdram_ba    <= 2'b11;sdram_addr  <= 13'h1fff;endendcase
endendmodule 

这个 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) beginif(!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) beginif(!rst_n) sdram_dout_r <= 16'd0;else if(work_state == `W_RD) sdram_dout_r <= sdram_data;	    //寄存从SDRAM中读取的数据
endendmodule 
  • 功能:处理 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代码来自正点原子,整理用于自己学习与经验交流。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/817039.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

面试题速刷 - 实战会碰到的一些问题

页面如何进行首屏优化?路由懒加载服务端渲染SSR只获取HTML就可以,里面包含data。 APP预取(啥东西)APP结合H5、结合JS bridge 分页图片懒加载 lazyloadHybrid总结:后端一次性返回10w条数据,你会如何渲染? 本身后端设计方案的设计就不合理!非要的话......自定义中间层:虚…

氏发

这个作业属于哪个课程 2024高级语言程序设计 (福州大学 - 计算机与大数据学院)这个作业要求在哪里 高级语言程序设计课程第三次个人作业学号 102400117姓名 廖逸轩

二、STM32F103C8T6-定时器

STM32F103C8T6 定时器概述 STM32F103C8T6 作为一款广泛使用的微控制器,内置多个定时器,能够支持多种计时和控制功能,如精确延时、脉冲宽度调制(PWM)、捕获比较(Capture/Compare)、输入捕获 和 输出比较 等。这些功能在电机控制、信号测量、周期性事件触发等应用中非常常…

Sparse Table

Sparse Table 可用于解决这样的问题:给出一个 \(n\) 个元素的数组 \(a_1, a_2, \cdots, a_n\),支持查询操作计算区间 \([l,r]\) 的最小值(或最大值)。这种问题被称为区间最值查询问题(Range Minimum/Maximum Query,简称 RMQ 问题)。预处理的时间复杂度为 \(O(n \log n)\…

计量经济学(十一)——联立方程模型的估计

img { display: block; margin-left: auto; margin-right: auto } table { margin-left: auto; margin-right: auto } 联立方程模型(Simultaneous Equations Model, SEM)是一类包含多个相互依赖变量的统计模型,用来描述这些变量之间的相互关系。在传统的单一方程模型中,通常…

数据结构与算法 课程随记

数据结构与算法 课程随记因为有时候需要在不同设备编辑同一份文档,本地不太方便了,先在放着博客园比较省事吧。 但是博客园是不是快要四了啊,没事再整一个个人博客吧。 Class https://www.runoob.com/cplusplus/cpp-classes-objects.html大纲 定义成员函数,(无论public/pri…

pve安装后删除local-lvm并把其空间全部分给local

在安装pve的时候,系统默认分配给local的空间非常小,我们可以通过以下方法把local-lvm删除,并将其空间还给local。 在webui的pve节点的磁盘选项中找到LVM-Thin,删除data卷。删除后此处为空。 接着打开终端执行以下命令: lvresize --extents +100%FREE --resizefs pve/root此…

PYNQ Z2 读取xadc外部通道电压

使用XADC 或者JTAG只能读取XADC的内部电压, 而无法读取外部通道的电压 现在使用xsysmon.h库里面的函数进行XADC外部通道的电压 为了方便观察,增加了PL GPIO KEY LED进行观察 1. 配置ZYNQ70002.添加两个axi gpio并进行配置 AXI GPIO0AXI GPIO13. 添加XADC进行配置 这里选择axi l…

10.18 模拟赛

炼石计划 10 月 04 日 NOIP 模拟赛 #8【补题】 - 比赛 - 梦熊联盟 (mna.wang) 复盘 T1 有种 div.2 B 的风格,没秒,想看题。 T2。只判是否无解?\(k \le 100\)?把 \(200\) 个关键连通块拿出来建图跑传递闭包不就做完了。 一遍过大样例?简直不可思议,但还是把 T2 关了吧。 用…

小心!这样分享 B 站视频会暴露身份

已经有被开盒的案例了。‍ 在 2022 年 6 月 10 日 0 点,B 站在视频的网址上加了个参数 ?vd_source=XXXXXXXXXXXXXXX​,如图: ​ 经过网友的测试,这个参数值很可能就是用户 ID 的 hash 值(简单来说就是用户身份),所以如果直接复制网址的话,是有可能被“开盒”的。 ‍ 其…

局部静态变量的初始化观测

局部静态变量的初始化观测//全局变量int global=0x11111;int main(int argc, char* argv[]){ //局部变量 int temp=0x160; global=global+temp; return 0;}6: int global=0x111111;7: int main(int argc, char* argv[])8: {00401010 push …

想玩Steam游戏,但配置太低?ToDesk云电脑一招搞定!

在游戏爱好者的世界里,汇集了许多游戏大作的Steam平台无疑是一座宝库。但对于许多玩家来说,拥有一颗渴望畅玩游戏的心,却常常被低配置的电脑设备所束缚。尤其是面对硬件要求极高的3A大作时,低配置的电脑往往力不从心,卡顿、掉帧等问题让人苦恼不已。但别担心!小编最近发现…