Tracealyzer Recorderの実装方法 - renesas/rx72n-envision-kit GitHub Wiki

準備する物

前提条件

前提知識

プロジェクトを新規作成する

  • 新規プロジェクト作成方法(FreeRTOS) を参考に e2 studio で新規プロジェクトを作成する
  • 利用するFreeRTOSのバージョンは 10.4.3-rx-1.0.6 とする
    • image
  • 新規プロジェクト作成方法(FreeRTOS) の記事を作成した当時と比較し、ボードに依存する設定項目の多くが自動化されたため、クロック設定 は不要になった
    • が、システム開発上、クロック設定値によりCPUクロック(ICLK)や周辺クロック(PCLK)が何MHzで動作しているのか、また、クロック源が何MHzでPLL(逓倍回路)の設定で何逓倍されていて、どのような分配器を経て各機能のクロックソースとなっているかなどを理解するためには、マイコンのハードウェアマニュアルを参照し、クロック設定の項目及び出力コードを改めて確認することを推奨する
  • また、新規プロジェクト作成方法(FreeRTOS) ではヒープサイズを 8KB で設定しているが、Tracealyzerの利用時は 128KB 以上を推奨する (32KBでもモニタ可能であることは確認済)
    • モニタするFreeRTOSのリソース(タスクやセマフォなど)の量やシステムコールを呼び出す頻度に依存して必要になるバッファ量を柔軟に調整するため、多めのサイズ設定としている
    • 必要に応じてヒープサイズは調整すること
      • image

デバッガ設定と動作確認

Tracealyzer Recorder を e2 studio のプロジェクトに登録する

  • 大まかなメカニズム、データフローを理解する
  • TracealyzerのヘルプからTraceRecorderフォルダを開く
    • image
      • e2 studio のプロジェクトエクスプローラ上で「src」フォルダを右クリックし、「system explorer」を選択
        • image
          • Tracealyzerのヘルプから開いたTraceRecorderフォルダをすべて「src」フォルダにコピーする
            • 補足:
              • TraceRecoderフォルダは以下Percepio社のGitHubで同一のものが公開されている
      • TraceRecorderフォルダは以下のようにプロジェクト登録される
        • エクスプローラ上でstreamports 以下はすべて消しておき、代わりに「Renesas_RX_UART」フォルダとその内部にconfigフォルダ、includeフォルダを作って、「trcStreamPort.c」「trcStreamPort.h」「trcStreamPortConfig.h」「Readme-Streamport.txt」をそれぞれ空のファイルを作って置いておく
          • image
  • trcStreamPort.c に以下をコピーペーストする
#include <string.h>

#include "trcRecorder.h"
#include "r_sci_rx_if.h"
#include "r_sci_rx_pinset.h"

#if (TRC_CFG_RECORDER_MODE == TRC_RECORDER_MODE_STREAMING)
#if (TRC_USE_TRACEALYZER_RECORDER == 1)

static uint8_t string[1024];
static uint8_t sci_buffer[1024];
static uint32_t sci_current_received_size = 0;
static volatile uint32_t wait_sending = 0;

extern sci_hdl_t sci_handle_tracealyzer;

void sci_callback_tracealyzer(void *arg);

traceResult xTraceStreamPortInitialize(TraceStreamPortBuffer_t* pxBuffer)
{
	TRC_ASSERT_EQUAL_SIZE(TraceStreamPortBuffer_t, TraceStreamPortUSBBuffers_t);

	if (pxBuffer == 0)
	{
		return TRC_FAIL;
	}

	return xTraceInternalEventBufferInitialize(pxBuffer->buffer, sizeof(pxBuffer->buffer));
}

traceResult prvTraceUARTTransmit(void* pvData, uint32_t uiSize, int32_t* piBytesSent)
{
	int32_t error_code = -1;

	while(1)
	{
		if(wait_sending)
		{
			xTraceKernelPortDelay(1);
		}
		else
		{
			break;
		}
	}

	if(uiSize < sizeof(string))
	{
		memcpy(string, pvData, uiSize);
		if(SCI_SUCCESS == R_SCI_Send(sci_handle_tracealyzer, string, uiSize))
		{
			wait_sending = 1;
			*piBytesSent = uiSize;
			error_code = 0;
		}
	}
	return error_code;
}

traceResult prvTraceUARTReceive(void* data, uint32_t uiSize, int32_t* piBytesReceived)
{
	if(sci_current_received_size == uiSize)
	{
		memcpy(data, sci_buffer, sci_current_received_size);
		*piBytesReceived = sci_current_received_size;
		sci_current_received_size = 0;
	}
	return 0;
}

void sci_callback_tracealyzer(void *arg)
{
	sci_cb_args_t   *p_args;
	p_args = (sci_cb_args_t *)arg;

	if (SCI_EVT_RX_CHAR == p_args->event)
	{
		R_SCI_Receive(p_args->hdl, &sci_buffer[sci_current_received_size], 1);
		if(sci_current_received_size == (sizeof(sci_buffer) - 1)) /* -1 means string terminator after "\n" */
		{
			sci_current_received_size = 0;
		}
		else
		{
			sci_current_received_size++;
		}
	}
	else if(SCI_EVT_TEI == p_args->event)
	{
		wait_sending = 0;
	}
}
  • trcStreamPortConfig.h に以下をコピーペーストする
#ifndef TRC_STREAM_PORT_CONFIG_H
#define TRC_STREAM_PORT_CONFIG_H

#ifdef __cplusplus
extern "C" {
#endif

/*******************************************************************************
* Configuration Macro: TRC_CFG_STREAM_PORT_INTERNAL_BUFFER_SIZE
*
* Specifies the size of the internal buffer.
******************************************************************************/
#define TRC_CFG_STREAM_PORT_INTERNAL_BUFFER_SIZE 1024

#ifdef __cplusplus
}
#endif

#endif /* TRC_STREAM_PORT_CONFIG_H */
  • trcStreamPort.h に以下をコピーペーストする
#ifndef TRC_STREAM_PORT_H
#define TRC_STREAM_PORT_H

#include <trcTypes.h>
#include <trcStreamPortConfig.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef struct TraceStreamPortBuffer
{
	uint8_t buffer[(TRC_CFG_STREAM_PORT_INTERNAL_BUFFER_SIZE) + sizeof(TraceUnsignedBaseType_t)];
} TraceStreamPortBuffer_t;

traceResult prvTraceUARTReceive(void* data, uint32_t uiSize, int32_t* piBytesReceived);

traceResult prvTraceUARTTransmit(void* pvData, uint32_t uiSize, int32_t* piBytesSent);

/**
 * @internal Stream port initialize callback.
 *
 * This function is called by the recorder as part of its initialization phase.
 *
 * @param[in] pxBuffer Buffer
 *
 * @retval TRC_FAIL Initialization failed
 * @retval TRC_SUCCESS Success
 */
traceResult xTraceStreamPortInitialize(TraceStreamPortBuffer_t* pxBuffer);

/**
 * @brief Allocates data from the stream port.
 *
 * @param[in] uiSize Allocation size
 * @param[out] ppvData Allocation data pointer
 *
 * @retval TRC_FAIL Allocate failed
 * @retval TRC_SUCCESS Success
 */
#define xTraceStreamPortAllocate(uiSize, ppvData) ((void)uiSize, xTraceStaticBufferGet(ppvData))

/**
 * @brief Commits data to the stream port, depending on the implementation/configuration of the
 * stream port this data might be directly written to the stream port interface, buffered, or
 * something else.
 *
 * @param[in] pvData Data to commit
 * @param[in] uiSize Data to commit size
 * @param[out] piBytesCommitted Bytes committed
 *
 * @retval TRC_FAIL Commit failed
 * @retval TRC_SUCCESS Success
 */
#define xTraceStreamPortCommit xTraceInternalEventBufferPush

/**
 * @brief Writes data through the stream port interface.
 *
 * @param[in] pvData Data to write
 * @param[in] uiSize Data to write size
 * @param[out] piBytesWritten Bytes written
 *
 * @retval TRC_FAIL Write failed
 * @retval TRC_SUCCESS Success
 */
#define xTraceStreamPortWriteData prvTraceUARTTransmit

/**
 * @brief Reads data through the stream port interface.
 *
 * @param[in] pvData Destination data buffer
 * @param[in] uiSize Destination data buffer size
 * @param[out] piBytesRead Bytes read
 *
 * @retval TRC_FAIL Read failed
 * @retval TRC_SUCCESS Success
 */
#define xTraceStreamPortReadData prvTraceUARTReceive

#define xTraceStreamPortOnEnable(uiStartOption) ((void)(uiStartOption), TRC_SUCCESS)

#define xTraceStreamPortOnDisable() (TRC_SUCCESS)

#define xTraceStreamPortOnTraceBegin() (TRC_SUCCESS)

#define xTraceStreamPortOnTraceEnd() (TRC_SUCCESS)

#ifdef __cplusplus
}
#endif

#endif /* TRC_STREAM_PORT_H */
  • Readme-Streamport.txt は空のままでよい
  • FreeRTOSConfig.h の一番下に#include "trcRecorder.h" を追加
    • image
  • trcConfig.h を以下のように変更する
    • #error ... の行をコメントアウト
    • TRC_CFG_HARDWARE_PORT に TRC_HARDWARE_PORT_Renesas_RX600 を指定
      • image
  • trcKernelPortConfig.h を以下のように変更する
    • TRC_CFG_RECORDER_MODEに TRC_RECORDER_MODE_STREAMING を指定
    • TRC_CFG_FREERTOS_VERSION に TRC_FREERTOS_VERSION_10_4_1を指定 ⇒使用するFreeRTOSのバージョンに拠るので自分が使っているFreeRTOSのバージョンを確認し一致させること
      • image
  • Tracealyzerのモニタデータ出力用のUARTの設定を行う
    • スマートコンフィグレータでSCIのFITモジュールを追加/設定
      • image
        • フィルタに「sci」を入力してもSCIのFITモジュールが表示されない場合は、「最新版のFITドライバとミドルウェアをダウンロードする」を選択する
      • コンポーネントタブでSCIチャネル7を設定する
        • RX72N Envision Kit のPMODのCN6を使用する
          • image
        • チャネル7の送信バッファの容量を80バイトから1024バイトに増やす
          • image
        • チャネル7の送信バッファ空割り込みを使用する設定に変更する
          • image
        • チャネル7をフロー制御無しUARTで利用するので、フロー制御用端子(RTS/CTS)を無効化し、送受信用端子(TxD/RxD)のみ有効にしておく
          • image
    • 端子タブでSCIチャネル7を設定する
      • image
  • コンパイラ設定でTracealyzerに必要なインクルードパスを追加する
    • プロジェクトエクスプローラでプロジェクト名「rx72n_envision_kit」を右クリックし、プロパティを選択
      • image
        • C/C++ビルド -> 設定 -> ツール設定 -> Compiler -> ソース -> 追加 ボタンを押す
          • image
            • 以下5種類のパスを追加
              • "${workspace_loc:/${ProjName}/src/smc_gen/r_bsp/mcu/rx72n/register_access/ccrx}"
              • "${workspace_loc:/${ProjName}/src/TraceRecorder/config}"
              • "${workspace_loc:/${ProjName}/src/TraceRecorder/include}"
              • "${workspace_loc:/${ProjName}/src/TraceRecorder/streamports/Renesas_RX_UART/config}"
              • "${workspace_loc:/${ProjName}/src/TraceRecorder/streamports/Renesas_RX_UART/include}"
            • 注意
              • "${workspace_loc:/${ProjName}/src/smc_gen/r_bsp/mcu/rx72n/register_access/ccrx}" は、スマートコンフィグレータによりコード生成が実行される度に削除されるため、都度復旧させる必要がある
                • これはTracealyzerが旧来のレジスタアクセスファイル iodefine.h を参照しているためであり、プラットフォーム化された後(FITが適用されたRXv2世代以降)の platform.h を参照していないためである
                • Tracealyzerを開発するPercepio社と協議し、プラットフォーム化された後の環境においては、 iodefine.h を読み込まないように大元のファイルを改良していただくよう交渉予定
  • FreeRTOS-Kernel の portmacro.h の実装がTracealyzerの呼び出しに対し不完全なため、修正する
    /* As this port allows interrupt nesting... */
        static int32_t set_interrupt_mask_from_isr( void );
        static int32_t set_interrupt_mask_from_isr( void )
        {
        	int32_t tmp = __get_ipl();
        	__set_ipl( ( long ) configMAX_SYSCALL_INTERRUPT_PRIORITY );
        	return tmp;
        }
        #define portSET_INTERRUPT_MASK_FROM_ISR()                              set_interrupt_mask_from_isr()
        #define portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus )    set_ipl( ( long ) uxSavedInterruptStatus )
  • FreeRTOSカーネル起動前のフック関数(freertos_start.cのProcessing_Before_Start_Kernel())にTracealyzerおよびSCIの初期化コードを仕込む
    • image
#include "r_sci_rx_if.h"
#include "r_sci_rx_pinset.h"

static sci_cfg_t my_sci_config;
sci_hdl_t sci_handle_tracealyzer;

extern void sci_callback_tracealyzer(void *arg);

void Processing_Before_Start_Kernel(void)
{
    BaseType_t ret;

    /* Create all other application tasks here */
    /* Set up the configuration data structure for asynchronous (UART) operation. */
    my_sci_config.async.baud_rate    = 921600;
    my_sci_config.async.clk_src      = SCI_CLK_INT;
    my_sci_config.async.data_size    = SCI_DATA_8BIT;
    my_sci_config.async.parity_en    = SCI_PARITY_OFF;
    my_sci_config.async.parity_type  = SCI_EVEN_PARITY;
    my_sci_config.async.stop_bits    = SCI_STOPBITS_1;
    my_sci_config.async.int_priority = 15; /* disable 0 - low 1 - 15 high */

    R_SCI_Open(SCI_CH7, SCI_MODE_ASYNC, &my_sci_config, sci_callback_tracealyzer, &sci_handle_tracealyzer);
    R_SCI_PinSet_SCI7();

    xTraceInitialize();
  • mainタスク(rx72n_envision_kit.c)でTracealyzerの動作開始のコードを仕込む
    • image
void main_task(void *pvParameters)
{
	xTraceEnable(TRC_START);
	/* Create all other application tasks here */
	while(1)
	{
		vTaskDelay(10);
	}
	vTaskDelete(NULL);
}

Tracealyzer を起動し設定する

  • Recording Settings を選択
    • image
      • 以下のように設定する
        • image
          • COMポートの番号は、RX72N Envision KitのPMODに接続されたUSBシリアル変換チップと対応するものを設定する
  • Record Streaming Trace を選択
    • image
      • Reconnect -> Start Session を選択し、Tracealyzer側を待ち状態にしておく
        • image

e2 studio でマイコンボード側のソフトウェアを動作状態にする

  • 動作確認を参考に、ソフトウェアを動作状態にする
  • RX72N Envision KitとPC(Tracealyzer)の通信が始まり、FreeRTOSの内部状態がTracealyzer上で可視化される
    • image
  • たとえば、以下ウィンドウではタスクスイッチおよびシステムコールの発行状況を完全にモニタすることができている
    • image
      • 今回作成したシステムでは、led_taskが100msに1回、MAIN_TASKが10msに1回、TzCtrl(Tracealyzer用タスク)が2msに1回起動し、上記はこの3タスクが同時に起動されるタイミングをモニタしたものである
      • 優先度はMAIN_TASK(3) > led_task(1) == TzCtrl(1) となっており、システム起動時にled_taskの方が早く生成されたため、この順番の通りタスクスイッチしていることが分かる
      • なお、同一優先度のタスクがtimetick(通常1ms)の時間より処理が長引く場合、かつ、FreeRTOSConfig.h の configUSE_PREEMPTION がONの場合、timetick毎に同一優先度内で実行されるタスクが順繰りに切り替わる、いわゆる「ラウンドロビン」方式での実行となる

最後に

  • 先述の通り、本稿での解説はSCIを1チャネルTracealyzerの為に占有してしまっている
  • これは実システムで利用できるSCIチャネルが1チャネル減ってしまうことを意味するため、物理1チャネルに論理複数チャネルを重畳できるEthernet等をTracealyzer通信に利用することを推奨する
    • また、UARTの特性上1Mbps程度が通信速度の限界であるため、10タスク程度のモニタで限界となる可能性が高い
    • 複雑なシステムをモニタする場合もEthernet等をTracealyzer通信に利用することを推奨する
  • EthernetをTracealyzer通信に用いる方法は 複雑なシステムのTracealyzer Recorder実装方法 に記載するものとする <工事中>
  • また、RAファミリの場合は、SeggerのJlinkのRTT機能を活用したTracealyzerモニタ方法が、以下アプリケーションノートにより解説されている(RXファミリでも適宜情報追加を行っていく)
⚠️ **GitHub.com Fallback** ⚠️