分享到plurk 分享到twitter 分享到facebook

版本 65c3ac207f3f44f9679b8eb276ae179727fd8c39

embedded/nuttx

Changes from 65c3ac207f3f44f9679b8eb276ae179727fd8c39 to 7e7cc3cdabebd9a4a4c026631ebbfbe0f3704ac6

---
title: nuttx
categories: embedded, arm, stm32, stm32f429
toc: no
...

組員
----
* 翁振育
* 黃亮穎
* 蘇偉嘉
* 李英碩

hackpad : https://stm32f429.hackpad.com/NuttX--hqco2YSgAcJ

Nuttx 參考應用
---------------
* `PX4 autopilot<http://pixhawk.org/dev/nuttx/start>`_

NuttX 架構
------------

* 可混用task跟thread。
* 支援FPU。
* 可混用FIFO跟RR。

與平台相關
------------

Run Nuttx on STM32F429-Disco
----------------------------------------------
| (1) 從 https://github.com/deadbeefcafe/nuttx-stm32f4disc-bb 複製Nuttx到workspace
| (2) cd workspace/nuttx-stm32f4disc-bb/nuttx/tools
| (3) ./configure.sh stm32f429i-disco/nsh
| (4) cd ..
| (5) make CROSSDEV=arm-none-eabi- 
| (6) st-flash write nuttx.bin 0x8000000
| (7) sudo screen /dev/ttyUSB0 115200 8n1



設定相關應用程式與功能
------------------
| at stm32f429i-disco/nsh/defconfig
| 120 CONFIG_ARCH_FPU=n
| 465 CONFIG_RTC=y
| 466 CONFIG_RTC_DATETIME=y
| 653 CONFIG_EXAMPLES_HELLO=y
| 676 CONFIG_EXAMPLES_OSTEST=y

Task Control Interfaces
-----------------------


- task_create : 產生一個activates的新task,並回傳系統指派的pid
- task_init : 將task的TCB初始化,但並不activate task
- task_activate : activate task,否則scheduler沒辦法執行它
- task_delete : 將task關掉,並deallocate它的TCB與stack
- task_restart : 將task的children砍掉,deallocate TCB,reset priority
- task_exit : 關掉目前在運行的task







Task state
------------
include/nuttx/sched.h

.. code-block:: c

 enum tstate_e
 {
     TSTATE_TASK_INVALID    = 0, /* INVALID      - The TCB is uninitialized */
     TSTATE_TASK_PENDING,        /* READY_TO_RUN - Pending preemption unlock */
     TSTATE_TASK_READYTORUN,     /* READY-TO-RUN - But not running */
     TSTATE_TASK_RUNNING,        /* READY_TO_RUN - And running */
     TSTATE_TASK_INACTIVE,       /* BLOCKED      - Initialized but not yet activated */
     TSTATE_WAIT_SEM,            /* BLOCKED      - Waiting for a semaphore */
  #ifndef CONFIG_DISABLE_SIGNALS
     TSTATE_WAIT_SIG,            /* BLOCKED      - Waiting for a signal */
  #endif
  #ifndef CONFIG_DISABLE_MQUEUE
     TSTATE_WAIT_MQNOTEMPTY,     /* BLOCKED      - Waiting for a MQ to become not empty. */
     TSTATE_WAIT_MQNOTFULL,      /* BLOCKED      - Waiting for a MQ to become not full. */
  #endif
  #ifdef CONFIG_PAGING
     TSTATE_WAIT_PAGEFILL,       /* BLOCKED      - Waiting for page fill */
  #endif
     NUM_TASK_STATES             /* Must be last */
 };

Task list 
------------
sched/os_internal.h

.. code-block:: c

  typedef struct tasklist_s tasklist_t;
   

  extern volatile dq_queue_t g_pendingtasks;
  extern volatile dq_queue_t g_waitingforsemaphore;
  #ifndef CONFIG_DISABLE_SIGNALS
  extern volatile dq_queue_t g_waitingforsignal;
  #endif
  #ifndef CONFIG_DISABLE_MQUEUE
  extern volatile dq_queue_t g_waitingformqnotempty;
  #endif
  #ifndef CONFIG_DISABLE_MQUEUE
  extern volatile dq_queue_t g_waitingformqnotfull;
  #endif
  #ifdef CONFIG_PAGING
  extern volatile dq_queue_t g_waitingforfill;
  #endif
  extern volatile dq_queue_t g_inactivetasks;
  extern volatile sq_queue_t g_delayed_kufree;
  #if defined(CONFIG_NUTTX_KERNEL) && defined(CONFIG_MM_KERNEL_HEAP)
  extern volatile sq_queue_t g_delayed_kfree;
  #endif
  extern volatile pid_t g_lastpid;
  extern pidhash_t g_pidhash[CONFIG_MAX_TASKS];
  extern const tasklist_t g_tasklisttable[NUM_TASK_STATES];
 
TCB 
------------
include/nuttx/sched.h

.. code-block:: c

 struct tcb_s
 {
   /* Fields used to support list management *************************************/
   FAR struct tcb_s *flink;               /* Doubly linked list */
   FAR struct tcb_s *blink;
  
   /* Task Management Fields *****************************************************/
 
   pid_t    pid;                          /* This is the ID of the thread        */
 
   start_t  start;                        /* Thread start function               */
   entry_t  entry;                        /* Entry Point into the thread         */
   uint8_t  sched_priority;               /* Current priority of the thread      */
 
   uint8_t  task_state;                   /* Current state of the thread         */
   uint16_t flags;                        /* Misc. general status flags          */
   int16_t  lockcount;                    /* 0=preemptable (not-locked)          */

  #if CONFIG_RR_INTERVAL > 0
    int      timeslice;                    /* RR timeslice interval remaining     */
  #endif

   /* Stack-Related Fields *******************************************************/

  #ifndef CONFIG_CUSTOM_STACK
    size_t    adj_stack_size;              /* Stack size after adjustment         */
                                          /* for hardware, processor, etc.       */
                                          /* (for debug purposes only)           */
    FAR void *stack_alloc_ptr;             /* Pointer to allocated stack          */
                                           /* Need to deallocate stack            */
    FAR void *adj_stack_ptr;               /* Adjusted stack_alloc_ptr for HW     */
                                          /* The initial stack pointer value     */
  #endif
 
  
    /* POSIX Semaphore Control Fields *********************************************/
 
   sem_t *waitsem;                        /* Semaphore ID waiting on             */
 
   /* POSIX Signal Control Fields ************************************************/
 
 #ifndef CONFIG_DISABLE_SIGNALS
   sigset_t   sigprocmask;                /* Signals that are blocked            */
   sigset_t   sigwaitmask;                /* Waiting for pending signals         */
   sq_queue_t sigactionq;                 /* List of actions for signals         */
   sq_queue_t sigpendactionq;             /* List of pending signal actions      */
   sq_queue_t sigpostedq;                 /* List of posted signals              */
   siginfo_t  sigunbinfo;                 /* Signal info when task unblocked     */
 #endif
 
   /* POSIX Named Message Queue Fields *******************************************/
 
 #ifndef CONFIG_DISABLE_MQUEUE
   FAR msgq_t *msgwaitq;                  /* Waiting for this message queue      */
 #endif
 
 
   /* The form and content of these fields are processor-specific.               */
 
   struct xcptcontext xcp;                /* Interrupt register save area        */
 
 #if CONFIG_TASK_NAME_SIZE > 0
   char name[CONFIG_TASK_NAME_SIZE];      /* Task name                           */
 #endif
                                                   :
                                                   : 
                                                   : 
                                                   :
                                                   :
                                                   :
 };
 
Register
---------

.. image:: /ARM_register.jpg



Communication
---------------------
 
Scheduling
----------------
include/sys/types.h

.. code-block:: c 

 /* HPUX-like MIN/MAX value */
 
 #define PRIOR_RR_MIN      0
 #define PRIOR_RR_MAX    255
 #define PRIOR_FIFO_MIN    0
 #define PRIOR_FIFO_MAX  255
 #define PRIOR_OTHER_MIN   0           /*  Not use    */
 #define PRIOR_OTHER_MAX 255           /*  Not use    */
 
 /* Scheduling Priorities.  NOTE:  Only the idle task can take
  * the true minimum priority. */
 
 #define SCHED_PRIORITY_MAX     255
 #define SCHED_PRIORITY_DEFAULT 100
 #define SCHED_PRIORITY_MIN       1
 #define SCHED_PRIORITY_IDLE      0
 
| 1. RR 時間間隔是 20 ticks 做一次 context switch。
| 觀看每個設定time slice的相關檔案其運算是皆為 timeslice = CONFIG_RR_INTERVAL / MSEC_PER_TICK 
| 在 nuttx/include/nuttx/config.h 中
| CONFIG_RR_INTERVAL = 200
| MSEC_PER_TICK = 10

| 2. 若要禁止 context switch,可用sched_lock()
| 此區禁止 context switch
| sched_unlock()用在修改scheduler和priority。

| 3. 若要先將 timer interrupt 暫停,可用saved_state = irqsave(); 
| 此區暫停 timer interrupt
| irqrestore(saved_state);
| 用在修改 scheduler。
| 注意:timer interrupt 跟 context switch 是不同系統。

| 4. Nuttx 只有兩個 scheduler policy,其 tcb->timeslice = 0 則表示FIFO, 非0則為RR。

Timer interrupt
----------------------

| /nuttx/arch/arm/src/stm32/stm32_vectors.S 
| 
| processor exception


.. code-block:: c 

	.word	IDLE_STACK			/* Vector  0: Reset stack pointer */
	.word	__start				/* Vector  1: Reset vector */
	.word	stm32_nmi			/* Vector  2: Non-Maskable Interrupt (NMI) */
	.word	stm32_hardfault		/* Vector  3: Hard fault */
	.word	stm32_mpu			/* Vector  4: Memory management (MPU) */
	.word	stm32_busfault		/* Vector  5: Bus fault */
	.word	stm32_usagefault	/* Vector  6: Usage fault */
	.word	stm32_reserved		/* Vector  7: Reserved */
	.word	stm32_reserved		/* Vector  8: Reserved */
	.word	stm32_reserved		/* Vector  9: Reserved */
	.word	stm32_reserved		/* Vector 10: Reserved */
	.word	stm32_svcall		/* Vector 11: SVC call */
	.word	stm32_dbgmonitor	/* Vector 12: Debug monitor */
	.word	stm32_reserved		/* Vector 13: Reserved */
	.word	stm32_pendsv		/* Vector 14: Pendable system service request */
	.word	stm32_systick		/* Vector 15: System tick */

/nuttx/arch/arm/src/stm32/stm32_timerisr.c (in line 109 )

.. code-block:: c 

 int up_timerisr(int irq, uint32_t *regs)
 {
     /* Process timer interrupt */
     sched_process_timer();
     return 0;
  }


Interrupt Handler
------------------------
在nuttx/arch/arm/include/stm32/irq.h和nuttx/arch/arm/include/stm32/stm32f40xxx_irq.h中分別定義了可用的ISR name和對應的vector name

Processor Exceptions (vectors 0-15)

包括Internal和Software Interrupts,設定在nuttx/arch/arm/src/chip/stm32_irq.c:void up_irqinitialize(void)

.. code-block:: c

 //nuttx/arch/arm/include/stm32/irq.h
 #define STM32_IRQ_RESERVED       (0) /* Reserved vector (only used with CONFIG_DEBUG) */
                                      /* Vector  0: Reset stack pointer value */
                                      /* Vector  1: Reset (not handler as an IRQ) */
 #define STM32_IRQ_NMI            (2) /* Vector  2: Non-Maskable Interrupt (NMI) */
 #define STM32_IRQ_HARDFAULT      (3) /* Vector  3: Hard fault */
 #define STM32_IRQ_MEMFAULT       (4) /* Vector  4: Memory management (MPU) */
 #define STM32_IRQ_BUSFAULT       (5) /* Vector  5: Bus fault */
 #define STM32_IRQ_USAGEFAULT     (6) /* Vector  6: Usage fault */
 #define STM32_IRQ_SVCALL        (11) /* Vector 11: SVC call */
 #define STM32_IRQ_DBGMONITOR    (12) /* Vector 12: Debug Monitor */
                                     /* Vector 13: Reserved */
 #define STM32_IRQ_PENDSV        (14) /* Vector 14: Pendable system service request */
 #define STM32_IRQ_SYSTICK       (15) /* Vector 15: System tick */
 
External interrupts (vectors >= 16) 

外部中斷,設定在nuttx/arch/arm/src/stm32/chip/stm32f40xxx_vectors.h

.. code-block:: c

 //nuttx/arch/arm/include/stm32/stm32f40xxx_irq.h
 #define STM32_IRQ_WWDG        (STM32_IRQ_INTERRUPTS+0)  /* 0:  Window Watchdog interrupt */
 #define STM32_IRQ_PVD         (STM32_IRQ_INTERRUPTS+1)  /* 1:  PVD through EXTI Line detection interrupt */
 #define STM32_IRQ_TAMPER      (STM32_IRQ_INTERRUPTS+2)  /* 2:  Tamper and time stamp interrupts */
 #define STM32_IRQ_TIMESTAMP   (STM32_IRQ_INTERRUPTS+2)  /* 2:  Tamper and time stamp interrupts */
 #define STM32_IRQ_RTC_WKUP    (STM32_IRQ_INTERRUPTS+3)  /* 3:  RTC global interrupt */
 #define STM32_IRQ_FLASH       (STM32_IRQ_INTERRUPTS+4)  /* 4:  Flash global interrupt */
 .
 .
 .

如何設定Interrupt Handler?

.. code-block:: c

 irq_attach(STM32_IRQ_SVCALL, up_svcall); //System Call
 //irq_attach(STM32_IRQ_SVCALL, up_svcall); //System Call

 nuttx/sched/irq_attach.c
 //nuttx/sched/irq_attach.c
 int irq_attach(int irq, xcpt_t isr)
 {
  state = irqsave();	//disables all interrupts
  if(isr == NULL)
  {
   up_disable_irq(irq);
   isr = irq_unexpected_isr;
  }
   g_irqvector[irq] = isr; //Vector Table
   irqrestore(state);
   ret = ok;
 }


Interrupt Handling的分層架構

1. low-level logic, arch/arm/src/armv7-m/up_exception.S, 接收Interrupt並決定IRQ number

2. That low-level logic than calls some MCU-specific, intermediate level function up_doirq(), mask and acknowledge the interrupt, dispatch, unmask

3. That MCU-specific function then calls the NuttX common interrupt dispatching logic irq_dispatch()

STM32_IRQ_SYSTICK

.. code-block:: c

 (void)irq_attach(STM32_IRQ_SYSTICK, (xcpt_t)up_timerisr);
 up_exception.S -> up_doirq()-> up_timerisr(int irq, uint32_t *regs) -> sched_process_timer(void)
 //(void)irq_attach(STM32_IRQ_SYSTICK, (xcpt_t)up_timerisr);
 //up_exception.S -> up_doirq(int irq, uint32_t *regs)-> irq_dispatch(int irq, FAR void *context) -> up_timerisr(int irq, uint32_t *regs) -> sched_process_timer(void)
 static void sched_process_timeslice(void)
 {
 	if((rtcb->flags & TCB_FLAG_ROUND_ROBIN) != 0)
 	{
 		if(rtcb->timeslice <= 1)
 		{ 
 			if(!rtcb->lockcount)
 			{
 				rtcb->timeslice = CONFIG_RR_INTERVALMSEC_PER_TICK;
 				if (rtcb->flink && rtcb->flink->sched_priority >= rtcb->sched_priority)
 				{
 					up_reprioritize_rtr(rtcb, rtcb->sched_priority);
 				}
 			}
 		}
 		else
 		{
 			rtcb->timeslice--;
 		}
 	}
  }



FPU
-------
| arch/arm/src/armv7-m/up_fpu.S
| 儲存 fpu 的 context switch

.. code-block:: c

 up_savefpu:
 
 	add		r1, r0, #(4*REG_S0)		/* R1=Address of FP register storage */
 
 	/* Some older GNU assemblers don't support all the newer UAL mnemonics. */
 
 #if 1 /* Use UAL mnemonics */
 	/* Store all floating point registers.  Registers are stored in numeric order,
 	 * s0, s1, ... in increasing address order.
 	 */
 
 	vstmia	r1!, {s0-s31}			/* Save the full FP context */
 
 	/* Store the floating point control and status register.  At the end of the
 	 * vstmia, r1 will point to the FPCSR storage location.
 	 */
  
 	vmrs	r2, fpscr				/* Fetch the FPCSR */
 	str		r2, [r1], #4			/* Save the floating point control and status register */
 #else
 	/* Store all floating point registers */
  
 #if 1 /* Use store multiple */
 	fstmias	r1!, {s0-s31}			/* Save the full FP context */
 #else
 	vmov	r2, r3, d0				/* r2, r3 = d0 */
 	str		r2, [r1], #4			/* Save S0 and S1 values */
 	str		r3, [r1], #4
 	vmov	r2, r3, d1				/* r2, r3 = d1 */
 	str		r2, [r1], #4			/* Save S2 and S3 values */
 	str		r3, [r1], #4
 	vmov	r2, r3, d2				/* r2, r3 = d2 */
 	str		r2, [r1], #4			/* Save S4 and S5 values */
 	str		r3, [r1], #4
 	vmov	r2, r3, d3				/* r2, r3 = d3 */
 	str		r2, [r1], #4			/* Save S6 and S7 values */
 	str		r3, [r1], #4
 	vmov	r2, r3, d4				/* r2, r3 = d4 */
 	str		r2, [r1], #4			/* Save S8 and S9 values */
 	str		r3, [r1], #4
 	vmov	r2, r3, d5				/* r2, r3 = d5 */
 	str		r2, [r1], #4			/* Save S10 and S11 values */
 	str		r3, [r1], #4
 	vmov	r2, r3, d6				/* r2, r3 = d6 */
 	str		r2, [r1], #4			/* Save S12 and S13 values */
 	str		r3, [r1], #4
 	vmov	r2, r3, d7				/* r2, r3 = d7 */
 	str		r2, [r1], #4			/* Save S14 and S15 values */
 	str		r3, [r1], #4
 	vmov	r2, r3, d8				/* r2, r3 = d8 */
 	str		r2, [r1], #4			/* Save S16 and S17 values */
 	str		r3, [r1], #4
 	vmov	r2, r3, d9				/* r2, r3 = d9 */
 	str		r2, [r1], #4			/* Save S18 and S19 values */
 	str		r3, [r1], #4
 	vmov	r2, r3, d10				/* r2, r3 = d10 */
 	str		r2, [r1], #4			/* Save S20 and S21 values */
 	str		r3, [r1], #4
 	vmov	r2, r3, d11				/* r2, r3 = d11 */
 	str		r2, [r1], #4			/* Save S22 and S23 values */
 	str		r3, [r1], #4
 	vmov	r2, r3, d12				/* r2, r3 = d12 */
 	str		r2, [r1], #4			/* Save S24 and S25 values */
 	str		r3, [r1], #4
 	vmov	r2, r3, d13				/* r2, r3 = d13 */
  	str		r2, [r1], #4			/* Save S26 and S27 values */
 	str		r3, [r1], #4
 	vmov	r2, r3, d14				/* r2, r3 = d14 */
 	str		r2, [r1], #4			/* Save S28 and S29 values */
 	str		r3, [r1], #4
 	vmov	r2, r3, d15				/* r2, r3 = d15 */
 	str		r2, [r1], #4			/* Save S30 and S31 values */
 	str		r3, [r1], #4
 #endif
 
 	/* Store the floating point control and status register */
 
 	fmrx	r2, fpscr				/* Fetch the FPCSR */
 	str		r2, [r1], #4			/* Save the floating point control and status register */
 #endif
 	bx		lr
 
 	.size	up_savefpu, .-up_savefpu



 
| 載回 fpu 的 context switch 

.. code-block:: c

 up_restorefpu:
 
 	add		r1, r0, #(4*REG_S0)		/* R1=Address of FP register storage */
 
 	/* Some older GNU assemblers don't support all the newer UAL mnemonics. */
 
 #if 1 /* Use UAL mnemonics */
 	/* Load all floating point registers.  Registers are loaded in numeric order,
 	 * s0, s1, ... in increasing address order.
 	 */
 
 	vldmia	r1!, {s0-s31}			/* Restore the full FP context */
 
 	/* Load the floating point control and status register.   At the end of the
 	 * vstmia, r1 will point to the FPCSR storage location.
 	 */
 
 	ldr		r2, [r1], #4			/* Fetch the floating point control and status register */
 	vmsr	fpscr, r2				/* Restore the FPCSR */
 #else
 	/* Load all floating point registers  Registers are loaded in numeric order,
 	 * s0, s1, ... in increasing address order.
 	 */
 
 #if 1 /* Use load multiple */
 	fldmias	r1!, {s0-s31}			/* Restore the full FP context */
 #else
 	ldr		r2, [r1], #4			/* Fetch S0 and S1 values */
 	ldr		r3, [r1], #4
 	vmov	d0, r2, r3				/* Save as d0 */
 	ldr		r2, [r1], #4			/* Fetch S2 and S3 values */
 	ldr		r3, [r1], #4
 	vmov	d1, r2, r3				/* Save as d1 */
 	ldr		r2, [r1], #4			/* Fetch S4 and S5 values */
 	ldr		r3, [r1], #4
 	vmov	d2, r2, r3				/* Save as d2 */
 	ldr		r2, [r1], #4			/* Fetch S6 and S7 values */
 	ldr		r3, [r1], #4
 	vmov	d3, r2, r3				/* Save as d3 */
 	ldr		r2, [r1], #4			/* Fetch S8 and S9 values */
 	ldr		r3, [r1], #4
 	vmov	d4, r2, r3				/* Save as d4 */
 	ldr		r2, [r1], #4			/* Fetch S10 and S11 values */
 	ldr		r3, [r1], #4
 	vmov	d5, r2, r3				/* Save as d5 */
 	ldr		r2, [r1], #4			/* Fetch S12 and S13 values */
 	ldr		r3, [r1], #4
 	vmov	d6, r2, r3				/* Save as d6 */
 	ldr		r2, [r1], #4			/* Fetch S14 and S15 values */
 	ldr		r3, [r1], #4
 	vmov	d7, r2, r3				/* Save as d7 */
 	ldr		r2, [r1], #4			/* Fetch S16 and S17 values */
 	ldr		r3, [r1], #4
 	vmov	d8, r2, r3				/* Save as d8 */
 	ldr		r2, [r1], #4			/* Fetch S18 and S19 values */
 	ldr		r3, [r1], #4
 	vmov	d9, r2, r3				/* Save as d9 */
 	ldr		r2, [r1], #4			/* Fetch S20 and S21 values */
 	ldr		r3, [r1], #4
 	vmov	d10, r2, r3				/* Save as d10 */
 	ldr		r2, [r1], #4			/* Fetch S22 and S23 values */
 	ldr		r3, [r1], #4
 	vmov	d11, r2, r3				/* Save as d11 */
 	ldr		r2, [r1], #4			/* Fetch S24 and S25 values */
 	ldr		r3, [r1], #4
 	vmov	d12, r2, r3				/* Save as d12 */
 	ldr		r2, [r1], #4			/* Fetch S26 and S27 values */
 	ldr		r3, [r1], #4
 	vmov	d13, r2, r3				/* Save as d13 */
 	ldr		r2, [r1], #4			/* Fetch S28 and S29 values */
 	ldr		r3, [r1], #4
 	vmov	d14, r2, r3				/* Save as d14 */
 	ldr		r2, [r1], #4			/* Fetch S30 and S31 values */
 	ldr		r3, [r1], #4
 	vmov	d15, r2, r3				/* Save as d15 */
 #endif
 
 	/* Load the floating point control and status register.  r1 points t
 	 * the address of the FPCSR register.
 	 */
 
 	ldr		r2, [r1], #4			/* Fetch the floating point control and status register */
 	fmxr	fpscr, r2				/* Restore the FPCSR */
 #endif
 	bx		lr
 
 	.size	up_restorefpu, .-up_restorefpu
 #endif /* CONFIG_ARCH_FPU */
 

| /nuttx/arch/arm/include/armv7-m/irq.h
| struct xcptcontext
| /nuttx/arch/arm/include/armv7-m/irq_lazyfpu.h
| XCPTCONTEXT_REGS = HW_XCPT_REGS + SW_XCPT_REGS
| XCPTCONTEXT_SIZE = 4 * XCPTCONTEXT_REGS


 
測試環境架設
------------
 
硬體驅動原理
------------
* `UART<embedded/USART>`_
* FPU (Floating Point Unit)

效能表現
--------

參考資料
--------
* https://github.com/deadbeefcafe/nuttx-stm32f4disc-bb
* http://bibliodigital.itcr.ac.cr/xmlui/bitstream/handle/2238/3051/FinalReport.pdf