加入星計劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入
  • 正文
    • 高級設(shè)計:SDR SDRAM 驅(qū)動設(shè)計
  • 推薦器件
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請入駐 產(chǎn)業(yè)圖譜

FPGA零基礎(chǔ)學(xué)習(xí)精選 | SDR SDRAM 驅(qū)動設(shè)計

01/19 10:50
2051
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

作者:郝旭帥??校對:陸輝

大俠好,歡迎來到FPGA技術(shù)江湖,江湖偌大,相見即是緣分。大俠可以關(guān)注FPGA技術(shù)江湖,在“闖蕩江湖”、"行俠仗義"欄里獲取其他感興趣的資源,或者一起煮酒言歡。

本系列將帶來FPGA的系統(tǒng)性學(xué)習(xí),從最基本的數(shù)字電路基礎(chǔ)開始,最詳細(xì)操作步驟,最直白的言語描述,手把手的“傻瓜式”講解,讓電子、信息、通信類專業(yè)學(xué)生、初入職場小白及打算進(jìn)階提升的職業(yè)開發(fā)者都可以有系統(tǒng)性學(xué)習(xí)的機(jī)會。

系統(tǒng)性的掌握技術(shù)開發(fā)以及相關(guān)要求,對個人就業(yè)以及職業(yè)發(fā)展都有著潛在的幫助,希望對大家有所幫助。后續(xù)會陸續(xù)更新 Xilinx 的 Vivado、ISE 及相關(guān)操作軟件的開發(fā)的相關(guān)內(nèi)容,學(xué)習(xí)FPGA設(shè)計方法及設(shè)計思想的同時,實操結(jié)合各類操作軟件,會讓你在技術(shù)學(xué)習(xí)道路上無比的順暢,告別技術(shù)學(xué)習(xí)小BUG卡破腦殼,告別目前忽悠性的培訓(xùn)誘導(dǎo),真正的去學(xué)習(xí)去實戰(zhàn)應(yīng)用,這種快樂試試你就會懂的。話不多說,上貨。

高級設(shè)計:SDR SDRAM 驅(qū)動設(shè)計

本篇實現(xiàn)基于叁芯智能科技的SANXIN -B01 FPGA開發(fā)板,以下為配套的教程,如有入手開發(fā)板,可以登錄官方淘寶店購買,還有配套的學(xué)習(xí)視頻。

FPGA入門進(jìn)階板卡推薦 | SANXIN B01 FPGA開發(fā)板(Intel)

SANXIN-B01開發(fā)板verilog教程V3電子版

FPGA就業(yè)班,2024.02.26開班,新增課程內(nèi)容不加價,高薪就業(yè),線上線下同步!

本系列的技術(shù)文檔、源工程代碼可以登錄叁芯智能科技官方技術(shù)論壇下載。論壇網(wǎng)址:www.sxznfpga.com

隨機(jī)訪問存儲器(RAM)分為靜態(tài)RAM(SRAM)和動態(tài)RAM(DRAM)。由于動態(tài)存儲器存儲單元的結(jié)構(gòu)非常簡單,所以它能達(dá)到的集成度遠(yuǎn)高于靜態(tài)存儲器。但是動態(tài)存儲器的存取速度不如靜態(tài)存儲器快。

RAM的動態(tài)存儲單元是利用電容可以存儲電荷的原理制成的。由于存儲單元的機(jī)構(gòu)能夠做得很簡單,所以在大容量、高集成度的RAM中得到了普遍的應(yīng)用。但是由于電容的容量很小,而漏電流又不可能絕對等于零,所以電荷保存的時間有限。為了及時補(bǔ)充漏掉的電荷以避免存儲的信號丟失,必須定時地給電容補(bǔ)充電荷,通常將這種操作稱為刷新。

行列地址線被選中后,數(shù)據(jù)線(data_bit)直接和電容相連接。當(dāng)寫入時,數(shù)據(jù)線給電容充放電;讀取時,電容將數(shù)據(jù)線拉高或者置低。

SDRAM 的全稱即同步動態(tài)隨機(jī)存儲器(Synchronous Dynamic Random Access Memory);這里的同步是指其時鐘頻率與對應(yīng)控制器的系統(tǒng)時鐘頻率相同,并且內(nèi)部命令的發(fā)送與數(shù)據(jù)傳輸都是以該時鐘為基準(zhǔn);動態(tài)是指存儲陣列需要不斷的刷新來保證數(shù)據(jù)不丟失。

SDR SDRAM中的SDR是指單數(shù)據(jù)速率,即每一根數(shù)據(jù)線上,每個時鐘只傳輸一個bit的數(shù)據(jù)。SDR SDRAM的時鐘頻率可以達(dá)到100MHz以上,按照100MHz的速率計算,一片16位數(shù)據(jù)寬度的SDR SDRAM的讀寫數(shù)據(jù)帶寬可以達(dá)到1.6Gbit/s。

SANXIN – B01的開發(fā)板上有一個容量為256Mbit(16M x 16bit)的SDR SDRAM(H57V2562GTR)。其內(nèi)部存儲時,分為了4個獨立的區(qū)域(BANK),每個bank為4Mx16bit的存儲空間;每個bank在存儲時,按照二維的方式進(jìn)行存儲,利用行列來進(jìn)行確定,有8192行(13bit地址線),有512列(9bit地址線),8192 x 512為4M的存儲量。

在進(jìn)行指定某個地址時,共需要2位bank地址,13位行地址,9位列地址,合計共24位地址。但是在SDR SDRAM的指定某個地址時,行地址和列地址不是同時給出,SDR SDRAM采用行列地址線復(fù)用,所以地址線合計為2(bank 地址)+13(行、列地址復(fù)用)。

SDR SDRAM需要時鐘端和時鐘使能端。SDR SDRAM所有的操作都依靠于此時鐘;當(dāng)時鐘使能端無效時,SDR SDRAM自動忽略時鐘上升沿。

SDR SDRAM擁有四個命令控制線,分別為CS、RAS、CAS、WE。組成的命令表如下:

在寫入數(shù)據(jù)時,有時會出現(xiàn)不想對某8bit進(jìn)行寫入,就可以采用DQM進(jìn)行控制。

SDR SDRAM的內(nèi)部機(jī)構(gòu)為:

由于SDR SDRAM為DRAM,內(nèi)部的存儲都是靠電容進(jìn)行保存數(shù)據(jù),電容的保持?jǐn)?shù)據(jù)的時間為64ms,SDR SDRAM每次只能夠刷新一行,為了不丟失任何數(shù)據(jù),所以要保證64ms內(nèi),將所有的行都要刷新一遍。

SDR SDRAM支持讀寫的長度為1、2、4、8和一行(整頁)。

具體的SDR SDRAM的介紹可以查看手冊。下面只介紹幾個相對重要的時序圖。

在SDR SDRAM正常使用之前,需要進(jìn)行初始化。初始化的時序圖如下:

在PRECHARGE時,A10為高,表示選中所有的bank;A10為低,表示選中BA0、BA1所指定的bank。初始化中,A10置高。

在LOAD MOOE REGISTER中,采用地址線進(jìn)行配置模式寄存器。說明如下:

在模式配置中,利用CL(CAS Latency)表示列選通潛伏期,利用BL(Burst Length)表示突發(fā)長度。

SDR SDRAM中有內(nèi)部的刷新控制器和刷新的行計數(shù)器,外部控制器只需要保證在64ms之內(nèi)進(jìn)行8192次刷新即可。

在進(jìn)行PRECHARGE時,A10要為高電平。

SDR SDRAM中,我們可以在任意位置進(jìn)行寫入。寫入的時序圖如下:

SDR SDRAM中,我們可以在任意位置進(jìn)行讀出。讀出的時序圖如下:

在各個時序中的時序參數(shù)如下:

設(shè)計要求

設(shè)計一個突發(fā)長度為2,列選通潛伏期為2的SDR SDRAM的控制器。

設(shè)計分析

該控制器共有四部分功能,初始化、刷新、寫和讀。四部分的執(zhí)行控制采用一個模塊來控制。

SDR SDRAM必須要進(jìn)行初始化,初始化只用執(zhí)行一次。然后啟動一個計時器,等計時器達(dá)到后,進(jìn)行刷新。在刷新的間隔中,根據(jù)讀寫的要求進(jìn)行讀寫。

四個模塊都會對SDR SDRAM的命令線和地址線進(jìn)行控制,所以輸出時,采用多路選擇器對齊進(jìn)行選擇輸出。

四個模塊按照對應(yīng)的時序圖進(jìn)行編寫代碼即可。

架構(gòu)設(shè)計和信號說明

該控制器命名為sdr_drive。

pll_sdr(鎖相環(huán)模塊):產(chǎn)生驅(qū)動所需要的100MHz的時鐘(0度相位)、SDR SDRAM所需要的100MHz的時鐘(270度相位)、以及PLL鎖定信號當(dāng)作系統(tǒng)復(fù)位使用。

timer(刷新計時器):當(dāng)啟動計時器后,開始計時,當(dāng)計時到規(guī)定時間后,輸出刷新請求,計數(shù)器直接清零計數(shù)計數(shù)。當(dāng)控制器響應(yīng)后,輸出清除信號后,刷新請求拉低。

refresh(刷新模塊)、init(初始化模塊)、sdr_write(寫模塊)、sdr_read(讀模塊):當(dāng)啟動模塊后,按照規(guī)定的時序進(jìn)行輸出即可,然后輸出完成信號。

sdr_ctrl(控制模塊):控制各個模塊協(xié)調(diào)工作。

mux4_1(四選一多路選擇器模塊):選擇對應(yīng)的bus總線作為輸出。

*_bus的組成為:高四位為sdr_cs_n、sdr_ras_n、sdr_cas_n、sdr_we_n。然后是bank的兩位,后續(xù)為13位的sdr_addr。

sdr_drive_head聲明

將驅(qū)動中用到各種參數(shù)定義在該文件中。

`define???????SDR_ADDR_WIDTH????????????????????13`define       SDR_COL_ADDR_WIDTH                9`define       SDR_REFRESH_TIME                  64_000_000
`define       ADDR_WIDTH   2 + `SDR_ADDR_WIDTH + `SDR_COL_ADDR_WIDTH`define       BUS_WIDTH    4 + 2 + `SDR_ADDR_WIDTH
`define       CMD_INH                           4'b1000`define       NOP                               4'b0111`define       ACT                               4'b0011`define       RD                                4'b0101`define       WR                                4'b0100`define       BT                                4'b0110`define       PREC                              4'b0010`define       REFR                              4'b0001`define       LMR                               4'b0000
`define       PU_DELAY                          20_000`define       Trp                               3`define       Trfc                              7`define       Tmrd                              3`define       Trcd                              3`define       Twr                               3`define       Tcl                               2
`define       CODE                             13'b000_0_00_010_0_001`define     REFRESH_TIME  (`SDR_REFRESH_TIME/(2**`SDR_ADDR_WIDTH))/10

pll_sdr設(shè)計實現(xiàn)

該模塊為IP core,輸出0相位的100MHz(系統(tǒng)時鐘)和270相位的100MHz(SDR的時鐘)。系統(tǒng)設(shè)計中,信號在上升沿輸出;對于外部器件(相位調(diào)整為270),能夠較好的滿足建立和保持時間。

init設(shè)計實現(xiàn)

該模塊負(fù)責(zé)將SDR SDRAM進(jìn)行初始化。上電延遲(PU_DELAY)設(shè)置為200us;預(yù)充電時間(Trp)設(shè)置為3個時鐘周期(30ns);自刷新時間(Trfc)設(shè)置為7個時鐘周期(70ns);模式寄存器應(yīng)用時間(Tmrd)設(shè)置為3個時鐘周期(30ns);突發(fā)長度為2;列選通潛伏期為3。

按照對應(yīng)的初始化的時序圖,做出如下設(shè)計。

本模塊采用狀態(tài)機(jī)的方式設(shè)計實現(xiàn)。

設(shè)計代碼為:

`include "../rtl/sdr_drive_head.v"
module init (
  input     wire                                  clk,  input     wire                                  rst_n,
  input     wire                                  init_en,  output    reg                                   init_done,
  output    wire        [`BUS_WIDTH - 1 : 0]      init_bus);
  localparam      IDLE              =             7'b000_0001;  localparam      PUD               =             7'b000_0010;  localparam      PRECHARGE         =             7'b000_0100;  localparam      AUTOREFR1         =             7'b000_1000;  localparam      AUTOREFR2         =             7'b001_0000;  localparam      LMR_STATE         =             7'b010_0000;  localparam      INITDONE          =             7'b100_0000;
  reg                   [6:0]                     c_state;  reg                   [6:0]                     n_state;  wire                  [1:0]                     sdr_bank;  reg                   [3:0]                     sdr_cmd;  reg                   [`SDR_ADDR_WIDTH - 1 : 0] sdr_addr;  reg                   [14:0]                    cnt;                    
  assign sdr_bank = 2'b00;  assign init_bus = {sdr_cmd,sdr_bank,sdr_addr};
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      c_state <= IDLE;    else      c_state <= n_state;  end
  always @ * begin    case (c_state)      IDLE        :     begin        if (init_en == 1'b1)          n_state = PUD;        else          n_state = IDLE;      end
      PUD         :     begin        if (cnt == `PU_DELAY - 1'b1)          n_state = PRECHARGE;        else          n_state = PUD;      end
      PRECHARGE   :     begin        if (cnt == `Trp - 1'b1)          n_state = AUTOREFR1;        else          n_state = PRECHARGE;      end
      AUTOREFR1   :     begin        if (cnt == `Trfc - 1'b1)          n_state = AUTOREFR2;        else            n_state = AUTOREFR1;      end
      AUTOREFR2   :     begin        if (cnt == `Trfc - 1'b1)          n_state = LMR_STATE;        else            n_state = AUTOREFR2;      end
      LMR_STATE   :     begin        if (cnt == `Tmrd - 1'b1)          n_state = INITDONE;        else          n_state = LMR_STATE;      end
      INITDONE    :     begin        n_state = INITDONE;      end
      default     :   n_state = IDLE;    endcase  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      sdr_cmd <= `NOP;    else      case (c_state)        IDLE        :   sdr_cmd <= `NOP;        PUD         :   begin          if (cnt == `PU_DELAY - 1'b1)            sdr_cmd <= `PREC;          else            sdr_cmd <= `NOP;        end        PRECHARGE   :   begin          if (cnt == `Trp - 1'b1)            sdr_cmd <= `REFR;          else            sdr_cmd <= `NOP;        end        AUTOREFR1   :   begin          if (cnt == `Trfc - 1'b1)            sdr_cmd <= `REFR;          else            sdr_cmd <= `NOP;        end        AUTOREFR2   :   begin          if (cnt == `Trfc - 1'b1)            sdr_cmd <= `LMR;          else            sdr_cmd <= `NOP;        end        LMR_STATE   :   sdr_cmd <= `NOP;        INITDONE    :   sdr_cmd <= `NOP;        default     :   sdr_cmd <= `NOP;      endcase  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      cnt <= 15'd0;    else      case (c_state)        IDLE        :   cnt <= 16'd0;        PUD         :   begin          if (cnt < `PU_DELAY - 1'b1)            cnt <= cnt + 1'b1;          else            cnt <= 16'd0;        end
        PRECHARGE    :   begin          if (cnt < `Trp - 1'b1)            cnt <= cnt + 1'b1;          else            cnt <= 16'd0;        end        AUTOREFR1    :   begin          if (cnt < `Trfc - 1'b1)            cnt <= cnt + 1'b1;          else            cnt <= 16'd0;        end        AUTOREFR2    :   begin          if (cnt < `Trfc - 1'b1)            cnt <= cnt + 1'b1;          else            cnt <= 16'd0;        end        LMR_STATE    :   begin          if (cnt < `Tmrd - 1'b1)            cnt <= cnt + 1'b1;          else            cnt <= 16'd0;        end        INITDONE    :   cnt <= 16'd0;        default     :   cnt <= 16'd0;      endcase  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      init_done <= 1'b0;    else      if (c_state == LMR_STATE && cnt == `Tmrd - 1'b1)        init_done <= 1'b1;      else        init_done <= 1'b0;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      sdr_addr <= 0;    else      if (c_state == PUD && cnt == `PU_DELAY - 1'b1)        sdr_addr[10] <= 1'b1;      else        if (c_state == AUTOREFR2 && cnt == `Trfc - 1'b1)          sdr_addr <= `CODE;        else          sdr_addr <= 0;  end
endmodule

timer設(shè)計實現(xiàn)

SDR SDRAM內(nèi)部構(gòu)造為DRAM,需要不間斷的刷新,要求64ms刷新一遍。每次刷新為一行,開發(fā)板上的SDR SDRAM共有8192行,平均需要7812.5ns刷新一次,我們選擇7810刷新一次。

到達(dá)規(guī)定的刷新時間時,控制器有可能正在進(jìn)行其他的操作。在設(shè)計時,達(dá)到時間后,發(fā)出刷新請求,當(dāng)外部執(zhí)行刷新后,將次請求清除。發(fā)出刷新請求的同時,計數(shù)器重新歸零計數(shù)。

`include "../rtl/sdr_drive_head.v"
module timer (
  input   wire                    clk,  input   wire                    rst_n,
  input   wire                    time_en,  input   wire                    req_clr,
  output  reg                     refresh_req);
  reg               [9:0]         cnt;
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      cnt <= 10'd0;    else      if (time_en == 1'b1 && cnt < `REFRESH_TIME - 1'b1)        cnt <= cnt + 1'b1;      else          cnt <= 10'd0;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      refresh_req <= 1'b0;    else      if (cnt == `REFRESH_TIME - 1'b1)        refresh_req <= 1'b1;      else        if (req_clr == 1'b1)          refresh_req <= 1'b0;        else          refresh_req <= refresh_req;  end
endmodule

refresh設(shè)計實現(xiàn)

該模塊負(fù)責(zé)刷新,按照對應(yīng)的時序圖進(jìn)行控制即可。

該模塊利用狀態(tài)機(jī)的方式實現(xiàn)。狀態(tài)轉(zhuǎn)移圖如下:

設(shè)計代碼為:

`include "../rtl/sdr_drive_head.v"
module refresh (
  input     wire                                  clk,  input     wire                                  rst_n,
  input     wire                                  refresh_en,  output    reg                                   refresh_done,
  output    wire        [`BUS_WIDTH - 1 : 0]      refresh_bus);
  localparam      IDLE              =             5'b0_0001;  localparam      PRECHARGE         =             5'b0_0010;  localparam      AUTOREFR1         =             5'b0_0100;  localparam      AUTOREFR2         =             5'b0_1000;  localparam      REFRDONE          =             5'b1_0000;
  reg                   [4:0]                     c_state;  reg                   [4:0]                     n_state;  wire                  [1:0]                     sdr_bank;  reg                   [3:0]                     sdr_cmd;  reg                   [`SDR_ADDR_WIDTH - 1 : 0] sdr_addr;  reg                   [3:0]                     cnt;                    
  assign sdr_bank = 2'b00;  assign refresh_bus = {sdr_cmd,sdr_bank,sdr_addr};
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      c_state <= IDLE;    else      c_state <= n_state;  end
  always @ * begin    case (c_state)      IDLE        :     begin        if (refresh_en == 1'b1)          n_state = PRECHARGE;        else          n_state = IDLE;      end
      PRECHARGE   :     begin        if (cnt == `Trp - 1'b1)          n_state = AUTOREFR1;        else          n_state = PRECHARGE;      end
      AUTOREFR1   :     begin        if (cnt == `Trfc - 1'b1)          n_state = AUTOREFR2;        else            n_state = AUTOREFR1;      end
      AUTOREFR2   :     begin        if (cnt == `Trfc - 1'b1)          n_state = REFRDONE;        else            n_state = AUTOREFR2;      end
      REFRDONE    :     begin        n_state = IDLE;      end
      default     :   n_state = IDLE;    endcase  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      sdr_cmd <= `NOP;    else      case (c_state)        IDLE        :   begin          if (refresh_en == 1'b1)            sdr_cmd <= `PREC;          else            sdr_cmd <= `NOP;        end        PRECHARGE   :   begin          if (cnt == `Trp - 1'b1)            sdr_cmd <= `REFR;          else            sdr_cmd <= `NOP;        end        AUTOREFR1   :   begin          if (cnt == `Trfc - 1'b1)            sdr_cmd <= `REFR;          else            sdr_cmd <= `NOP;        end        AUTOREFR2   :   sdr_cmd <= `NOP;
        REFRDONE    :   sdr_cmd <= `NOP;        default     :   sdr_cmd <= `NOP;      endcase  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      cnt <= 4'd0;    else      case (c_state)        IDLE        :   cnt <= 4'd0;
        PRECHARGE    :   begin          if (cnt < `Trp - 1'b1)            cnt <= cnt + 1'b1;          else            cnt <= 4'd0;        end        AUTOREFR1    :   begin          if (cnt < `Trfc - 1'b1)            cnt <= cnt + 1'b1;          else            cnt <= 4'd0;        end        AUTOREFR2    :   begin          if (cnt < `Trfc - 1'b1)            cnt <= cnt + 1'b1;          else            cnt <= 4'd0;        end        REFRDONE    :   cnt <= 4'd0;        default     :   cnt <= 4'd0;      endcase  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      refresh_done <= 1'b0;    else      if (c_state == AUTOREFR2 && cnt == `Trfc - 1'b1)        refresh_done <= 1'b1;      else        refresh_done <= 1'b0;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      sdr_addr <= 0;    else      if (c_state == IDLE && refresh_en == 1'b1)        sdr_addr[10] <= 1'b1;      else        sdr_addr <= 0;  end
endmodule

sdr_write設(shè)計實現(xiàn)

該模塊負(fù)責(zé)將外部的數(shù)據(jù)寫入到規(guī)定的地址中去。在SDR SDRAM中,每操作(讀寫)一次,都會引起該存儲位的漏電,每次結(jié)束時,可以進(jìn)行預(yù)充電。SDR SDRAM提供了自動預(yù)充電的機(jī)制,在讀寫命令時,sdr_addr[10]=1,即可自動預(yù)充電。在設(shè)計時,應(yīng)該要為自動預(yù)充電預(yù)留出足夠的時間。

根據(jù)對應(yīng)的寫入時序圖,利用狀態(tài)機(jī)完成此設(shè)計。

設(shè)計代碼如下:

`include "../rtl/sdr_drive_head.v"
module sdr_write (
  input     wire                            clk,  input     wire                            rst_n,
  input     wire                            write_en,  input     wire  [`ADDR_WIDTH - 1 : 0]     wr_addr,  input     wire  [31:0]                    wr_data,
  output    reg   [15:0]                    odq,  output    wire  [`BUS_WIDTH - 1 : 0]      wr_bus,  output    reg                             wr_done);
  localparam    IDLE            =           4'b0001;  localparam    ACT_STATE       =           4'b0010;  localparam    WR1             =           4'b0100;  localparam    WR2             =           4'b1000;
  reg                   [3:0]                     c_state;  reg                   [3:0]                     n_state;  wire                  [1:0]                     sdr_bank;  reg                   [3:0]                     sdr_cmd;  reg                   [`SDR_ADDR_WIDTH - 1 : 0] sdr_addr;  reg                   [14:0]                    cnt;                    
  assign sdr_bank = wr_addr[23:22];  assign wr_bus = {sdr_cmd,sdr_bank,sdr_addr};
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      c_state <= IDLE;    else      c_state <= n_state;  end
  always @ * begin    case (c_state)      IDLE        :   begin        if (write_en == 1'b1)          n_state = ACT_STATE;        else          n_state = IDLE;      end
      ACT_STATE   :   begin        if (cnt == `Trcd - 1'b1)          n_state = WR1;        else          n_state = ACT_STATE;      end
      WR1         :    n_state = WR2;
      WR2         :   begin        if (cnt == `Twr + `Trp - 1'b1)          n_state = IDLE;        else          n_state = WR2;      end
      default     :   n_state = IDLE;    endcase  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      sdr_cmd <= `NOP;    else      if (c_state == IDLE && write_en == 1'b1)        sdr_cmd <= `ACT;      else        if (c_state == ACT_STATE && cnt == `Trcd - 1'b1)          sdr_cmd <= `WR;        else          sdr_cmd <= `NOP;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      sdr_addr <= 0;    else      if (c_state == IDLE && write_en == 1'b1)        sdr_addr <= wr_addr[21:9];      else        if (c_state == ACT_STATE && cnt == `Trcd - 1'b1) begin          sdr_addr[10] <= 1'b1;          sdr_addr[8:0] <= wr_addr[8:0];        end        else          sdr_addr <= 0;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      cnt <= 4'd0;    else      if (c_state == ACT_STATE && cnt < `Trcd - 1'b1)        cnt <= cnt + 1'b1;      else        if (c_state == WR2 && cnt < `Twr + `Trp - 1'b1)          cnt <= cnt + 1'b1;        else          cnt <= 4'd0;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      odq <= 16'd0;    else      if (c_state == ACT_STATE && cnt == `Trcd - 1'b1)        odq <= wr_data[15:0];      else        if (c_state == WR1)          odq <= wr_data[31:16];        else          odq <= 16'd0;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      wr_done <= 1'b0;    else      if (c_state == WR2 && cnt < `Twr + `Trp - 1'b1)        wr_done <= 1'b1;      else        wr_done <= 1'b0;  end
endmodule

sdr_read設(shè)計實現(xiàn)

該模塊負(fù)責(zé)從指定的地址中,將數(shù)據(jù)讀出。

按照對應(yīng)的讀時序圖即可實現(xiàn)功能,本模塊采用狀態(tài)機(jī)方式實現(xiàn),狀態(tài)轉(zhuǎn)移圖如下:

設(shè)計代碼為:

module sdr_read (
  input   wire                              clk,  input   wire                              rst_n,
  input   wire                              read_en,  input   wire    [`ADDR_WIDTH - 1 : 0]     rd_addr,
  input   wire    [15:0]                    sdr_dq,
  output  reg     [31:0]                    rd_data,  output  reg                               rd_done,
  output  wire    [`BUS_WIDTH - 1 : 0]      rd_bus);
  localparam    IDLE            =           5'b00001;  localparam    ACT_STATE       =           5'b00010;  localparam    READ_STATE      =           5'b00100;  localparam    RD1             =           5'b01000;  localparam    RD2             =           5'b10000;
  reg                   [4:0]                     c_state;  reg                   [4:0]                     n_state;  wire                  [1:0]                     sdr_bank;  reg                   [3:0]                     sdr_cmd;  reg                   [`SDR_ADDR_WIDTH - 1 : 0] sdr_addr;  reg                   [3:0]                     cnt;                    
  assign sdr_bank = rd_addr[23:22];  assign rd_bus = {sdr_cmd,sdr_bank,sdr_addr};
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      c_state <= IDLE;    else      c_state <= n_state;  end
  always @ * begin    case (c_state)      IDLE        :   begin        if (read_en == 1'b1)          n_state = ACT_STATE;        else          n_state = IDLE;      end
      ACT_STATE   :   begin        if (cnt == `Trcd - 1'b1)          n_state = READ_STATE;        else          n_state = ACT_STATE;      end
      READ_STATE  :   begin        if (cnt == `Tcl)          n_state = RD1;        else          n_state = READ_STATE;      end
      RD1         :   n_state = RD2;
      RD2         :   begin        if (cnt == `Trp - 1'b1)          n_state = IDLE;        else          n_state = RD2;      end
      default     :   n_state = IDLE;    endcase  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      sdr_cmd <= `NOP;    else      if (c_state == IDLE && read_en == 1'b1)        sdr_cmd <= `ACT;      else        if (c_state == ACT_STATE && cnt == `Trcd - 1'b1)          sdr_cmd <= `RD;        else          sdr_cmd <= `NOP;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      sdr_addr <= 0;    else      if (c_state == IDLE && read_en == 1'b1)        sdr_addr <= rd_addr[21:9];      else        if (c_state == ACT_STATE && cnt == `Trcd - 1'b1) begin          sdr_addr[10] <= 1'b1;          sdr_addr[8:0] <= rd_addr[8:0];        end        else          sdr_addr <= 0;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      cnt <= 4'd0;    else      case (c_state)        IDLE      :     cnt <= 4'd0;        ACT_STATE :     begin          if (cnt < `Trcd - 1'b1)            cnt <= cnt + 1'b1;          else            cnt <= 4'd0;        end        READ_STATE:     begin          if (cnt < `Tcl)            cnt <= cnt + 1'b1;          else            cnt <= 4'd0;        end
        RD1       :     cnt <= 4'd0;        RD2       :     begin          if (cnt < `Trp - 1'b1)            cnt <= cnt + 1'b1;          else            cnt <= 4'd0;        end        default   :     cnt <= 4'd0;      endcase  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rd_data <= 32'd0;    else      if (c_state == READ_STATE && cnt == `Tcl)        rd_data[15:0] <= sdr_dq;      else        if (c_state == RD1)          rd_data[31:16] <= sdr_dq;        else          rd_data <= rd_data;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rd_done <= 1'b0;    else      if (c_state == RD2 && cnt < `Trp - 1'b1)        rd_done <= 1'b1;      else        rd_done <= 1'b0;  end
endmodule

mux4_1設(shè)計實現(xiàn)

該模塊負(fù)責(zé)選擇出對應(yīng)的bus,然后將對應(yīng)位作為輸出即可。

設(shè)計代碼為:

module mux4_1 (
  input   wire  [`BUS_WIDTH - 1 : 0]      init_bus,  input   wire  [`BUS_WIDTH - 1 : 0]      refresh_bus,  input   wire  [`BUS_WIDTH - 1 : 0]      wr_bus,  input   wire  [`BUS_WIDTH - 1 : 0]      rd_bus,  input   wire  [1:0]                     mux_sel,
  output  wire  [1 : 0]                   sdr_bank,  output  wire  [`ADDR_WIDTH - 1 : 0]     sdr_addr,  output  wire                            sdr_cs_n,  output  wire                            sdr_ras_n,  output  wire                            sdr_cas_n,  output  wire                            sdr_we_n);
  reg           [`BUS_WIDTH - 1 : 0]      sdr_bus;
  assign sdr_cs_n = sdr_bus[18];  assign sdr_ras_n = sdr_bus[17];  assign sdr_cas_n = sdr_bus[16];  assign sdr_we_n = sdr_bus[15];
  assign sdr_bank = sdr_bus[14:13];
  assign sdr_addr = sdr_bus[12:0];
  always @ * begin    case (mux_sel)      2'b00       :   sdr_bus = init_bus;      2'b01       :   sdr_bus = refresh_bus;      2'b10       :   sdr_bus = wr_bus;      2'b11       :   sdr_bus = rd_bus;      default     :   sdr_bus = init_bus;    endcase  end
endmodule

sdr_ctrl設(shè)計實現(xiàn)

該模塊負(fù)責(zé)調(diào)度整個控制器,利用狀態(tài)機(jī)實現(xiàn)。

設(shè)計代碼為:

`include "../rtl/sdr_drive_head.v"
module sdr_ctrl (
  input   wire                          clk,  input   wire                          rst_n,
  input   wire                          wr_en,  input   wire                          rd_en,  input   wire    [`ADDR_WIDTH - 1 : 0] addr,  input   wire    [31:0]                wdata,  output  reg     [31:0]                rdata,  output  reg                           rd_valid,
  output  wire                          sdr_busy,
  output  reg     [1:0]                 mux_sel,
  output  reg                           init_en,  input   wire                          init_done,
  output  reg                           time_en,  input   wire                          refresh_req,  output  reg                           req_clr,
  output  reg                           refresh_en,  input   wire                          refresh_done,
  output  reg                           out_en,  output  reg                           write_en,  output  reg     [`ADDR_WIDTH - 1 : 0] wr_addr,  output  reg     [31:0]                wr_data,  input   wire                          wr_done,
  output  reg                           read_en,  output  reg     [`ADDR_WIDTH - 1 : 0] rd_addr,  input   wire    [31:0]                rd_data,  input   wire                          rd_done);
  localparam      IDLE            =     6'b000_001;  localparam      INIT_STATE      =     6'b000_010;  localparam      REFRESH_STATE   =     6'b000_100;  localparam      NO_BUSY         =     6'b001_000;  localparam      WR_STATE        =     6'b010_000;  localparam      RD_STATE        =     6'b100_000;
  reg             [5:0]                 c_state;  reg             [5:0]                 n_state;  reg                                   wren;  reg                                   wren_clr;  reg                                   rden;  reg                                   rden_clr;  reg             [`ADDR_WIDTH - 1 : 0] addrr;  reg             [31:0]                wdatar;  reg                                   busy;
  assign sdr_busy = busy | rd_en | wr_en;
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      wren <= 1'b0;    else      if (wr_en == 1'b1)        wren <= 1'b1;      else        if (wren_clr == 1'b1)          wren <= 1'b0;        else          wren <= wren;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rden <= 1'b0;    else      if (rd_en == 1'b1)        rden <= 1'b1;      else        if (rden_clr == 1'b1)          rden <= 1'b0;        else          rden <= rden;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      wdatar <= 32'd0;    else      if (wr_en == 1'b1)        wdatar <= wdata;      else        wdatar <= wdatar;  end 
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      addrr <= 0;    else      if (wr_en == 1'b1 || rd_en == 1'b1)        addrr <= addr;      else        addrr <= addrr;  end 
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      c_state <= IDLE;    else      c_state <= n_state;  end
  always @ * begin    case (c_state)      IDLE              :   n_state = INIT_STATE;
      INIT_STATE        :   begin        if (init_done == 1'b1)          n_state = REFRESH_STATE;        else          n_state = INIT_STATE;      end
      REFRESH_STATE     :   begin        if (refresh_done == 1'b1)          n_state = NO_BUSY;        else          n_state = REFRESH_STATE;      end
      NO_BUSY           :   begin        if (refresh_req == 1'b1)          n_state = REFRESH_STATE;        else          if (wren == 1'b1)            n_state = WR_STATE;          else            if (rden == 1'b1)              n_state = RD_STATE;            else              n_state = NO_BUSY;      end
      WR_STATE          :   begin        if (wr_done == 1'b1)          n_state = NO_BUSY;        else          n_state = WR_STATE;      end
      RD_STATE          :   begin        if (rd_done == 1'b1)          n_state = NO_BUSY;        else          n_state = RD_STATE;      end
      default           :   n_state = IDLE;    endcase  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      busy <= 1'b1;    else      if (c_state == NO_BUSY && wren == 1'b0 && rden == 1'b0 && refresh_req == 1'b0)        busy <= rd_en | wr_en;      else        busy <= 1'b1;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      init_en <= 1'b0;    else      if (c_state == IDLE)        init_en <= 1'b1;      else        init_en <= 1'b0;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      time_en <= 1'b0;    else      if (c_state == INIT_STATE && init_done == 1'b1)        time_en <= 1'b1;      else        time_en <= time_en;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      refresh_en <= 1'b0;    else      if (c_state == INIT_STATE && init_done == 1'b1)        refresh_en <= 1'b1;      else        if (c_state == NO_BUSY && refresh_req == 1'b1)          refresh_en <= 1'b1;        else          refresh_en <= 1'b0;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      req_clr <= 1'b0;    else      if (c_state == NO_BUSY && refresh_req == 1'b1)        req_clr <= 1'b1;      else        req_clr <= 1'b0;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      write_en <= 1'b0;    else      if (c_state == NO_BUSY && refresh_req == 1'b0 && wren == 1'b1)        write_en <= 1'b1;     else        write_en <= 1'b0;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      out_en <= 1'b0;    else      if (c_state == NO_BUSY && refresh_req == 1'b0 && wren == 1'b1)        out_en <= 1'b1;     else        if (c_state == WR_STATE && wr_done == 1'b1)          out_en <= 1'b0;        else          out_en <= out_en;  end 
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      wr_addr <= 0;    else      if (c_state == NO_BUSY && refresh_req == 1'b0 && wren == 1'b1)        wr_addr <= addrr;     else        wr_addr <= wr_addr;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      wr_data <= 0;    else      if (c_state == NO_BUSY && refresh_req == 1'b0 && wren == 1'b1)        wr_data <= wdatar;     else        wr_data <= wr_data;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      wren_clr <= 1'b0;    else      if (c_state == NO_BUSY && refresh_req == 1'b0 && wren == 1'b1)        wren_clr <= 1'b1;     else        wren_clr <= 1'b0;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rden_clr <= 1'b0;    else      if (c_state == NO_BUSY && refresh_req == 1'b0 && wren == 1'b0 && rden == 1'b1)        rden_clr <= 1'b1;     else        rden_clr <= 1'b0;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      read_en <= 1'b0;    else      if (c_state == NO_BUSY && refresh_req == 1'b0 && wren == 1'b0 && rden == 1'b1)        read_en <= 1'b1;     else        read_en <= 1'b0;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rd_addr <= 0;    else      if (c_state == NO_BUSY && refresh_req == 1'b0 && wren == 1'b0 && rden == 1'b1)        rd_addr <= addrr;     else        rd_addr <= rd_addr;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rdata <= 32'd0;    else      if (c_state == RD_STATE && rd_done == 1'b1)        rdata <= rd_data;      else        rdata <= rdata;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rd_valid <= 1'b0;    else      if (c_state == RD_STATE && rd_done == 1'b1)        rd_valid <= 1'b1;      else        rd_valid <= 1'b0;  end 
  always @ (posedge clk, negedge rst_n)  begin    if (rst_n == 1'b0)      mux_sel <= 2'b00;    else      case (c_state)        IDLE        :       mux_sel <= 2'b00;        INIT_STATE  :       begin          if (init_done == 1'b1)            mux_sel <= 2'b01;          else              mux_sel <= mux_sel;        end        REFRESH_STATE:      mux_sel <= mux_sel;        NO_BUSY     :       begin          if (refresh_req == 1'b1)            mux_sel <= 2'b01;          else            if (wren == 1'b1)              mux_sel <= 2'b10;            else              if (rden == 1'b1)                mux_sel <= 2'b11;              else                mux_sel <= mux_sel;        end        RD_STATE    :   mux_sel <= mux_sel;        WR_STATE    :   mux_sel <= mux_sel;
        default     :   mux_sel <= 2'b00;      endcase  end
endmodule

為了防止在進(jìn)行刷新的起始部分丟失讀寫命令,所以在設(shè)計時,加入了緩存結(jié)構(gòu),只要有讀寫命令時,都會進(jìn)行保存。在讀寫執(zhí)行時,才會清除此命令。

RTL仿真

為了能夠仿真此設(shè)計,需要用到SDR SDRAM的仿真模型。仿真模型在msim的sdr_sim_module中,將其修改為行線為13bit,列為9bit,每個bank有4194304個存儲空間。

在仿真時,在第二個bank,第五行,第10列,寫入一個隨機(jī)值。然后讀取出來。

仿真代碼為:

`timescale 1ns/1ps
module sdr_drive_tb;
  reg                     clk;  reg                     rst_n;
  wire                    sys_clk;  wire                    sys_rst_n;
  wire                    sdr_busy;  reg                     wr_en;  reg                     rd_en;  reg       [23:0]        addr;  reg       [31:0]        wdata;  wire      [31:0]        rdata;  wire                    rd_valid;
  wire                    sdr_clk;  wire                    sdr_cke;  wire                    sdr_cs_n;  wire                    sdr_ras_n;  wire                    sdr_cas_n;  wire                    sdr_we_n;  wire      [15:0]        sdr_dq;  wire      [1:0]         sdr_bank;  wire      [1:0]         sdr_dqm;  wire      [12:0]        sdr_addr;
  sdr_drive sdr_drive_inst(
      .clk                  (clk),      .rst_n                (rst_n),
      .sys_clk              (sys_clk),      .sys_rst_n            (sys_rst_n),
    //  local      .sdr_busy             (sdr_busy),      .wr_en                (wr_en),      .rd_en                (rd_en),      .addr                 (addr),      .wdata                (wdata),      .rdata                (rdata),      .rd_valid             (rd_valid),
    //  sdr      .sdr_clk              (sdr_clk),      .sdr_cke              (sdr_cke),      .sdr_cs_n             (sdr_cs_n),      .sdr_ras_n            (sdr_ras_n),      .sdr_cas_n            (sdr_cas_n),      .sdr_we_n             (sdr_we_n),      .sdr_bank             (sdr_bank),      .sdr_addr             (sdr_addr),      .sdr_dqm              (sdr_dqm),      .sdr_dq               (sdr_dq)    );
  mt48lc32m16a2 mt48lc32m16a2_inst(      .Dq                   (sdr_dq),       .Addr                 (sdr_addr),       .Ba                   (sdr_bank),       .Clk                  (sdr_clk),       .Cke                  (sdr_cke),       .Cs_n                 (sdr_cs_n),       .Ras_n                (sdr_ras_n),       .Cas_n                (sdr_cas_n),       .We_n                 (sdr_we_n),       .Dqm                  (sdr_dqm)    );
  initial clk = 1'b0;  always # 10 clk = ~clk;
  initial begin    rst_n = 1'b0;    wr_en = 1'b0;    rd_en = 1'b0;    addr = {2'b01, 13'd5,9'd10};    wdata = 32'd0;    # 201    rst_n = 1'b1;    @ (negedge sdr_busy);    @ (posedge sys_clk);    # 2;    wr_en = 1'b1;    wdata = $random;    @ (posedge sys_clk);    # 2;    wr_en = 1'b0;    # 2000;    @ (negedge sdr_busy);    @ (posedge sys_clk);    # 2;    rd_en = 1'b1;    @ (posedge sys_clk);    # 2;    rd_en = 1'b0;    # 2000;    $stop;  end
endmodule

這設(shè)置激勵時,將tb文件和仿真模型文件同時加入添加文件中。

在modelsim的報告界面會顯示出具體的配置信息以及讀寫信息。

從打印的報告中可以看出,在初始化時,列選通潛伏期為2,突發(fā)長度為2。在后續(xù)的讀寫時,在指定的位置,寫入了13604,后續(xù)的一個位置為4629;在讀出時,也正確的讀出了數(shù)據(jù)。

報告打印出寫入數(shù)據(jù),即認(rèn)為寫入成功;報告打印出讀出數(shù)據(jù),只能證明控制器將數(shù)據(jù)讀出,并不表示控制器能把數(shù)據(jù)接收到。

通過控制輸出的rdata以及對應(yīng)的rd_valid信號,確定讀出成功。在rdata中顯示為16進(jìn)制,16進(jìn)制的1215為十進(jìn)制的4629;16進(jìn)制的3524的為十進(jìn)制的13604。證明讀數(shù)據(jù)接收正確。

板級測試

編寫控制器的上游模塊(sdr_drive_test_crtl),控制寫入和讀出。在固定的地址中addr = {2'b01, 13'd128, 9'd20},寫入一個固定的數(shù)字wdata = 32'h5a5aa5a5,然后讀出,進(jìn)行驗證。

讀者在進(jìn)行驗證時,可以采樣其他的地址或者數(shù)據(jù)進(jìn)行驗證,且可以進(jìn)行多次嘗試,保證設(shè)計正確。

該模塊采用狀態(tài)機(jī)設(shè)計實現(xiàn)。

設(shè)計代碼為:

`include "../rtl/sdr_drive_head.v"
module sdr_drive_test_ctrl (
  input     wire                                    clk,  input     wire                                    rst_n,
  input     wire                                    sdr_busy,  output    reg                                     wr_en,  output    reg                                     rd_en,  output    wire      [31:0]                        wdata,  input     wire                                    rd_valid,  input     wire      [31:0]                        rdata,  output    wire      [`ADDR_WIDTH - 1 : 0]         addr);
  localparam          IDLE        =   4'b0001;  localparam          WR_STATE    =   4'b0010;  localparam          RD_STATE    =   4'b0100;  localparam          TEST_DONE   =   4'b1000;
  reg                 [3:0]         c_state;  reg                 [3:0]         n_state;
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      c_state <= IDLE;    else      c_state <= n_state;  end
  always @ * begin    case (c_state)      IDLE          :   begin        if (sdr_busy == 1'b0)          n_state = WR_STATE;        else          n_state = IDLE;      end
      WR_STATE      :   begin        if (sdr_busy == 1'b0)          n_state = RD_STATE;        else          n_state = WR_STATE;      end
      RD_STATE      :   begin        if (rd_valid == 1'b1 && rdata == 32'h5a5aa5a5)          n_state = TEST_DONE;        else          n_state = RD_STATE;      end
      TEST_DONE     :   n_state = TEST_DONE;

      default       :   n_state = IDLE;    endcase  end
  assign wdata = 32'h5a5aa5a5;  assign addr = {2'b01, 13'd128, 9'd20};
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      wr_en <= 1'b0;    else      if (c_state == IDLE && sdr_busy == 1'b0)        wr_en <= 1'b1;      else        wr_en <= 1'b0;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rd_en <= 1'b0;    else      if (c_state == WR_STATE && sdr_busy == 1'b0)        rd_en <= 1'b1;      else        rd_en <= 1'b0;  end
endmodule

編寫測試頂層,模塊命名為sdr_drive_test,并且設(shè)置為頂層。

此模塊負(fù)責(zé)例化sdr_drive和sdr_drive_test_ctrl,完成連接功能,以此測試。

代碼為:

`include "../rtl/sdr_drive_head.v"
module sdr_drive_test (
  input     wire                                    clk,  input     wire                                    rst_n,
//  sdr  output    wire                                    sdr_clk,  output    wire                                    sdr_cke,  output    wire                                    sdr_cs_n,  output    wire                                    sdr_ras_n,  output    wire                                    sdr_cas_n,  output    wire                                    sdr_we_n,  output    wire      [1:0]                         sdr_bank,  output    wire      [`SDR_ADDR_WIDTH - 1 : 0]     sdr_addr,  output    wire      [1:0]                         sdr_dqm,  inout     wire      [15:0]                        sdr_dq);
  wire                                    sys_clk;  wire                                    sys_rst_n;
//  local  wire                                    sdr_busy;  wire                                    wr_en;  wire                                    rd_en;  wire      [`ADDR_WIDTH - 1 : 0]         addr;  wire      [31:0]                        wdata;  wire      [31:0]                        rdata;  wire                                    rd_valid;
  sdr_drive_test_ctrl sdr_drive_test_ctrl_inst(
      .clk                (sys_clk),      .rst_n              (sys_rst_n),
      .sdr_busy           (sdr_busy),      .wr_en              (wr_en),      .rd_en              (rd_en),      .wdata              (wdata),      .rd_valid           (rd_valid),      .rdata              (rdata),      .addr               (addr)    );
  sdr_drive sdr_drive_inst(
      .clk                (clk),      .rst_n              (rst_n),
      .sys_clk            (sys_clk),      .sys_rst_n          (sys_rst_n),
    //  local      .sdr_busy           (sdr_busy),      .wr_en              (wr_en),      .rd_en              (rd_en),      .addr               (addr),      .wdata              (wdata),      .rdata              (rdata),      .rd_valid           (rd_valid),
    //  sdr      .sdr_clk            (sdr_clk),      .sdr_cke            (sdr_cke),      .sdr_cs_n           (sdr_cs_n),      .sdr_ras_n          (sdr_ras_n),      .sdr_cas_n          (sdr_cas_n),      .sdr_we_n           (sdr_we_n),      .sdr_bank           (sdr_bank),      .sdr_addr           (sdr_addr),      .sdr_dqm            (sdr_dqm),      .sdr_dq             (sdr_dq)    );
endmodule

經(jīng)過綜合分析后,進(jìn)行分配管腳。在分配管腳后,需要將雙功能管腳中的NCEO設(shè)置為普通用戶IO。如果不設(shè)置,將會出現(xiàn)如下錯誤:

右擊器件名稱,選擇DEVICE。

選擇device and pin option。

選擇dual – purpose pins。

將nceo設(shè)置為 use as regular IO。

點擊OK,進(jìn)行編譯即可。

連接上開發(fā)板,啟動邏輯分析儀。

將采樣時鐘選擇為,sys_clk(PLL的c0)。采樣深度選擇為1K。

添加觀測信號如下,將wr_en的上升沿設(shè)置為觸發(fā)條件。

經(jīng)過保存,重新形成配置文件后,進(jìn)行下板測試。

下板后,按下復(fù)位。等待波形觸發(fā)。

通過邏輯分析儀,就可以看出可以正確的寫入和讀出數(shù)據(jù)。

讀者也可以進(jìn)行嘗試一次性寫入多個數(shù)據(jù),然后進(jìn)行讀出,進(jìn)行驗證設(shè)計的正確性。

推薦器件

更多器件
器件型號 數(shù)量 器件廠商 器件描述 數(shù)據(jù)手冊 ECAD模型 風(fēng)險等級 參考價格 更多信息
XC2C384-10FTG256I 1 AMD Xilinx Flash PLD, 10ns, 384-Cell, CMOS, PBGA256, 17 X 17 MM, 1 MM PITCH, LEAD FREE, FTBGA-256
$596.21 查看
EPM570T100C5N 1 Intel Corporation Flash PLD, 8.7ns, 440-Cell, CMOS, PQFP100, 16 X 16 MM, 0.50 MM PITCH, LEAD FREE, TQFP-100

ECAD模型

下載ECAD模型
$23.38 查看
ICE40LP8K-CM225 1 Lattice Semiconductor Corporation Field Programmable Gate Array, 960 CLBs, 133MHz, 7680-Cell, CMOS, PBGA225, UCBGA-225
$10.96 查看

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

任何技術(shù)的學(xué)習(xí)就好比一個江湖,對于每一位俠客都需要不斷的歷練,從初入江湖的小白到歸隱山林的隱世高人,需要不斷的自我感悟自己修煉,讓我們一起仗劍闖FPGA乃至更大的江湖。