LoginSignup
0
0

More than 1 year has passed since last update.

NiosIIでSDRAMを追加する際の設定ポイント

Posted at

この記事の概要

Intel FPGA搭載のDe0-Nanoボードにて、NiosIIへSDRAMを追加する際の設定について、試して理解したことを書き残します。

使用したもの 開発環境:Windows10Professional 開発ツール:Quartus Prime 18.1 Lite Edition ターゲットボード:DE0-Nano Development Board 参考書:小林優『改定2版 FPGAボードで学ぶ組込みシステム開発入門[Intel FPGA編]』技術評論社

結論

NiosIIプラットフォームへPLLを組み込む

  • 使用IP:「Basic Functions」→「Clocks; PLLs and Resets」→「PLL」→「ALTPLL Intel FPGA IP」
  • PLLから2つのクロックを出力する
    • 一つはAvalonバスクロックとして使う
    • もう一つの出力クロックをSDRAMへの物理的クロックとして使う

NiosIIプラットフォームへSDRAMコントローラーを組み込む

  • 使用IP:「Memory Interfaces and Controlls」→「SDRAM」→「SDRAM Controller Intel FPGA IP」
  • DE0-Nano Development Boardに搭載されたSDRAMに合わせた設定を入力する
    • 「Memory Profile」タブはSDRAMのデータシートを見ればすぐに入力できる
    • 「Timing」タブはよくわからないので、DE0-Nano Development Board付属のサンプルプロジェクトの値を拝借

NiosIIソフトウェアでSDRAMへの読み書きを実装する

  • ソフトウェアから見るとAvalonバススレーブのレジスタへの読み書きに過ぎない

詳細

まずはPlatform Designerにて、基本的な構成のNiosIIプラットフォームまで構築したものとします。
基本的な構成とは下記のモジュールで構成された必要最低限のシステムのこととします。

  • clk_0
  • nios2_gen2_0
  • onchip_memory2_0
  • jtag_uart_0
  • sysid_qsys_0

図1.png

NiosIIプラットフォームへPLLを組み込む

IP Catalogから、下記のツリーを辿ってIPをNiosIIプラットフォームへ追加します。

  • 「Basic Functions」→「Clocks; PLLs and Resets」→「PLL」→「ALTPLL Intel FPGA IP」

追加したらPLLを設定。

  • 入力クロックは50MHz 図2.png
  • リセット信号は入力しない 図3.png
  • 逓倍出力したいクロックは100MHz 図4.png
  • もう一つの逓倍出力したいクロックは100MHzで位相を3000ps早める。(位相をずらす理由は後述)
    • 位相を遅らせる方をプラスとして設定するため、早める方向はマイナスとなります。 図5.png

次にNiosIIプラットフォームのクロックの付替えを行います。
元々clk_0のclkをAvalonバスへ供給していましたが、それを全部外します。外したclkはaltpll_0のinclk_interfaceへ繋ぎ、altpll_0のc0出力をAvalonバスへ供給します。これでAvalonバスのクロックはPLLで設定した逓倍出力100MHzに置き換わりまし
た。
また、PLLで設定したもう一つの逓倍出力100MHzで位相3000ps早めたものは、altpll_0_c1として外部出力にし、SDRAMへの物理的クロックとします。(ついでにPLLのフェーズロック信号もaltpll_0_locked_conduitとして外部出力しておきましたが、この後特に使う用途は無いのであまり気にせずに。。。)
図6.png

NiosIIプラットフォームへSDRAMコントローラーを組み込む

IP Catalogから、下記のツリーを辿ってIPをNiosIIプラットフォームへ追加します。

  • 「Memory Interfaces and Controlls」→「SDRAM」→「SDRAM Controller Intel FPGA IP」

追加したらDE0-Nano Development Boardに搭載されたSDRAM(品名:IS42S16160G-7TLI)に合わせた設定を入力。

  • 「Memory Profile」タブはSDRAMのデータシートを見ればすぐに入力できる内容ばかりです。
    図7.png

  • 一方で「Timing」タブに設定する数値は何が適切なのか解らずじまいでした。仕方なくDE0-Nano Development Board付属のサンプルプロジェクトの値を拝借してみました。

    • SDRAMの仕様をしっかり理解していればデータシートから読み取れると思うので、ここは今後の勉強テーマです。

図8.png

最後にAvalonバス上の接続を行います。

  • new_sdram_controller_0のwireは、Export列をダブルクリックして、外部との入出力ができるようにしておきます。
    • この信号が物理的なSDRAMとつながる信号線です。 図9.png

こうしてできたNiosIIプラットフォームを、最上位階層にてインスタンスして、論理合成、配置配線を実施します。

最上位階層
module DE0nano
(
    // CLOCK
    input        CLOCK_50,
    // LED
    output[7:0]  LED,
    // KEY
    input [1:0]  KEY,
    // SW
    input [3:0]  SW,
    // SDRAM
    output[12:0] DRAM_ADDR,
    output[1:0]  DRAM_BA,
    output       DRAM_CAS_N,
    output       DRAM_CKE,
    output       DRAM_CLK,
    output       DRAM_CS_N,
    inout [15:0] DRAM_DQ,
    output[1:0]  DRAM_DQM,
    output       DRAM_RAS_N,
    output       DRAM_WE_N,
    // EPCS
    output       EPCS_ASDO,
    input        EPCS_DATA0,
    output       EPCS_DCLK,
    output       EPCS_NCSO,
    // Accelerometer and EEPROM
    output       G_SENSOR_CS_N,
    input        G_SENSOR_INT,
    output       I2C_SCLK,
    inout        I2C_SDAT,
    // ADC
    output       ADC_CS_N,
    output       ADC_SADDR,
    output       ADC_SCLK,
    input        ADC_SDAT,
    // 2x13 GPIO Header
    inout [12:0] GPIO_2,
    input [2:0]  GPIO_2_IN,
    // GPIO_0, GPIO_0 connect to GPIO Default
    inout [33:0] GPIO_0,
    input [1:0]  GPIO_0_IN,
    // GPIO_1, GPIO_1 connect to GPIO Default
    inout [33:0] GPIO_1,
    input [1:0]  GPIO_1_IN
);
//-----------------------------------------------------------------------------
    // CPU SubSystemのインスタンス
    cpuss u0 (
        .clk_clk                          (CLOCK_50),
        .reset_reset_n                    (KEY[0]),
        .altpll_0_locked_conduit_export   (LED[7]),
        .altpll_0_c1_clk                  (DRAM_CLK),
        .new_sdram_controller_0_wire_addr (DRAM_ADDR),
        .new_sdram_controller_0_wire_ba   (DRAM_BA),
        .new_sdram_controller_0_wire_cas_n(DRAM_CAS_N),
        .new_sdram_controller_0_wire_cke  (DRAM_CKE),
        .new_sdram_controller_0_wire_cs_n (DRAM_CS_N),
        .new_sdram_controller_0_wire_dq   (DRAM_DQ),
        .new_sdram_controller_0_wire_dqm  (DRAM_DQM),
        .new_sdram_controller_0_wire_ras_n(DRAM_RAS_N),
        .new_sdram_controller_0_wire_we_n (DRAM_WE_N)
    );
//=============================================================================
endmodule

NiosIIソフトウェアでSDRAMへの読み書きを実装する

Eclipseを起動してBSPとアプリケーションのソフトウェアをビルドします。
アプリケーションのソフトウェアはこんな感じです。SDRAMへの読み書きチェックをして、デバッグコンソールへエラーの有無を表示するだけのお試しソフトです。
こちらを見てもらえればおわかりいただけると思いますが、SDRAMへのアクセスも、単なるAvalonバス上のレジスタリードライトとなっています。

SDRAM読み書きチェック
#include "system.h"
#include "io.h"
#include "sys/alt_cache.h"
#include "sys/alt_stdio.h"

#define MEMSIZE 0x02000000

int main() {
    //---- to check SDRAM ----
    unsigned i;
    unsigned long err=0;

    unsigned int i1;
    unsigned int i2;
    unsigned int i3;
    unsigned int ary[]={
        0x47BD0F6A,
        0x1E806C95,
        0x2FDC3A5C
    };

    // SDRAMへ書き込み:1BYTEずつ
    for(i=0; i<MEMSIZE-3; i=i+3){
        IOWR_8DIRECT(NEW_SDRAM_CONTROLLER_0_BASE, i  , (unsigned char)ary[0]);
        IOWR_8DIRECT(NEW_SDRAM_CONTROLLER_0_BASE, i+1, (unsigned char)ary[1]);
        IOWR_8DIRECT(NEW_SDRAM_CONTROLLER_0_BASE, i+2, (unsigned char)ary[2]);
    }
    alt_dcache_flush_all();

    // SDRAMから読み出し:1BYTEずつ
    err=0;
    for(i=0; i<MEMSIZE-3; i=i+3){
        i1=IORD_8DIRECT(NEW_SDRAM_CONTROLLER_0_BASE, i  );
        i2=IORD_8DIRECT(NEW_SDRAM_CONTROLLER_0_BASE, i+1);
        i3=IORD_8DIRECT(NEW_SDRAM_CONTROLLER_0_BASE, i+2);

        if ( i1!=(ary[0]&0xFF) ) err|= 0x11;
        if ( i2!=(ary[1]&0xFF) ) err|=(0x12<<8);
        if ( i3!=(ary[2]&0xFF) ) err|=(0x13<<16);
    }
    alt_printf("err=0x%x\n",err);


    // SDRAMへ書き込み:2BYTEずつ
    for(i=0; i<MEMSIZE-6; i=i+6){
        IOWR_16DIRECT(NEW_SDRAM_CONTROLLER_0_BASE, i  , (unsigned short)ary[0]);
        IOWR_16DIRECT(NEW_SDRAM_CONTROLLER_0_BASE, i+2, (unsigned short)ary[1]);
        IOWR_16DIRECT(NEW_SDRAM_CONTROLLER_0_BASE, i+4, (unsigned short)ary[2]);
    }
    alt_dcache_flush_all();

    // SDRAMから読み出し:2BYTEずつ
    err=0;
    for(i=0; i<MEMSIZE-6; i=i+6){
        i1=IORD_16DIRECT(NEW_SDRAM_CONTROLLER_0_BASE, i  );
        i2=IORD_16DIRECT(NEW_SDRAM_CONTROLLER_0_BASE, i+2);
        i3=IORD_16DIRECT(NEW_SDRAM_CONTROLLER_0_BASE, i+4);

        if ( i1!=(ary[0]&0xFFFF) ) err|= 0x21;
        if ( i2!=(ary[1]&0xFFFF) ) err|=(0x22<<8);
        if ( i3!=(ary[2]&0xFFFF) ) err|=(0x23<<16);
    }
    alt_printf("err=0x%x\n",err);


    // SDRAMへ書き込み:4BYTEずつ
    for(i=0; i<MEMSIZE-12; i=i+12){
        IOWR_32DIRECT(NEW_SDRAM_CONTROLLER_0_BASE, i  , (unsigned long)ary[0]);
        IOWR_32DIRECT(NEW_SDRAM_CONTROLLER_0_BASE, i+4, (unsigned long)ary[1]);
        IOWR_32DIRECT(NEW_SDRAM_CONTROLLER_0_BASE, i+8, (unsigned long)ary[2]);
    }
    alt_dcache_flush_all();

    // SDRAMから読み出し:4BYTEずつ
    err=0;
    for(i=0; i<MEMSIZE-12; i=i+12){
        i1=IORD_32DIRECT(NEW_SDRAM_CONTROLLER_0_BASE, i  );
        i2=IORD_32DIRECT(NEW_SDRAM_CONTROLLER_0_BASE, i+4);
        i3=IORD_32DIRECT(NEW_SDRAM_CONTROLLER_0_BASE, i+8);

        if ( i1!=ary[0] ) err|= 0x31;
        if ( i2!=ary[1] ) err|=(0x32<<8);
        if ( i3!=ary[2] ) err|=(0x33<<16);
    }
    alt_printf("err=0x%x\n",err);
    //---- end check SDRAM ----

    while(1){
        // do nothing ...
    }
    return 0;
}

あとがき

この記事ではEclipseを起動してBSPとアプリケーションのソフトウェアをビルドします、とさらっと述べましたが、ここでかなりハマりました。Eclipseは絶対パスで関連ファイルが記録されていることに起因するポカミスが多発したというのが概要ですが・・。後日、この辺を整理して記事にしたいと思います。

0
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0