目录
一. 搭建仿真平台
二. 实现SDRAM连续写入1024个数据,然后再连续读出,并比较
1. 调试过程中问题:
2. 顶层代码
3. 功能代码
三. SDRAM+FIFO实现上述功能调试
1. 代码设计要点
2. 仿真过程问题
3. 上板运行调试
安陆反馈:
一. 搭建仿真平台
EGS20这款IC内部集成了SDRAM,不需要硬件电路板再外部挂一个存储器。使用起来很方便。在代码设计时,可以在顶层直接调用其IP,就可以直接使用连接内部SDRAM。实际上板时不需要调用代码如下:
EG_PHY_SDRAM_2M_32 sdram(
.clk(SDRAM_CLK),
.ras_n(SDR_RAS),
.cas_n(SDR_CAS),
.we_n(SDR_WE),
.addr(SDR_ADDR[10:0]),
.ba(SDR_BA),
.dq(SDR_DQ),
.cs_n(1'b0),
.dm0(SDR_DM[0]),
.dm1(SDR_DM[1]),
.dm2(SDR_DM[2]),
.dm3(SDR_DM[3]),
.cke(1'b1)
);
用Modelsim仿真时,可以在testbench文件中直接调用模拟SDRAM的IP核,就可以软件仿真SDRAM的读写过程了。代码如下:
IS42s32200 SDR1
(
.Clk ( SDRAM_CLK),
.Cke ( SDRAM_CKE),
.Cs_n ( 1'b0),
.Ras_n ( SDRAM_RASn),
.Cas_n ( SDRAM_CASn),
.We_n ( SDRAM_WEn),
.Ba ( SDRAM_BA),
.Dqm ( SDRAM_DQM),
.Addr ( SDRAM_ADDR),
.Dq ( SDRAM_DQ)
);
上面的文件IS42s32200需要与FAE联系获取。
二. 实现SDRAM连续写入1024个数据,然后再连续读出,并比较
1. 调试过程中问题:
1)连续读写SDRAM是否中间需要等待一段时间
不需要,可以写完后直接读。
2)确认PLL的使用限制3)SDRAM的使用时钟限制
使用100M时钟,相位180°。如图1,当写完立马开始读后,DQ的每4个数据的最后一个数据,在数据先上保留的时间比其他数据短,导致读到的数据不是4的倍数,发生错误。用高于100M以上的时钟,如200M,结果如图2,读到的数据不完整。
使用75M时钟,50M时钟,相位180,读写正确。
数据手册上显示,自带的SDRAM读写频率最高支持是200M,FAE反馈可能是自带的IS42s32200模型,读出的数据频率最高是75M 。100M及以上会出错。
图1
图2
仿真过程中Modelsim出现以下错误:
这个是时序警告,仿真用的模型IS42s32200 -3 最高只能跑到143MHz
搭建硬件平台:在开发板上下载bit文件。相比仿真调试,需要将PLL的复位修改成正常的。同时需要调用EG_PHY_SDRAM_2M_3模块,将内置的SDRAM的接口给引出来。以下时运行实际效果,总体来说,需要调节相位,不同相位可能导致数据错位。
1)时钟100M,相位180°,调试结果OK;
2)时钟150M,相位180°,读SDRAM错误;
3)时钟50M,相位180°、70°、45°,读SDRAM错误;
4)时钟50M,相位225°、270°时,调试结果OK。
2. 顶层代码
`include "..\para.v"
`define SIMULATION
module MotorControl(
/*基准时钟及复位信号*/
input sys_clk, //系统时钟,25 MHz
input sys_rst_n, //复位信号,低电平有效
`ifdef SIMULATION
output SDRAM_CLK,
output SDR_CKE,
output SDR_RAS,
output SDR_CAS,
output SDR_WE,
output [`BA_WIDTH-1:0] SDR_BA,
output [`ROW_WIDTH-1:0]SDR_ADDR,
output [`DM_WIDTH-1:0] SDR_DM,
inout [`DATA_WIDTH-1:0] SDR_DQ,
`endif
input key, //按键输入
output error_led, //LED错误信号
output work_led //正常状态接口
);
wire [`BA_WIDTH-1:0] SDR_BA;
wire [`DM_WIDTH-1 :0]SDR_DM;
wire [`ROW_WIDTH-1:0]SDR_ADDR;
wire [`DATA_WIDTH-1:0] SDR_DQ;
/*对于复位情况下的亚稳态,常常是由于恢复时间和移除时钟不满足造成的,
因此,最常用的处理方式是采用异步复位、同步释放。常用电路模型如所示。
采用第二级寄存器输出作为全局复位信号输出。*/
reg[1:0] rst_n_r;
wire rst_n;
wire clk_50m /* synthesis keep */;
wire clk_100m1 /* synthesis keep */;
wire clk_100m_75deg /* synthesis keep */;
wire clk_200m;
wire clk_locked_flag /* synthesis keep */; //clk ready flag
/*消除亚稳态的复位信号*/
assign rst_n = rst_n_r[1] & clk_locked_flag; // synthesis keep
always @(posedge sys_clk or negedge sys_rst_n)
begin
if(!sys_rst_n)
rst_n_r <= 2'd0;
else
rst_n_r <= {rst_n_r[0], 1'b1};
end
`ifdef SIMULATION
reg [5:0] por_cnt=0;
wire reset;
always @(posedge sys_clk ) begin
if (por_cnt <30) por_cnt<=por_cnt+1'b1;
else por_cnt<=por_cnt;
end
assign reset=(por_cnt==30)?1'b1:1'b0;
`endif
PLL_50M u_Pll_50M(
.refclk( sys_clk ),
`ifdef SIMULATION
.reset(~reset),
`else
.reset(~rst_n_r[1]), //PLL 高电平复位
`endif
.extlock( clk_locked_flag ),
.clk0_out ( clk_50m ),
.clk1_out( clk_100m ),
.clk2_out( clk_100m_75deg )
//.clk3_out( clk_200m )
);
//按键
wire sdram_test_en;
wire wr_sdram_done,sdram_test_done;
ANJIAN ANJIAN(
.clk(clk_100m), //系统时钟
.rst(rst_n), //系统复位,低电平有效
.key(key), //按键输入信号
.sdram_test_done(sdram_test_done),
.sdram_test_en(sdram_test_en)//按下案件后开始测试置1,测试结束或者等待时间结束重新置0
);
//led指示灯模块
wire error_flag;
led led_top (
.clk(clk_100m), //1M clk
.rst_n(rst_n), //系统复位,低电平有效
.error_flag(error_flag),
.work_led(work_led),
.error_led(error_led)
);
/**********一:连续读写0x7FFFF深度的sdram地址区间***********/
wire [`DATA_WIDTH-1:0] sdram_dout; //从sdram中读出的数据
wire sdram_wr_reqT, sdram_rd_reqT;
wire [`ADDR_WIDTH-1:0] sdram_wr_addrT;
wire [`ADDR_WIDTH-1:0] sdram_rd_addrT;
wire [`DATA_WIDTH-1:0] sdram_dinT;
wire [`DM_WIDTH-1:0] sdram_wr_dm;
wire sdram_init_ref_vld;
wire sdram_init_done;
wire sdram_rd_en;
//wrd_sdram_page1 wrd_sdram_page_inst //使用FIFO,连续读写1024个数据并比较
wrd_sdram_page u1_app //连续读写1024个数据并比较
(
.clk(clk_100m) , // input clk_sig
.rstn(rst_n) , // input rstn_sig
.sdram_init_done(sdram_init_done),
.sdram_init_ref_vld(sdram_init_ref_vld),
.sdram_wr_burst(1'b1), //写突发长度
.sdram_rd_burst(1'b1), //读突发长度
// .wr_max_addr(19'h7FFFF), //结束地址
// .wr_min_addr(19'd0), //起始地址
.sdram_test_en(sdram_test_en),
.sdram_rd_data(sdram_dout) , // input [15:0] sdram_rd_data_sig
.sdram_rd_en(sdram_rd_en),
.sdram_rd_addr(sdram_rd_addrT),
.sdram_wr_addr(sdram_wr_addrT) , // output [23:0] sdram_wr_addr_sig
.sdram_wr_data(sdram_dinT) , // output [15:0] sdram_wr_data_sig
.sdram_wr_req(sdram_wr_reqT) , // output sdram_wr_req_sig
.sdram_rd_req(sdram_rd_reqT) , // output sdram_rd_req_sig
.sdram_wr_dm(sdram_wr_dm),
.rd_done(sdram_test_done),
.error_flag(error_flag) //测试错误指示
);
//*******************安陆SDRAM例化************************//
sdr_as_ram #( .self_refresh_open(1'b1))
u2_ram(
.Sdr_clk(clk_100m),
.Sdr_clk_sft(clk_100m_75deg),
.Rst(!rst_n),
.Sdr_init_done(sdram_init_done),
.Sdr_init_ref_vld(sdram_init_ref_vld), //刷新有效
.Sdr_busy(),
.App_ref_req(1'b0),
.App_wr_en(sdram_wr_reqT),
.App_wr_addr(sdram_wr_addrT),
.App_wr_dm(4'd0),
.App_wr_din(sdram_dinT),
.App_rd_en(sdram_rd_reqT),
.App_rd_addr(sdram_rd_addrT),
.Sdr_rd_en (sdram_rd_en), //读有效
.Sdr_rd_dout(sdram_dout),
.SDRAM_CLK(SDRAM_CLK),
.SDR_RAS(SDR_RAS),
.SDR_CAS(SDR_CAS),
.SDR_WE(SDR_WE),
.SDR_BA(SDR_BA),
.SDR_ADDR(SDR_ADDR),
.SDR_DM(SDR_DM),
.SDR_DQ(SDR_DQ)
);
assign SDR_CKE=1'b1;
`ifndef SIMULATION
EG_PHY_SDRAM_2M_32 sdram(
.clk(SDRAM_CLK),
.ras_n(SDR_RAS),
.cas_n(SDR_CAS),
.we_n(SDR_WE),
.addr(SDR_ADDR[10:0]),
.ba(SDR_BA),
.dq(SDR_DQ),
.cs_n(1'b0),
.dm0(SDR_DM[0]),
.dm1(SDR_DM[1]),
.dm2(SDR_DM[2]),
.dm3(SDR_DM[3]),
.cke(1'b1)
);
`endif
endmodule
3. 功能代码
/************************/
//测试SDRAM读写
//按键触发读写,连续写多个地址,再连续读,判断读写数据是否一致
//页突发模式,突发长度1
/***********************/
//`define DATA_WIDTH 32
//`define ADDR_WIDTH 21
//`define DM_WIDTH 4
//`define ROW_WIDTH 11
//`define BA_WIDTH 2
`include "E:\yanghaizhu\work\12.program_exchange\TD\1 SDRAM_TEST\rtl\para.v"
module wrd_sdram_page (
input clk,
input rstn,
input sdram_init_done,
input sdram_init_ref_vld,
input [9:0] sdram_wr_burst, //写突发长度
input [9:0] sdram_rd_burst, //写突发长度
// input [23:0] wr_max_addr, //结束地址
// input [23:0] wr_min_addr, //起始地址
input sdram_test_en,
input [`DATA_WIDTH-1:0] sdram_rd_data, //读入的数据
input sdram_rd_en, //实际读SDRAM数据时的使能信号
output sdram_wr_req, //写请求
output [`ADDR_WIDTH-1:0] sdram_wr_addr, //写地址
output [`DATA_WIDTH-1:0] sdram_wr_data, //写数据
output sdram_rd_req, //读请求
output [`ADDR_WIDTH-1:0] sdram_rd_addr,
output [`DM_WIDTH-1:0]sdram_wr_dm,
output reg rd_done, //完成一次读写测试
output reg error_flag
);
assign sdram_wr_dm=4'b0;
//输入信号、数据寄存
reg [`DATA_WIDTH-1:0] rd_data;
reg sdr_rd_en;
always @(posedge clk or negedge rstn) begin
if(!rstn)
begin
rd_data<=0;
sdr_rd_en<=0;
end
else if (sdram_test_en)
begin
rd_data<=sdram_rd_data;
sdr_rd_en<=sdram_rd_en;
end
else
begin
rd_data<=0;
sdr_rd_en<=0;
end
end
//sdram写计数器
reg [15:0]wr_cnt;
always @(posedge clk or negedge rstn) begin
if(!rstn) wr_cnt<=0;
else if(sdram_test_en)
if(sdram_init_done &(~sdram_init_ref_vld) &(wr_cnt<=16'd1023))
wr_cnt<=wr_cnt+1'b1;
else wr_cnt<=wr_cnt;
else wr_cnt<=0;
end
//sdram写使能、写地址、写数据
reg wr_req;
reg [`ADDR_WIDTH-1:0]wr_addr,wr_addr_r;
reg [`DATA_WIDTH-1:0]wr_data;
assign sdram_wr_req=wr_req;
assign sdram_wr_addr=wr_addr_r;
assign sdram_wr_data=wr_data;
always @(posedge clk or negedge rstn) begin
if(!rstn)
begin
wr_req <= 0;
wr_addr <= 0;
wr_data <= 0;
end
else if(sdram_test_en)
if(sdram_init_done &(~sdram_init_ref_vld))
if(wr_cnt==16'd1024) wr_req <= 0;
else if (wr_cnt<16'd1024)
begin
wr_req <= 1;
wr_addr <= wr_addr + sdram_wr_burst;
wr_data <= wr_data + 1;
end
else
begin
wr_req <= wr_req;
wr_addr <= wr_addr;
wr_data <= wr_data;
end
else
begin
wr_req <= 0;
wr_addr <= wr_addr;
wr_data <= wr_data;
end
else
begin
wr_req <= 0;
wr_addr <= 0;
wr_data <= 0;
end
end
always @(posedge clk or negedge rstn) begin
if(!rstn) wr_addr_r<=0;
else wr_addr_r<=wr_addr;
end
//写完成标志
reg wr_done,wr_done_r;
always @(posedge clk or negedge rstn) begin
if(!rstn) wr_done<=0;
else if(sdram_test_en)
if (wr_data==16'd1024) wr_done<=1;
else wr_done<=wr_done;
else wr_done<=0;
end
always @(posedge clk or negedge rstn) begin
if(!rstn) wr_done_r<=0;
else wr_done_r<=wr_done;
end
//开始读等待时间
reg [15:0]wait_cnt;
wire rd_begin;
assign rd_begin=(wait_cnt==16'd100)?1'b1:1'b0;
always @(posedge clk or negedge rstn) begin
if(!rstn) wait_cnt<=0;
else if(sdram_test_en & wr_done_r)
if (wait_cnt<16'd100) wait_cnt<=wait_cnt + 1'b1;
else wait_cnt<=wait_cnt;
else wait_cnt<=0;
end
//读地址计数器
reg [15:0]rd_cnt;
always @(posedge clk or negedge rstn) begin
if(!rstn) rd_cnt<=0;
else if(sdram_test_en)
if(rd_begin & (rd_cnt<=16'd1023))
rd_cnt<=rd_cnt+1'b1;
else rd_cnt<=rd_cnt;
else rd_cnt<=0;
end
//读使能、读地址
reg rd_req;
reg [`ADDR_WIDTH-1:0]rd_addr,rd_addr_r;
assign sdram_rd_req=rd_req;
assign sdram_rd_addr=rd_addr_r;
always @(posedge clk or negedge rstn) begin
if(!rstn)
begin
rd_req <= 0;
rd_addr <= 0;
end
else if (sdram_test_en)
if(rd_begin & (~sdram_init_ref_vld))
if(rd_cnt==16'd1024)
begin
rd_req <= 0;
rd_addr <= rd_addr;
end
else if(rd_cnt<=16'd1023)
begin
rd_req <= 1;
rd_addr <= rd_addr + sdram_rd_burst;
end
else
begin
rd_req <= rd_req;
rd_addr <= rd_addr;
end
else
begin
rd_req <= 0;
rd_addr <= rd_addr;
end
else
begin
rd_req <= 0;
rd_addr <= 0;
end
end
always @(posedge clk or negedge rstn) begin
if(!rstn) rd_addr_r<=0;
else rd_addr_r<=rd_addr;
end
//读完成信号
always @(posedge clk or negedge rstn) begin
if(!rstn) rd_done<=0;
else if (sdram_test_en)
if(rd_cnt==16'd1024) rd_done<=1;
else rd_done<=rd_done;
else rd_done<=0;
end
//生成对比数据
reg [15:0] init_data;
always @(posedge clk or negedge rstn) begin
if(!rstn) init_data<=0;
else if(sdram_rd_en) init_data<=init_data+1'b1;
else init_data<=init_data;
end
//出现错误,报保持error_led一直亮,直到复位
always @(posedge clk or negedge rstn) begin
if(!rstn) error_flag<=0;
else if(sdr_rd_en)
if(init_data!=rd_data) error_flag<=1;
else error_flag<=error_flag;
end
endmodule
三. SDRAM+FIFO实现上述功能调试
1. 代码设计要点
1)FIFO例化;2)FIFO的读写时序;3)FIFO的满空标志的利用;4)FIFO与SDRAM之间的信号;
2. 仿真过程问题
1)代码出现如下bug:
原因是调用文件绝对路径或者相对路径格式, `ifdef的使用及中间连线wire的定义。
2)出现如下图所示的数据线高阻态,可能是数据在该输入的时候没有数据输入。
3)从FIFO读出,数据会比请求信号延迟2个时钟。因此输入到SDRAM的请求信号需要比读FIFO的请求信号延迟2个时钟。 从SDRAM读出的数据,Sdr_rd_dout会比Sdr_rd_en延迟一个时钟,因此写入FIFO的写请求信号应该是Sdr_rd_en延迟一个时钟后的。(与后面的上版调试结果不同,上板调试Sdr_rd_dout和Sdr_rd_en是同时给出的。)
正确FIFO读写过程时序如下图所示:
a)往FIFO中写入数据,以及从FIFO中读出。(读出信号未找到)
b)从SDRAM读出数据SDR_DQ,sdr_as_ram传输实际可看到的读使能sdram_rd_en和数据Sdr_rd_out。将数据打拍后写入到FIFO:rd_fifo_req,rd_fifo_data。时序图如下:
读到的部分数据显示错误是因为安陆提供的硬件仿真文件BUG。实际上板跑没有问题。
3. 上板运行调试
如下图所示,读到的结果与上面最终仿真出的结果时序有不一样,发生了一位错位。
代码是一样的,因此以实际板上跑的结果为准。
观察SDRAM的输入,如下图,输入是正确的
输出如下:可以看到sdram_rd_en和数据Sdr_rd_data是同时给出的。(这里与上面的仿真结果不同。)
因此,在仿真OK的情况下修改代码,将从SDRAM输出的sdram_rd_en和数据Sdr_rd_data都打一拍寄存输入给FIFO。上板调试结果OK,波形如下:
关于时钟限制:
1)100M,相位180°,运行OK;
2)50M,相位225°、270°,运行OK。相位180°,运行NG;
3)150M,相位135°,运行OK。相位180°、225°,运行NG;
4)200M,相位180°、225°,运行NG;
安陆反馈:
关于SDRAM输出的sdram_rd_en和数据Sdr_rd_data同时到还是数据延迟一个时钟到,安陆技术支持反馈说上板和用do文件仿真是同时到,用modelsim的phy仿真模型仿真是晚一个时钟到。
关于时序,技术支持反馈如果使用的IP核是 FIFO是硬核,不需要进行时序约束。如果使用的是RAMFIFO是软核,需要进行时序约束,好像是两个信号线的路径不一致。