3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Pico + FreeRTOS SMP で USB に 表示出力するとハング

Last updated at Posted at 2024-02-10

はじめに

Raspberry Pi Pico + FreeRTOS SMP で stdio を usb にすると std::cout でたまにハングする。
どこかの設定で回避できるのかも知れないが、見つけられなかった。
正しい処置が不明だが、調べた事と 考えた対策を記述する。

発生事象

  • 開発環境
    • Raspberry Pi Pico C/C++ SDK 1.5.1
    • FreeRTOS branch main #982
    • Windows11 + VSCode

  • task 1本でもハングする
    • task を 1本だけ起こして そこから出力しただけでもハングする

    • std::cout << std::endl 2行は 発生頻度を上げるため

    • std::cout << std::endl を増やしたり、delay を小さくすると 更に頻度アップ

      void test_func(void*)
      {
      	int n = 0 ;
      	while(true)	{
      		vTaskDelay( pdMS_TO_TICKS(50) ) ;
      		std::cout << n++ << std::endl ;
      		std::cout << std::endl ;
      		std::cout << std::endl ;
      	}
      }
      
      int main(void)
      {
      	stdio_init_all() ;
      	xTaskCreate(test_func, "test_func", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL) ;
          vTaskStartScheduler();
      }
      
      pico_enable_stdio_usb (${project_name} 1)
      pico_enable_stdio_uart(${project_name} 0)
      

確認内容

  • stack size
    stack は 確認できた時点で 108 x 4 byte 残っており、stack 不足では無さそう。
    stack size を増やしても効果なし

    IDLE1           R       0       232     4
    IDLE0           R       0       226     3
    test_func       B       1       108     2
    Tmr Svc         R       31      972     5
    
  • configUSE_PREEMPTION
    変更しても変化なし

  • configRUN_MULTIPLE_PRIORITIES
    頻度は下がるがハングする

  • core を 0 に固定
    vTaskCoreAffinitySet() で core を 0 に固定すると 発生しない!!

  • core を 1 に固定
    vTaskCoreAffinitySet() で core を 1 に固定すると 頻度爆上がり

と言うことで、std::cout 実行時に core 1 が割り当てられると 不都合があるようだ。

対策

  • std::cout の実行時は core 0 に固定する
  • std::cout 呼び出し時に 一々 vTaskCoreAffinitySet() するのは面倒なので、自分の stream を定義する
  • ここで core 0 に固定してから std::cout に渡し、終わったら元に戻す
  • このコードで ハングしなくなった
    class CNkStringBuf : public std::stringbuf
    {
    	virtual int sync() override
    	{
    		UBaseType_t  pre = vTaskCoreAffinityGet(NULL) ;
    		vTaskCoreAffinitySet(NULL, 1) ;
    
    		std::cout << str() << std::flush ;
    		str("");
    
    		vTaskCoreAffinitySet(NULL, pre) ;
    		return 0;
    	}
    };
    
    namespace nk
    {
    	CNkStringBuf cout_buf ;
    	std::ostream cout(&cout_buf) ;	
    }
    
    void test_func(void*)
    {
    	int n = 0 ;
    	while(true)	{
    		vTaskDelay( pdMS_TO_TICKS(50) ) ;
    		nk::cout << n++ << std::endl ;
    		nk::cout << std::endl ;
    		nk::cout << std::endl ;
    	}
    }
    

まとめ

  • std::cout を core 0 で実行するとハングしない
  • 本来の対策 ( 設定? ) は 未だ不明
  • 対策として、自前の stream で core 0 に固定してから std::cout を呼ぶ

おまけ

  • Core の lock guard を作ってみた
class CCoreLock
{
protected:
	UBaseType_t  pre ;
public:
	CCoreLock() = delete ;
	CCoreLock(uint core_id)
	{
#if configNUMBER_OF_CORES>1 && configUSE_CORE_AFFINITY==1
		pre = vTaskCoreAffinityGet(NULL) ;
		vTaskCoreAffinitySet(NULL, 1<<core_id) ;
#endif
	}

	~CCoreLock()
	{
#if configNUMBER_OF_CORES>1 && configUSE_CORE_AFFINITY==1
		vTaskCoreAffinitySet(NULL, pre) ;
#endif
	}
} ;

class CNkStringBuf : public std::stringbuf
{
	virtual int sync() override
	{
		CCoreLock core(0) ;
		std::cout << str() << std::flush ;
		str("");
		return 0;
	}
};
3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?