こんにちは。NoMaYです。別スレッド『Amazon AWSのFreeRTOS Kernel Developer GuideのサンプルコードをRenesas RX SimulatorのDebug Consoleで試せるようにしてみた』で気付いたことですが、FreeRTOSでは、ポートレイヤーの実装に内蔵周辺のソフトウェア割り込み機能(これはCPUのソフトウェア割り込み命令のことでは無いです)を使用することで、素朴に簡単に割り込みルーチンからFreeRTOS APIを呼び出せるようになることに気付きました。そこで、FreeRTOS v10.2.1 RL78ポートレイヤーをそのように改変してみました。以下、プロジェクトのファイル一式です。(CC-RL版はCS+ V8.01/e2 stuiod v7.40+CC-RL V1.02でビルド(e2 studio用.project/.cproject等を同梱))(GNURL78版はe2 studio v7.40+GNURL78 2019q2(4.9.2.201902)でビルド)(共にzipファイルをe2 studioに直接インポート可能) プロジェクト構造は極力RX版と同じにしてあります。([追記] すみません。以下に含まれるMAPファイルが別のものでした。タスク側のクリティカルセクションが以前のままのものでした。この投稿の末尾に追加したMOTファイルのzipファイルには訂正版を入れてあります。)sim_rl78_freertos_full_demo_ccrl_c_csplus_20190705.zip 628KBsim_rl78_freertos_full_demo_gnurl78_c_e2v740_20190705.zip 582KBそのスレッドに投稿していたRL78ポートレイヤー(これもFreeRTOS v10.2.1 RL78ポートレイヤーを改変したものです)では、割り込みルーチンからFreeRTOS APIを呼び出すには以下のような特別なおまじないを記述する必要がありました。(以下はCC-RLの場合のものですがGNURL78でも同様です。)
#pragma interrupt r_intc3_interrupt(vect=INTP3)/* Start user code for pragma. Do not edit comment generated here */R_PRAGMA_FREERTOS_INTERRUPT(r_intc3_interrupt)#define r_intc3_interrupt _r_intc3_interrupt/* End user code. Do not edit comment generated here */
そのスレッドにはRXポートレイヤーの改変についても投稿していたのですが、その時、最初に書いたことに気付きました。RL78の内蔵周辺には専用のソフトウェア割り込み機能はありませんが、以下のハードウェアマニュアルに記載されている通り、プログラムで割り込み要求フラグをセットすると割り込みを発生させることが出来ますので、実質的に任意の空き割り込みをソフトウェア割り込み機能として使うことが出来ます。(今回は、とりあえず、ウォッチドッグタイマのオーバフロー時間の75%到達のインターバル割り込みを使用してみました。) RL78/G13 ユーザーズマニュアル ハードウェア編からの抜粋www.renesas.com/ja-jp/doc/products/mpumcu/doc/rl78/r01uh0146jj0340_rl78g13.pdfなお、今回のRL78ポートレイヤーの改変で、先ほどの特別なおまじないを記述する必要は無くなりますが、割り込みルーチンからFreeRTOS APIを呼び出してブロック解除待ちタスクをブロック解除する場合のタスク切り替えの遅延時間が以下のRenesas RL78 SimulatorのSimulator GUIの画面コピーのように数十クロックほど(32MHz動作では600nsほど)延びます。(以下はCC-RLの場合のものですがGNURL78でも同様(ただし数百nsほど長い)です。) ちなみに、タスク側のクリティカルセクション(その区間では割り込み禁止となる)への出入りによるタイミングへの影響を避ける為、今回はクリティカルセクションとして扱うことしていませんので、以下の画面コピーでは別スレッドの画面コピーよりもポート出力トグルの速さが速くなっています。今回の改変前:今回の改変後:以下、今回のRL78ポートレイヤの改変内容です。コメントの訂正や不要になったルーチンの削除は省略しています。(以下はCC-RLの場合のものですがGNURL78でも同様です。)src/FreeRTOS/Source/portable/Renesas/RL78/portmacro.h今回の改変前:
/* Task utilities. */#define portYIELD() __brk()#define portYIELD_FROM_ISR( xHigherPriorityTaskWoken ) if( xHigherPriorityTaskWoken ) vTaskSwitchContext()#define portNOP() __nop()
今回の改変後: 赤文字行を追加/変更
#include "iodefine.h"
/* Task utilities. */#define portYIELD() do{ WDTIIF = 1; } while (0)#define portYIELD_FROM_ISR( xHigherPriorityTaskWoken ) if( xHigherPriorityTaskWoken ) portYIELD()#define portNOP() __nop()
src/FreeRTOS/Source/portable/Renesas/RL78/port.c今回の改変後: 赤文字行を追加
BaseType_t xPortStartScheduler( void ){ /* Setup the hardware to generate the tick. Interrupts are disabled when this function is called. */ configSETUP_TICK_INTERRUPT(); /* Setup the hardware to generate the yield interrupt. */ WDTIPR1 = 1; WDTIPR0 = 1; WDTIMK = 0; /* Restore the context of the first task that is going to run. */ vPortStartFirstTask(); /* Execution should not reach here as the tasks are now running! */ return pdTRUE;}
src/FreeRTOS/Source/portable/Renesas/RL78/portasm.asm今回の改変前:
_vPortYield: portSAVE_CONTEXT ; Save the context of the current task. call@ _vTaskSwitchContext ; Call the scheduler to select the next task. portRESTORE_CONTEXT ; Restore the context of the next task to run. retb
_vPortYield .VECTOR 0x7E
今回の改変後: 赤文字行を変更
_vPortYield: portSAVE_CONTEXT ; Save the context of the current task. call@ _vTaskSwitchContext ; Call the scheduler to select the next task. portRESTORE_CONTEXT ; Restore the context of the next task to run. reti
_vPortYield .VECTOR 0x04
以下、今回のタスク側のプログラムです。先ほど書いた通り、今回は各タスクのポート出力をトグルさせる部分をクリティカルセクションとして扱うことしていません。(なお、src/frtos_config/FreeRTOSConfig.hの#define FREERTOS_USER_MAIN 0の行で0→1の変更を行うことで以下が実行されるようになります。)src/user_main.c
void main_task(void *pvParameters){ (void) pvParameters; while (1) { P1_bit.no6 = !P1_bit.no6; }}void second_task(void *pvParameters){ (void) pvParameters; while (1) { P1_bit.no5 = !P1_bit.no5; }}void third_task(void *pvParameters){ (void) pvParameters; while (1) { P1_bit.no4 = !P1_bit.no4; }}void intp3_task(void *pvParameters){ (void) pvParameters; R_INTC3_Start(); LED_INIT(); while (1) { xSemaphoreTake( xSemaphoreINTP3, portMAX_DELAY ); LED_BIT = !LED_BIT; }}
[追記]MOTファイルsim_rl78_freertos_full_demo_ccrl_c_csplus_20190705_mot.zip 2019/07/12追加sim_rl78_freertos_full_demo_gnurl78_c_e2v740_20190705_mot.zip 2019/07/24追加
こんにちは。NoMaYです。#4連投の4つ目です。DTC版のCONIOタスクのソースは以下の通りです。今回はFreeRTOSのAPI関数(のラッパー関数)をタスク側ソースではなくコード生成側ソースで呼ぶようにしています。(コード生成側ソースの関数を呼び出している箇所を青文字にしています。また、割り込みのみ版に対してDTC版に特有の部分を赤文字にしています。)freertos_sampleprog1_gnurl78_c/src/frtos_skeleton/task_CONIO.c
void task_CONIO(void * pvParameters){/* ...略... */ volatile uint8_t recv_buff[CON_RECV_DEMO_SIZE + 1]; volatile uint8_t err_type; MD_STATUS status; uint32_t cnt; INTERNAL_NOT_USED( pvParameters ); U_UART3_Using_DTC_Start(); cnt = 0; for (;;) { memset( (char *)recv_buff, 0, CON_RECV_DEMO_SIZE + 1 ); if (0 == cnt) { status = U_UART3_Send_Receive_Wait( (uint8_t *)CON_START_MESSAGE, sizeof(CON_START_MESSAGE), recv_buff, CON_RECV_DEMO_SIZE, &err_type, CON_RECV_TIMEOUT_MS ); } else { status = U_UART3_Receive_Wait( recv_buff, CON_RECV_DEMO_SIZE, &err_type, CON_RECV_TIMEOUT_MS ); } cnt++; nop(); /* for breakpoint, check timing chart on Simulator GUI */ if (MD_OK == status) { taskENTER_CRITICAL(); vPrintString( "Received Message: " ); vPrintString( (char *)recv_buff ); vPrintString( "\n" ); taskEXIT_CRITICAL(); } else { if (MD_RECV_TIMEOUT == status) { vPrintString( "Recv Timeout Error\n" ); } else if (MD_RECV_ERROR == status) { if (SCI_EVT_RXDTC_SWOR & err_type) { vPrintString( "DTC Software Overrun Error\n" ); } if (SCI_EVT_FRAMING_ERR & err_type) { vPrintString( "Framing Error\n" ); } if (SCI_EVT_PARITY_ERR & err_type) { vPrintString( "Parity Error\n" ); } if (SCI_EVT_OVFL_ERR & err_type) { vPrintString( "Overrun Error\n" ); } } else if (MD_SEND_ERROR == status) { vPrintString( "Send Error\n" ); } g_task_CONIO_error = true; LED1 = LED_ON; while (true == g_task_CONIO_error) { vTaskDelay( LED1_ERROR_BLINK_FREQUENCY_MS ); LED1 = ~LED1; } LED1 = LED_OFF; U_UART3_Receive_ClearError(); /* if not called, will be called internally */ cnt = 0; continue; } status = U_UART3_Send_Wait( (uint8_t *)recv_buff, CON_RECV_DEMO_SIZE ); nop(); /* for breakpoint, check timing chart on Simulator GUI */ }/* ...略... */}
コード生成されたシリアル処理のユーザ記述部(抜粋)のソースは以下の通りです。(FreeRTOSのAPI関数(のラッパー関数)を呼び出している箇所を赤文字にしています。) (なお、受信完了で受信割り込みを止めるようにしています。)freertos_sampleprog1_gnurl78_c/src/r_cg_serial.c
/* Start user code for adding. Do not edit comment generated here */...略...void U_UART3_Using_DTC_Start(void){ R_UART3_Start(); U_UART3_Receive_Stop();}
MD_STATUS U_UART3_Receive_Wait(volatile uint8_t * rx_buf, uint16_t rx_num, volatile uint8_t * p_err_type, TickType_t rx_wait){ MD_STATUS status = MD_OK; if (rx_num < 1U || 255U < rx_num) { status = MD_ARGERROR; } else { if (0U != g_uart3_rx_error_type) { U_UART3_Receive_ClearError(); } g_uart3_rx_task = xTaskGetCurrentTaskHandle_R_Helper(); U_UART3_Receive( rx_buf, rx_num ); /* Wait for a notification from the interrupt/callback */ ulTaskNotifyTake_R_Helper( rx_wait ); status = U_UART3_Receive_Complete( p_err_type ); if (MD_RECV_TIMEOUT == status) { U_UART3_Receive_Stop(); g_uart3_rx_task = NULL; /* Clear an unhandled notification from timeout till stop */ ulTaskNotifyTake_R_Helper( 0 ); } } return status;}static void U_UART3_Receive(volatile uint8_t * rx_buf, uint16_t rx_num){ /* This function should be called in SRMK3==1 */ g_uart3_rx_ready_flag = false; gp_uart3_rx_address = &g_uart3_rx_error_data; U_DTCD0_UserInit( rx_buf, rx_num ); SRMK3 = 0U; /* enable INTSR3 interrupt */ SREMK3 = 0U; /* enable INTSRE3 interrupt */#if 0 /* for debugging about SCI_EVT_RXDTC_SWOR */ SRIF3 = 1U; /* ATTENTION: signal from SAU to DTC cannot be masked */#endif R_DTCD0_Start();}
MD_STATUS U_UART3_Send_Wait(uint8_t * tx_buf, uint16_t tx_num){ MD_STATUS status = MD_OK; if (tx_num < 1U || 256U < tx_num) { status = MD_ARGERROR; } else { g_uart3_tx_task = xTaskGetCurrentTaskHandle_R_Helper(); U_UART3_Send( tx_buf, tx_num ); /* Wait for a notification from the interrupt/callback */ ulTaskNotifyTake_R_Helper( portMAX_DELAY ); } return status;}
MD_STATUS U_UART3_Send_Receive_Wait(uint8_t * tx_buf, uint16_t tx_num, volatile uint8_t * rx_buf, uint16_t rx_num, volatile uint8_t * p_err_type, TickType_t txrx_wait){ MD_STATUS status; if (tx_num < 1U || 256U < tx_num || rx_num < 1U || 255U < rx_num) { status = MD_ARGERROR; } else { if (0U != g_uart3_rx_error_type) { U_UART3_Receive_ClearError(); } g_uart3_rx_task = xTaskGetCurrentTaskHandle_R_Helper(); U_UART3_Receive( rx_buf, rx_num ); g_uart3_tx_task = NULL; U_UART3_Send( tx_buf, tx_num ); /* Wait for a notification from the interrupt/callback */ ulTaskNotifyTake_R_Helper( txrx_wait ); if (false == g_uart3_tx_ready_flag) { U_UART3_Send_Stop(); } status = U_UART3_Receive_Complete( p_err_type ); if (MD_RECV_TIMEOUT == status) { U_UART3_Receive_Stop(); g_uart3_rx_task = NULL; /* Clear an unhandled notification from timeout till stop */ ulTaskNotifyTake_R_Helper( 0 ); } if (false == g_uart3_tx_ready_flag && SCI_EVT_RXDTC_SWOR != g_uart3_rx_error_type) { *p_err_type = 0U; status = MD_SEND_ERROR; } } return status;}/* End user code. Do not edit comment generated here */
freertos_sampleprog1_gnurl78_c/src/r_cg_serial_user.c (以下では関数の順番を入れ替えています)
static void r_uart3_callback_error(uint8_t err_type){ /* Start user code. Do not edit comment generated here */ U_UART3_Receive_Stop(); g_uart3_rx_error_type = err_type; vTaskNotifyGiveFromISR_R_Helper( &g_uart3_rx_task ); /* End user code. Do not edit comment generated here */}
static void r_uart3_callback_receiveend(void){ /* Start user code. Do not edit comment generated here */ if (0U == g_uart3_rx_error_type) { U_UART3_Receive_Stop(); g_uart3_rx_ready_flag = true; vTaskNotifyGiveFromISR_R_Helper( &g_uart3_rx_task ); } /* End user code. Do not edit comment generated here */}
static void r_uart3_callback_sendend(void){ /* Start user code. Do not edit comment generated here */#if 0 /* for debugging about MD_SEND_ERROR */ return;#endif U_UART3_Send_Stop(); g_uart3_tx_ready_flag = true; vTaskNotifyGiveFromISR_R_Helper( &g_uart3_tx_task ); /* End user code. Do not edit comment generated here */}
なお、DTC版では、以前の投稿と同様に、CC-RL/GNURL78の最適化オプションで未使用の変数/関数を削除する最適化を実施することを前提にして、マクロの小技を使用して、割り込み処理ルーチンを差し替えています。(以下では以前の投稿には無かった処理を追加した部分を赤文字にしています。)freertos_sampleprog1_gnurl78_c/src/r_cg_serial_user.c
/* Start user code for global. Do not edit comment generated here */...略...R_CG_DEFAULT_ISR_UNUSED(r_uart3_interrupt_receive)R_CG_DEFAULT_ISR_UNUSED(r_uart3_interrupt_send)#define r_uart3_interrupt_receive r_uart3_interrupt_receive_UNUSED#define r_uart3_interrupt_send r_uart3_interrupt_send_UNUSED/* End user code. Do not edit comment generated here */void r_uart3_interrupt_receive(void) ← コード生成機能が生成した割り込み関数(使用しない){...略...}void r_uart3_interrupt_send(void) ← コード生成機能が生成した割り込み関数(使用しない){...略...}...略.../* Start user code for adding. Do not edit comment generated here */#undef r_uart3_interrupt_receive#undef r_uart3_interrupt_sendR_CG_REDEFINE_ISR(r_uart3_interrupt_receive)R_CG_REDEFINE_ISR(r_uart3_interrupt_send)void r_uart3_interrupt_receive(void){ uint8_t err_type; /* For the case that INTSR3 is accepted prior to INTSRE3. */ err_type = (uint8_t)(SSR13 & 0x0007U); SIR13 = (uint16_t)err_type; if (U_DTCD0_Is_Operating() || (0U < U_DTCD0_Get_Count())) { /* Unfortunately INTSR3 is accepted before DTC0 is enabled. */ err_type = SCI_EVT_RXDTC_SWOR; } if (err_type != 0U) { r_uart3_callback_error(err_type); } r_uart3_callback_receiveend();}void r_uart3_interrupt_send(void){ /* DTC has finished data transfer operation. (i.e. The last data has been * written to SDR register.) But the data may stay in either SDR register * or shift register. */ /* It is preferred that next interrupt will be a transmission end interrupt * (i.e. not only SDR register but also shift register become empty) than * a SDR register empty interrupt. */ SMR12 &= ~_0001_SAU_BUFFER_EMPTY; /* In normal case of interrupt, UART has just finished data transmission * operation at 2nd interrupt. But in abnormal case of after long or very * long time pending of data transfer completion interrupt, though at 1st * interrupt, SDR register may already become empty or, moreover, shift * register may already have finished data transmission operation. In the * latter case, it is not clear whether data transmission end interrupt * will occur or not, so the following check is useful. */ if ((SSR12 & _0040_SAU_UNDER_EXECUTE) == 0U) { /* UART has finished data transmission operation. Note that one more * interrupt may occur by above change of SMR register setting unless * transmission interrupt becomes disabled. (In this code, it becomes * disabled in the following callback function.) */ r_uart3_callback_sendend(); } else { /* If ((SSR12 & _0040_SAU_UNDER_EXECUTE) == 0U) changes from false to true * just here, changing SMR register setting at the following timing may * lose data transmission end interrupt. * * SMR12 &= ~_0001_SAU_BUFFER_EMPTY; */ }}/* End user code. Do not edit comment generated here */
[追記]なお、4連投の1つ目は、こちらです。