简易sdram控制1
跟着开源骚客的教程写的sdram程序,前后调了大约5天,今天晚上能实现简单的读写数据了,如果以后用到摄像头,再在这个基础上加些功能吧。
整个程序框架就如图品所示,一共有五个模块还有初始化模块,图片上没有显示呢。
初始化模块
初始化模块 1上电 2 等待200us 3 给所有bank充电 4 八次自刷新 5 设置模式寄存器
初始化模块代码
/*----------------------------------------------------------------------- Date : 2017-08-29 Description : Design for sdram_init . -----------------------------------------------------------------------*/ module sdram_init ( //global clock input clk, //system clock input rst_n, //sync reset //sdram_init interface output reg [3:0] init_cmd, //sdram 命令寄存器 output reg [12:0] init_addr, //地址线 // output [1:0] init_bank, //user interface output reg flag_init_end //sdram初始化标志 ); //-------------------------------- //Funtion : 参数定义 parameter NOP = 4'b0111, PRECGE = 4'b0010, AUTO_REF = 4'b0001, MODE_SET = 4'b0000, CMD_END = 6'd35, DELAY_200US = 10000; //200us delay reg [13:0] cnt_200us; wire flag_200us; //初始化标志 reg flag_init; //cmd指令计数 reg [5:0] cnt_cmd; //-------------------------------- //Funtion : 200us always @(posedge clk or negedge rst_n) begin if(!rst_n) cnt_200us <= 1'd0; else if(flag_200us == 1'b0) cnt_200us <= cnt_200us + 1'b1; end assign flag_200us = (cnt_200us >= DELAY_200US - 1'b1) ? 1'b1 : 1'b0; //-------------------------------- //Funtion : sdram 初始化标志位是否结束 always @(posedge clk or negedge rst_n) begin if(!rst_n) flag_init <= 1'b1; else if(cnt_cmd == CMD_END) //auto refresh结束标志位 flag_init <= 1'b0; end //-------------------------------- //Funtion : cnt_cmd 200us延时后计数 always @(posedge clk or negedge rst_n) begin if(!rst_n) cnt_cmd <= 1'd0; else if(flag_200us == 1'b1 && flag_init == 1'b1) cnt_cmd <= cnt_cmd + 1'b1; end //-------------------------------- //Funtion : 初始化结束标志 always @(posedge clk or negedge rst_n) begin if(!rst_n) flag_init_end <= 1'b0; else if(cnt_cmd >= CMD_END) flag_init_end <= 1'b1; else flag_init_end <= 1'b0; end //-------------------------------- //Funtion : cmd_reg always @(posedge clk or negedge rst_n) begin if(!rst_n) init_cmd <= NOP; else if(cnt_200us == DELAY_200US - 2'd3) init_cmd <= PRECGE; else if(flag_200us) begin case(cnt_cmd) //8 auto refresh 6'd0 , 6'd6 , 6'd10 , 6'd14 , 6'd18 , 6'd22 , 6'd26 , 6'd30: init_cmd <= AUTO_REF; //mode set 6'd34 : init_cmd <= MODE_SET; default : init_cmd <= NOP; endcase end end //-------------------------------- //Funtion : sdram_addr always @(posedge clk or negedge rst_n) begin if(!rst_n) init_addr <= 13'd0; else if(cnt_200us == DELAY_200US - 2'd3) //预充电 init_addr <= 13'b0_0100_0000_0000; else if(cnt_cmd == 6'd34) //模式寄存器设置 init_addr <= 13'b0_0000_0011_0010; else init_addr <= 13'd0; end //-------------------------------- //Funtion : init_bank //assign init_bank = 2'd0; endmodule
write 模块
1、给一个激活命令同时选中行和列 2、然后给一个写命令 同事给定列地址,此时需要写第一个数据了 3、如果需要换行需要进行预充电指令
write模块代码
/*----------------------------------------------------------------------- Date : 2017-08-29 Description : Design for sdram write . -----------------------------------------------------------------------*/ module sdram_write ( //global clock input clk, //system clock input rst_n, //sync reset //sdram_write interface input wr_en, //写使能 output reg wr_req, //写请求 output reg flag_wr_end, //写结束 input wr_trig, //写触发 //sdram interface output reg [3:0] wr_cmd, output reg [12:0] wr_addr, //output [1:0] wr_bank, output reg [15:0] wr_dq, //afresh interface //input ref_req input [4:0] state ); //-------------------------------- //Funtion : 参数定义 /* localparam S_IDLE = 5'b0_0001; localparam S_REQ = 5'b0_0010; localparam S_ACT = 5'b0_0100; localparam S_WR = 5'b0_1000; localparam S_PRE = 5'b1_0000; reg flag_wr; reg [4:)] state; */ parameter NOP = 4'b0111, ACT = 4'b0011, WR = 4'b0100, PRE = 4'b0010, CMD_END = 4'd8, //指令delay COL_END = 10'd1020, //列地址最后四个地址 ROW_END = 13'd8191, //行地址结束 AREF = 5'b0_0100, WRITE = 5'b0_1000; reg flag_pre; //换行时需要预充电指令 reg [9:0] col_addr; reg flag_act; reg [12:0] row_addr; reg [12:0] row_addr_reg; reg [3:0] cmd_cnt; //-------------------------------- //Funtion : flag_pre always @(posedge clk or negedge rst_n) begin if(!rst_n) flag_pre <= 1'd0; else if(col_addr == 1'd0 && flag_wr_end ) flag_pre <= 1'd1; else if(flag_wr_end) flag_pre <= 1'd0; end //-------------------------------- //Funtion : flag_act 没刷新一次写一次数据 always @(posedge clk or negedge rst_n) begin if(!rst_n) flag_act <= 1'd0; else if(flag_wr_end) flag_act <= 1'd0; else if(state == AREF) flag_act <= 1'd1; end //-------------------------------- //Funtion : wr_req always @(posedge clk or negedge rst_n) begin if(!rst_n) wr_req <= 1'd0; else if(wr_en) wr_req <= 1'd0; else if(state != WRITE && wr_trig) wr_req <= 1'd1; end //-------------------------------- //Funtion : flag_wr_end always @(posedge clk or negedge rst_n) begin if(!rst_n) flag_wr_end <= 1'd0; else if(cmd_cnt == CMD_END) flag_wr_end <= 1'd1; else flag_wr_end <= 1'd0; end //-------------------------------- //Funtion : cmd_cnt always @(posedge clk or negedge rst_n) begin if(!rst_n) cmd_cnt <= 1'd0; else if(state == WRITE) cmd_cnt <= cmd_cnt + 1'b1; else cmd_cnt <= 1'd0; end //-------------------------------- //Funtion : wr_cmd always @(posedge clk or negedge rst_n) begin if(!rst_n) wr_cmd <= 1'd0; else case(cmd_cnt) //预充电 或者 nop 3'd1 : begin if(flag_pre) wr_cmd <= PRE; else wr_cmd <= NOP; end //act 3'd2 : begin if(flag_act || col_addr == 9'd0) wr_cmd <= ACT; else wr_cmd <= NOP; end //write 3'd3 : begin wr_cmd <= WR; end default : wr_cmd <= NOP; endcase end //-------------------------------- //Funtion : wr_dq always @(posedge clk or negedge rst_n) begin if(!rst_n) wr_dq <= 1'd0; else case(cmd_cnt) 3'd3 : wr_dq <= 16'd1000; 3'd4 : wr_dq <= 16'd2000; 3'd5 : wr_dq <= 16'd3000; 3'd6 : wr_dq <= 16'd4000; default : wr_dq <= 1'd0; endcase end //-------------------------------- //Funtion : row_addr_reg always @(posedge clk or negedge rst_n) begin if(!rst_n) row_addr_reg <= 1'd0; else if(row_addr_reg == ROW_END && col_addr == COL_END && flag_wr_end) row_addr_reg <= 1'd0; else if(col_addr == COL_END && flag_wr_end) row_addr_reg <= row_addr_reg + 1'b1; end //-------------------------------- //Funtion : row_addr ///--------------------------------------------------------?????? always @(posedge clk or negedge rst_n) begin if(!rst_n) row_addr <= 1'd0; else case(cmd_cnt) //prechage A10 3'd1 ,3'd2: // 测试 row_addr <= 1'd0; default : row_addr <= row_addr_reg; endcase end //-------------------------------- //Funtion : col_addr always @(posedge clk or negedge rst_n) begin if(!rst_n) col_addr <= 1'd0; else if(col_addr == COL_END && flag_wr_end) col_addr <= 1'd0; else if(cmd_cnt == CMD_END) col_addr <= col_addr + 3'd4; end //-------------------------------- //Funtion : wr_addr always @(posedge clk or negedge rst_n) begin if(!rst_n) wr_addr <= 1'd0; else case(cmd_cnt) // 3'd2 : // wr_addr <= row_addr; 3'd3 : wr_addr <= {3'd0 , col_addr}; default : wr_addr <= row_addr; endcase end //-------------------------------- //Funtion : wr_bank //assign wr_bank = 2'd0; endmodule
3、read模块
read时序图跟write差不多,不同的时read数据出来的时候,需要等待几个周期才可以,这个时间由模式寄存器确定
/*----------------------------------------------------------------------- Date : 2017-08-30 Description : Design for sdram_read . -----------------------------------------------------------------------*/ module sdram_read ( //global clock input clk, //system clock input rst_n, //sync reset //read interface output reg rd_req, //读请求 output reg flag_rd_end, //读结束标志 input rd_wrig, input rd_en, //sdram interface output reg [3:0] rd_cmd, output reg [12:0] rd_addr, // output [1:0] rd_bank, input [15:0] sdram_dq, output [15:0] rd_dq, // input [4:0] state ); //-------------------------------- //Funtion : 参数化定义 parameter NOP = 4'b0111, PRE = 4'b0010, ACT = 4'b0011, RD = 4'b0101, CMD_END = 4'd12, COL_END = 10'd1020, ROW_END = 13'd8191, AREF = 5'b0_0100, READ = 5'b1_0000; reg [12:0] row_addr; reg [9:0] col_addr; reg [3:0] cmd_cnt; reg flag_act; //-------------------------------- //Funtion : flag_act 每刷新一次读一次数据 always @(posedge clk or negedge rst_n) begin if(!rst_n) flag_act <= 1'd0; else if(flag_rd_end) flag_act <= 1'd0; else if(state == AREF) flag_act <= 1'd1; end //-------------------------------- //Funtion : rd_req always @(posedge clk or negedge rst_n) begin if(!rst_n) rd_req <= 1'b0; else if(rd_en) rd_req <= 1'b0; else if(rd_wrig && state != READ) rd_req <= 1'b1; end //-------------------------------- //Funtion : cmd_cnt always @(posedge clk or negedge rst_n) begin if(!rst_n) cmd_cnt <= 1'd0; else if(state == READ) cmd_cnt <= cmd_cnt + 1'b1; else cmd_cnt <= 1'd0; end //-------------------------------- //Funtion : flag_rd_end always @(posedge clk or negedge rst_n) begin if(!rst_n) flag_rd_end <= 1'd0; else if(cmd_cnt == CMD_END) flag_rd_end <= 1'd1; else flag_rd_end <= 1'd0; end //-------------------------------- //Funtion : row_addr always @(posedge clk or negedge rst_n) begin if(!rst_n) row_addr <= 1'd0; else if(row_addr == ROW_END && col_addr == COL_END && flag_rd_end) row_addr <= 1'd0; else if(col_addr == COL_END && flag_rd_end) row_addr <= row_addr + 1'b1; end //-------------------------------- //Funtion : col_addr always @(posedge clk or negedge rst_n) begin if(!rst_n) col_addr <= 1'd0; else if(col_addr == COL_END && flag_rd_end) col_addr <= 1'd0; else if(flag_rd_end) col_addr <= col_addr + 3'd4; end //-------------------------------- //Funtion : rd_cmd always @(posedge clk or negedge rst_n) begin if(!rst_n) rd_cmd <= NOP; else case(cmd_cnt) 4'd1 : if(col_addr == 1'd0) rd_cmd <= PRE; else rd_cmd <= NOP; 4'd2 : if(flag_act || col_addr == 1'd0) // rd_cmd <= NOP; //测试 rd_cmd <= ACT; else rd_cmd <= NOP; 4'd3 : rd_cmd <= RD; default : rd_cmd <= NOP; endcase end //-------------------------------- //Funtion : rd_addr always @(posedge clk or negedge rst_n) begin if(!rst_n) rd_addr <= 1'd0; else case(cmd_cnt) 4'd3 : rd_addr <= {3'd0 , col_addr}; default : rd_addr <= row_addr; endcase end //-------------------------------- //Funtion : rd_bank //assign rd_bank = 2'd0; assign rd_dq = sdram_dq; endmodule
待续。。。。。。。