简易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
    

待续。。。。。。。

原文地址:https://www.cnblogs.com/bixiaopengblog/p/7460415.html