FreeRTOS.org からリリースされている Demo Example の中に、Intel FPGA / NiosII 向け Example があります(FreeRTOS/Demo/NiosII_CycloneIII_DBC3C40_GCC/)。
生憎、この Example は非常に古いため、そのままでは最新の NiosII EDS では動作しません。
そこで、DE0-Nano で(とりあえず)動作させる手順を、以下に纏めましたので、参考までに、ご紹介します。
※FreeRTOS.org 提供のNiosII向け Exampleコード「全体」に対して、詳細な動作検証までは行っておりませんので、その点はご了承ください。
* 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 !