2017년 1월 17일 화요일

아두이노 듀에에서 TRNG(True Random Number Generator) 사용하기

아두이노 듀에는 ATMEL의 SAM3X8E (ARM Cortex M3)를 사용하는데 이 칩은 하드웨어 TRNG(True Random Number Generator)를 가지고 있다.
아두이노 환경에서 이 TRNG를 사용하는 방법을 소개한다.



가장 간단한 방법은 다음과 같다.

void setup()

  Serial.begin(115200);
  pmc_enable_periph_clk(ID_TRNG);
  trng_enable(TRNG);
}

void loop()
{
  uint32_t t = trng_read_output_data(TRNG);
  Serial.println(t);
  delay(50);
}

pmc_enable_periph_clk 는 hardware/arduino/sam/system/libsam/source/pmc.c 에 정의되어 있고 trng_enable(), trng_read_output_data() 는 hardware/arduino/system/libsam/source/trng.c 에 정의되어 있다. 엄밀히 말하면 데이터를 읽기 전에 random number generator에서 데이터를 읽을 수 있을 때 까지 기다려야 한다. 하지만 SAM3X8E의 TRNG가 빠르기 때문에 (매 84 클럭 사이클마다 랜덤넘버 하나를 생성) 따로 기다리지 않아도 안전하다. 위의 코드에서 생성된 랜덤 넘버는 unsigned 32비트 숫자로 취급한다.

다른 방법으로는 TRNG 인터럽트를 활성화해서 랜덤넘버가 생성될 때 마다 인터럽트가 그 값을 처리하도록 할 수도 있다.

void setup()

  Serial.begin(115200);
  pmc_enable_periph_clk(ID_TRNG);
  trng_enable(TRNG);

  NVIC_DisableIRQ(TRNG_IRQn);
  NVIC_ClearPendingIRQ(TRNG_IRQn);
  NVIC_SetPriority(TRNG_IRQn, 0);
  NVIC_EnableIRQ(TRNG_IRQn);
  trng_enable_interrupt(TRNG);
}

void TRNG_Handler(void)
{
  uint32_t stat = trng_get_interrupt_status(TRNG);

  if ((stat & TRNG_ISR_DATRDY) == TRNG_ISR_DATRDY) {
    int r = trng_read_output_data(TRNG);
    Serial.println(r);
  }
}

void loop()
{
}

여기서는 TRNG ISR의 상태를 확인해 랜덤넘버를 읽기 전에 값이 생성되었는지 본다. trng_read_output_data 가 리턴하는 값은 signed 또는 unsigned로 해석해도 상관 없다. 위의 예제에서는 signed 32비트 integer로 처리하기 때문에 랜덤값은 0을 기준으로 위/아래로 균등하게 분포하고 있다. NVIC_* 루틴은 hardware/arduino/sam/system/CMSIS/CMSIS/Include/core_cm3.h 에 inline 되어 있다.

데이터쉬트를 보면 SAM3X8E의 TRNG는 American NIST Special Publication 800-22와 Diehard Random Tests를 통과했다고 쓰여 있다.