聯盛德微W806芯片移植RTthread-Nano
移植前知識點
1、W806芯片基於平頭哥E804 CPU內核,該芯片數據手冊下載鏈接:
datasheet/玄鐵E804用戶手冊_v04.pdf · ZH-OuYangLei/W806-RTT-Nano - 碼雲 - 開源中國 (gitee.com)
2、E804處理器存在兩種運行模式: 普通用戶模式和超級用戶模式,芯片在復位后自動進入超級用戶模式,超級用戶模式下CPU可以訪問所有的寄存器,而用戶模式下只能訪問少量的寄存器。在對RTT系統移植中,始終保持系統運行在超級用戶模式。用戶模式對應能訪問的寄存器如下圖所示:
3、編程模型
寄存器 | 功能 |
---|---|
R0 | 函數調用時第一個參數 |
R1 | 函數調用時第二個參數 |
R2 | 函數調用時第三個參數 |
R3 | 函數調用時第四個參數 |
R14 | 堆棧指針 |
R15 | 鏈接寄存器 |
EPSR | 處理器狀態寄存器 PSR的影子寄存器,在進入異常中斷時自動保存當前PSR的值 |
EPC | 程序計數器PC的影子寄存器,在進入異常中斷時自動保存當前PC的值 |
堆棧初始化
文件路徑: rtthread->libcpu->x804->cpu.c
typedef struct stack_frame
{
rt_uint32_t epc;
rt_uint32_t epsr;
rt_uint32_t r4;
rt_uint32_t r5;
rt_uint32_t r6;
rt_uint32_t r7;
rt_uint32_t r8;
rt_uint32_t r9;
rt_uint32_t r10;
rt_uint32_t r11;
rt_uint32_t lr; //R15
rt_uint32_t r16;
rt_uint32_t r17;
rt_uint32_t r18;
rt_uint32_t r19;
rt_uint32_t r20;
rt_uint32_t r21;
rt_uint32_t r22;
rt_uint32_t r23;
rt_uint32_t r24;
rt_uint32_t r25;
rt_uint32_t r26;
rt_uint32_t r27;
rt_uint32_t r28;
rt_uint32_t r29;
rt_uint32_t r30;
rt_uint32_t r31;
rt_uint32_t fr0;
rt_uint32_t fr1;
rt_uint32_t fr2;
rt_uint32_t fr3;
rt_uint32_t fr4;
rt_uint32_t fr5;
rt_uint32_t fr6;
rt_uint32_t fr7;
rt_uint32_t fr8;
rt_uint32_t fr9;
rt_uint32_t fr10;
rt_uint32_t fr11;
rt_uint32_t fr12;
rt_uint32_t fr13;
rt_uint32_t fr14;
rt_uint32_t fr15;
rt_uint32_t r0;
rt_uint32_t r1;
rt_uint32_t r2;
rt_uint32_t r3;
rt_uint32_t r12;
rt_uint32_t r13;
}stack_frame;
rt_uint8_t *rt_hw_stack_init(void *tentry,
void *parameter,
rt_uint8_t *stack_addr,
void *texit)
{
stack_frame *sf;
rt_uint8_t *stk;
unsigned long i;
stk = stack_addr + sizeof(rt_uint32_t);
stk = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stk, 8);
stk -= sizeof(stack_frame);
sf = (stack_frame *)stk;
/* init all register */
for (i = 0; i < sizeof(stack_frame) / sizeof(rt_uint32_t); i ++)
{
((rt_uint32_t *)sf)[i] = 0xdeadbeef;
}
sf->r0 = (unsigned long)parameter; /* r0 : argument */
sf->r1 = 0x01010101L; /* r1 */
sf->r2 = 0x02020202L; /* r2 */
sf->r3 = 0x03030303L; /* r3 */
sf->r12 = 0x12121212L; /* r12 */
sf->r13 = 0x13131313L; /* r12 */
sf->lr = (rt_uint32_t)texit; /* lr */
sf->epc = (rt_uint32_t)tentry; /* entry point, pc */
sf->epsr = 0x80000140L; /* PSR */
/* return task's current stack address */
return stk;
}
上下文切換匯編代碼編寫
文件路徑: rtthread->libcpu->x804->contex_x804.s
關閉中斷
/*
* rt_base_t rt_hw_interrupt_disable(void);
*/
.global rt_hw_interrupt_disable //函數 rt_hw_interrupt_disable
.type rt_hw_interrupt_disable, %function
rt_hw_interrupt_disable:
mfcr r0, CR<0,0> //讀取當前PSR寄存器,函數返回值保存在R0寄存器中
psrclr ee,ie //清除ESP中的EE、IE,關閉異常和中斷
rts
開啟中斷
/*
* void rt_hw_interrupt_enable(rt_base_t psr);
*/
.global rt_hw_interrupt_enable //函數 rt_hw_interrupt_enable
.type rt_hw_interrupt_enable, %function
rt_hw_interrupt_enable:
mtcr r0, CR<0,0> //調用函數,實參保存在R0中,該實參為函數rt_hw_interrupt_disable的返回值
rts
第一次切換上下文
/*
* void rt_hw_context_switch_to(rt_uint32 to);
* R0 --> to
*/
.global rt_hw_context_switch_to
.type rt_hw_context_switch_to, %function
rt_hw_context_switch_to:
psrclr ee,ie
lrw r1, rt_interrupt_to_thread
st.w r0, (r1,0)
/* set form thread = 0 */
lrw r1, rt_interrupt_from_thread
lrw r0, 0
st.w r0, (r1,0)
lrw r1, rt_thread_switch_interrupt_flag
lrw r0, 1
st.w r0, (r1,0)
/* 初始化TSPEND */
lrw r0, 0xE000EC10 //設置最低的中斷優先級
lrw r1, 0xff
st.w r1, (r0,0)
lrw r0, 0xE000EC0C //清除狀態
lrw r1, 0
st.w r1, (r0,0)
/*觸發tspend中斷*/
lrw r0, 0XE000EC08
bgeni r1, 0
st.w r1, (r0,0)
psrset ee,ie
rts
中斷中切換上下文和一般切換上下文
/*
* void rt_hw_context_switch_interrupt(rt_uint32 from, rt_uint32 to);
* r0 --> from
* r1 --> to
*/
.global rt_hw_context_switch_interrupt
.type rt_hw_context_switch_interrupt, %function
rt_hw_context_switch_interrupt:
lrw r2, rt_thread_switch_interrupt_flag
ld.w r3, (r2,0)
bhz r3, _to_thread1
lrw r3, 1
st.w r3, (r2,0)
lrw r2, rt_interrupt_from_thread /* set rt_interrupt_from_thread */
st.w r0, (r2,0)
_to_thread1:
lrw r2, rt_interrupt_to_thread /* set rt_interrupt_to_thread */
st.w r1, (r2,0)
lrw r0, 0XE000EC08
bgeni r1, 0
st.w r1, (r0,0)
rts
掛起中斷代碼實現
關鍵點:
異常中斷響應時,當前的PSR、EPC寄存器自動保存到EPSR、EPC寄存器中
異常中斷返回時,EPSR、EPC寄存器會自動更新到當前的PSR、PC寄存器中
.global tspend_handler
.type tspend_handler, %function
tspend_handler:
psrclr ee,ie
///壓棧 R13-R12,R3-R0
ipush
///判斷上下文切換標志
///如果為0,說明沒有發生上下文切換調用,則重新開啟異常中斷,並恢復現場
///如果大於0,說明有發生上下文切換調用,則清除標志,保存from上下文
lrw r2, rt_thread_switch_interrupt_flag
ld.w r3, (r2,0)
bez r3, _enable_ie_ee
lrw r3, 0
st.w r3, (r2,0)
///如果不存在from線程,則直接恢復to線程上下文
lrw r2, rt_interrupt_from_thread
ld.w r3, (r2,0)
bez r3, _to_thread3
///保存from線程上下文
subi sp, sp, 64
fstms fr0-fr15, (sp)
subi sp, sp, 68
stm r15-r31, (sp)
subi sp, sp, 32
stm r4-r11, (sp)
subi sp, sp, 8
mfcr r0, CR<4,0>
mfcr r1, CR<2,0>
stm r0-r1, (sp)
///更新堆棧地址到thread->sp
st.w sp, (r3,0)
///恢復to線程上下文
_to_thread3:
lrw r2, rt_interrupt_to_thread
ld.w r0, (r2,0)
ld.w sp, (r0,0)
ldm r0-r1, (sp)
mtcr r0, CR<4,0>
mtcr r1, CR<2,0>
addi sp, sp, 8
ldm r4-r11, (sp)
addi sp, sp, 32
ldm r15-r31, (sp)
addi sp, sp, 68
fldms fr0-fr15,(sp)
addi sp, sp, 64
///開啟異常中斷,設置PSR備份寄存器EPSR
_enable_ie_ee:
mfcr r0, CR<2,0>
bseti r0, 6
bseti r0, 8
mtcr r0, CR<2,0>
//出棧 R0-R3,R12-R13
ipop
rte
修改Rtthread入口函數
文件路徑: platform->arch->xt804->bsp->startup.S
將其中的 jbsr main
替換為 jbsr entry
硬件初始化
在rt_hw_board_init()函數中初始化時鍾、GPIO、串口等硬件和相關軟件資源,請查看相關軟件代碼。
編寫示例運行結果如下:
圖中顯示主任務、串口任務、LED閃爍鈎子函數已經都成功被調用運行。
整個工程代碼請見鏈接: