///////////////////////////////////////////////////////////////////////////
// SDRAM(EPI0) 8MB SDRAM(MT48LC4M16A2B4-6AIT:J)
//
// 2015/11/16
///////////////////////////////////////////////////////////////////////////
#define	MONITOR_DEF_SDRAM
#include "Monitorbase.h"

static	volatile	WORD	*gwarrSDRAM;				// externを行うと怒られる

// The Mapping address space for the EPI SDRAM.
//#define	SDRAM_MAPPING_ADDRESS				(0x60000000)

// A pointer to the EPI memory aperture.  Note that g_pui16EPISdram is declared
// as volatile so the compiler should not optimize reads out of the image.
//static	volatile	WORD	*gwarrSDRAM;			// この形ではexternが行えない

// A table used to determine the EPI clock frequency band in use.
typedef struct {
	uint32_t ui32SysClock;
	uint32_t ui32FreqFlag;
} tSDRAMFreqMapping;

static tSDRAMFreqMapping g_psSDRAMFreq[] = {
	// SysClock >= 100MHz, EPI clock >= 50Mhz (divided by 2)
	{100000000, EPI_SDRAM_CORE_FREQ_50_100},
	// SysClock >= 60MHz, EPI clock >= 30MHz (divided by 2)
	{60000000, EPI_SDRAM_CORE_FREQ_50_100},
	// SysClock >= 50MHz, EPI clock >= 50MHz (no divider)
	{50000000, EPI_SDRAM_CORE_FREQ_50_100},
	// SysClock >= 30MHz, EPI clock >= 30MHz (no divider)
	{50000000, EPI_SDRAM_CORE_FREQ_30_50},
	// SysClock >= 15MHz, EPI clock >= 15MHz (no divider)
	{15000000, EPI_SDRAM_CORE_FREQ_15_30},
	// SysClock < 15Mhz, EPI clock < 15Mhz (no divider)
	{0, EPI_SDRAM_CORE_FREQ_0_15}
};

#define	NUM_SDRAM_FREQ	(sizeof(g_psSDRAMFreq) / sizeof(tSDRAMFreqMapping))


// SDRAMの最大サイズ 0x003FFFFF まで使用可能

// 画面用バッファサイズ
// 32bit col
// 4bytes * 800 * 480 = 1536000
// 1536000 = 0x00177000
// グラフィック用バッファーA		0x00000000 〜 0x00176FFF まで
// グラフィック用バッファーB		0x00177000 〜 0x002EDFFF まで
// 残 0x00400000 - 0x002EE000 = 0x00112000 = 2MB程度

// FatFs用バッファーサイズ 256KB = 128Kwords = 0x20000 * 2 = 0x40000
// Flash @FatFs用バッファー			0x002EE000 〜 0x0032DFFF まで
// 残 0x00400000 - 0x0032E000 = 0x000D2000 = 1.6MB程度

// RAMディスクサイズ 1.5MB = 0.75Mwords = 0x000C0000
// グラフィック用RAMディスク		0x0032E000 〜 0x003EDFFF まで
// 残 0x00400000 - 0x003EE000 = 0x00012000 = 0.14MB程度
#define	SDRAM_GBUF_SIZE							(0x00177000)
#define	SDRAM_GBUF_WSIZE						(0x002EE000)
#define	SDRAM_RAMDISK_SIZE					(0x000C0000)
#define	SDRAM_FATBUF_SIZE						(0x00040000)

#define	SDRAM_GBUF_A_STARTADD				(0x00000000)																				// 画像バッファーA 先頭アドレス
#define	SDRAM_GBUF_A_ENDADD					(SDRAM_GBUF_SIZE - 1)																// 画像バッファーA 最終アドレス
#define	SDRAM_GBUF_B_STARTADD				(SDRAM_GBUF_SIZE)																		// 画像バッファーB 先頭アドレス
#define	SDRAM_GBUF_B_ENDADD					(SDRAM_GBUF_WSIZE - 1)															// 画像バッファーB 最終アドレス
#define	SDRAM_RAMDISK_STARTADD			(SDRAM_GBUF_WSIZE)																	// RAMディスク 先頭アドレス
#define	SDRAM_RAMDISK_ENDADD				(SDRAM_RAMDISK_STARTADD + SDRAM_RAMDISK_SIZE - 1)		// RAMディスク 最終アドレス
#define	SDRAM_FATBUF_STARTADD				(SDRAM_RAMDISK_ENDADD + 1)													// FAT用バッファー 先頭アドレス
#define	SDRAM_FATBUF_ENDADD					(SDRAM_FATBUF_STARTADD + SDRAM_FATBUF_SIZE -1)			// FAT用バッファー 最終アドレス

//////////////////////////////////////////////////////////// [[[
// SDRAM初期化関数
//	引数
//		クロックを設定した際の戻り値
//	戻り値
//		true / false
bool	SDramInit(uint32_t dwSysClock){
	DWORD	ui32Val;
	DWORD	ui32Freq;

	// Is our current system clock faster than we can drive the SDRAM clock?
	/*
	if(dwSysClock > 60000000){
		// Yes. Set the EPI clock to half the system clock.
		// こっちが実行される
		ROM_EPIDividerSet(EPI0_BASE, 1);
	} else {
		// With a system clock of 60MHz or lower, we can drive the SDRAM at
		// the same rate so set the divider to 0.
		ROM_EPIDividerSet(EPI0_BASE, 0);
	}
	*/
	ROM_EPIDividerSet(EPI0_BASE, 1);

	// Sets the usage mode of the EPI module.  For this example we will use
	// the SDRAM mode to talk to the external 64MB SDRAM daughter card.
	ROM_EPIModeSet(EPI0_BASE, EPI_MODE_SDRAM);

	// Keep the compiler happy by setting a default value for the frequency flag.
	ui32Freq = g_psSDRAMFreq[NUM_SDRAM_FREQ - 1].ui32FreqFlag;

	// Examine the system clock frequency to determine how to set the SDRAM controller's frequency flag.
	for(ui32Val = 0; ui32Val < NUM_SDRAM_FREQ; ui32Val++){
		// Is the system clock frequency above the break point in the table?
		if(dwSysClock >= g_psSDRAMFreq[ui32Val].ui32SysClock){
			// Yes - remember the frequency flag to use and exit the loop.
			ui32Freq = g_psSDRAMFreq[ui32Val].ui32FreqFlag;
			break;
		}
	}
	
	// Configure the SDRAM mode.  We configure the SDRAM according to our core
	// clock frequency.  We will use the normal (or full power) operating
	// state which means we will not use the low power self-refresh state.
	// Set the SDRAM size to 64MB with a refresh interval of 1024 clock ticks.
	// SDRAMモードを設定します。
	// コア・クロック周波数に応じてSDRAMを設定します。
	// 我々は、低消費電力のセルフリフレッシュ状態を使用しないことを意味し、通常の（またはフルパワー）動作状態を使用します。
	// クロックティック1024のリフレッシュ間隔を64メガバイトまでのSDRAMサイズを設定します。
	ROM_EPIConfigSDRAMSet(EPI0_BASE, (ui32Freq | EPI_SDRAM_FULL_POWER | EPI_SDRAM_SIZE_64MBIT), 1024);

	// Set the address map.  The EPI0 is mapped from 0x60000000 to 0x01FFFFFF.
	// For this example, we will start from a base address of 0x60000000 with
	// a size of 256MB.  Although our SDRAM is only 64MB, there is no 64MB
	// aperture option so we pick the next larger size.
	// アドレスマップを設定します。
	// EPI0は0x60000000から0x01FFFFFFにマッピングされます。
	// この例では、256メガバイトのサイズで0x60000000のベースアドレスから開始します。
	// 私たちのSDRAMは唯一の64メガバイトですが、我々は次に大きいサイズを選ぶように、何の64メガバイトの開口オプションはありません。
	ROM_EPIAddressMapSet(EPI0_BASE, EPI_ADDR_RAM_SIZE_16MB | EPI_ADDR_RAM_BASE_6);

	// Wait for the SDRAM wake-up to complete by polling the SDRAM
	// initialization sequence bit.  This bit is true when the SDRAM interface
	// is going through the initialization and false when the SDRAM interface
	// it is not in a wake-up period.
	while(HWREG(EPI0_BASE + EPI_O_STAT) &  EPI_STAT_INITSEQ){
		// SDRAM起動待ち
	}

	// Set the EPI memory pointer to the base of EPI memory space.  Note that
	// g_pui16EPISdram is declared as volatile so the compiler should not
	// optimize reads out of the memory.  With this pointer, the memory space
	// is accessed like a simple array.
	// EPIメモリ空間のベースにEPIのメモリポインタを設定します。
	// g_pui16EPISdramはvolatileとして宣言されているので、最適化するべきではありませんコンパイラは、メモリの読み出しを行います。
	// このポインタにより、メモリ空間は、単純な配列のようにアクセスされます。
	gwarrSDRAM = (WORD *)SDRAM_MAPPING_ADDRESS;

	return true;
}

//////////////////////////////////////////////////////////// [[[
// SDRAMのアドレス取得
//	引数
//		ナシ
//	戻り値
//		SDRAMの先頭アドレス
WORD*	GetSDRAMBaseAddress(void){
	return	(WORD*)&gwarrSDRAM[0];
}

//////////////////////////////////////////////////////////// [[[
// SDRAMのアドレス取得(LCD用バッファー)
//	引数
//		ナシ
//	戻り値
//		SDRAMのアドレス
WORD*	GetSDRAMLCDBufAddressA(void){
	return	(WORD*)&gwarrSDRAM[SDRAM_GBUF_A_STARTADD];
}
WORD*	GetSDRAMLCDBufAddressB(void){
	return	(WORD*)&gwarrSDRAM[SDRAM_GBUF_B_STARTADD];
}

//////////////////////////////////////////////////////////// [[[
// SDRAMのアドレス取得(Fat用バッファー)
//	引数
//		ナシ
//	戻り値
//		SDRAMのアドレス
WORD*	GetSDRAMFatBufAddress(void){
	return	(WORD*)&gwarrSDRAM[SDRAM_FATBUF_STARTADD];
}

//////////////////////////////////////////////////////////// [[[
// Fat用バッファーのクリア
//	引数
//		ナシ
//	戻り値
//		true / false
bool	SDRAM_FatBufClr(void){
	DWORD	dw;
	for(dw=0;dw<SDRAM_FATBUF_SIZE;dw++){
		gwarrSDRAM[SDRAM_FATBUF_STARTADD + dw] = 0x0000;				// ワードサイズのバッファーのクリア
	}
	return true;
}

//////////////////////////////////////////////////////////// [[[
// Fat用バッファーへの格納
//	引数
//		BYTE* 保存する256kバイトデータ列へのポインター
//	戻り値
//		true / false
bool	SDRAM_FatBufSave(BYTE* pbDat){
	DWORD	dw;
	WORD	w;
	for(dw=0;dw<SDRAM_FATBUF_SIZE;dw++){
		w = MAKEWORD( *(pbDat + 2*dw) , *(pbDat + 2*dw +1) );
		gwarrSDRAM[SDRAM_FATBUF_STARTADD + dw] = w;							// ワードサイズのバッファーの保存
	}
	return true;
}

//////////////////////////////////////////////////////////// [[[
// Fat用バッファーへの格納（指定アドレス）
//	引数
//		WORD 開始位置（バイト）、WORD 長さ（BYTE数）、BYTE* 保存するデータ列へのポインター
//	戻り値
//		true / false
bool	SDRAM_FatBufByteSave(WORD wStart, WORD wLength, BYTE* pbDat){
	DWORD	dw;
	WORD	w;
	WORD	wStartTmp;
	DWORD	dwEnd;
	bool	bEnd1ByteFlag = false;
	if(wStart + wLength > (SDRAM_FATBUF_SIZE << 1)){	return false;	}		// サイズ超過
	
	if((wStart & 0x0001) == 0x0001){
		// 開始位置が奇数の場合
		w = gwarrSDRAM[SDRAM_FATBUF_STARTADD + (wStart >> 1)];
		w = MAKEWORD(HIBYTE(w) , *pbDat);
		gwarrSDRAM[SDRAM_FATBUF_STARTADD + (wStart >> 1)] = w;
		if((wLength & 0x0001) == 0x0001){
			// 終了位置が奇数の場合、開始が奇数だったので偶数となる
			dwEnd = (wLength - 1) >> 1;
			wStartTmp = 1;
		} else {
			// 終了位置が偶数の場合、開始が奇数だったので奇数となる
			dwEnd = (wLength >> 1) - 1;				// あとで1バイト分の処理を別に行うため、1ワード短くなる
			wStartTmp = 0;
		}
		if((wLength & 0x0001) == 0x0000){
			// 終了位置が偶数の場合、開始が奇数だったため、最後の1バイト分を書き換える必要がある
			bEnd1ByteFlag = true;
		}
	} else {
		// 開始位置が偶数の場合
		wStartTmp = 0;
		if((wLength & 0x0001) == 0x0001){
			// 終了位置が奇数の場合、あとで1バイト分を別に行う必要がある
			dwEnd = (wLength >> 1) - 1;
		} else {
			// 終了位置が偶数の場合、特に問題なし
			dwEnd = wLength >> 1;
		}
		if((wLength & 0x0001) == 0x0001){
			// 終了位置が奇数の場合、最後の1バイト分を書き換える必要がある
			bEnd1ByteFlag = true;
		}
	}

	// データの書き込み
	for(dw=0;dw<dwEnd;dw++){
		w = MAKEWORD( *(pbDat + wStartTmp), *(pbDat + wStartTmp +1) );
		gwarrSDRAM[SDRAM_FATBUF_STARTADD + dw] = w;
		wStartTmp += 2;
	}
	if(bEnd1ByteFlag == true){
		// trueの場合、最後の1バイト分を書き換える必要がある
		w = gwarrSDRAM[SDRAM_FATBUF_STARTADD + dw];
		w = MAKEWORD(HIBYTE(w) , *(pbDat + wLength -1));
		gwarrSDRAM[SDRAM_FATBUF_STARTADD + dw] = w;
	}
	return true;
}

//////////////////////////////////////////////////////////// [[[
// Fat用バッファーからの取り出し
//	引数
//		BYTE* 受け渡し用4kバイトデータ列へのポインター
//	戻り値
//		true / false
bool	SDRAM_FatBufLoad(BYTE* pbDat){
	DWORD	dw;
	WORD	w;
	for(dw=0;dw<SDRAM_FATBUF_SIZE;dw++){
		w = gwarrSDRAM[SDRAM_FATBUF_STARTADD + dw];
		*(pbDat + 2*dw) = HIBYTE(w);
		*(pbDat + 2*dw +1) = LOBYTE(w);
	}
	return true;
}


