
ハードウェア編の記事で、SDRAMコントローラやniosなどをPlatform Designerを使って実装し、DE0-NanoのFPGAに書き込みました。
今回は、Nios II Software Build Tools for Eclipseを使って、SDRAMにデータをリード・ライトするniosのプログラムを作っていきたいと思います。
【改訂2版】FPGAボードで学ぶ 組込みシステム開発入門[Intel FPGA編] 新品価格 | ![]() |

事前準備
まずは、Nios II Software Build Tools for Eclipseで新規プロジェクトを作成します。
以下の記事を参考に、”soft_test_bspの設定”までを実施しておきます。
以降は、プロジェクト名を”soft_test”とした場合について記載します。
BSP Editorの設定
以下の図のようにBSP Editorを起動します。

BSP Editorが起動したら、Settings -> Commonをクリックします。
以下のようなウインドウが開くと思います。
“exception_stack_memory_region_name”と”interrupt_stack_memory_region_name”では、SDRAMを選択することもできると思いますが、ここではFPGA内蔵の”onchip_memory”を選択しました。

続いて、上の図の点線の赤枠で囲った、”Linker Script”を選択します。
下のウインドウが開いたら、上の段(Linker Section Mappings)の、Linker Region Nameで欄をonchip_memoryにします。
SDRAMも選択できると思いますが、FPGA内蔵のonchip_memoryを選択します。

それと、上の図の点線赤枠内に、SDRAMのアドレスレンジが表示されていました。
設定が終了したら、ウインドウ右下にある、”Generate”ボタンをクリックします。
処理が終わったら、”Exit”ボタンでBSP Editorを終了させます。
参考記事 第3回 Nios IIで遊ぼう Nios II Software Build Tools for Eclipse編
SDRAMへのリード・ライト
SDRAMへのリードとライトは、unsigned char、unsigned short、unsigned intの3タイプでデータを書き込み、その後にデータを読み込んで、書き込んだデータと読み込んだデータが一致しているかを確認してみたいと思います。
プログラムの実行方法は以下の参考記事を参照ください。
参考記事 第3回 Nios IIで遊ぼう Nios II Software Build Tools for Eclipse編
ソースコード
以下が今回作成したソースコードです。
#include "sys/alt_stdio.h" #include "system.h" #include <stdlib.h> #include <stdio.h> #include <time.h> #define MEMSIZE 0x2000000 int main() { int i; unsigned char random_num_char[4]; unsigned char read_data_char[4]; unsigned short random_num_short[4]; unsigned short read_data_short[4]; unsigned int random_num_int[4]; unsigned int read_data_int[4]; alt_printf("Hello SDRAM !! \n"); // // char // alt_printf("\n\n"); alt_printf("----------------------------- \n"); alt_printf("----1Byte Write/Read Test---- \n"); alt_printf("----------------------------- \n"); //Generate random number(not work properly...) srand( (unsigned int)time(NULL) ); for(i=0; i<4; i++){ random_num_char[i] = rand(); } alt_printf("\n --Generated Random Number char-- \n"); for(i=0; i<4; i++){ alt_printf("Random Number char%x = %x\n", i, random_num_char[i]); } //Write 1byte data to sdram alt_printf("\n --Write 1byte data to sdram-- \n"); *(volatile unsigned char *) (SDRAM_BASE + 0) = random_num_char[0]; *(volatile unsigned char *) (SDRAM_BASE + 1) = random_num_char[1]; *(volatile unsigned char *) (SDRAM_BASE + MEMSIZE - 2) = random_num_char[2]; *(volatile unsigned char *) (SDRAM_BASE + MEMSIZE - 1) = random_num_char[3]; //Read 1byte data from sdram alt_printf("\n --Read 1byte data from sdram-- \n"); read_data_char[0] = *(volatile unsigned char *) (SDRAM_BASE + 0); read_data_char[1] = *(volatile unsigned char *) (SDRAM_BASE + 1); read_data_char[2] = *(volatile unsigned char *) (SDRAM_BASE + MEMSIZE - 2); read_data_char[3] = *(volatile unsigned char *) (SDRAM_BASE + MEMSIZE - 1); //Verify char alt_printf("\n --Verify char-- \n"); for(i=0; i<4; i++){ if(random_num_char[i] == read_data_char[i]) alt_printf("Verify char%x = Pass\n", i); else alt_printf("Verify char%x = Fail\n", i); } // // short // alt_printf("\n\n"); alt_printf("----------------------------- \n"); alt_printf("----2Byte Write/Read Test---- \n"); alt_printf("----------------------------- \n"); //Generate random number(not work properly...) srand( (unsigned int)time(NULL) ); for(i=0; i<4; i++){ random_num_short[i] = rand(); } alt_printf("\n --Generated Random Number short-- \n"); for(i=0; i<4; i++){ alt_printf("Random Number short%x = %x\n", i, random_num_short[i]); } //Write 2byte data to sdram alt_printf("\n --Write 2byte data to sdram-- \n"); *(volatile unsigned short *) (SDRAM_BASE + 0) = random_num_short[0]; *(volatile unsigned short *) (SDRAM_BASE + 2) = random_num_short[1]; *(volatile unsigned short *) (SDRAM_BASE + MEMSIZE - 4) = random_num_short[2]; *(volatile unsigned short *) (SDRAM_BASE + MEMSIZE - 2) = random_num_short[3]; //Read 2byte data from sdram alt_printf("\n --Read 2byte data from sdram-- \n"); read_data_short[0] = *(volatile unsigned short *) (SDRAM_BASE); read_data_short[1] = *(volatile unsigned short *) (SDRAM_BASE + 2); read_data_short[2] = *(volatile unsigned short *) (SDRAM_BASE + MEMSIZE - 4); read_data_short[3] = *(volatile unsigned short *) (SDRAM_BASE + MEMSIZE - 2); //Verify short alt_printf("\n --Verify short-- \n"); for(i=0; i<4; i++){ if(random_num_short[i] == read_data_short[i]) alt_printf("Verify short%x = Pass\n", i); else alt_printf("Verify short%x = Fail\n", i); } // // int // alt_printf("\n\n"); alt_printf("----------------------------- \n"); alt_printf("----4Byte Write/Read Test---- \n"); alt_printf("----------------------------- \n"); //Generate random number(not work properly...) srand( (unsigned int)time(NULL) ); for(i=0; i<4; i++){ random_num_int[i] = rand(); } alt_printf("\n --Generated Random Number int-- \n"); for(i=0; i<4; i++){ alt_printf("Random Number int%x = %x\n", i, random_num_int[i]); } //Write 4byte data to sdram alt_printf("\n --Write 4byte data to sdram-- \n"); *(volatile unsigned int *) (SDRAM_BASE + 0) = random_num_int[0]; *(volatile unsigned int *) (SDRAM_BASE + 4) = random_num_int[1]; *(volatile unsigned int *) (SDRAM_BASE + MEMSIZE - 8) = random_num_int[2]; *(volatile unsigned int *) (SDRAM_BASE + MEMSIZE - 4) = random_num_int[3]; //Read 4byte data from sdram alt_printf("\n --Read 4byte data from sdram-- \n"); read_data_int[0] = *(volatile unsigned int *) (SDRAM_BASE); read_data_int[1] = *(volatile unsigned int *) (SDRAM_BASE + 4); read_data_int[2] = *(volatile unsigned int *) (SDRAM_BASE + MEMSIZE - 8); read_data_int[3] = *(volatile unsigned int *) (SDRAM_BASE + MEMSIZE - 4); //Verify int alt_printf("\n --Verify int-- \n"); for(i=0; i<4; i++){ if(random_num_int[i] == read_data_int[i]) alt_printf("Verify int%x = Pass\n", i); else alt_printf("Verify int%x = Fail\n", i); } return (0); }
unsigned char、unsigned short、unsigned intの3タイプとも、SDRAMの先頭アドレスとその次のアドレス、最終アドレスとその前のアドレスの4アドレスにデータをライトしてから、リードし、ライトデータとリードデータを比較しています。
コンソールウインドウに表示された実行結果
alt_printfでコンソールウインドウに表示させた実行結果です。
Hello SDRAM !! ----------------------------- ----1Byte Write/Read Test---- ----------------------------- --Generated Random Number char-- Random Number char0 = ff Random Number char1 = 74 Random Number char2 = 4d Random Number char3 = 73 --Write 1byte data to sdram-- --Read 1byte data from sdram-- --Verify char-- Verify char0 = Pass Verify char1 = Pass Verify char2 = Pass Verify char3 = Pass ----------------------------- ----2Byte Write/Read Test---- ----------------------------- --Generated Random Number short-- Random Number short0 = 8aff Random Number short1 = 974 Random Number short2 = 4c4d Random Number short3 = 7973 --Write 2byte data to sdram-- --Read 2byte data from sdram-- --Verify short-- Verify short0 = Pass Verify short1 = Pass Verify short2 = Pass Verify short3 = Pass ----------------------------- ----4Byte Write/Read Test---- ----------------------------- --Generated Random Number int-- Random Number int0 = 74438aff Random Number int1 = 71820974 Random Number int2 = 27d24c4d Random Number int3 = 62db7973 --Write 4byte data to sdram-- --Read 4byte data from sdram-- --Verify int-- Verify int0 = Pass Verify int1 = Pass Verify int2 = Pass Verify int3 = Pass
結論から言うと、ライトした値が正しくリードできたようです。
以下でソースコードの中身をもう少し詳しく見ていきます。
実行内容の詳細
疑似乱数の生成はできず
SDRAMにライトするデータは、以下のように疑似乱数で生成しようと思いました。
srand( (unsigned int)time(NULL) ); for(i=0; i<4; i++){ random_num_short[i] = rand(); }
これでデータは生成されるのですが、何回実行しても同じデータが生成されました。
なのでrand()で疑似乱数を生成することはできませんでしたが、修正はせずにこのままにしておきました。
SDRAMのアドレス
上にも書きましたが、SDRAMのリードとライトのテストに使うアドレスは、先頭アドレスとその次のアドレス、最終アドレスとその前のアドレスの4アドレスです。
Cのソースコードでは以下のように記載している部分です。
//unsigned char SDRAM_BASE + 0 //先頭アドレス SDRAM_BASE + 1 //先頭アドレスの次 SDRAM_BASE + MEMSIZE - 2 //最終アドレスの前 SDRAM_BASE + MEMSIZE - 1 //最終アドレス //unsigned short SDRAM_BASE + 0 //先頭アドレス SDRAM_BASE + 2 //先頭アドレスの次 SDRAM_BASE + MEMSIZE - 4 //最終アドレスの前 SDRAM_BASE + MEMSIZE - 2 //最終アドレス //unsigned int SDRAM_BASE + 0 //先頭アドレス SDRAM_BASE + 4 //先頭アドレスの次 SDRAM_BASE + MEMSIZE - 8 //最終アドレスの前 SDRAM_BASE + MEMSIZE - 4 //最終アドレス
ライトするデータのバイト数によって、先頭アドレスとその次のアドレス、最終アドレスとその前のアドレスの4アドレスが異なっています。
正直、ライトするデータのバイト数とアドレスについては、あまり理解できていません。。。
たしかniosは32bitのマイコンで、SDRAMのデータ幅16bit、みたいになっていると混乱してしまって整理できてないです。
そこで、整理の意味も込めて、Terasmic社のツールを使って検証してみました。
Terasic社のツールを使った検証
検証に使ったツールは、Terasic社のDE0_Nano_ControlPanel.exeです。
上に記述したソースコードで、unsigned char、unsigned short、unsigned intの3タイプのデータをそれぞれ実行した後に、Terasic社のツールでメモリ内容をリードして、データが正しいかを検証してみます。
懸念はSDRAMのリフレッシュです。
Terasic社のツールを使うにはFPGAの書き換えが必要なので、その間にSDRAMのリフレッシュが止まり、データが消えたり化けたりすることです。
unsigned charのライトとリード
ソースコードでの、unsigned charのライトとリードはこの部分です。unsigned shortとunsigned intのライトとリードはコメントアウトして実行しないようにしておきます。
//Write 1byte data to sdram alt_printf("\n --Write 1byte data to sdram-- \n"); *(volatile unsigned char *) (SDRAM_BASE + 0) = random_num_char[0]; *(volatile unsigned char *) (SDRAM_BASE + 1) = random_num_char[1]; *(volatile unsigned char *) (SDRAM_BASE + MEMSIZE - 2) = random_num_char[2]; *(volatile unsigned char *) (SDRAM_BASE + MEMSIZE - 1) = random_num_char[3]; //Read 1byte data from sdram alt_printf("\n --Read 1byte data from sdram-- \n"); read_data_char[0] = *(volatile unsigned char *) (SDRAM_BASE + 0); read_data_char[1] = *(volatile unsigned char *) (SDRAM_BASE + 1); read_data_char[2] = *(volatile unsigned char *) (SDRAM_BASE + MEMSIZE - 2); read_data_char[3] = *(volatile unsigned char *) (SDRAM_BASE + MEMSIZE - 1);
ライトしたデータは以下です。
Random Number char0 = ff Random Number char1 = 74 Random Number char2 = 4d Random Number char3 = 73
ライトとリードのプログラム実行が終わったら、Eclipseは停止させておきます。EclipseとTearasicのツールは同時に動かないので、Terasicの動作前にEclipseを停止させておきます。
この時、DE0-Nanoの電源は供給したままにしておきます。電源を切ると、たぶんSDRAMのデータが消えてしまうと思うので。
Terasicのツールが起動すると、FPGAの書き換えが行われ、以下のウインドウが開きます。

- 最初はDisconnectedになっていると思うので、ConnectボタンをクリックしてDE0-Nanoと接続します
- Memoryを選択します
- Memory TypeをSDRAMにします
- Random AccessのAddressを0にセットします
- Readボタンをクリックします
これでアドレス0のデータが表示されます。
ちなみに、Addressにセットできる最大値はFFFFFFです。それ以上の値を設定するとエラーメッセージが表示されます。

それでは、ようやく先頭アドレス0と最終アドレスFFFFFFをリードしてみます。


先頭アドレス0からは”74FF”、最終アドレスFFFFFFからは”734D”がリードされました。
これはEclipseで書き込んだ値なので、無事にリードができたようです。
1回のリードで2byteのデータが読み込まれるので、Address0のリードで”SDRAM_BASE + 0″と”SDRAM_BASE + 1″に書き込んだデータがリードされたようです。
同様に、Address FFFFFFのリードで”SDRAM_BASE + MEMSIZE – 2″と”SDRAM_BASE + MEMSIZE – 1″に書き込んだデータがリードされたようです。
TerasicのツールでFPGAの書き換えを行った際にSDRAMのリフレッシュが止まり、データが消えるかと思っていましたが、大丈夫だったようです。
unsigned shortのライトとリード
unsigned charの検証が終わったら、次はunsigned shortの検証です。
この際、FPGAはTerasicのツールによって書き換えられているので、こちらの記事で作成した自作のSDRAMコントローラにQuartusを使ってFPGAを戻しておきます。
Eclipseの実行でライトした、unsigned shortのデータは以下です。
Random Number short0 = 8aff Random Number short1 = 974 Random Number short2 = 4c4d Random Number short3 = 7973
続いて、Terasic社のツールでリードした結果ですが、まずはAddress 000000と000001をリードした値です。


次に、Address FFFFFEとFFFFFFをリードした値です。


値は正しくリードできたようです。
//Write 2byte data to sdram alt_printf("\n --Write 2byte data to sdram-- \n"); *(volatile unsigned short *) (SDRAM_BASE + 0) = random_num_short[0]; *(volatile unsigned short *) (SDRAM_BASE + 2) = random_num_short[1]; *(volatile unsigned short *) (SDRAM_BASE + MEMSIZE - 4) = random_num_short[2]; *(volatile unsigned short *) (SDRAM_BASE + MEMSIZE - 2) = random_num_short[3];
EclipseのCソースコードでは、アドレスを2の倍数で設定していました。
しかし、Terasicのツールではアドレスを2の倍数にする必要はないようです。
unsigned intのライトとリード
最後にunsigned intの検証です。
Eclipseの実行でライトした、unsigned intのデータは以下です。
Random Number int0 = 74438aff Random Number int1 = 71820974 Random Number int2 = 27d24c4d Random Number int3 = 62db7973
続いて、Terasic社のツールでリードした結果ですが、まずはAddress 000000から000003をリードした値です。




次に、Address FFFFFCとFFFFFFをリードした値です。




SDRAMのデータバス幅は16bitでしたが、unsigned int型でもライトとリードはできるようです。
それと、EclipseのCソースコードではアドレスを4の倍数で設定していましたが、Terasicのツールではアドレスを4の倍数にする必要はないようです。
Terasic社のツールを使った検証のまとめ
unsigned char、unsigned short、unsigned intのいずれでもSDRAMにリードとライトができていることをTerasicのツールを使って確認できました。
FPGAチャレンジャー入門編:ALTERA-Cyclone-IV版 キット CD (キットで学ぶ! シリーズ) 新品価格 | ![]() |

FPGAスタータ・キットで初体験!オリジナル・マイコン作り?フリーのCPUコアNios 2/eと高速ロジックで七変化 (トライアルシリーズ) 新品価格 | ![]() |

まとめ
DE0-NanoのSDRAMへのリードとライトを、ハードウェア編とソフトウェア編の2回に分けて記事にしてみました。
疑似乱数の生成は上手く動きませんでしたが、SDRAMへのリードとライトはできたんじゃないかと思います。
しかし、アドレスとデータサイズの関係は未だにはっきりと理解できず。。。
