SSブログ

beagleboard を触ろう - メモリ [組み込みソフト]


beagleboard には、RAM と ROM が 2 つずつ搭載されています。
OMAP3530 内部に内臓 RAM と内臓 ROM、外部に SDRAM と NAND フラッシュメモリ、です。

内臓 RAM は、 OMAP35x TRM では SRAM internal や、OCM RAM などと表記されています。
(OCM は On Chip Memory の略)
容量 64KB の SRAM です。

この内臓 SRAM、 OMAP35x TRM にはあまり説明が載っていません。
「リセット後、2KB だけが使えるが、変更できる」と書いてあるのですが、どうやって変更するかという説明は、ありません。
xloader が動作しているときは、64KB が使えています。
イニシャルブートストラップである Boot ROM が、SRAM を 64KB 使えるように設定した上で xloader を実行しているのですが、実際にどうやっているのかは不明です。
蛇足ですが、これまでに何回か触れた通り、xloader は内臓 SRAM にロードされて実行されています。


次に、内臓 ROM ですが、 OMAP35x TRM では、OCM ROM などと表記されています。
マスク ROM ですので、書き換えはできません。

内臓 ROM にはイニシャルブートストラップが格納されており、リセット直後にイニシャルブートストラップのコードが実行されますので、何もしなくてもアクセスできるメモリ、ということになります。
(TRM にも「常にアクセスできる」と書いてあります。)

イニシャルブートストラップ以降は、内臓 ROM にアクセスすることは殆どないんでしょうねえ。
何らかのサービス関数が格納されている可能性はありますが。


・・・

SDRAM は、OMAP3530 の上に POP (Package-on-Package) として実装されています。
System Reference Manual によると、Micron 製の 2Gb (= 256MB) Mobile DDR SDRAM が載っています。

pop_memory.png

POP 実装されている Micron のチップの型番は、MT29C2G48MAKLCJI-6 なるもののようです。
MT29C2G48MAKLCJI-6 は、Mobile LPDRAM と NAND フラッシュがパッケージされた製品の型番です。
データレーン x16 の 2Gb NAND フラッシュメモリと、データレーン x32 の 1Gb LPDRAM が 2 つ載っています。

SDRAM は、SDRC (SDRAM Controller) によって制御されます。
SDRC は CS0 と CS1 の 2 つの Chip Select 信号を持っており、beagleboard でも CS0、CS1 両方を使っています。
1Gb のチップが 2 つなので。

Chip Select は、デバイスがアクセスされる際にアサートされる信号です。
CPU から、SDRAM がマップされているアドレスにアクセスすると、SDRC によって Chip Select 信号が SDRAM に対してアサートされます。


xloader に制御が移ってきた時点では、SDRAM にアクセスすることはできません。
誤ってアクセスすると、例外が発生してしまいます。
SDRC に対して初期化を行うことによって、初めてアクセスできるようになります。

Texas Instruments の WIKI に、SDRC 初期化に関する情報があります。

http://processors.wiki.ti.com/index.php/Setting_up_AM37x_SDRC_registers

OMAP35x TRM にも、もちろん説明はあるのですが、WIKI の方が分量も少ないし、こちらを見つつ、必要ならば TRM も参照するというのがいいのではないでしょうか。

それでは、xloader での SDRC 初期化を見てみましょう。

xloader では、config_3430sdram_ddr() という関数で SDRC の初期化を行っています。

271: void config_3430sdram_ddr(void)
272: {
273:   /* reset sdrc controller */
274:   __raw_writel(SOFTRESET, SDRC_SYSCONFIG);
275:   wait_on_value(BIT0, BIT0, SDRC_STATUS, 12000000);
276:   __raw_writel(0, SDRC_SYSCONFIG);
277: 
278:   /* setup sdrc to ball mux */
279:   __raw_writel(SDRC_SDP_SHARING, SDRC_SHARING);
280: 
281:   if (beagle_revision() == REVISION_XM) {
282:     __raw_writel(0x2, SDRC_CS_CFG); /* 256MB/bank */
283:     __raw_writel(SDP_SDRC_MDCFG_0_DDR_XM, SDRC_MCFG_0);
284:     __raw_writel(SDP_SDRC_MDCFG_0_DDR_XM, SDRC_MCFG_1);
285:     __raw_writel(MICRON_V_ACTIMA_200, SDRC_ACTIM_CTRLA_0);
286:     __raw_writel(MICRON_V_ACTIMB_200, SDRC_ACTIM_CTRLB_0);
287:     __raw_writel(MICRON_V_ACTIMA_200, SDRC_ACTIM_CTRLA_1);
288:     __raw_writel(MICRON_V_ACTIMB_200, SDRC_ACTIM_CTRLB_1);
289:     __raw_writel(SDP_3430_SDRC_RFR_CTRL_200MHz, SDRC_RFR_CTRL_0);
290:     __raw_writel(SDP_3430_SDRC_RFR_CTRL_200MHz, SDRC_RFR_CTRL_1);
291:   } else {
292:     __raw_writel(0x1, SDRC_CS_CFG); /* 128MB/bank */
293:     __raw_writel(SDP_SDRC_MDCFG_0_DDR, SDRC_MCFG_0);
294:     __raw_writel(SDP_SDRC_MDCFG_0_DDR, SDRC_MCFG_1);
295:     __raw_writel(MICRON_V_ACTIMA_165, SDRC_ACTIM_CTRLA_0);
296:     __raw_writel(MICRON_V_ACTIMB_165, SDRC_ACTIM_CTRLB_0);
297:     __raw_writel(MICRON_V_ACTIMA_165, SDRC_ACTIM_CTRLA_1);
298:     __raw_writel(MICRON_V_ACTIMB_165, SDRC_ACTIM_CTRLB_1);
299:     __raw_writel(SDP_3430_SDRC_RFR_CTRL_165MHz, SDRC_RFR_CTRL_0);
300:     __raw_writel(SDP_3430_SDRC_RFR_CTRL_165MHz, SDRC_RFR_CTRL_1);
301:   }
302: 
303:   __raw_writel(SDP_SDRC_POWER_POP, SDRC_POWER);
304: 
305:   /* init sequence for mDDR/mSDR using manual commands (DDR is different) */
306:   __raw_writel(CMD_NOP, SDRC_MANUAL_0);
307:   __raw_writel(CMD_NOP, SDRC_MANUAL_1);
308: 
309:   delay(5000);
310: 
311:   __raw_writel(CMD_PRECHARGE, SDRC_MANUAL_0);
312:   __raw_writel(CMD_PRECHARGE, SDRC_MANUAL_1);
313: 
314:   __raw_writel(CMD_AUTOREFRESH, SDRC_MANUAL_0);
315:   __raw_writel(CMD_AUTOREFRESH, SDRC_MANUAL_1);
316: 
317:   __raw_writel(CMD_AUTOREFRESH, SDRC_MANUAL_0);
318:   __raw_writel(CMD_AUTOREFRESH, SDRC_MANUAL_1);
319: 
320:   /* set mr0 */
321:   __raw_writel(SDP_SDRC_MR_0_DDR, SDRC_MR_0);
322:   __raw_writel(SDP_SDRC_MR_0_DDR, SDRC_MR_1);
323: 
324:   /* set up dll */
325:   __raw_writel(SDP_SDRC_DLLAB_CTRL, SDRC_DLLA_CTRL);
326:   delay(0x2000);	/* give time to lock */
327: 
328: }


274:   __raw_writel(SOFTRESET, SDRC_SYSCONFIG);
275:   wait_on_value(BIT0, BIT0, SDRC_STATUS, 12000000);
276:   __raw_writel(0, SDRC_SYSCONFIG);

274 - 276 行目は、SDRC のリセットです。
275 行目は、SDRC_STATUS レジスタの ビット 0 (RESETDONE ビット) が立つのを、タイムアウト付きで待っています。

279:   __raw_writel(SDP_SDRC_SHARING, SDRC_SHARING);

279 行目で、SDRC_SHARING レジスタに、0x0000_0100 (= SDP_SDRC_SHARING) を設定しています。
これにより、CS0, CS1 ともに 32 ビットデータレーンとして設定しています。
MT29C2G48MAKLCJI-6 の LPDRAM は 32 ビットバス幅を持っていますので、これで OK ですね。

292:     __raw_writel(0x1, SDRC_CS_CFG); /* 128MB/bank */

292 行目では、CS1 の開始アドレスを設定しています。
CS0 の開始アドレスは 0x8000_0000 であり、この値は変更することができません(多分)。
しかし、CS1 のアドレス範囲は S/W で設定可能です。
292 行目により、CS1 の開始アドレスが 0x8800_0000 になります。
この結果、0x8000_0000 - 0x8800_0000 が CS0 に繋がったチップによってカバーされ、0x8800_0000 - 0x9000_0000 が CS1 に繋がったチップによってカバーされることになります。
つまり、0x8000_0000 - 0x9000_0000 の 256MB の連続空間が、SDRAM にマップされることになります。

ここから後は、CS0 と CS1 の両方に対する設定を、組にして行います。

293:     __raw_writel(SDP_SDRC_MDCFG_0_DDR, SDRC_MCFG_0);
294:     __raw_writel(SDP_SDRC_MDCFG_0_DDR, SDRC_MCFG_1);

293, 294 行目により、

・RAS 幅は 13 ビット
・CAS 幅は 10 ビット
・RAM サイズは 128 MB
・外部 SDRAM バス幅は 32 ビット
・deep-power-down mode サポート
・メモリタイプは DDR-SDRAM

という設定になります。

295:     __raw_writel(MICRON_V_ACTIMA_165, SDRC_ACTIM_CTRLA_0);
296:     __raw_writel(MICRON_V_ACTIMB_165, SDRC_ACTIM_CTRLB_0);
297:     __raw_writel(MICRON_V_ACTIMA_165, SDRC_ACTIM_CTRLA_1);
298:     __raw_writel(MICRON_V_ACTIMB_165, SDRC_ACTIM_CTRLB_1);

295 行目から 298 行目は、タイミングパラメータです。
接続されている SDRAM に依存するパラメータであり、どのような値が適正値か知るのは困難ですが、

http://processors.wiki.ti.com/index.php/Setting_up_AM37x_SDRC_registers

に、OMAP35x/AM/DM37x DDR register calc tool なるツールがあり、これを使うと、適正値を得ることができます。
でも、SDRAM のデータシートが必要ですが。

搭載 SDRAM の型番は、MT46H32M32LFJG-6 IT のようです。
MT46H32M32LFJG-6 IT のデータシートを、適当に探して中を見てみると・・・

・・・必要なタイミング値が載っていない・・・。
うーん、まあ、仕方ないですね。
今の設定値で普通に SDRAM 使えてますし、多分、適正な値が設定されているのでしょう。

299: 		__raw_writel(SDP_3430_SDRC_RFR_CTRL_165MHz, SDRC_RFR_CTRL_0);
300:     __raw_writel(SDP_3430_SDRC_RFR_CTRL_165MHz, SDRC_RFR_CTRL_1);

299, 300 行目により、リフレッシュレートを設定します。
こちらも SDRAM 依存なので、データシートのタイミング値があれば、より適切な設定値が分かる可能性があるのですが、仕方ないですね。
今の設定値で普通に SDRAM 使えてますし、多分、適正な値が設定されているのでしょう。

303:   __raw_writel(SDP_SDRC_POWER_POP, SDRC_POWER);

303 行目は、パワーセーブ系の設定です。
しかし、詳細を説明できるほどの知識がありません・・。

305:   /* init sequence for mDDR/mSDR using manual commands (DDR is different) */
306:   __raw_writel(CMD_NOP, SDRC_MANUAL_0);
307:   __raw_writel(CMD_NOP, SDRC_MANUAL_1);
308: 
309:   delay(5000);
310: 
311:   __raw_writel(CMD_PRECHARGE, SDRC_MANUAL_0);
312:   __raw_writel(CMD_PRECHARGE, SDRC_MANUAL_1);
313: 
314:   __raw_writel(CMD_AUTOREFRESH, SDRC_MANUAL_0);
315:   __raw_writel(CMD_AUTOREFRESH, SDRC_MANUAL_1);
316: 
317:   __raw_writel(CMD_AUTOREFRESH, SDRC_MANUAL_0);
318:   __raw_writel(CMD_AUTOREFRESH, SDRC_MANUAL_1);
319: 
320:   /* set mr0 */
321:   __raw_writel(SDP_SDRC_MR_0_DDR, SDRC_MR_0);
322:   __raw_writel(SDP_SDRC_MR_0_DDR, SDRC_MR_1);

305 行目から 318 行目までは、SDRAM に対する初期化シーケンスの発行です。
このようにやりなさい、というのが、OMAP35x TRM の Low-Power SDR/Mobile DDR Initialization Sequence 節に書かれています。

その後、SDRC_MR レジスタを設定しなさい、ということも書かれていて、321 行目、322 行目はそれをやっています。
これは、CAS レイテンシーを 3 にし、バースト長を 4 に設定しています。

324:   /* set up dll */
325:   __raw_writel(SDP_SDRC_DLLAB_CTRL, SDRC_DLLA_CTRL);

最後、325 行目ですが、これは、Double Data Ratio (DDR) のタイミングを調整するもののようです。
DDR の場合、データを取り込む際に、クロック信号ではなく、データストローブ信号を使うようなのですが、このデータストローブ信号をクロック信号より 90度遅らせるようにする、というのが 325 行目かな・・。
すみません、よく分かっていません (^^;

まあ、これでとにかく、SDRAM が使えるようになります。


・・・

最後、NAND フラッシュです。

NAND フラッシュは、メモリといっても RAM のように簡単にアクセスすることはできません。
読むのも書くのも消去するのも、コマンドを発行することによって行います。

また、読み出しも、書き込みも、ページという単位で行います。
beagleboard に載っている NAND フラッシュは、MT29F2G16ABDHC-ET という型番なのですが(多分)、MT29F2G16ABDHC-ET の 1 ページは 2112 バイトです(ユーザーデータ 2048 + ECC データ 64)。

読み出す場合は、NAND メモリアレイから 1 ページ分(NAND フラッシュ内部の) DATA レジスタに読み込み、CPU は DATA レジスタから値を取得します。
(レジスタというと、普通は 4 バイト長ですが、ここでいう DATA レジスタはページサイズ分の大きさがあります。)
CPU が、DATA レジスタの値をどれくらい読み出すかは、自由です。
1 バイトでも 1 ページ分でも構いません。
ただ、1 バイトだけ読み出すだけでも、内部的には 1 ページ分の読み出し動作が行われます。

NAND_read.png

書き込みの場合、(NAND フラッシュ内部の)DATA レジスタはオール 1 にリセットされます(すべてのバイトが 0xFF)。
その状態で、CPU は DATA レジスタに、必要な分だけ値を上書きします。
DATA レジスタにどれくらい書き込むかは自由です。
1 バイトでも 1 ページ分でも構いません。
その後、DATA レジスタから、NAND メモリアレイに 1 ページ分書き込まれます。

すぐ後で触れますが、1 を NAND メモリアレイに書き込んでも、ビット値は変化しないので、CPU が上書きしなかったところは、不変になります。

NAND_program.png

フラッシュメモリへの書き込みは、通常、プログラムと言います。
プログラムは、各ビットを 1 から 0 にすることしかできません。
0 から 1 にすることはできないのです。
例えば、あるバイトを 0xFF から 0x00 にすることはできても、0x00 から 0xFF にすることはできません。
あるビットに 1 を書くと、もともと 1 であった場合は 1 がキープされますし、0 であった場合も 0 → 1 への変更はできないので、0 がキープされます。
つまり、あるバイトに 0xFF を書くと、そのバイトは変更されません。

プログラムを行う前には、通常、消去を行います。
消去は、すべてのビットを 1 にします。
消去の単位は、プログラムの単位であったページよりも更に広いブロックという単位で行います。
MT29F2G16ABDHC-ET の 1 ブロックは、64 ページです。

消去およびプログラムを行うことにより、任意の値を書き込むことができます。


NAND フラッシュはアドレスピンを持っていません。
コマンド、アドレス、データ、いずれも I/O ピン上を流れます。
今送っているのがコマンドなのか、アドレスなのか、データなのかが判別できるように、CLE, ALE といった信号を使っています。

NAND_signal.png


OMAP3530 では、NAND フラッシュは GPMC (General Purpose Memory Controller) モジュールの下に繋がれています。
GPMC は、GPMC_NAND_COMMAND, GPMC_NAND_ADDRESS, GPMC_NAND_DATA というレジスタを持っており、それぞれのレジスタに値を書き込むと、NAND フラッシュにはそれぞれ、コマンド、アドレス、データとして書き込まれます。

また、GPMC は、制御信号のタイミングの面倒も見てくれます。
CPU 側と NAND フラッシュは、nCE, ALE, CLE, nRE, nWE, I/O といった信号を制御してデータをやり取りするわけですが、信号を active/inactive にするタイミングを NAND フラッシュに合わせないと、うまくやり取りできません。

あらかじめ GPMC にタイミング値を設定しておくと、GPMC はその設定に従ってタイミングを計りながら NAND フラッシュにアクセスしてくれます。
S/W としては、何も考えずに GPMC_NAND_COMMAND, GPMC_NAND_ADDRESS, GPMC_NAND_DATA に値を書くだけで、後は GPMC がよきに計らってくれます。

GPMC.png


NAND からデータを読み書きするには、GPMC_NAND_COMMAND, GPMC_NAND_ADDRESS, GPMC_NAND_DATA にアクセスすればよいだけです。
これらのレジスタに対する基本的なオペレーションは、

・GPMC_NAND_COMMAND に 1 バイトのコマンドを書く
・GPMC_NAND_ADDRESS に数バイトのアドレスを書く
(・GPMC_NAND_COMMAND に 1 バイトの第 2 コマンドを書く)
・GPMC_NAND_DATA から必要なバイト数読み書きする

といった感じです。

コマンドの種類と、アドレスの与え方は、以下の表のようになっています。

NAND_command.png

アドレスの与え方は、下の表に従います。

NAND_addressing.png

与えるアドレスは、x16 製品の場合は 2 バイト単位だと思います。
(例えば、先頭から 128K バイト目を指定したい場合は、アドレス 64K を指定する。)
少なくとも、MT29F2G16ABDHC-ET は、そうです。
x8 製品だったら、多分、バイト単位でしょうね。


読み出し、消去、書き込み(プログラム)は、以下のようにします。

READ PAGE オペレーション
・コマンド 0x00 を GPMC_NAND_COMMAND に書き込み
・アドレス First, Second, Third, Fourth, Fifth を、この順に GPMC_NAND_ADDRESS に書き込み
・コマンド 0x30 を GPMC_NAND_COMMAND に書き込み
・GPMC_NAND_DATA から必要な分だけデータ読み出し(ただし、ページ境界まで)

ERASE BLOCK オペレーション
・コマンド 0x60 を GPMC_NAND_COMMAND に書き込み
・アドレス Third, Fourth, Fifth を、この順に GPMC_NAND_ADDRESS に書き込み
・コマンド 0xD0 を GPMC_NAND_COMMAND に書き込み

PROGRAM PAGE オペレーション
・コマンド 0x80 を GPMC_NAND_COMMAND に書き込み
・アドレス First, Second, Third, Fourth, Fifth を、この順に GPMC_NAND_ADDRESS に書き込み
・GPMC_NAND_DATA に必要な分だけデータ書き込み(ただし、ページ境界まで)
・コマンド 0x10 を GPMC_NAND_COMMAND に書き込み

xloader の NAND ドライバーは k9f1g08r0a.c というファイルです。
元々は、READ PAGE しかサポートしていないのですが、ERASE BLOCK, PROGRAM PAGE を追加すると、以下のようになります。
やっていることは、上の説明の通りです。
(使いやすくするため、コマンドの完了チェックを追加しています。)

330: u_char nand_status(void)
331: {
332:   u16 status;
333:
334:   NanD_Command(NAND_CMD_STATUS);
335:   status = READ_NAND(NAND_ADDR);
336:	
337:   return (u_char)status;
338: }
339:
340: int nand_erase_block(ulong block_addr)
341: {
342:   u_char status = 0;
343:
344:   NanD_Command(NAND_CMD_ERASE);
345:   NanD_Address(ADDR_PAGE, block_addr);
346:   NanD_Command(NAND_CMD_ERASESTART);
347:
348:   while (1) {
349:     status = nand_status();
350:     serial_printf("[%s] status = 0x%x\n", __FUNCTION__, status);
351:     if ((status & 0x60) == 0x60)
352:       break;
353:     wait_us_polling(1, 1000000);
354:   }
355:
356:   if (status & 0x01)
357:     return 1;
358:   else
359:     return 0;
360: }
361:
362: int nand_program_page(u_char *buf, int len, ulong page_addr)
363: {
364:   int cntr;
365:   u16 *p = (u16 *)buf;
366:   u_char status = 0;
367:
368:   NanD_Command(NAND_CMD_PROGRAM);
369:   NanD_Address(ADDR_COLUMN_PAGE, page_addr);
370:
371:   if (bus_width == 16)
372:     len /= 2;
373:   for (cntr = 0; cntr < len; cntr++, p++) {
374:     WRITE_NAND(*p, NAND_ADDRESS);
375:   }
376:
377:   NanD_Command(NAND_CMD_PROGRAMSTART);
378:
379:   while (1) {
380:     status = nand_status();
381:     serial_printf("[%s] status = 0x%x\n", __FUNCTION__, status);
382:     if ((status & 0x60) == 0x60)
383:       break;
384:     wait_us_polling(1, 1000000);
385:   }
386:
387:   if (status & 0x01)
388:     return 1;
389:   else
390:     return 0;
391: }


あ、NAND は write protect がかけられるようになっていて、xloader による初期設定では、write protect がオンになっています。
消去、プログラムを試そうと思ったら、write protect を外しておく必要があります。

sr32(GPMC_CONFIG, 4, 1, 1);

この 1 行を、どこか適当なところ(nand_init() 関数とか)に追加しておく必要があります。


・・・


とりあえずこれで、NAND フラッシュの説明はおしまいです。


NAND フラッシュ全般に関しては、この EE Times の記事↓などを参考にされるとよいかと思います。

http://www.eetimes.com/design/memory-design/4009410/Flash-memory-101-An-Introduction-to-NAND-flash

この記事の内容は、ほぼそのまま beagleboard の MT29F2G16ABDHC-ET にも適用できると思いますが、ページ真中辺のアドレスの与え方の説明が、ちょっとだけ違います。
EE Times のは、容量 = 2GB, I/O = x8 の NAND フラッシュの説明になっているみたいで、MT29F2G16ABDHC-ET は 2GB, x16 なので、微妙に違います。

下の図で、上側が x16, 下側が x8 のアドレスの与え方です。

NAND_addressing_x16x8.png
nice!(0)  コメント(0)  トラックバック(0) 
共通テーマ:パソコン・インターネット

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。