99在线精品免费视频九九视-99在线精品视频-99在线精品视频免费观里-99在线精品视频在线观看-99在线免费播放



官方論壇
官方淘寶
官方博客
微信公眾號
點擊聯系吳工 點擊聯系周老師
您的當前位置:主頁 > 高級實訓案例 >

【至簡設計案例系列】基于FPGA的SDRAM控制器設計(三)讀寫

發布時間:2020-03-18   作者:admin 瀏覽量:

本文為明德揚原創及錄用文章,轉載請注明出處!


SDRAM控制器設計的主要功能是能對SDRAM進行讀寫操作,本工程實現了SDRAM的初始化、自動刷新、讀、寫等功能。


初始化功能和刷新功能在前一章的分享中已經進行了比較詳細的描述,感興趣的同學可以搜索學習下,這里不再贅述。今天我們主要討論SDRAM讀寫的功能以及實現。


一、原理功能


1、讀寫突發模式



在初始化里的模式寄存器配置中我們將讀寫的突發長度(BL)設置為4,列選通潛伏期(CL)設為3,突發類型為順序模式(每一次突發只給出起始地址即可)。對于預充電選擇(Auto Precharge)自動預充電,即外部不需要發送預充電命令


2、讀、寫時序圖


null

                                                圖表1寫時序圖

null

                                                      圖表2讀時序圖

3、時序圖解讀

由時序圖可知,讀寫模塊采用線性序列機設計比較簡單。在每次讀寫突發之前都需要發送ACTIVE命令,同時給出bank地址和row地址,2拍后發出讀或寫命令,同時將A10拉高并給出col地址。對于寫操作沒有潛伏期,由于DQ是雙向端口,所以需要一個三態門控制DQ總線方向,當寫操作時為輸出方向,讀操作時為輸入方向。對于讀操作,發出讀命令后會有CL拍的潛伏期,本實驗里CL=3,即發出讀命令后3拍,數據才會出現在DQ總線上。


二、FPGA實現


1、模塊架構


null

注:信號方向請看箭頭


2、模塊架構解讀


要完整實現SDRAM控制器必須要完成初始化,刷新,讀寫這四部分功能。所以模塊劃分大體依照次為指導。由于SDRAM控制器工作時鐘為100MHz,且要輸出一個頻率相同相位相差180°的時鐘給SDRAM,所以要有一個鎖相環模塊。刷新需要計時刷新間隔,所以要加入一個刷新定時器模塊,由于初始化,刷新,讀,寫等模塊都要輸出sdr_cke,sdr_cs_n,sdr_cas_n,sdr_ras_n,sdr_we_n,sdr_ba,sdr_a等信號到SDRAM,所以需要一個選擇模塊。將sdr_cke,sdr_cs_n,sdr_cas_n,sdr_ras_n,sdr_we_n,sdr_ba,sdr_a等信號組合成bus信號輸入到選擇模塊。


新加入的信號說明,其他信號在前兩篇中已經詳細說明,讀者可以參考。


信號

功能

說明

頂層



local_data

寫突發數據輸入

外部輸入

local_addr

地址總線

外部輸入

local_q

讀突發數據輸出

輸出

local_wrreq

寫請求

外部輸入

local_rdreq

讀請求

外部輸入

local_reday

可以進行讀寫操作信號

輸出

local_rdata_vaild

輸出數據有效信號

輸出

寫模塊



wr_en

寫使能

仲裁模塊輸出到寫模塊

wr_done

寫完成

寫模塊輸出到仲裁模塊

wr_bus

寫操作總線

寫模塊輸出到選擇模塊

sdr_dq

SDRAM數據總線

雙向端口,對于寫模塊是輸出,讀模塊是輸入

讀模塊



rd_bus

讀操作總線

讀模塊輸出到選擇模塊

rd_en

讀使能

仲裁模塊輸出到讀模塊

rd_done

讀完成

讀模塊輸出到仲裁模塊


新加入了寫模塊和讀模塊。


寫模塊主要是完成一次寫突發操作,將local_data信號寫入到SDRAM中的指定地址local_addr中。local_addr主要由bank地址,行地址和列地址組合而成。在讀和寫模塊代碼中均有體現,可以參考。


讀模塊主要完成一次讀突發操作,將SDRAM中指定的地址中的數據讀出來,并賦值給local_q信號。


由于加入了寫模塊和讀模塊,所以仲裁模塊也要做出相應的修改,當收到刷新請求時,即rt_flag信號后,在結束本次讀突發或者寫突發后,拉高刷新使能(ref_en)信號,當收到寫請求且沒有刷新請求時,拉高寫使能(wr_en)信號,當收到讀請求且沒有刷新請求和寫請求時,拉高讀使能(rd_en)信號。


3、頂層模塊參考代碼


module sdram_top(

   clk   ,

   sys_rst_n ,

   //其他信號,舉例dout

   local_addr,

   local_data,

   local_q,

   local_rdreq,

   local_wrreq,

   local_ready,

   local_rdata_valid,

   init_done,

   sdr_cke,

   sdr_cs_n,

   sdr_ras_n,

   sdr_cas_n,

   sdr_we_n,

   sdr_ba,

   sdr_a,

   sdr_dq,

   sdr_dqm,

   sdr_clk

   );

   

   input clk;

   input sys_rst_n;

   input [24:0] local_addr;

   input [63:0] local_data;

   output [63:0] local_q;

   input local_rdreq;

   input local_wrreq;

   output local_ready;

   output local_rdata_valid;

   output init_done;

   output sdr_cke;

   output sdr_cs_n;

   output sdr_ras_n;

   output sdr_cas_n;

   output sdr_we_n;

   output [1:0] sdr_ba;

   output [12:0] sdr_a;

   inout [15:0] sdr_dq;

   output [1:0] sdr_dqm;

   output sdr_clk;

   

   wire phy_clk;

   wire rst_n;

   wire rt_flag;

   wire rt_clear;

   wire rt_en;

   wire ref_en;

   wire ref_done;

   wire wr_done;

   wire wr_en;

   wire rd_en;

   wire rd_done;

   wire [1:0] sel_sm;

   wire [19:0] sdr_bus;

   wire [19:0] init_bus;

   wire [19:0] ref_bus;

   wire [19:0] wr_bus;

   wire [19:0] rd_bus;

   

   assign {sdr_cke, sdr_cs_n, sdr_ras_n, sdr_cas_n, sdr_we_n, sdr_ba, sdr_a} = sdr_bus;

   assign sdr_dqm = 2'b00;

       

   sdram_init sdram_init_inst(

       .clk           (phy_clk)       ,

       .rst_n         (rst_n)       ,

       //其他信號,舉例dout

       .init_done     (init_done)       ,

       .init_bus      (init_bus)

   );

   

   arbitrate arbitrate_inst(

       .clk(phy_clk),

       .rst_n(rst_n),

       .rt_flag(rt_flag),

       .rt_en(rt_en),

       .rt_clear(rt_clear),

       .ref_done(ref_done),

       .ref_en(ref_en),

       .init_done(init_done),

       .sel_sm(sel_sm),

      .local_rdata_valid(local_rdata_valid),

       .local_ready(local_ready),

       .local_wrreq(local_wrreq),

       .local_rdreq(local_rdreq),

       .wr_done(wr_done),

       .wr_en(wr_en),

       .rd_en(rd_en),

       .rd_done(rd_done)

   );

   

   ref_timer ref_timer_inst(

       .clk(phy_clk),

       .rst_n(rst_n),

       .rt_en(rt_en),

       .rt_clear(rt_clear),

       .rt_flag(rt_flag)

   );

   

   sdram_ref sdram_ref_inst(

       .clk(phy_clk),

       .rst_n(rst_n),

       .ref_en(ref_en),

       .ref_done(ref_done),

       .ref_bus(ref_bus)

   );

   

   sdram_write sdram_write_inst(

       .clk(phy_clk),

       .rst_n(rst_n),

       .wr_en(wr_en),

       .wr_done(wr_done),

       .wr_bus(wr_bus),

       .sdr_dq(sdr_dq),

       .local_data(local_data),

       .local_addr(local_addr)

   );

   

   sdram_read sdram_read_inst(

       .clk(phy_clk),

       .rst_n(rst_n),

       .rd_en(rd_en),

       .rd_done(rd_done),

       .rd_bus(rd_bus),

       .sdr_dq(sdr_dq),

       .local_q(local_q),

       .local_addr(local_addr)

   );

   

   sdram_mux sdram_mux_inst(

       .clk(phy_clk),

       .rst_n(rst_n),

       .init_bus(init_bus),

       .ref_bus(ref_bus),

       .wr_bus(wr_bus),

       .rd_bus(rd_bus),

       .sdr_bus(sdr_bus),

       .sel_sm(sel_sm)

   );

   

   my_pll PLL(

       .areset    (~sys_rst_n)        ,

       .inclk0    (clk)               ,

       .c0        (phy_clk)           ,

       .c1        (sdr_clk)           ,

       .locked    (rst_n)

   );


endmodule


4、模塊功能


PLL模塊,初始化模塊,刷新模塊在前面兩篇文章中已經討論過,這里不再描述。


(1)寫模塊


主要完成寫突發,采用線性序列機設計,當檢測到wr_en為高電平時,計數器開始計時,并發出開ACT命令,并將row地址賦值給sdr_a。兩拍之后,發出寫命令,并將A10拉高,然后開始將數據賦值給sdr_dq信號。然后計數到8-1時將寫完成信號wr_done拉高。


可以對照時序圖閱讀代碼,其代碼如下:


module sdram_write(clk, rst_n, wr_en, wr_done, wr_bus, sdr_dq, local_data, local_addr);


   input clk;

   input rst_n;

   input wr_en;

   output reg wr_done;

   output [19:0] wr_bus;

   inout [15:0] sdr_dq;

   input [63:0] local_data;

   input [24:0] local_addr;

   

   parameter CNT_MAX = 8;

   parameter NOP = 4'b0111;

   parameter ACT = 4'b0011;

   parameter WR  = 4'b0100;

   

   reg [3:0] cnt;

   reg [3:0] sdr_cmd;

   reg [1:0] sdr_ba;

   reg [12:0] sdr_a;

   reg [15:0] temp;

   reg out_en;

   

   wire [9:0] col;

   wire [12:0] row;

   wire [1:0] ba;

   wire sdr_cke;

   wire add_cnt;

   wire end_cnt;

   

   assign {ba, row, col} = local_addr;

   assign sdr_dq = out_en ? temp : 16'dz;

   assign sdr_cke = 1'b1;

   assign wr_bus = {sdr_cke, sdr_cmd, sdr_ba, sdr_a};

   

   always @(posedge clk or negedge rst_n)begin

       if(!rst_n)begin

           cnt <= 0;

       end

       else if(add_cnt)begin

           if(end_cnt)

               cnt <= 0;

           else

               cnt <= cnt + 1;

       end

   end


   assign add_cnt = wr_en;      

   assign end_cnt = add_cnt && cnt==CNT_MAX - 1 ;


   always @ (posedge clk or negedge rst_n)begin

       if(!rst_n)begin

           sdr_cmd <= NOP;

       end

       else if(add_cnt && cnt == 1 - 1)begin

           sdr_cmd <= ACT;

       end

       else if(add_cnt && cnt == 3 - 1)begin

           sdr_cmd <= WR;

       end

       else begin

           sdr_cmd <= NOP;

       end

   end

   

   always @(posedge clk or negedge rst_n)begin

       if(!rst_n)begin

           sdr_ba <= 2'd0;

       end

       else begin

           sdr_ba <= ba;

       end

   end

   

   always @(posedge clk or negedge rst_n)begin

       if(!rst_n)begin

           sdr_a <= 13'd0;

       end

       else if(add_cnt && cnt == 1 - 1)begin

           sdr_a <= row;

       end

       else if(add_cnt && cnt == 3 - 1)begin

           sdr_a <= {2'd0, 1'b1, col};

       end

       else begin

           sdr_a <= 13'd0;

       end

   end

   

   always @(posedge clk or negedge rst_n)begin

       if(!rst_n)begin

           temp <= 16'd0;

       end

       else if(add_cnt && cnt == 4 - 1)begin

           temp <= local_data[15:0];

       end

       else if(add_cnt && cnt == 5 - 1)begin

           temp <= local_data[31:16];

       end

       else if(add_cnt && cnt == 6 - 1)begin

           temp <= local_data[47:32];

       end

       else if(add_cnt && cnt == 7 - 1)begin

           temp <= local_data[63:48];

       end

       else begin

           temp <= 16'd0;

       end

   end

   

   always @(posedge clk or negedge rst_n)begin

       if(!rst_n)begin

           out_en <= 1'b0;

       end

       else if(add_cnt && cnt == 4 - 1)begin

           out_en <= 1'b1;

       end

       else if(add_cnt && cnt == 8 - 1)begin

           out_en <= 1'b0;

       end

       else begin

           out_en <= out_en;

       end

   end

   

   always @(posedge clk or negedge rst_n)begin

       if(!rst_n)begin

           wr_done <= 1'b0;

       end

       else if(add_cnt && cnt == 1 - 1)begin

           wr_done <= 1'b0;

       end

       else if(add_cnt && cnt == 8 - 1)begin

           wr_done <= 1'b1;

       end

       else begin

           wr_done <= wr_done;

       end

   end


endmodule


(2)讀模塊


讀模塊主要完成讀突發,對于讀模塊來說,sdr_dq信號是輸入信號,且不是同一時鐘域信號,所以要加兩級同步寄存器。當檢測到讀使能rd_en為高時,計數器開始計數,并發出ACT命令,同時給出row地址,兩拍之后發出讀命令,并將A10拉高,由于加入了兩級同步寄存器且讀潛伏期為3,所以5拍之后才可以采集數據,即計數到9-1時采集數據,4拍之后數據采集完成,下一拍將讀完成信號rd_done拉高。


可以對照時序圖閱讀代碼,代碼如下


module sdram_read(clk, rst_n, rd_en, rd_done, rd_bus, sdr_dq, local_q, local_addr);


   input clk;

   input rst_n;

   input rd_en;

   output reg rd_done;

   output [19:0] rd_bus;

   input [15:0] sdr_dq;

   output reg [63:0] local_q;

   input [24:0] local_addr;

   

   parameter CNT_MAX = 14;

   parameter NOP = 4'b0111;

   parameter ACT = 4'b0011;

   parameter RD  = 4'b0101;



   reg [3:0] cnt;

   reg [3:0] sdr_cmd;

   reg [1:0] sdr_ba;

   reg [12:0] sdr_a;

   reg [15:0] temp0, temp1;

   

   wire [9:0] col;

   wire [12:0] row;

   wire [1:0] ba;

   wire sdr_cke;

   wire add_cnt;

   wire end_cnt;

   

   assign {ba, row, col} = local_addr;

   assign sdr_cke = 1'b1;

   assign rd_bus = {sdr_cke, sdr_cmd, sdr_ba, sdr_a};

   

   always @(posedge clk)begin

       temp0 <= sdr_dq;

       temp1 <= temp0;

   end

   

   always @(posedge clk or negedge rst_n)begin

       if(!rst_n)begin

           cnt <= 0;

       end

       else if(add_cnt)begin

           if(end_cnt)

               cnt <= 0;

           else

               cnt <= cnt + 1;

       end

   end


   assign add_cnt = rd_en;      

   assign end_cnt = add_cnt && cnt==CNT_MAX - 1 ;

   

   always @ (posedge clk or negedge rst_n)begin

       if(!rst_n)begin

           sdr_cmd <= NOP;

       end

       else if(add_cnt && cnt == 1 - 1)begin

           sdr_cmd <= ACT;

       end

       else if(add_cnt && cnt == 3 - 1)begin

           sdr_cmd <= RD;

       end

       else begin

           sdr_cmd <= NOP;

       end

   end

   

   always @(posedge clk or negedge rst_n)begin

       if(!rst_n)begin

           sdr_ba <= 2'd0;

       end

       else begin

           sdr_ba <= ba;

       end

   end

   

   always @(posedge clk or negedge rst_n)begin

       if(!rst_n)begin

           sdr_a <= 13'd0;

       end

       else if(add_cnt && cnt == 1 - 1)begin

           sdr_a <= row;

       end

       else if(add_cnt && cnt == 3 - 1)begin

           sdr_a <= {2'd0, 1'b1, col};

       end

       else begin

           sdr_a <= 13'd0;

       end

   end

   

   always @(posedge clk or negedge rst_n)begin

       if(!rst_n)begin

           local_q <= 64'd0;

       end

       else if(add_cnt && cnt == 9 - 1)begin

           local_q[15:0] <= temp1;

       end

       else if(add_cnt && cnt == 10 - 1)begin

           local_q[31:16] <= temp1;

       end

       else if(add_cnt && cnt == 11 - 1)begin

           local_q[47:32] <= temp1;

       end

       else if(add_cnt && cnt == 12 - 1)begin

           local_q[63:48] <= temp1;

       end

       else begin

           local_q <= local_q;

       end

   end

   

   always @(posedge clk or negedge rst_n)begin

       if(!rst_n)begin

           rd_done <= 1'b0;

       end

       else if(add_cnt && cnt == 1 - 1)begin

           rd_done <= 1'b0;

       end

       else if(add_cnt && cnt == 13 - 1)begin

           rd_done <= 1'b1;

       end

       else begin

           rd_done <= rd_done;

       end

   end


endmodule


(3)仲裁模塊


由于加入了寫模塊和讀模塊,所以仲裁模塊也要做出相應的修改,當收到刷新請求時,即rt_flag信號后,在結束本次讀突發或者寫突發后,拉高刷新使能(ref_en)信號,當收到寫請求且沒有刷新請求時,拉高寫使能(wr_en)信號,當收到讀請求且沒有刷新請求和寫請求時,拉高讀使能(rd_en)信號。


主要代碼如下:


module arbitrate(clk, rst_n, rt_flag, rt_en, rt_clear, ref_done,ref_en, init_done, sel_sm, local_rdata_valid,

         local_ready,local_wrreq, local_rdreq, wr_done, wr_en, rd_en, rd_done);

 

         input clk, rst_n;

         input rt_flag;

         output reg rt_en,rt_clear;

         input ref_done;

         output reg ref_en;

         input init_done;

         output reg [1:0]sel_sm;

         output reglocal_rdata_valid;

         output reglocal_ready;

         input local_wrreq,local_rdreq;

         input wr_done;

         output reg wr_en;

         output reg rd_en;

         input rd_done;

        

         localparam SM_INIT =2'd0;

         localparam SM_REF  = 2'd1;

         localparam SM_WR   = 2'd2;

         localparam SM_RD   = 2'd3;

        

         always @(posedge clkor negedge rst_n)begin

                   if(!rst_n)begin

                            rt_en<= 0;

                   end

                   elseif(init_done)begin

                            rt_en<= 1;

                   end

                   else begin

                            rt_en<= rt_en;

                   end

         end

        

         always @(posedge clkor negedge rst_n)begin

                   if(!rst_n)begin

                            rt_clear<= 0;

                   end

                   elseif(ref_en)begin

                            rt_clear<= 1;

                   end

                   elseif(ref_done)begin

                            rt_clear<= 0;

                   end

                   else begin

                            rt_clear<= rt_clear;

                   end

         end

        

         always @(posedge clkor negedge rst_n)begin

                   if(!rst_n)begin

                            ref_en<= 0;

                   end

                   elseif(rt_flag)begin

                            ref_en<= 1;

                   end

                   elseif(ref_done)begin

                            ref_en<= 0;

                   end

                   else begin

                            ref_en<= ref_en;

                   end

         end

        

         always @(posedge clkor negedge rst_n)begin

                   if(!rst_n)begin

                            sel_sm<= SM_INIT;

                   end

                   elseif(!init_done)begin

                            sel_sm<= SM_INIT;

                   end

                   elseif(rt_flag)begin

                            sel_sm<= SM_REF;

                   end

                   elseif(local_wrreq && !rt_flag)begin

                            sel_sm<= SM_WR;

                   end

                   elseif(local_rdreq && !rt_flag)begin

                            sel_sm<= SM_RD;

                   end

                   else begin

                            sel_sm<= sel_sm;

                   end

         end

        

         always @(posedge clkor negedge rst_n)begin

                   if(!rst_n)begin

                            wr_en<= 0;

                   end

                   elseif(local_wrreq && !rt_flag)begin

                            wr_en<= 1;

                   end

                   elseif(wr_done)begin

                            wr_en<= 0;

                   end

                   else begin

                            wr_en<= wr_en;

                   end

         end

        

         always @(posedge clkor negedge rst_n)begin

                   if(!rst_n)begin

                            rd_en<= 0;

                   end

                   elseif(local_rdreq && !local_wrreq && !rt_flag)begin

                            rd_en<= 1;

                   end

                   elseif(rd_done)begin

                            rd_en<= 0;

                   end

                   else begin

                            rd_en<= rd_en;

                   end

         end

        

         always @(posedge clkor negedge rst_n)begin

                   if(!rst_n)begin

                            local_ready<= 1;

                   end

                   elseif(wr_en || rd_en)begin

                            local_ready<= 0;

                   end

                   else begin

                            local_ready<= 1;

                   end

         end

        

         always @(posedge clkor negedge rst_n)begin

                   if(!rst_n)begin

                            local_rdata_valid<= 0;

                   end

                   elseif(rd_done)begin

                            local_rdata_valid<= 1;

                   end

                   else begin

                            local_rdata_valid<= 0;

                   end

         end

 

endmodule



(5)仿真驗證


向SDRAM中寫入64’h1122334455667788,然后讀出來。由圖可知SDRAM正確讀出了數據。(波形顯示的是十六進制,報告是十進制)

null


null

  •   
  •   
  •   
  •  
  • FPGA教育領域第一品牌
  • 咨詢熱線:020-39002701
  • 技術交流Q群:544453837
主站蜘蛛池模板: 日本免费久久久久久久网站 | 青草视频免费观看 | 亚洲综合男人的天堂色婷婷 | 毛片一区二区三区 | 亚洲欧美日韩国产精品久久 | 国产高清一级毛片 | 伊人久久精品亚洲精品一区 | 国产精品嫩草影院人体模特 | 亚洲欧美在线一区 | 国产99精品一区二区三区免费 | 精品视频在线播放 | 91精品国产91久久久久福利 | 精品久久一区 | a级国产视频 | 91国内在线观看 | 国产成人免费网站在线观看 | 国产精品免费综合一区视频 | 亚洲国产高清一区二区三区 | 蜜桃视频一区 | 992人人tv香蕉国产精品 | 日本欧美成人免费观看 | 日本无卡码免费一区二区三区 | 国产丝袜护土调教在线视频 | 国产性大片黄在线观看在线放 | 91精品最新国内在线播放 | 久久国产三级 | 狠狠久久 | 日本大片久久久高清免费看 | 国产视频每日更新 | 成人欧美日韩高清不卡 | 一区二区三区四区无限乱码 | 黄色免费一级视频 | 亚洲欧美一区二区三区麻豆 | 国产精品福利一区二区亚瑟 | 小草免费在线视频 | 国产精品情侣久久婷婷文字 | 亚洲人视频 | 天海翼一区二区三区高清视频 | 日韩欧美精品综合久久 | 国内自拍视频在线看免费观看 | xxx国产精品视频 |