文章目录
- 一.SDRAM简介(手册分析)
- 1.1存储空间
- 1.2特征
- 1.3引脚
- 1.4内部结构
- 1.5需要关注的一些时间
- 1.6模式寄存器
- 1.7命令真值表
- 二.时序分析(手册分析)
- 2.1Avalon时序
- 2.2行激活时序
- 2.3列读写时序
- 2.4读数据
- 2.5写数据
- 三.初步设计
- 3.1状态转移图
- 3.2模块设计图
- 四.操作步骤
- 五.代码
- 六.仿真&效果
- 七.参考
一.SDRAM简介(手册分析)
- SDRAM(同步动态随机存取存储器)。
1.1存储空间
- 本实验中使用的SDRAM存储空间被分为4个bank(如下图就是一个bank),每个bank选定行地址13位,列地址9位可以确定一个存储单元,每个存储单元16bit。那么它的存储空间就是(4 * (2^13) * (2^9) * 16) = 256M(bit)
1.2特征
-
7.8us刷新一行
-
CAS延迟为2或3
-
突发长度和突发延迟控制
(这里先有印象,具体CAS latency,突发长度和突发类型后续会提到)
1.3引脚
1.4内部结构
1.5需要关注的一些时间
1.6模式寄存器
- 那么突发长度是什么意思?所谓翻译为突发不如翻译成连续。那所谓的“突发”就是当我们对一个地址寻址并操作完成后,不用再去寻址,直接进行操作,那么就可以节省下来很多时间
- 突发长度1、2、4、8、全页都是啥意思呢?那经过前面解释突发,就是连续操作多少个数据;至于全页,是每次写入某个bank512个数据(同一个bank,同一个行地址)
1.7命令真值表
二.时序分析(手册分析)
2.1Avalon时序
2.2行激活时序
-
初始化操作完成后,无论读写都要进行行激活(选bank和行地址)
-
CS片选信号:低有效;
-
BA0,BA1:bank寻址;
-
Address中A0-A12:行地址寻址;
-
RAS行选通低,CAS列选通高:进行行选址;
2.3列读写时序
-
行激活操作后,进行列寻址。因为行列地址是复用的,列寻址的地址线仍然是行地址的A0-A12。上文有提到,通过RAS和CAS高低电平来控制Address是行地址还是列地址。
-
CS片选信号:低有效;
-
Adress中A0-A8:列地址寻址;
-
Adress中A10:是否进行预充电;
-
RAS行选通高,CAS列选通低:进行列选址;
-
WE位低:写命令;WE为高读命令;
2.4读数据
- CAS发完后,经过一段时间才有数据输出,我们之前提到过CAS潜伏周期,根据模式寄存器配置,这里延时对应的周期数
2.5写数据
- 注意:写数据是不需要CAS潜伏周期的
三.初步设计
3.1状态转移图
- 手册上的状态图如上,感觉有些乱,个人简化整理了一下
3.2模块设计图
四.操作步骤
-
Tools -> Platform Designer
-
搜索SDRAM并选择
-
配置参数如下
-
相关延时如下
-
时钟设置为100mHz
-
连线和端口命名
-
Generate
五.代码
- sdram_control模块
/**************************************功能介绍***********************************
Date :
Author : Alegg xy.
Version :
Description:
*********************************************************************************/
//---------<模块及端口声名>------------------------------------------------------
module sdram_control(
input clk ,
input rst_n ,
//写数据
// input wr_req ,
input [7:0] wr_data ,
input wr_data_vld ,
//读数据
input rd_req ,
input rd_ready ,//串口发送模块就绪
output [7:0] rd_data ,
output rd_data_vld ,
//clk
input wr_clk ,
input rd_clk ,
//avalon_master
output [23:0] am_addr ,
output am_wr_req_n ,
output am_rd_req_n ,
output [15:0] am_wr_data ,
input [15:0] am_rd_data ,
input am_rd_data_vld ,
output [1:0] am_be_n ,
output am_cs ,
input am_waitrequest
);
//---------<参数定义>---------------------------------------------------------
//状态机参数定义
localparam IDLE = 4'b0001,//
WRITE = 4'b0010,//
READ = 4'b0100,//
DONE = 4'b1000;//
reg [3:0] cstate ;//现态
reg [3:0] nstate ;//次态
wire IDLE_WRITE ;
wire IDLE_READ ;
wire WRITE_DONE ;
wire READ_DONE ;
wire DONE_IDLE ;
//fifo
wire wr_fifo_rd_req ;
wire [15:0] wr_fifo_rd_data ;
wire wr_fifo_empty ;
wire wr_fifo_full ;
wire [5:0] wr_fifo_usedw ;
wire rd_fifo_wr_req ;
wire [15:0] rd_fifo_wr_data ;
wire rd_fifo_empty ;
wire rd_fifo_full ;
wire [5:0] rd_fifo_usedw ;
//计数器
reg [8:0] cnt_burst ;
wire add_cnt_burst ;
wire end_cnt_burst ;
reg [23:0] cnt_wr_addr ;
wire add_cnt_wr_addr ;
wire end_cnt_wr_addr ;
reg [23:0] cnt_rd_addr ;
wire add_cnt_rd_addr ;
wire end_cnt_rd_addr ;
parameter ADDR_MAX = 512,
BURST_MAX = 5;
//---------<内部信号定义>-----------------------------------------------------
//fifo
wrfifo wrfifo_inst (
.aclr ( ~rst_n ),
.data ( wr_data ),
.rdclk ( clk ),
.rdreq ( wr_fifo_rd_req ),
.wrclk ( wr_clk ),
.wrreq ( wr_data_vld && ~wr_fifo_full ),
.q ( wr_fifo_rd_data ),
.rdusedw( wr_fifo_usedw ),
.rdempty( wr_fifo_empty ),
.wrfull ( wr_fifo_full )
);
assign wr_fifo_rd_req = add_cnt_wr_addr && ~wr_fifo_empty;
rdfifo rdfifo_inst (
.aclr ( ~rst_n ),
.data ( rd_fifo_wr_data ),
.rdclk ( rd_clk ),
.rdreq ( rd_data_vld ),
.wrclk ( clk ),
.wrreq ( rd_fifo_wr_req && ~rd_fifo_full ),
.q ( rd_data ),
.rdusedw( rd_fifo_usedw ),
.rdempty( rd_fifo_empty ),
.wrfull ( rd_fifo_full )
);
assign rd_data_vld = rd_ready && ~rd_fifo_empty ;
assign rd_fifo_wr_data = am_rd_data;
assign rd_fifo_wr_req = am_rd_data_vld;
//第一段:时序逻辑描述状态转移
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cstate <= IDLE;
end
else begin
cstate <= nstate;
end
end
//第二段:组合逻辑描述状态转移规律和状态转移条件
always @(*) begin
case(cstate)
IDLE :begin
if (IDLE_WRITE) begin
nstate = WRITE;
end
else if (IDLE_READ) begin
nstate = READ;
end
else begin
nstate = cstate;
end
end
WRITE :begin
if (WRITE_DONE) begin
nstate = DONE;
end
else begin
nstate = cstate;
end
end
READ :begin
if (READ_DONE) begin
nstate = DONE;
end
else begin
nstate = cstate;
end
end
DONE :begin
if (DONE_IDLE) begin
nstate = IDLE;
end
else begin
nstate = cstate;
end
end
default : nstate = IDLE;
endcase
end
assign IDLE_WRITE = (cstate == IDLE) && wr_fifo_usedw >= BURST_MAX;
assign IDLE_READ = (cstate == IDLE) && rd_req;
assign WRITE_DONE = (cstate == WRITE) && end_cnt_burst;
assign READ_DONE = (cstate == READ) && end_cnt_burst;
assign DONE_IDLE = (cstate == DONE) && 1'b1;
//突发计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_burst <= 'd0;
end
else if(add_cnt_burst)begin
if(end_cnt_burst)begin
cnt_burst <= 'd0;
end
else begin
cnt_burst <= cnt_burst + 1'd1;
end
end
end
assign add_cnt_burst = (cstate == WRITE || cstate == READ) && ~am_waitrequest;
assign end_cnt_burst = add_cnt_burst && cnt_burst == BURST_MAX - 1;
//地址计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_wr_addr <= 'd0;
end
else if(add_cnt_wr_addr)begin
if(end_cnt_wr_addr)begin
cnt_wr_addr <= 'd0;
end
else begin
cnt_wr_addr <= cnt_wr_addr + 1'd1;
end
end
end
assign add_cnt_wr_addr = (cstate == WRITE) && ~am_waitrequest;
assign end_cnt_wr_addr = add_cnt_wr_addr && cnt_wr_addr == ADDR_MAX - 1;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_rd_addr <= 'd0;
end
else if(add_cnt_rd_addr)begin
if(end_cnt_rd_addr)begin
cnt_rd_addr <= 'd0;
end
else begin
cnt_rd_addr <= cnt_rd_addr + 1'd1;
end
end
end
assign add_cnt_rd_addr = (cstate == READ) && ~am_waitrequest;
assign end_cnt_rd_addr = add_cnt_rd_addr && cnt_rd_addr == ADDR_MAX - 1;
//avalon_master时序
assign am_wr_req_n = ~(cstate == WRITE);
assign am_rd_req_n = ~(cstate == READ);
assign am_addr = (cstate == WRITE) ? cnt_wr_addr : cnt_rd_addr;
assign am_wr_data = wr_fifo_rd_data;
assign am_be_n = 2'b00;
assign am_cs = 1'b1;
endmodule
- 顶层模块(这里有用到PLL,需要100mHz时钟)
/**************************************功能介绍***********************************
Date :
Author : Alegg xy.
Version :
Description:
*********************************************************************************/
//---------<模块及端口声名>------------------------------------------------------
module top(
input clk ,
input rst_n ,
input rx ,
output tx ,
input [3:0] key_in ,
output sdram_clk ,
output [12:0] sdram_addr ,
output [1:0] sdram_ba ,
output sdram_cas_n ,
output sdram_cke ,
output sdram_cs_n ,
inout [15:0] sdram_dq ,
output [1:0] sdram_dqm ,
output sdram_ras_n ,
output sdram_we_n
);
//---------<参数定义>---------------------------------------------------------
//PLL
wire clk_50mhz ;
wire clk_100mhz ;
wire clk_offset ;
wire locked ;
//key_debounce
wire [3:0] key_out ;
//uart
wire [7:0] wr_data ;
wire wr_data_vld ;
wire [7:0] rd_data ;
wire rd_data_vld ;
wire rd_ready ;
//control
wire [23:0] am_addr ;
wire am_wr_req_n ;
wire am_rd_req_n ;
wire [15:0] am_wr_data ;
wire [15:0] am_rd_data ;
wire am_rd_data_vld;
wire [1:0] am_be_n ;
wire an_cs ;
wire am_waitrequest;
//---------<内部信号定义>-----------------------------------------------------
pll pll_inst (
.areset ( ~rst_n ),
.inclk0 ( clk ),
.c0 ( clk_50mhz ),
.c1 ( clk_100mhz ),
.c2 ( sdram_clk ),
.locked ( locked )
);
key_debounce u_key_debounce(
.clk (clk_50mhz),
.rst_n (rst_n),
.key_in (key_in),
.key_out (key_out)
);
uart_rx u_uart_rx(
.clk (clk_50mhz),
.rst_n (rst_n),
.rx (rx),
.rx_data_vld(wr_data_vld),
.rx_data (wr_data)
);
uart_tx u_uart_tx(
.clk (clk_50mhz),
.rst_n (rst_n),
.tx_data (rd_data),
.tx_data_vld(rd_data_vld),
.ready (rd_ready),
.tx (tx)
);
sdram_control u_sdram_control(
.clk (clk_100mhz),
.rst_n (rst_n),
.wr_data (wr_data),
.wr_data_vld (wr_data_vld),
.rd_req (key_out[0]),
.rd_ready (rd_ready),
.rd_data (rd_data),
.rd_data_vld (rd_data_vld),
.wr_clk (clk_50mhz),
.rd_clk (clk_50mhz),
.am_addr (am_addr),
.am_wr_req_n (am_wr_req_n),
.am_rd_req_n (am_rd_req_n),
.am_wr_data (am_wr_data),
.am_rd_data (am_rd_data),
.am_rd_data_vld (am_rd_data_vld),
.am_be_n (am_be_n),
.am_cs (am_cs),
.am_waitrequest (am_waitrequest)
);
sdram u0 (
.avalon_slave_address (am_addr), // avalon_slave.address
.avalon_slave_byteenable_n (am_be_n), // .byteenable_n
.avalon_slave_chipselect (am_cs), // .chipselect
.avalon_slave_writedata (am_wr_data), // .writedata
.avalon_slave_read_n (am_rd_req_n), // .read_n
.avalon_slave_write_n (am_wr_req_n), // .write_n
.avalon_slave_readdata (am_rd_data), // .readdata
.avalon_slave_readdatavalid (am_rd_data_vld), // .readdatavalid
.avalon_slave_waitrequest (am_waitrequest), // .waitrequest
.clk_clk (clk_100mhz), // clk.clk
.reset_reset_n (rst_n), // reset.reset_n
.sdram_addr (sdram_addr), // sdram.addr
.sdram_ba (sdram_ba), // .ba
.sdram_cas_n (sdram_cas_n), // .cas_n
.sdram_cke (sdram_cke), // .cke
.sdram_cs_n (sdram_cs_n), // .cs_n
.sdram_dq (sdram_dq), // .dq
.sdram_dqm (sdram_dqm), // .dqm
.sdram_ras_n (sdram_ras_n), // .ras_n
.sdram_we_n (sdram_we_n) // .we_n
);
endmodule
六.仿真&效果
- 仿真代码(这里用到了仿真模型)
`timescale 1ns/1ns
module tb_top();
//激励信号定义
reg clk ;
reg rst_n ;
reg rx ;
reg [3:0] key_in ;
//输出信号定义
wire tx ;
wire sdram_clk ;
wire [12:0] sdram_addr ;
wire [1:0] sdram_ba ;
wire sdram_cas_n ;
wire sdram_cke ;
wire sdram_cs_n ;
wire [15:0] sdram_dq ;
wire [1:0] sdram_dqm ;
wire sdram_ras_n ;
wire sdram_we_n ;
wire clk_50mhz ;
wire clk_100mhz ;
wire clk_offset ;
wire locked ;
//时钟周期参数定义
parameter CLOCK_CYCLE = 20;
//模块例化
top u_top(
.clk (clk),
.rst_n (rst_n),
.rx (rx),
.tx (tx),
.key_in (key_in),
.sdram_addr (sdram_addr ),
.sdram_ba (sdram_ba ),
.sdram_cas_n (sdram_cas_n),
.sdram_cke (sdram_cke ),
.sdram_cs_n (sdram_cs_n ),
.sdram_dq (sdram_dq ),
.sdram_dqm (sdram_dqm ),
.sdram_ras_n (sdram_ras_n),
.sdram_we_n (sdram_we_n )
);
assign sdram_clk = clk_offset;
sdr u_sdr
(
.Dq (sdram_dq),
.Addr (sdram_addr),
.Ba (sdram_ba),
.Clk (sdram_clk),
.Cke (sdram_cke),
.Cs_n (sdram_cs_n),
.Ras_n (sdram_ras_n),
.Cas_n (sdram_cas_n),
.We_n (sdram_we_n),
.Dqm (sdram_dqm)
);
pll pll_inst (
.areset ( ~rst_n ),
.inclk0 ( clk ),
.c0 ( clk_50mhz ),
.c1 ( clk_100mhz ),
.c2 ( clk_offset ),
.locked ( locked )
);
//产生时钟
initial clk = 1'b0;
always #(CLOCK_CYCLE/2) clk = ~clk;
integer i;
//产生激励
initial begin
rst_n = 1'b1;
rx = 0;
key_in[0] = 1;
#(CLOCK_CYCLE*2);
rst_n = 1'b0;
#(CLOCK_CYCLE*20);
rst_n = 1'b1;
#2000;
//写数据
repeat(5)begin
for (i = 0 ; i < 16 ; i = i + 1) begin
rx = {$random} % 1;
#20;
end
end
//读数据
key_in[0] = 0;
#(CLOCK_CYCLE * 200);
key_in[0] = 1;
#10000;
$stop;
end
endmodule
-
仿真模型
https://download.csdn.net/download/weixin_67803687/88445512 -
仿真效果
-
上板效果
七.参考
https://blog.csdn.net/qq_52215423/article/details/132897181