2020年6月28日日曜日

FreeRTOS v10.3.1: NiosII 用デモを DE0-Nano + Quartus/NiosII EDS v18.1 で動作させる

 

 
FreeRTOS.org からリリースされている Demo Example の中に、Intel FPGA / NiosII 向け Example があります(FreeRTOS/Demo/NiosII_CycloneIII_DBC3C40_GCC/)。

生憎、この Example は非常に古いため、そのままでは最新の NiosII EDS では動作しません。

そこで、DE0-Nano で(とりあえず)動作させる手順を、以下に纏めましたので、参考までに、ご紹介します。
 
 
 
※FreeRTOS.org 提供のNiosII向け Exampleコード「全体」に対して、詳細な動作検証までは行っておりませんので、その点はご了承ください。

* FreeRTOS.org:  オープンソースの RTOS https://freertos.org/
* FreeRTOS v10.3 コード(含 Examle): Github, FreeRTOS donwload v10.3
* NiosII 向け Example: FreeRTOS/Demo/NiosII_CycloneIII_DBC3C40_GCC/
* DE0-Nano:  Terasic社製 Cyclone IV 低価格評価ボード  
* Intel Quartus Prime / NiosII EDS: 今回は、v18.1 を使用


※1:モチベーション:
手持ちの低価格評価ボード DE0-Nano を使用して、最小限の工数で、一先ず、FreeRTOS を動かしてみたい。

※2:CPU 及び Cyclone IV 上のハードウェア(HW):
CPU: NiosII
HW: ゴールデン・リファレンス・デザイン (Terasic提供)をカスタマイズ

※3:HAL / OS
HAL: NiosII EDS にて生成
OS: FreeRTOS


[1] FreeRTOS.org 提供の Example は、そのまま DE0-Nano のゴールデン・リファレンス・デザイン (GRD) で動作させることはできるのか?

 
 
 
残念ながら、次の理由で、動作させることはできません。
 
 
(a) FreeRTOS.org提供のExampleコードには、UART を使用する コードがある。しかし、GRD には UART ペリフェラルがない。→ Platform Designer (旧Qsys) + Quartus で、Hardware デザインを変更 + コンパイルする必要がある。
 

(b) 追加のマクロ定義をしないと、Compile エラーとなる。→ ソースコードの変更
 

(c) 古い Quartus / NiosII EDS を前提にコードが提供されているため、そのままではBSP (Board Support Package) / HAL を生成できない。→ Platform Designer (旧Qsys)からの出力ファイル *.socinfo ファイルと BSP Editor を使用して生成する。
 

(d) LED の記述を、DE0-Nano の GRD に合わせて変更する必要がある。
 

(e) vTaskDelay() 関数を使用すると、vTaskDelay() 以降の処理が実行されない。→ FreeRTOS 提供の NiosII 用 Exampleには、Exception Handler が含まれている。これが、最新の NiosII HAL/BSP の記述と相性が悪い(前提が異なる)。
 

(f) alt_irq_handler() 内部で無限ループとなる。→ 前述(e)と同じで、恐らく相性問題。詳細は追っていないが、とりあえずの回避策あり。
 
 


[2] 対策方法:

前述の(a)~(f) の対策を以下に記載します。

(a) Platform Designer (旧Qsys) + Quartus で、Hardware デザインを変更 + コンパイルする必要がある。


DE0-Nano 付属の CD-ROM に、各種ファイルがあります。"Demonstration” フォルダー以下に、「DE0_Nano_QSYS_DEMO」というデザインがありますので、これを変更して使用します。

Quartus の使用方法の詳細は割愛します。
*DE0_Nano.qpf を使用して、Quartus を開きます。
*DE0_Nano_SOPC.qsys を使用して、Platform Designer を開きます。
*"UART" を追加します。"Name" の箇所を "uart" に変更します(下図参照)。クロックはNiosIIと同じクロックで、IRQ もNiosIIに接続します。

*上記変更&ファイルを保存後、Platform上で Generate HDL を行います。また、Quartus 上で、Compile を行い、*.sof ファイルを生成します。Quartus Lite Edition を使用している場合には、生成される sof ファイル名は、「DE0_Nano_time_limited.sof」になります。
*この sof ファイルを、ソフトウェアを動作させる前に、Quartus の Programmer を使用して、NE0-Nanoボードにダウンロードします。
 


(b) 追加のマクロ定義をしないと、Compile エラーとなる。→ ソースコードの変更

以下のコードを、FreeRTOS Example コードに追加します。

FreeRTOSConfig.hファイル:
//Added 100MHz clock for NiosII
#define SYS_CLK_FREQ 100000000

portable/GCC/NiosII/port.cファイル:
#include "altera_avalon_timer.h"
#define  SYS_CLK_IRQ  0

//SYS_CLK_BASE を TIMER_BASE に変更(6箇所)
//例:
IOWR_ALTERA_AVALON_TIMER_CONTROL( SYS_CLK_BASE, ALTERA_AVALON_TIMER_CONTROL_STOP_MSK );
 ↓
IOWR_ALTERA_AVALON_TIMER_CONTROL( TIMER_BASE, ALTERA_AVALON_TIMER_CONTROL_STOP_MSK );

 

(c) Platform Designer (旧Qsys)からの出力ファイル *.socinfo ファイルと BSP Editor を使用して生成する。

NiosII EDS 上で、前述(a)の操作で生成された *.socinfo ファイルを使用して、BSP を生成する。BSP Editor 上では、特に変更する設定は無い。Generate ボタンをクリックするだけ。
(BSP Editor 操作の詳細は割愛)
 


(d) LED の記述を、DE0-Nano の GRD に合わせて変更する必要がある。


ParTest/ParTest.c ファイル:

//LED_PIO_BASE を LED_BASE に変更(2箇所)
//
//1.
IOWR_ALTERA_AVALON_PIO_DIRECTION( LED_PIO_BASE, ALTERA_AVALON_PIO_DIRECTION_OUTPUT );
 ↓
IOWR_ALTERA_AVALON_PIO_DIRECTION( LED_BASE, ALTERA_AVALON_PIO_DIRECTION_OUTPUT );

//2.
IOWR_ALTERA_AVALON_PIO_DATA( LED_PIO_BASE, ulLedStates );
 ↓
IOWR_ALTERA_AVALON_PIO_DATA( LED_BASE, ulLedStates );
 


(e) vTaskDelay() 関数を使用すると、vTaskDelay() 以降の処理が実行されない。→ FreeRTOS 提供の NiosII 用 Exampleには、Exception Handler が含まれている。これが、最新の NiosII HAL/BSP の記述と相性が悪い(前提が異なる)。


(ここが原因を探るのに最も苦労したところ)


portable/GCC/NiosII/port_asm.S ファイル:
 

[call_scheduler オリジナル記述]
call_scheduler:
addi ea, ea, 4 # A trap was called, increment the program counter so it is not called again.
stw ea, 72(sp) # Save the new program counter to the context.
call vTaskSwitchContext # Pick the next context.
br restore_sp_from_pxCurrentTCB # Switch in the task context and restore. 


[call_scheduler 変更後(line 147 に、movia,ldw,ldw の3行を追加)]
call_scheduler:
addi ea, ea, 4 # A trap was called, increment the program counter so it is not called again.

#New lines (start)
movia et, pxCurrentTCB # Load the address of the pxCurrentTCB pointer
ldw et, (et) # Load the value of the pxCurrentTCB pointer
ldw sp, (et) # Load the stack pointer with the top value of the TCB
#New lines (end)

stw ea, 72(sp) # Save the new program counter to the context.
call vTaskSwitchContext # Pick the next context.
br restore_sp_from_pxCurrentTCB # Switch in the task context and restore. 

 
 

(f) alt_irq_handler() 内部で無限ループとなる。対策は以下。


※注意事項: BSP Editor で生成する度に、以下の対策を実行する必要あり(上書きされてしまう)。

*_bsp/HAL/src/alt_irq_handler.cファイル:
line 141行の do{ ~ } while(1) を、if (active) { ~ } で囲む。

    if (active) //added for FreeRTOS demo
    {
    do
    {
      if (active & mask)
      { 
#ifdef ALT_ENHANCED_INTERRUPT_API_PRESENT
        alt_irq[i].handler(alt_irq[i].context); 
#else
        alt_irq[i].handler(alt_irq[i].context, i); 
#endif
        break;
      }
      mask <<= 1;
      i++;

    } while (1);
    }

 


[3] 確認したFreeRTOS を含むソフトウェア・コード:


とりあえず、動作確認したコードを紹介します。ここでは、vTaskDelay() を使用したLEDの点滅動作を確認しています。ボード上のLED01 が点滅します。

#vTaskDelay()を動作させるには、前述(e) が不可欠ですのでご注意を!


Common_Demo_Tasks/flash.cファイル:

static portTASK_FUNCTION( debug_task, pvParameters ){
  for(;;) {
            vParTestToggleLED( 1 ); //LED01
    vTaskDelay(pdMS_TO_TICKS(50));
  }
}

void vStartLEDFlashTasks( UBaseType_t uxPriority )
{
if (xTaskCreate( debug_task, "DEBUGx", ledSTACK_SIZE, NULL, uxPriority + 1, ( TaskHandle_t * ) NULL ) != pdPASS) {
printf("debug: xTaskCreate failed, debug_task\n");
}
}



main.cファイル:

int main( void )
{
prvSetupHardware();
    
vStartLEDFlashTasks( tskIDLE_PRIORITY );

vTaskStartScheduler();

for( ;; );

return 0;
}



※その他1:

Makefile 中のALT_INCLUDE_DIRS変数の設定も忘れずに!

(設定例)
ALT_INCLUDE_DIRS := . FreeRTOS/include Common_Demo_Tasks/include FreeRTOS/portable/GCC/NiosII



※その他2:


UART を動作させるには、上記の他に、UART のTX/RX ピンを、GPIO に割り当てる処理が必要になります。こちらについては、今回の記載には含まれておりません。こちらは、別途更新しようと思います。


Enjoy !