DE0-Nanoボードで、SDRAMコントローラを使ってNiosからリード・ライトしてみる ソフトウェア編

FPGA

ハードウェア編の記事で、SDRAMコントローラやniosなどをPlatform Designerを使って実装し、DE0-NanoのFPGAに書き込みました。

今回は、Nios II Software Build Tools for Eclipseを使って、SDRAMにデータをリード・ライトするniosのプログラムを作っていきたいと思います。

Terasic DE0-NANO開発ボード 【P0082】

新品価格
¥14,520から
(2020/12/9 11:15時点)

スポンサーリンク

事前準備

まずは、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の書き換えが行われ、以下のウインドウが開きます。

  1. 最初はDisconnectedになっていると思うので、ConnectボタンをクリックしてDE0-Nanoと接続します
  2. Memoryを選択します
  3. Memory TypeをSDRAMにします
  4. Random AccessのAddressを0にセットします
  5. 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のツールを使って確認できました。

まとめ

DE0-NanoのSDRAMへのリードとライトを、ハードウェア編とソフトウェア編の2回に分けて記事にしてみました。

疑似乱数の生成は上手く動きませんでしたが、SDRAMへのリードとライトはできたんじゃないかと思います。

しかし、アドレスとデータサイズの関係は未だにはっきりと理解できず。。。

タイトルとURLをコピーしました