090825

|


ロガーのSDカードI/O部分の書き出し部分をDMAを使うようにした。(今迄は全力
でFIFOに書き込んでいた。これではSD/MMCコントローラの書き出しスピードを
最低にまで落とさないと(クロックを最低に設定)、FIFO under runでSD/MMCコ
ントローラが止まってしまう。

LPC2388のGPDMAはメモリ側にUSB RAMと外部拡張メモリしかアクセスできないの で、今迄内部RAMにとっていたバッファ領域をUSB RAMに変更。スピードメータ、 あるいはP-LAPからの割り込みが入ったら、その時間を直接USB RAMに書き出す。
バッファは512Bのダブルバッファなので、フルになったらそのバンクをDMAで SD/MMCコントローラに送る。フローコントロールはSD/MMC側として、調停はお まかせ。
終了の検出はポーリング、DMAのターミナルカウントの割り込みまで寝るの二種 類を試してみたところ、ポーリングで250ms、割り込みで50msだったので、割り 込みに。このあたりは転送速度を上げていくとどうなるかわからない。 寝るお膳立てをしてる間にDMAが終了するようになったらポーリングにしよう。
とりあえずこの仕様でピストンナラシのついでにテスト。
STATIC thread_t sleeping_thread;

void
dma_init ()
{
  // Power on 起動時にはDMAの電源は入っていない。
  mcu_peripheral_power (PCONP_PCGPDMA, TRUE);

  // Src, Dstともにエンディアンの設定ができる。
  *DMACConfig = DMACConfigEnable; // Little-endian
  //  *DMACConfig = 0;
}

void
dma_fini ()
{

  *DMACConfig = 0;
  mcu_peripheral_power (PCONP_PCGPDMA, FALSE);
}

void
dma_ch0_mci_start (uint32_t src_addr)
{
  // Clear pending interrupt.
  *DMACIntTCClear = IntTCClear0;
  *DMACIntErrClr = IntErrClr0;
  // Source address.
  *DMACC0SrcAddr = src_addr;
  // Destination address.
  *DMACC0DstAddr = MCI_FIFO_ADDR;

  // not Linked List Item.
  // 512BドカDMAなのでLLIは使わない。
  *DMACC0LLI = 0;

  // Disable and reset configuration parameter of channel 0.
  *DMACC0Config &= ConfigReservedMask;

  *DMACC0Control = (512 & TransferSizeMask) | //512B送ります。
    (BurstSize32 << SBSizeShift) | // バーストサイズはKeilのサンプルを
    (BurstSize8 << DBSizeShift) |  // 参考にした。考える必要あり。
    (WidthSize32bit << SWidthShift) |
    (WidthSize32bit << DWidthShift) |
    DMASrcIncr;	// 出所(USB RAM)だけインクリメント。
//目的地(MCI_FIFO_ADDR)はインクリメントしない。

//目的地SD/MMCの指定と、フローコントローラをDMAかSD/MMC側かを指定。
  *DMACC0Config |= (PeripheralSDMMC << DstPeripheralShift) |
    (FlowCntrlM2P_P << FlowCntrlShift);// Peripheral Flow Control.

#define	DMA_INTR
#ifdef DMA_INTR
  // 50ms
// マイOSではIRQしか再スケジューリングをしないので、IRQじゃないとだめ。
  *VICIntSelect &= ~VIC_GPDMA; // IRQ
  *VICIntEnable |= VIC_GPDMA;
  sleeping_thread = current_thread;
// カウント終了で割り込みを発生させるように。
  *DMACC0Control |= TerminalCountIntr;
  *DMACC0Config |= ConfigTerminalCount | ConfigIntrError;
#else
  // 250ms
#endif

  //  iprintf ("src=%x dst=%x\n", *DMACC0SrcAddr, *DMACC0DstAddr);
  // Kick DMA. DMA channel is automatically enabled.
  *DMACC0Config |= ChannelEnable;
#ifdef DMA_INTR
// DMAカウント終了の割り込みで起こされるまで寝ます。この間にサンプリング
//の割り込みがあれば、その後のデータ処理スレッドが動く。それらの間にはリ
//ングバッファがあるので、バッファが溢れない限りポーリングでも構わない。
// コントローラの7セグLEDの更新は止まるけれど、それはどうでもいい。
  thread_sleep (current_thread);
#endif
}

void
gpdma_intr ()
{

  *DMACIntTCClear = *DMACIntTCStatus;//割り込み要因をクリアして
  *VICIntEnable &= ~VIC_GPDMA;//次のDMAまで割り込みを使いません。
  thread_wakeup (sleeping_thread);
}


まずは書き込みだけDMA化。ロガーが読み込みをするのは起動時のみなので遅くとも
まったく問題ない。

bool
mmc_write (void *self, uint8_t *buf, daddr_t lba)
{
#define	MCI_USE_DMA
  struct cpsm_command *cmd = cpsm_command + MMC_CMD24;
  self = self;

  mci_tran_state ();

  *MCI_DATATIMER = ~0;
  *MCI_DATALENGTH = 512;
#ifdef MCI_USE_DMA
   // DMAを使うことを指定することで自動的にDMAと調停してくれる。
  *MCI_DATACTRL = DATACTRL_ENABLE | DATACTRL_DMAENABLE |
    ((9  & DATACTRL_BLOCKSIZE_MASK) << DATACTRL_BLOCKSIZE_SHIFT);
#else
  *MCI_DATACTRL = DATACTRL_ENABLE |
    ((9  & DATACTRL_BLOCKSIZE_MASK) << DATACTRL_BLOCKSIZE_SHIFT);
#endif

  *MCI_CLEAR = CLEAR_ALL;

  cmd->arg = card_info.block_address ? lba : lba * 512;
  if (!mci_cpsm_command (&card_info, cmd))
    return FALSE;

  md_timer_counter_set (cmd->timeout);
  while ((*MCI_STATUS & STATUS_CMDACTIVE) && md_timer_counter () != 0)
    ;
  if (md_timer_counter () == 0)
    {
      printf ("timeout 1\n");
      return FALSE;
    }
#ifdef MCI_USE_DMA
  dma_ch0_mci_start ((uint32_t)buf);
  //  md_timer_counter_set (1000);
  //DMA側が転送し終ってもまだSD/MMCコントローラ側は仕事してるかもしれないので。
  while (*MCI_STATUS & STATUS_TXACTIVE)	//XXXtimeout
    ;
  //  printf ("DMA %d\n", md_timer_counter ());
#else
  uint32_t *fifobuf = (uint32_t *)buf;
  int i;
  md_timer_counter_set (cmd->timeout);
  for (i = 0; i < 512 / 4; i++)
    {
      while (*MCI_STATUS & STATUS_TXFIFOFULL && md_timer_counter () != 0)
	;
      *MCI_FIFO_START = fifobuf[i];
    }
  // Transmit in prgress.
  while (*MCI_STATUS & STATUS_TXACTIVE)	//XXXtimeout
    ;
#endif
  // Stop DPSM
  *MCI_DATACTRL = 0;

  return TRUE;
}