IoT何をいまさら(95) ATSAMD51、TRNG発EVSYS経由PORT行

Joseph Halfmoon

周辺回路の動作完了をキッカケにメモリへ転送(DMA)とか、タイマのトリガでAD変換始めるとか、CPUの介在なしに装置間で制御を伝えることがあります。その中でもMicrochip社のATSAMマイコンが搭載しているEVSYSというものは極めて強力、ほとんどの周辺装置間でコミュニケーションがとれます。今回はTRNGでPort出力を制御してみます。

(実験に使用したソース全文は末尾に)

今回はSAMD51マイコンの内蔵ペリフェラルの中でも「手足に無縁な」TRNG(真性乱数生成器)の変換期間を、EVSYS経由でポート出力してみます。具体的な利用方法があるわけじゃないですが、TRNGのような「内弁慶」なペリフェラルでも他を制御できるEVSYSはなかなか凄いな、と。

前回もTRNGをいじってましたので、実は先週、本稿の実験をさらっとやって終わらせるつもりでした。しかし、以下の件があり1週遅れてしまいました。

トホホな疑問(40) ATSAMD51、Port Event制御1個しか動かないんですけど

本稿でも、上記の知見に基づき、Port Event制御の「動くところ」だけ使って問題回避しています。

実験に使用しているATSAMマイコンはATSAMD51P19Aであります。なお、毎度引用させていただいておりますが、Microchip社の製品ページは以下です。

ATSAMD51P19A | Microchip Technology

開発ボードは SeeedStudio社の Wio Terminalです。開発環境は VS Code + PlatformIOで、Arduinoプラットフォーム利用です。直接周辺回路等を扱う関係上、ハードウエアレジスタに直接アクセスしているところが多いです。ATSAMD51のデータシートには直接アクセスのために必要な情報が皆書かれているので良い感じです。

実験の準備

Wio Terminalの拡張端子(Raspberry Pi 互換配列)は裏面にあるので、お手製治具で上を向けたままでジャンパ線で信号を取り出せるようにしています。

  • GPIO27 (SAMD51のPB08、Wio TerminalのA0/D0)をTRNGモニタ端子
  • GPIO22 (SAMD51のPB09、Wio TerminalのA1/D1)を外部LED端子

として、GPIO27をオシロであたって波形をとれるようにし、動作中であることが目で分かるようにGPIO22にLEDを取り付けてあります(Wio Terminalの内蔵LEDは見ずらいです。)

こんな感じ。

TRNG_EVSYS_DUT

実験用コードの概要

目論見としては、TRNGの乱数発生期間(84サイクル固定)に相当するパルスをGPIO27で観察する、というものであります。Wio Terminalは120MHzで動作しているので、時間的には以下です。

0.0083μ秒 x 84 = 0.7μ秒

実際には出だしのところのソフト処理の時間も見えてしまうので0.7μ秒よりは若干長いくなるはずですが。

動作としては、

  1. TRNGをスタートさせる直前にGPIO27にロウ(ロウアクティブとします)を出力しておく(ソフトウエア。)
  2. TRNGをスタートする(ソフトウエア。)
  3. TRNGが生成完了する(ハードウエア、CPUは知らない。)
  4. TRNGの生成完了信号がEVSYSにおくられ、EVSYSで検出したイベントはPort(PB08)に送られて出力をトグルする(ハードウエア、トグルすれば先ほどソフトで出力したロウがハードでハイにひっくり返る)

というものです。

この準備のため、setup()関数内で以下の初期化を行っています。

  1. setupPortEvent()関数でポートのイベント受け入れ
  2. setupEVSYS()関数でTRNGのイベントの検出とポートへのルーティング
  3. setupTRNG()関数でTRNGのイベント発生
実験結果

生成されたロウパルスの時間幅を測定したところ、以下のようでした。約0.76μ秒。ほぼほぼ期待どおりかと。

TRNG_ACTIVE念のため、ソフトウエアで読み出しているTRNGの生成結果が以下に。裏でPortをトグルしているとも気づかず?TRNGは乱数を生成しつづけているようです。

641
POLL: 0x33862307
642
POLL: 0xee8b444a

今回は、TRNGとPORTというあまり使い道のない組み合わせでやってみました。実際にはEVENTの使い道は沢山あるように思います。割り込みを発生できるようなペリフェラルは皆EVENTもサポートしているようです。EVENTで済むのであればそうした方が、CPUのお手を煩わせず(負荷をかけず)に処理できるので良いかもしれません。EVSYS良いものに思えます。ポートのイベント設定の不具合?使い方が悪い?にはハマりましたが。

IoT何をいまさら(94) ATSAMD51、ArmのWFI命令使ってみた へ戻る

IoT何をいまさら(96) Wio Terminal、困った時の uf2。TC割り込み へ進む

実験に使用したソース全文
#include <Arduino.h>

// GPIO27 (WIO:A0/D0, SAMD51:PB08, Arduino:0) 
// GPIO22 (WIO:A1/D1, SAMD51:PB09, Arduino:1) 
#define GPIO27  (0)
#define GPIO22  (1)

uint32_t dataTRNG;
uint32_t counter;

void setupEVSYS() {
  MCLK->APBBMASK.bit.EVSYS_ = 1; //Enable EVSYS_APB
  EVSYS->USER[EVSYS_ID_USER_PORT_EV_1].bit.CHANNEL = 0x20; //User Multiplexer 1(PortB) <= EvChannel 31
  EVSYS->Channel[31].CHANNEL.reg = 0x0273; // CH31 source, Async, TRNG
}

void setupPortEvent() {
  PORT->Group[1].EVCTRL.reg = 0x0000E800; //PB08 Toggle
}

void setupTRNG() {
  MCLK->APBCMASK.bit.TRNG_ = 1; //Enable CLK_TRNG_APB
  TRNG->CTRLA.bit.ENABLE = 0;   //Disable TRNG 
  TRNG->INTFLAG.bit.DATARDY = 1;  //Clear Interrupt Flag
  TRNG->EVCTRL.bit.DATARDYEO = 1; //Enable TRNG EVNET Output 
}

void startTRNG() {
  TRNG->INTFLAG.bit.DATARDY = 1;  //Clear Interrupt Flag
  TRNG->CTRLA.bit.ENABLE = 1;     //Enable TRNG 
}

int pollReadTRNG(uint32_t *datReturn) {
  int timeout = 5;
  while (!TRNG->INTFLAG.bit.DATARDY) {
    if (--timeout < 0) {
      return 0; //Error return.
    }
  }
  TRNG->CTRLA.bit.ENABLE = 0;   //Disable TRNG 
  *datReturn = TRNG->DATA.reg;  //Reading DATA register make INT flag clear.
  return 1; //OK
}

void setup() {
  Serial.begin(9600);
  while(!Serial);
  Serial.printf("ATSAMD51 EVSYS test with TRNG.\r\n");
  pinMode(GPIO27, OUTPUT);
  pinMode(GPIO22, OUTPUT);
  digitalWrite(GPIO27, 1);
  digitalWrite(GPIO22, 1);
  setupPortEvent(); // Enable PB08 port event
  setupEVSYS();
  dataTRNG = 0;
  setupTRNG();
  counter = 0;
}

void loop() {
  uint32_t dat;
  Serial.println(counter++);
  digitalWrite(GPIO22, 0); // monitor LED 
  digitalWrite(GPIO27, 0); // start TRNG 
  startTRNG();             // will toggle TRNG
  if (pollReadTRNG(&dat)) {
    delay(1);
    Serial.printf("POLL: 0x%08x\r\n",dat);
  }
  delay(500);
  digitalWrite(GPIO22, 1); // monitor LED OFF 
  digitalWrite(GPIO27, 1); // make sure TRNG OFF 
  delay(500);
}