小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

RISC-V處理器的C語言啟動代碼設(shè)計方法

 西北望msm66g9f 2020-02-21

作者:唐思超

來源:嵌入式資訊精選

隨著微處理器市場競爭加劇,RISC-V指令集越來越受到關(guān)注。雖然RISC-V并非第一個開源的指令集(ISA),卻是第一個可依據(jù)實際應(yīng)用場景靈活選擇指令集的指令集架構(gòu)。RISC-V指令集架構(gòu)可以滿足從高性能服務(wù)器CPU直至超低功耗傳感器內(nèi)嵌CPU的全部應(yīng)用場景。

通常情況下,一款處理器的啟動代碼基本采用匯編語言設(shè)計。其原因包括:

  • 在處理器啟動階段,C運(yùn)行環(huán)境還未初始化;
  • 匯編語言實現(xiàn)的代碼不受編譯器影響;
  • 某些特殊寄存器操作無法通過C編譯得到對應(yīng)匯編代碼;
  • 處理器的某些特殊設(shè)計不利于C語言的使用等。

本文將解決前述問題,展示一種使用C語言為RISC-V處理器設(shè)計啟動代碼的方法。

為了更清晰地討論問題并最大程度的便于讀者理解某些流程,本文以芯來科技基于RV32IMC指令集的N205系列內(nèi)核作為目標(biāo)處理器,從N205內(nèi)核的對標(biāo)架構(gòu)——來自ARM的Cortex-M內(nèi)核在IAR EmbeddedWorkbench for ARM[1](后文簡稱IAR)環(huán)境下的C語言啟動代碼切入,逐步引入并實現(xiàn)SEGGER Embedded Studio[2](后文簡稱SES)環(huán)境下N205系列內(nèi)核的C語言啟動代碼。

一、Cortex-M內(nèi)核在IAR環(huán)境下的C語言啟動代碼

Cortex系列內(nèi)核是ARM公司迄今為止最成功的系列產(chǎn)品,包括A、R、M三類,其中M系列主要針對微控制器市場。

Cortex-M內(nèi)核具有以下特點(diǎn):

  • 內(nèi)核包含高級中斷控制器;
  • 中斷響應(yīng)時,處理器硬件將相應(yīng)的寄存器入棧和出棧;
  • 向量表中首單元內(nèi)容為棧地址,其余均為異?;蛑袛嗪瘮?shù)的入口地址;
  • 向量表中的內(nèi)容均為硬件自動載入。

代碼段1所示內(nèi)容是Cortex-M內(nèi)核在IAR環(huán)境下使用C語言開發(fā)的啟動代碼。

【代碼段-1】

#pragma language=extended ?
--snip--voidResetISR(void);           ?--snip--externvoid __iar_program_start(void);   ?staticunsigned long pulStack[64] @'.noinit'; ?typedefunion         ?{    void (*pfnHandler)(void);    unsigned long ulPtr;}uVectorEntry;__rootconst uVectorEntry __vector_table[] @'.intvec' =          ?{    { .ulPtr = (unsigned long)pulStack +sizeof(pulStack) },           ?    ResetISR,         --snip--};--snip--voidResetISR(void){    __iar_program_start();}

此處對上述代碼做簡要分析:

?是IAR的#pragma指導(dǎo)符。

?是復(fù)位函數(shù)聲明,復(fù)位函數(shù)是處理器復(fù)位后首先執(zhí)行的代碼,有時也稱為復(fù)位入口函數(shù)。

?是IAR系統(tǒng)函數(shù)聲明,__iar_program_start是IAR的系統(tǒng)函數(shù),主要作用是執(zhí)行C運(yùn)行環(huán)境初始化并調(diào)用系統(tǒng)主函數(shù)main。

?使用IAR @操作符定義系統(tǒng)棧區(qū)。

?聲明向量表的聯(lián)合類型。

?使用IAR對象屬性聲明__root及@操作符定義向量表,其中,第一個元素?保存了棧底地址,后續(xù)元素均為函數(shù)地址。

從上述分析過程可以看出啟動代碼的必要工作包括定義棧區(qū)、定義并初始化向量表,定義并實現(xiàn)系統(tǒng)復(fù)位函數(shù),初始化棧指針或棧寄存器等。依據(jù)處理器的架構(gòu)不同,上述操作中某些過程需要由軟件完成,有些則由硬件自動加載。

另外,有關(guān)IAR的指導(dǎo)符、對象屬屬性等內(nèi)容不屬于本文討論范疇,有需要可自行查閱。這里給出兩點(diǎn)提示:IAR環(huán)境的編譯系統(tǒng)為IAR自行開發(fā),故示例代碼中的指導(dǎo)符號不適用于GCC;某些指導(dǎo)符會因IAR環(huán)境版本不同而有所差異。

二、在SES環(huán)境下實現(xiàn)RISC-V內(nèi)核C語言啟動代碼的必要知識

前文提到,RISC-V是指令集而不是具體的設(shè)計實現(xiàn),這與之前討論的Cortex-M系列內(nèi)核有很大不同。簡單地說,不同廠商基于同種Cortex-M內(nèi)核的處理器,僅從內(nèi)核的層面來看可能沒有太大差異,但不同廠商開發(fā)的具有相同指令集的RISC-V處理器則各有千秋:一方面是相同功能的具體實現(xiàn)可能不同;另一方面,不同廠商可以實現(xiàn)不同的指令擴(kuò)展。

 這里對比Cortex-M內(nèi)核,列舉RISC-V處理器的一些特點(diǎn):不同廠商中斷控制器的實現(xiàn)各有特色;中斷響應(yīng)時,處理器硬件不會保存上下文,需要軟件完成該功能;向量表依據(jù)廠商不同而有明顯差異,可能向量表的首地址保存的是指令而非地址。

在不同廠商的Cortex-M內(nèi)核處理器間作切換時,由于處理器內(nèi)核的一致性,啟動代碼幾乎無需改動,因而使用匯編或者C語言來設(shè)計啟動代碼似乎差異不大,但要降低在不同廠商的RISC-V處理器間切換的復(fù)雜度,使用C語言開發(fā)啟動代碼是一種有效途徑。

前文曾提到啟動代碼的必要工作包括定義棧區(qū)、定義并初始化向量表,定義并實現(xiàn)系統(tǒng)復(fù)位函數(shù),初始化棧指針或棧寄存器等。在前述Cortex-M內(nèi)核的C啟動代碼中,IAR提供了接口__iar_program_start,該接口隱藏了幾乎所有細(xì)節(jié)。在SES環(huán)境下并沒有這樣的接口可供使用,為了實現(xiàn)RISC-V處理器的C語言啟動代碼,需要如下的編譯器及鏈接器相關(guān)知識。

(1)GCC內(nèi)聯(lián)匯編

RISC-V處理器中的CSR寄存器需要特殊的指令才能進(jìn)行訪問,C編譯器無法產(chǎn)生類似的指令,故C語言啟動代碼中仍然需要插入數(shù)條匯編指令。為了實現(xiàn)匯編指令與C語言的交互,需要使用GCC內(nèi)聯(lián)匯編,實例介紹如下:

asmvolatile (      ?'csrw0x307, %0'    ?:                   ?:'r'(vector_base) ?:                   ?);

其中:? asm為GCC內(nèi)聯(lián)匯編關(guān)鍵字,volatile為修飾符;? 雙引號引用的匯編指令列表,如有多條指令,可以使用'\n'分割;其中%0代表輸入操作數(shù)列表中的第一個值;? 可選的輸出操作數(shù)列表;? 可選的輸入操作數(shù)列表,此處'r'代表使用編譯器自動分配的寄存器來存儲變量vector_base;? 可選的受影響寄存器列表。

(2)section與初始化

簡單來講,將目標(biāo)文件中的sections鏈接起來就是可執(zhí)行文件。在默認(rèn)情況下,編譯器會創(chuàng)建標(biāo)準(zhǔn)sections。表1是標(biāo)準(zhǔn)section的簡單介紹。

表1   標(biāo)準(zhǔn)section概要

通過表1可以看出,程序的可執(zhí)行代碼存放于.text section,已初始化的全局和靜態(tài)變量存放于.data section。

一個典型的SoC系統(tǒng)通常包含兩類存儲器,即ROM和RAM。對于當(dāng)今的處理器來說,這兩部分通常是Flash和SRAM。系統(tǒng)掉電情況下,SRAM中是無法保存數(shù)據(jù)的,因此C語言中的變量初始值需要保存于Flash中。系統(tǒng)上電后,由初始化代碼將初始化數(shù)據(jù)從Flash拷貝到SRAM的目標(biāo)地址。如前所述,這是初始化代碼的重要工作之一。

接下來將闡述如何從Flash中找到初始化數(shù)據(jù)的位置并在C語言中引用。

(3)鏈接器變量的C語言訪問

從鏈接器的觀點(diǎn)看,初始值在Flash中的存放地址稱為LMA(加載存儲地址),對應(yīng)變量在SRAM的運(yùn)行時地址稱為VMA(虛擬存儲地址)。鏈接器腳本是用來描述處理器存儲器分布、各section 及標(biāo)準(zhǔn)section的包含關(guān)系、相應(yīng)LMA及VMA地址或存放區(qū)域等的文件。

代碼段2是一個標(biāo)準(zhǔn)鏈接器腳本的片段。這里通過這個片段來講述鏈接器變量的C語言訪問。

【代碼段-2】

MEMORY{ --snip--}SECTIONS{ --snip-- __data_load_start__ = ALIGN(__srodata_end__ ,4); .data ALIGN(__RAM_segment_start__ , 4) :AT(ALIGN(__srodata_end__ , 4)) { __data_start__ = .; *(.data .data.*) } __data_end__ = __data_start__ +SIZEOF(.data); __data_size__ = SIZEOF(.data); __data_load_end__ = __data_load_start__ +SIZEOF(.data); --snip--}

在代碼段2中,定義了鏈接器腳本變量__data_load_start__、__data_start__及__data_end__。

其中:

  • __data_load_start__代表LMA地址

  • __data_start__代表VMA地址

在C語言中訪問這些變量有以下兩種方法:

將鏈接器腳本變量聲明為數(shù)據(jù)類型,例如在C語言文件中聲明extern uint32_t __data_load_start__;通過&__data_load_start__獲取變量的值;

將鏈接器腳本變量聲明為數(shù)組,例如在C語言文件中聲明externuint32_t __data_load_start__[];通過__data_load_start__獲取變量的值。

(4)函數(shù)屬性

在通常情況下,編譯器會為每個函數(shù)自動產(chǎn)生序言和結(jié)尾序列,即在函數(shù)的頭部進(jìn)行一些入棧操作,在函數(shù)的末尾進(jìn)行對應(yīng)的出棧操作。一個明顯的問題就是在C語言啟動代碼中,復(fù)位函數(shù)執(zhí)行時可能棧指針或棧寄存器還沒有進(jìn)行初始化,這時的棧操作極有可能會導(dǎo)致處理器訪問非法地址而使程序崩潰。此外,如前文所提到的RISC-V處理器的復(fù)位入口可能保存的是跳轉(zhuǎn)指令而不是地址,短的跳轉(zhuǎn)地址可以保證用一條指令完成跳轉(zhuǎn)。

鑒于上述原因,需要使用相關(guān)的函數(shù)屬性來通知編譯器剔除默認(rèn)的函數(shù)序列并指定section,如下形式的復(fù)位函數(shù)定義可滿足該要求:

void __attribute__((section('.init'),naked)) reset_handler(){--snip--};

三、RISC-V內(nèi)核的C語言啟動代碼實例

前面內(nèi)容介紹了相關(guān)背景知識和技術(shù)手段,下面通過一個實際的框架程序來展示RISC-V處理器的C語言啟動代碼。其中,代碼段3是C語言啟動代碼的實現(xiàn),代碼段4是向量表。代碼中的所有關(guān)鍵點(diǎn)前文均有介紹,在此不在贅述。

【代碼段-3】

#include'riscv_encoding.h' #include<stdint.h>--snip--externuint32_t __data_load_start__; --snip--externuint32_t __bss_start__;--snip--externvoid (*const vector_base[])(void);externvoid main(void);--snip--conststruct { uint32_t* load; uint32_t* start; uint32_t* end;}dsection[3] = { --snip--};conststruct { uint32_t* start; uint32_t* end;}bsection[3] = { --snip--};void __attribute__((section('.init'),naked)) reset_handler() { register uint32_t *src, *dst; --snip-- /* 嵌入?yún)R編 */ asm volatile('csrw 0x307,%0'::'r'(vector_base));--snip-- asm volatile('la gp, __sdata_start__+0x800'); asm volatile('la sp,__stack_end__');--snip-- /* 進(jìn)行系統(tǒng)時鐘初始化等 */ init(); /* 將數(shù)據(jù)的初始化值拷貝至RAM */ if(&__vectors_load_start__ !=&__RAM_segment_start__){ for(uint8_t idx = 0; idx < 3; idx++){ src=dsection[idx].load; dst=dsection[idx].start; while(dst < dsection[idx].end){ *dst=*src; dst++; src++; } } } /* 將.bss區(qū)域清零 */ for(uint8_t idx=0;idx < 3;idx++){ dst=bsection[idx].start; while(dst<bsection[idx].end){ *dst=0U; dst++; } } /* 調(diào)用主函數(shù) */ main(); }--snip--

【代碼段-4】

.section .vectors, 'ax'  --snip-- .globl vector_basevector_base:  jreset_handler .align     2 .word     0  --snip--

四、結(jié)  語

通常半導(dǎo)體廠商會在配套的軟件開發(fā)包中提供處理器的啟動代碼,這導(dǎo)致多數(shù)嵌入式開發(fā)人員可能更關(guān)注應(yīng)用部分的代碼實現(xiàn)而忽視啟動代碼的存在。鑒于廠商提供的啟動代碼幾乎都用匯編語言編寫,這使得很多開發(fā)人員誤以為啟動代碼必須使用匯編語言開發(fā)。

事實上,大多數(shù)處理器的啟動代碼都可以使用C語言進(jìn)行開發(fā)且代碼效率與匯編幾乎沒有差異。在工程實踐中,很多深層次開發(fā)都需要對啟動代碼進(jìn)行修改或重寫,基于C語言的代碼可以節(jié)省開發(fā)人員在學(xué)習(xí)匯編指令方面的時間和精力,同時在后續(xù)的升級維護(hù)中更加高效。

補(bǔ)充知識點(diǎn):
[1]考慮到Cortex-M系列架構(gòu)的開發(fā)多使用IAR、MDK等環(huán)境,此處以IAR環(huán)境為例。

[2]考慮到當(dāng)前RISC-V的集成開發(fā)環(huán)境多基于Eclipse構(gòu)建,SEGGER Embedded Studio環(huán)境基于自有構(gòu)架且使用方便、功能強(qiáng)大,故此處以SES為例。另外,包括SES在內(nèi)的RISC-V開發(fā)環(huán)境下的編譯系統(tǒng)均基于GCC,故本文討論的方法也適用于其他開發(fā)環(huán)境。

[3]如果需要在GCC內(nèi)聯(lián)匯編代碼中使用宏定義,就需要使用一種稱為雙重宏定義的方法,示例如下:
#defineCSR_MTVT 0x307#defineSTR(R) #R#defineXSTR(R) STR(R) /*asm volatile('csrw 0x307, %0'::'r'(vector_base)); */asmvolatile('csrw 'XSTR(CSR_MTVT)',%0'::'r'(vector_base));

作者簡介:

唐思超,現(xiàn)任北京知存科技有限公司軟件開發(fā)經(jīng)理,負(fù)責(zé)人工智能芯片工具鏈及嵌入式開發(fā),具有14年硬件電路設(shè)計及軟件開發(fā)經(jīng)驗,擅長處理器、編譯系統(tǒng)及操作系統(tǒng)的相關(guān)設(shè)計開發(fā)及底層機(jī)制的綜合運(yùn)用。


聲明:本文內(nèi)容僅代表原創(chuàng)作者觀點(diǎn),如有錯誤敬請理解
????????????????  END  ????????????????

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多