【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
用fpga做图像的时候,如果图像的输入速度和显示速度不匹配,这个时候就需要把图像先放到ddr里面。因为fpga里面毕竟还是空间太小,把图像全部缓存到fpga里面是不现实的。但是提到ddr很多人都会担心学不会,一来ddr pin比较多,布局布线本身就很麻烦了,而且ddr还分成ddr1、ddr2等很多种类型,很担心自己学不会是正常的。但这也是没有必要的。
1、ddr类型多
ddr的类型确实很多,从ddr1到ddr5,类型确实不少。但是除了ddr1可以用状态机的方法去进行读取之外,其他ddr2以上的类型,一般都是用fpga厂商提供的ip,直接数据读取。也就是说,我们只要会用这些ip就可以操作ddr了。
此外,不管是哪一代的ddr,一般就是分成数据总线、地址总线和控制总线三部分。只不过由于速度越来越快,很多总线变成了差分结构,电压也是越来越低,电路上的布局布线也是越来越难,但对于使用者来说,最重要的还是怎么用起来,至于底层的原理,除了ddr1可以好好看看之外,其他的ddr只要会使用即可。
2、ddr1的读写
目前市面上大部分fpga对ddr都是基于第一代ddr进行操作的。这一代的ddr sdram封装简单,速率不高,所以用状态机的方法对它进行操作是完全可以的。ddr1 sdram也是分成数据总线、地址总线、控制总线三部分。其中数据总线和地址总线是复用的。实际使用的时候一般先发行信号,再发列信号。
3、ddr最特殊的地方
要说初始化、读、写,这些eeprom、nor flash、nand flash、tf卡都会涉及到。也就是说,我们要对数据进行读写,那么先要初始化,然后进行读写操作。但是ddr和它们不一致的地方,那就是需要对ddr进行定时刷新处理。这就意味着初始化之后处于idle状态的时候,还要定时进行ddr刷新的操作,并且刷洗的优先级是最高的,这是其他存储芯片所没有的。
4、zynq访问ddr的不同
在zynq芯片里面访问ddr有两种方式。一种是类似于7010、7020这种。即ps侧访问ddr,pl侧这边没有ddr。pl这边如果需要访问ddr的数据,基本上只有通过dma的方式去实现。第二种,就是7035或者是更好的soc,这一类的soc里面,pl侧是可以有自己的ddr,也就是说ps和pl都有自己的ddr芯片,好处是两者都可以有自己的数据访问区域,缺点就是价格比较贵。
5、特殊的inout信号
之前我们学习uart、pwm、vga和spi的时候,对于外部接口要么是输入,要么是输出,很少是有inout信号的。本质上inout就是wire,它是一种特殊接口,相当于我们可以通过这个接口发送数据,也可以通过这个接口接收数据。iic、sdio里面都有这样的信号。当然除了接口可以这么用之外,fpga内部是不允许inout信号存在的。使用的时候一般是这么用的,
module sdram_top(input clk,
input rst,
input oe, // 1-write, 0-read
input data,
output in,
inout io);
assign io = (oe) ? data : 1'bz;
assign in = io;
endmodule
6、先把ddr用起来
ddr本身布局布线比较麻烦,有很多要求。所以我们学习fpga的时候,可以先学习别人的板子,学习别人的verilog思路,等到自己有需求的时候,再去自己进行硬件电路设计。当然ddr1还是比较简单的,很多ddr1还是sop封装,这种不是bga封装,用起来比其他ddr容易很多,布局布线稍加注意即可。毕竟很多的学习板也是ddr1。
7、ddr1 sdram的读写代码
这部分代码很多,某火、某原子、某linx,都可以看到类似的verilog代码。除了代码,还可以在视频网站上看到对应的视频讲解。要说verilog最简单的,还是某linx,基本上不到400行就讲解清楚了。里面的实现主要就是状态机+延时操作,初始化好了之后,在idle状态下按需进入read、write、refresh的状态。状态机准备好了,就是进行输出、输入操作即可。原来的代码地址在这,
https://github.com/alinxalinx/AX301/blob/master/src/13_sdram_test/src/sdram/sdram_core.v
这里方便大家学习,粘贴一下。不同的命令、不同的输出方式、不同的延时条件,基本也是照着芯片手册去写的。
`timescale 1ns / 1ps
module sdram_core
#
(
parameter T_RP = 4,
parameter T_RC = 6,
parameter T_MRD = 6,
parameter T_RCD = 2,
parameter T_WR = 3,
parameter CASn = 3,
parameter SDR_BA_WIDTH = 2,
parameter SDR_ROW_WIDTH = 13,
parameter SDR_COL_WIDTH = 9,
parameter SDR_DQ_WIDTH = 16,
parameter SDR_DQM_WIDTH = SDR_DQ_WIDTH/8,
parameter APP_ADDR_WIDTH = SDR_BA_WIDTH + SDR_ROW_WIDTH + SDR_COL_WIDTH,
parameter APP_BURST_WIDTH = 9
)
(
input clk,
input rst, //reset signal,high for reset
//write
input wr_burst_req, // write request
input[SDR_DQ_WIDTH-1:0] wr_burst_data, // write data
input[APP_BURST_WIDTH-1:0] wr_burst_len, // write data length, ahead of wr_burst_req
input[APP_ADDR_WIDTH-1:0] wr_burst_addr, // write base address of sdram write buffer
output wr_burst_data_req, // wrtie data request, 1 clock ahead
output wr_burst_finish, // write data is end
//read
input rd_burst_req, // read request
input[APP_BURST_WIDTH-1:0] rd_burst_len, // read data length, ahead of rd_burst_req
input[APP_ADDR_WIDTH-1:0] rd_burst_addr, // read base address of sdram read buffer
output[SDR_DQ_WIDTH-1:0] rd_burst_data, // read data to internal
output rd_burst_data_valid, // read data enable (valid)
output rd_burst_finish, // read data is end
//sdram
output sdram_cke, //clock enable
output sdram_cs_n, //chip select
output sdram_ras_n, //row select
output sdram_cas_n, //colum select
output sdram_we_n, //write enable
output[SDR_BA_WIDTH-1:0] sdram_ba, //bank address
output[SDR_ROW_WIDTH-1:0] sdram_addr, //address
output[SDR_DQM_WIDTH-1:0] sdram_dqm, //data mask
inout[SDR_DQ_WIDTH-1: 0] sdram_dq //data
);
// State machine code
localparam S_INIT_NOP = 5'd0; //Wait for the power on stable 200us end
localparam S_INIT_PRE = 5'd1; //Precharge state
localparam S_INIT_TRP = 5'd2; //Wait for precharge to complete
localparam S_INIT_AR1 = 5'd3; //First self refresh
localparam S_INIT_TRF1 = 5'd4; //Wait for the first time since end refresh
localparam S_INIT_AR2 = 5'd5; //Second self refresh
localparam S_INIT_TRF2 = 5'd6; //Wait for the second time since end refresh
localparam S_INIT_MRS = 5'd7; //Mode register set
localparam S_INIT_TMRD = 5'd8; //Wait for the mode register set complete
localparam S_INIT_DONE = 5'd9; //The initialization is done
localparam S_IDLE = 5'd10; //Idle state
localparam S_ACTIVE = 5'd11; //Row activation, read and write
localparam S_TRCD = 5'd12; //Row activation wait
localparam S_READ = 5'd13; //Read data state
localparam S_CL = 5'd14; //Wait for latency
localparam S_RD = 5'd15; //Read data
localparam S_RWAIT = 5'd16; //Precharge wait state after read completion
localparam S_WRITE = 5'd17; //Write data state
localparam S_WD = 5'd18; //Write data
localparam S_TDAL = 5'd19; //Wait for the write data and the self refresh end
localparam S_AR = 5'd20; //Self-Refresh
localparam S_TRFC = 5'd21; //Wait for the self refresh
reg read_flag;
wire done_200us; //After power on, the 200us input is stable at the end of the flag bit
reg sdram_ref_req; // SDRAM self refresh request signal
wire sdram_ref_ack; // SDRAM self refresh request response signal
reg[SDR_BA_WIDTH-1:0] sdram_ba_r;
reg[SDR_ROW_WIDTH-1:0] sdram_addr_r;
reg ras_n_r;
reg cas_n_r;
reg we_n_r;
wire[APP_ADDR_WIDTH-1:0] sys_addr;
reg[14:0] cnt_200us;
reg[10:0] cnt_7p5us;
reg[SDR_DQ_WIDTH-1:0] sdr_dq_out;
reg[SDR_DQ_WIDTH-1:0] sdr_dq_in;
reg sdr_dq_oe;
reg[8:0] cnt_clk_r; //Clock count
reg cnt_rst_n; //Clock count reset signal
reg[4:0] state;
reg wr_burst_data_req_d0;
reg wr_burst_data_req_d1;
reg rd_burst_data_valid_d0;
reg rd_burst_data_valid_d1;
wire end_trp = (cnt_clk_r == T_RP) ? 1'b1 : 1'b0;
wire end_trfc = (cnt_clk_r == T_RC) ? 1'b1 : 1'b0;
wire end_tmrd = (cnt_clk_r == T_MRD) ? 1'b1 : 1'b0;
wire end_trcd = (cnt_clk_r == T_RCD-1) ? 1'b1 : 1'b0;
wire end_tcl = (cnt_clk_r == CASn-1) ? 1'b1 : 1'b0;
wire end_rdburst = (cnt_clk_r == rd_burst_len-4) ? 1'b1 : 1'b0;
wire end_tread = (cnt_clk_r == rd_burst_len+2) ? 1'b1 : 1'b0;
wire end_wrburst = (cnt_clk_r == wr_burst_len-1) ? 1'b1 : 1'b0;
wire end_twrite = (cnt_clk_r == wr_burst_len-1) ? 1'b1 : 1'b0;
wire end_tdal = (cnt_clk_r == T_WR) ? 1'b1 : 1'b0;
wire end_trwait = (cnt_clk_r == T_RP) ? 1'b1 : 1'b0;
always@(posedge clk or posedge rst)
begin
if(rst == 1'b1)
begin
wr_burst_data_req_d0 <= 1'b0;
wr_burst_data_req_d1 <= 1'b0;
rd_burst_data_valid_d0 <= 1'b0;
rd_burst_data_valid_d1 <= 1'b0;
end
else
begin
wr_burst_data_req_d0 <= wr_burst_data_req;
wr_burst_data_req_d1 <= wr_burst_data_req_d0;
rd_burst_data_valid_d0 <= rd_burst_data_valid;
rd_burst_data_valid_d1 <= rd_burst_data_valid_d0;
end
end
assign wr_burst_finish = ~wr_burst_data_req_d0 & wr_burst_data_req_d1;
assign rd_burst_finish = ~rd_burst_data_valid_d0 & rd_burst_data_valid_d1;
assign rd_burst_data = sdr_dq_in;
assign sdram_dqm = {SDR_DQM_WIDTH{1'b0}};
assign sdram_dq = sdr_dq_oe ? sdr_dq_out : {SDR_DQ_WIDTH{1'bz}};
assign sdram_cke = 1'b1;
assign sdram_cs_n = 1'b0;
assign sdram_ba = sdram_ba_r;
assign sdram_addr = sdram_addr_r;
assign {sdram_ras_n,sdram_cas_n,sdram_we_n} = {ras_n_r,cas_n_r,we_n_r};
assign sys_addr = read_flag ? rd_burst_addr:wr_burst_addr; //Read / write address bus switching control
// power on 200us time, done_200us=1
always@(posedge clk or posedge rst)
begin
if(rst == 1'b1)
cnt_200us <= 15'd0;
else if(cnt_200us < 15'd20_000)
cnt_200us <= cnt_200us + 1'b1;
end
assign done_200us = (cnt_200us == 15'd20_000);
//------------------------------------------------------------------------------
//7.5uS timer, every 8192 rows of 64ms storage for a Auto refresh
//------------------------------------------------------------------------------
always@(posedge clk or posedge rst)
begin
if(rst == 1'b1)
cnt_7p5us <= 11'd0;
else if(cnt_7p5us < 11'd750)
cnt_7p5us <= cnt_7p5us+1'b1;
else
cnt_7p5us <= 11'd0;
end
always@(posedge clk or posedge rst)
begin
if(rst == 1'b1)
sdram_ref_req <= 1'b0;
else if(cnt_7p5us == 11'd749)
sdram_ref_req <= 1'b1;
else if(sdram_ref_ack)
sdram_ref_req <= 1'b0;
end
//SDRAM state machine
always@(posedge clk or posedge rst)
begin
if(rst == 1'b1)
state <= S_INIT_NOP;
else
begin
case (state)
S_INIT_NOP:
state <= done_200us ? S_INIT_PRE : S_INIT_NOP; //After the end of the 200us / reset into the next state
S_INIT_PRE:
state <= S_INIT_TRP; //Precharge state
S_INIT_TRP:
state <= (end_trp) ? S_INIT_AR1 : S_INIT_TRP; //Precharge, waits for T_RP clock cycles
S_INIT_AR1:
state <= S_INIT_TRF1; //First self refresh
S_INIT_TRF1:
state <= (end_trfc) ? S_INIT_AR2 : S_INIT_TRF1; //Wait for first self refresh end, T_RC clock cycles
S_INIT_AR2:
state <= S_INIT_TRF2; //Second self refresh
S_INIT_TRF2:
state <= (end_trfc) ? S_INIT_MRS : S_INIT_TRF2; //Wait for second self refresh end T_RC clock cycles
S_INIT_MRS:
state <= S_INIT_TMRD;//Mode register set(MRS)
S_INIT_TMRD:
state <= (end_tmrd) ? S_INIT_DONE : S_INIT_TMRD; //wait mode register setting is complete with T_MRD clock cycles
S_INIT_DONE:
state <= S_IDLE; // SDRAM initialization setting complete flag
S_IDLE:
if(sdram_ref_req)
begin
state <= S_AR; //The timing of self refresh request
read_flag <= 1'b1;
end
else if(wr_burst_req)
begin
state <= S_ACTIVE; //write SDRAM
read_flag <= 1'b0;
end
else if(rd_burst_req)
begin
state <= S_ACTIVE; //read SDRAM
read_flag <= 1'b1;
end
else
begin
state <= S_IDLE;
read_flag <= 1'b1;
end
//row active
S_ACTIVE:
if(T_RCD == 0)
if(read_flag) state <= S_READ;
else state <= S_WRITE;
else state <= S_TRCD;
//row active wait
S_TRCD:
if(end_trcd)
if(read_flag) state <= S_READ;
else state <= S_WRITE;
else state <= S_TRCD;
//read data
S_READ:
state <= S_CL;
//read data wait
S_CL:
state <= (end_tcl) ? S_RD : S_CL;
//read data
S_RD:
state <= (end_tread) ? S_IDLE : S_RD;
//Precharge wait state after read completion
S_RWAIT:
state <= (end_trwait) ? S_IDLE : S_RWAIT;
//Write data state
S_WRITE:
state <= S_WD;
//write data
S_WD:
state <= (end_twrite) ? S_TDAL : S_WD;
//Wait for writing data and ending with self refresh
S_TDAL:
state <= (end_tdal) ? S_IDLE : S_TDAL;
//Self-Refresh
S_AR:
state <= S_TRFC;
//Self-Refresh wait
S_TRFC:
state <= (end_trfc) ? S_IDLE : S_TRFC;
default:
state <= S_INIT_NOP;
endcase
end
end
assign sdram_ref_ack = (state == S_AR);// SDRAM self refresh response signal
//1 clock to write ahead
assign wr_burst_data_req = ((state == S_TRCD) & ~read_flag) | (state == S_WRITE)|((state == S_WD) & (cnt_clk_r < wr_burst_len - 2'd2));
//Read the SDRAM response signal
assign rd_burst_data_valid = (state == S_RD) & (cnt_clk_r >= 9'd1) & (cnt_clk_r < rd_burst_len + 2'd1);
//Time delay for generating SDRAM sequential operation
always@(posedge clk or posedge rst)
begin
if(rst == 1'b1)
cnt_clk_r <= 9'd0;
else if(!cnt_rst_n)
cnt_clk_r <= 9'd0;
else
cnt_clk_r <= cnt_clk_r+1'b1;
end
//Counter control logic
always@(*)
begin
case (state)
S_INIT_NOP: cnt_rst_n <= 1'b0;
S_INIT_PRE: cnt_rst_n <= 1'b1; //Precharge delay count start
S_INIT_TRP: cnt_rst_n <= (end_trp) ? 1'b0:1'b1; //Wait until the precharge delay count is over and the counter is cleared
S_INIT_AR1,S_INIT_AR2:cnt_rst_n <= 1'b1; //Self refresh count start
S_INIT_TRF1,S_INIT_TRF2:cnt_rst_n <= (end_trfc) ? 1'b0:1'b1; //Wait until the refresh count is finished, and the counter is cleared
S_INIT_MRS: cnt_rst_n <= 1'b1; //Mode register setting, time counting start
S_INIT_TMRD: cnt_rst_n <= (end_tmrd) ? 1'b0:1'b1; //Wait until the refresh count is finished, and the counter is cleared
S_IDLE: cnt_rst_n <= 1'b0;
S_ACTIVE: cnt_rst_n <= 1'b1;
S_TRCD: cnt_rst_n <= (end_trcd) ? 1'b0:1'b1;
S_CL: cnt_rst_n <= (end_tcl) ? 1'b0:1'b1;
S_RD: cnt_rst_n <= (end_tread) ? 1'b0:1'b1;
S_RWAIT: cnt_rst_n <= (end_trwait) ? 1'b0:1'b1;
S_WD: cnt_rst_n <= (end_twrite) ? 1'b0:1'b1;
S_TDAL: cnt_rst_n <= (end_tdal) ? 1'b0:1'b1;
S_TRFC: cnt_rst_n <= (end_trfc) ? 1'b0:1'b1;
default: cnt_rst_n <= 1'b0;
endcase
end
always@(posedge clk or posedge rst)
begin
if(rst == 1'b1)
sdr_dq_out <= 16'd0;
else if((state == S_WRITE) | (state == S_WD))
sdr_dq_out <= wr_burst_data;
end
//Bidirectional data directional control logic
always@(posedge clk or posedge rst)
begin
if(rst == 1'b1)
sdr_dq_oe <= 1'b0;
else if((state == S_WRITE) | (state == S_WD))
sdr_dq_oe <= 1'b1;
else
sdr_dq_oe <= 1'b0;
end
//Reads data from the SDRAM
always@(posedge clk or posedge rst)
begin
if(rst == 1'b1)
sdr_dq_in <= 16'd0;
else if(state == S_RD)
sdr_dq_in <= sdram_dq;
end
always@(posedge clk or posedge rst)
begin
if(rst == 1'b1)
begin
{ras_n_r,cas_n_r,we_n_r} <= 3'b111;
sdram_ba_r <= {SDR_BA_WIDTH{1'b1}};
sdram_addr_r <= {SDR_ROW_WIDTH{1'b1}};
end
else
case(state)
S_INIT_NOP,S_INIT_TRP,S_INIT_TRF1,S_INIT_TRF2,S_INIT_TMRD:
begin
{ras_n_r,cas_n_r,we_n_r} <= 3'b111;
sdram_ba_r <= {SDR_BA_WIDTH{1'b1}};
sdram_addr_r <= {SDR_ROW_WIDTH{1'b1}};
end
S_INIT_PRE:
begin
{ras_n_r,cas_n_r,we_n_r} <= 3'b010;
sdram_ba_r <= {SDR_BA_WIDTH{1'b1}};
sdram_addr_r <= {SDR_ROW_WIDTH{1'b1}};
end
S_INIT_AR1,S_INIT_AR2:
begin
{ras_n_r,cas_n_r,we_n_r} <= 3'b001;
sdram_ba_r <= {SDR_BA_WIDTH{1'b1}};
sdram_addr_r <= {SDR_ROW_WIDTH{1'b1}};
end
S_INIT_MRS:
begin //Mode register setting, which can be set according to actual needs
{ras_n_r,cas_n_r,we_n_r} <= 3'b000;
sdram_ba_r <= {SDR_BA_WIDTH{1'b0}};
sdram_addr_r <= {
3'b000,
1'b0, //Operation mode setting (set here to A9=0, ie burst read / burst write)
2'b00, //Operation mode setting ({A8, A7}=00), the current operation is set for mode register
3'b011, //CAS latency setting
1'b0, //Burst mode
3'b111 //Burst length ,full page
};
end
S_IDLE,S_TRCD,S_CL,S_TRFC,S_TDAL:
begin
{ras_n_r,cas_n_r,we_n_r} <= 3'b111;
sdram_ba_r <= {SDR_BA_WIDTH{1'b1}};
sdram_addr_r <= {SDR_ROW_WIDTH{1'b1}};
end
S_ACTIVE:
begin
{ras_n_r,cas_n_r,we_n_r} <= 3'b011;
sdram_ba_r <= sys_addr[APP_ADDR_WIDTH - 1:APP_ADDR_WIDTH - SDR_BA_WIDTH];
sdram_addr_r <= sys_addr[SDR_COL_WIDTH + SDR_ROW_WIDTH - 1:SDR_COL_WIDTH];
end
S_READ:
begin
{ras_n_r,cas_n_r,we_n_r} <= 3'b101;
sdram_ba_r <= sys_addr[APP_ADDR_WIDTH - 1:APP_ADDR_WIDTH - SDR_BA_WIDTH];
sdram_addr_r <= {4'b0010,sys_addr[8:0]};//Column address A10=1, set write enable, allow precharge
end
S_RD:
begin
if(end_rdburst)
{ras_n_r,cas_n_r,we_n_r} <= 3'b110;
else begin
{ras_n_r,cas_n_r,we_n_r} <= 3'b111;
sdram_ba_r <= {SDR_BA_WIDTH{1'b1}};
sdram_addr_r <= {SDR_ROW_WIDTH{1'b1}};
end
end
S_WRITE:
begin
{ras_n_r,cas_n_r,we_n_r} <= 3'b100;
sdram_ba_r <= sys_addr[APP_ADDR_WIDTH - 1:APP_ADDR_WIDTH - SDR_BA_WIDTH];
sdram_addr_r <= {4'b0010,sys_addr[8:0]};//Column address A10=1, set write enable, allow precharge
end
S_WD:
begin
if(end_wrburst) {ras_n_r,cas_n_r,we_n_r} <= 3'b110;
else begin
{ras_n_r,cas_n_r,we_n_r} <= 3'b111;
sdram_ba_r <= {SDR_BA_WIDTH{1'b1}};
sdram_addr_r <= {SDR_ROW_WIDTH{1'b1}};
end
end
S_AR:
begin
{ras_n_r,cas_n_r,we_n_r} <= 3'b001;
sdram_ba_r <= {SDR_BA_WIDTH{1'b1}};
sdram_addr_r <= {SDR_ROW_WIDTH{1'b1}};
end
default:
begin
{ras_n_r,cas_n_r,we_n_r} <= 3'b111;
sdram_ba_r <= {SDR_BA_WIDTH{1'b1}};
sdram_addr_r <= {SDR_ROW_WIDTH{1'b1}};
end
endcase
end
endmodule