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

版本 5b5e3435ed5c2fdbfd451e76dc703c78aa306172

embedded/nuttx

Changes from 5b5e3435ed5c2fdbfd451e76dc703c78aa306172 to current

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

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

hackpad : https://stm32f429.hackpad.com/NuttX--hqco2YSgAcJ
| hackpad : https://stm32f429.hackpad.com/NuttX--hqco2YSgAcJ
| github : https://github.com/gototop/nuttx-stm32f4disc-bb

Nuttx 參考應用
---------------
* `PX4 autopilot<http://pixhawk.org/dev/nuttx/start>`_
* [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,不能在user mode被呼叫
- task_exit : 關掉目前在運行的task







Task state
------------
include/nuttx/sched.h
![](/task_state3.jpg)

.. code-block:: c
include/nuttx/sched.h

```
 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
Register
---------

.. code-block:: c
![](/ARM_register.jpg)

  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
Communication
---------------------
 
 | Semaphore
 |
 | sem_wait
 | sem_wait的主程式碼分為兩個部份,如果counter > 0,代表semaphore可以被hold,sem_wait會將task加入holder list
 | 否則sem_wait會block task,等待sem_hold unblock task,然後sem_wait再將task加入holder list

 struct tcb_s
```
 if (sem->semcount > 0)
 {
   /* 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      */
 	sem->semcount--;
 	sem_addholder(sem);
 	rtcb->waitsem = NULL;
 	ret = OK;
 }
 else
 {
 	sem->semcount--;
 	rtcb->waitsem = sem;
 	errno = 0;
 	up_block_task(rtcb, TSTATE_WAIT_SEM);
 
   uint8_t  task_state;                   /* Current state of the thread         */
   uint16_t flags;                        /* Misc. general status flags          */
   int16_t  lockcount;                    /* 0=preemptable (not-locked)          */
 	if (errno != EINTR && errno != ETIMEDOUT)
   	{
 		sem_addholder(sem);
 		ret = OK;
 	}
 }
``` 

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

   /* Stack-Related Fields *******************************************************/
 | sem_post
 | 一般而言,sem_post會在一個task要離開critical section的時候被呼叫
 | 它會先呼叫sem_releaseholder,然後檢查counter是否<= 0,若是,代表還有task在等semaphore,sem_post會找到waiting list的下一個task,unblock它
```
 if (sem)
 {
 	sem_releaseholder(sem);
 	sem->semcount++;
 	if (sem->semcount <= 0)
 	{
 		for (stcb = (FAR struct tcb_s*)g_waitingforsemaphore.head; (stcb && stcb->waitsem != sem); stcb = stcb->flink);
 		if (stcb) 
 		{
 			stcb->waitsem = NULL;
 			up_unblock_task(stcb);
 		}
 	}
  }
```

  #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
                                                   :
                                                   : 
                                                   : 
                                                   :
                                                   :
                                                   :
 };
----------------------------------------------------------

 | Mutex
 | nuttx的mutex實作是建立在semaphore上面
 | 在使用mutex時,會先init一個binary semaphore,然後呼叫pthread_mutex_lock,pthread_mutex_lock會呼叫pthread_takesemaphore
 | pthread_takesemaphore會跑一個判別式是sem_wait的while loop
 | 當owner要交出使用權的時候,會呼叫pthread_mutex_unlock,pthread_mutex_unlock會將mutex->pid設成0,然後呼叫pthread_givesemaphore
 | pthread_givesemaphore會呼叫sem_post
 |
 |
 | pthread_mutexinit.c
 
Register
---------
```
  int pthread_mutex_init(FAR pthread_mutex_t *mutex, FAR pthread_mutexattr_t *attr)
  {
  	:
  	:
  	status = sem_init((sem_t*)&mutex->sem, pshared, 1);
  	:
  	:
  }
```

.. image:: /ARM_register.jpg
------------------------------------------------

 | phread_mutexlock.c

```
 int pthread_mutex_lock(FAR pthread_mutex_t *mutex)
 {
 	:
 	:
 	:
        ret = pthread_takesemaphore((sem_t*)&mutex->sem);
        if (!ret)
        {
         	mutex->pid    = mypid;
        }
 }
```

Communication
---------------------
-------------------------------------------------

 | pthread_initalize.c

```
 int pthread_takesemaphore(sem_t *sem)
 {
 	if (sem)
 	{
 		while (sem_wait(sem) != OK)
 		{
  			:
 			:
 			:
 		}
   	return OK;
 	}
 
 }
```

```
 int pthread_givesemaphore(sem_t *sem)
 {
 	if (sem)
 	{
 		if (sem_post(sem) == OK)
  		{
 			return OK;
 		}
 		else
 		{
 			set_errno(EINVAL);
 			return ERROR;
 		}
 	}
 	else
 	{
 		set_errno(EINVAL);
 		return ERROR;
 	}
 }
```

---------------------------------------------------------

 | pthread_mutexunlock.c

```
 int pthread_mutex_unlock(FAR pthread_mutex_t *mutex)
 {
 	:
 	:
 	:
 	{       
 		{
 			mutex->pid    = 0;
 			:
 			:
 			:
 			ret = pthread_givesemaphore((sem_t*)&mutex->sem);
 		}
 		sched_unlock();
 	}
 	return ret;
 }
```


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
Interrupt Handler
------------------------
IRQ numbers 
在nuttx/arch/arm/include/stm32/irq.h和nuttx/arch/arm/include/stm32/stm32f40xxx_irq.h中分別定義了可用的ISR name和對應的vector name

.. code-block:: c
Processor Exceptions (vectors 0-15)

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

```
 //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
```
 //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?
```
 //irq_attach(STM32_IRQ_SVCALL, up_svcall); //System Call

 //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

```
 //(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) -> sched_process_timeslice()
 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_INTERVAL/MSEC_PER_TICK;
 				if (rtcb->flink && rtcb->flink->sched_priority >= rtcb->sched_priority)
 				{
 					up_reprioritize_rtr(rtcb, rtcb->sched_priority);
 				}
 			}
 		}
 		else
 		{
 			rtcb->timeslice--;
 		}
 	}
  }
```

Configuring High Priority Interrupts

How do you specify a high priority interrupt? if CONFIG_ARCH_RAMVECTORS=y, then vectors will be kept in RAM and the system will support the interface

int up_ramvec_attach(int irq, up_vector_t vector). That interface can be used to attach your C interrupt handler to the vector at run time.

```
 //up_vector_t g_ram_vectors[ARMV7M_VECTAB_SIZE]  __attribute__((section(".ram_vectors"))); //Using a RAM-based vector table
 //void up_ramvec_initialize(void) //Copy vectors to RAM an configure the NVIC to use the RAM vectors.
 int up_ramvec_attach(int irq, up_vector_t vector) //Configure the ram vector table so that IRQ number 'irq' will be dipatched by hardware to 'vector'
 {
 	int ret = ERROR;
 	if ((unsigned)irq < ARMV7M_PERIPHERAL_INTERRUPTS)
 	{
 		irqstate_t flags;
 		flags = irqsave();
 		if (vector == NULL)
 		{
 			up_disable_irq(irq);
 			vector = exception_common;
 		}
 		g_ram_vectors[irq] = vector;
 		irqrestore(flags);
 		ret = OK;
 	}
 	return ret;
 }
```


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
| page 40 ARM (2)

| 共會存入51個暫存器
| 其中8個為硬體一般暫存器包含R0 to R3, R12, R14 = LR, R15 = PC, XPSR
| 其中軟體暫存器浮點數FP共33個 S0 to S31, FPSCR
| 原本10個軟體一般暫存器 R13 = SP, PRIMASK, R4 to R11
| 依序存入XCPT由software registers SP開始到R11,浮點數S0到FPSCR,再來是 hardware registers R0到XPSR
 
測試環境架設
------------
 
硬體驅動原理
------------
* `UART<embedded/USART>`_
* [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