0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

ARM SCP入門-framework框架代碼分析

yzcdx ? 來源:OS與AUTOSAR研究 ? 2023-07-03 09:32 ? 次閱讀

一套軟硬件跑起來的樣子就像上面圖里面的一樣,it works。對應(yīng)我們的SCP固件中,有那些框架來支撐這個(gè)系統(tǒng)運(yùn)行起來,這里就需要一套基于M核或者單片機(jī)的通用框架程序,市面上的這種系統(tǒng)并不少見,例如freeRTOS等。

為了強(qiáng)調(diào)安全、簡單等特性,適配ARM控制系統(tǒng)固件,ARM又搞了這套通用的框架,適合在M核或者R核上工作,甚至A核的某些特權(quán)系統(tǒng)例如OPTEE中。安全的核心就是隔離,隔離就是按功能形成module或者domain,模塊之間禁止無權(quán)限的訪問。

1. module介紹

SCP的每個(gè)功能都實(shí)現(xiàn)為一個(gè)單獨(dú)的module,module間耦合盡量低,確保安全特性,通常固件所需的整體功能應(yīng)來自模塊之間的交互。module間隔離就像上圖中的狗咬架,一旦伸手產(chǎn)生交互就禍福不能預(yù)測了,所以加上欄桿規(guī)定好那些module間可以交互伸手,這都是通過API函數(shù)實(shí)現(xiàn)的,在系統(tǒng)初始化的時(shí)候設(shè)定死,下面模塊間綁定章節(jié)會講到。

SCP中的module分為兩部分:在代碼根目錄module文件夾下,共77個(gè)公共模塊,另外每個(gè)產(chǎn)品下面還有module,小100個(gè)可真不少。

240b4608-192e-11ee-962d-dac502259ad0.png

一個(gè)固件只包含一部分module,在Firmware.cmake中定義,gen_module_code.py腳本生成源碼

這些module在framework啟動時(shí)候初始化啟動運(yùn)行。

公共的module比較有通用性,產(chǎn)品自己的module一般是驅(qū)動需要進(jìn)行定制

模塊類型及軟件協(xié)議棧:

2445db74-192e-11ee-962d-dac502259ad0.png

這個(gè)協(xié)議棧就是SCP軟件跟外界交互的流程,一般消息都是通過驅(qū)動-》HAL層上來,然后處理的過程就是服務(wù)-》協(xié)議-》HAL-》驅(qū)動再操作硬件做出反應(yīng),這次交互就算結(jié)束了。具體如下:

2475854a-192e-11ee-962d-dac502259ad0.png

2.framework框架流程

2492c20e-192e-11ee-962d-dac502259ad0.png

framework框架負(fù)責(zé)固件的通用流程實(shí)現(xiàn),包括系統(tǒng)初始化,module初始化,中斷服務(wù)提供,event服務(wù)提供等。這樣module就可以專注于自己功能和對外交互api的實(shí)現(xiàn)。SCP framework初始化流程圖如下:

251ddaa6-192e-11ee-962d-dac502259ad0.png

備注:這里的framework框架流程適用于scp_romfwscp_ramfw,兩者區(qū)別只是包含module不同,定義包含了那些module在其目錄下的Firmware.cmake文件中。

編譯過程中gen_module_code.py腳本會生成module信息和配置信息的代碼,過程如下:SCP/MCP 軟件構(gòu)建系統(tǒng)由一個(gè)頂級 Makefile :Makefile.cmake和一組 .mk 文件組成,例如juno產(chǎn)品product/juno/product.mk

BS_PRODUCT_NAME := juno

BS_FIRMWARE_LIST := scp_romfw
scp_romfw_bypass
scp_ramfw

模塊可以在項(xiàng)目根目錄的 modules/ 目錄下實(shí)現(xiàn),也可以是產(chǎn)品特定的并在product//modules 目錄下實(shí)現(xiàn)。

gen_module_code.py腳本會根據(jù)

product/juno/scp_romfw/Firmware.cmake中SCP_MODULES變量

list(APPEND SCP_MODULES "juno-ppu")
list(APPEND SCP_MODULES "juno-rom")
list(APPEND SCP_MODULES "juno-soc-clock")
list(APPEND SCP_MODULES "clock")
list(APPEND SCP_MODULES "gtimer")
list(APPEND SCP_MODULES "sds")
list(APPEND SCP_MODULES "bootloader")

生成

?fwk_module_idx.h:包含構(gòu)成固件的模塊索引的枚舉。fwk_module_idx.h中枚舉中模塊索引的順序保證遵循固件firmware.mk 文件中 BS_FIRMWARE_MODULES列表中模塊名稱的順序。當(dāng)執(zhí)行涉及迭代固件中存在的所有模塊的操作時(shí),框架在運(yùn)行時(shí)使用相同的順序,例如 fwk_module.c 中的 init_modules() 函數(shù)。

enum fwk_module_idx {
FWK_MODULE_IDX_JUNO_PPU = 0,
FWK_MODULE_IDX_JUNO_ROM = 1,
FWK_MODULE_IDX_JUNO_SOC_CLOCK = 2,
FWK_MODULE_IDX_CLOCK = 3,
FWK_MODULE_IDX_GTIMER = 4,
FWK_MODULE_IDX_SDS = 5,
FWK_MODULE_IDX_BOOTLOADER = 6,
FWK_MODULE_IDX_COUNT = 7,
};

?fwk_module_list.c:包含一個(gè)指向模塊描述符的指針表,每個(gè)模塊對應(yīng)一個(gè)作為固件一部分構(gòu)建的模塊。該文件及其內(nèi)容由框架內(nèi)部使用,通常不應(yīng)由其他單元(如模塊)使用。

const struct fwk_module *module_table[FWK_MODULE_IDX_COUNT] = {
&module_juno_ppu,
&module_juno_rom,
&module_juno_soc_clock,
&module_clock,
&module_gtimer,
&module_sds,
&module_bootloader,

};
const struct fwk_module_config *module_config_table[FWK_MODULE_IDX_COUNT] = {
&config_juno_ppu,
&config_juno_rom,
&config_juno_soc_clock,
&config_clock,
&config_gtimer,
&config_sds,
&config_bootloader,
};

module_tablemodule_config_table用于模塊初始化。

固件的Firmware.cmake文件中可以對配置開關(guān)進(jìn)行聲明,例如:

set(SCP_FIRMWARE_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}")

set(SCP_GENERATE_FLAT_BINARY TRUE)
set(SCP_ARCHITECTURE "arm-m")

framework.md-固件相關(guān)配置文件說明

產(chǎn)品始終包含定義一個(gè)或多個(gè)固件目標(biāo)的product.mk文件。

在一個(gè)產(chǎn)品中,總會有至少一個(gè)固件。

對于每個(gè)固件,必須在fmw_memory.h文件中提供鏈接器信息,例如product/juno/scp_romfw/fmw_memory.h中:

#define FMW_MEM_MODE ARCH_MEM_MODE_DUAL_REGION_RELOCATION

/* ROM */
#define FMW_MEM0_SIZE SCP_ROM_SIZE
#define FMW_MEM0_BASE SCP_ROM_BASE

/* RAM */
#define FMW_MEM1_SIZE (16 * 1024)
#define FMW_MEM1_BASE (SCP_RAM_BASE + SCP_RAM_SIZE - FMW_MEM1_SIZE)

如果使用雙區(qū)域內(nèi)存配置,則還必須定義FMW_MEM1_BASE和 FMW_MEM1_SIZE 。

Toolchain-*.cmake 中定義image的體系結(jié)構(gòu)目標(biāo)

product/juno/scp_romfw/Toolchain-GNU.cmake

set(CMAKE_SYSTEM_PROCESSOR "cortex-m3")
set(CMAKE_TOOLCHAIN_PREFIX "arm-none-eabi-")
...

product/juno/scp_romfw/CMakeLists.txt 定義編譯范圍和目標(biāo),cmake會使用生成makefile文件。

編譯選項(xiàng)說明:

cmake_readme.md-構(gòu)建配置選項(xiàng):

?SCP_ENABLE_NOTIFICATIONS:啟用/禁用 SCP 固件中的通知。

?SCP_ENABLE_SCMI_NOTIFICATIONS:啟用/禁用 SCMI 通知。

?SCP_ENABLE_RESOURCE_PERMISSIONS:啟用/禁用資源權(quán)限設(shè)置。

單獨(dú)配置編譯:

配置生效命令:

cmake -B /tmp/build -DSCP_FIRMWARE_SOURCE_DIR:PATH=juno/scp_romfw -DSCP_ENABLE_DEBUG_UNIT=TRUE

然后就是編譯命令:

cmake --build /tmp/build

在編譯文件中配置,例如在product/juno/scp_romfw/Firmware.cmake中

set(SCP_ENABLE_NOTIFICATIONS TRUE)

修改后需要clean下,再繼續(xù)編譯。

2.1 平臺初始化

arch/arm/arm-m/CMakeLists.txt中,arch-arm-m庫的入口是arch_exception_reset()函數(shù):

if(CMAKE_C_COMPILER_ID  STREQUAL "ARMClang")     target_link_options(arch-arm-mPUBLIC  "LINKER:--entry=arch_exception_reset")endif()

arch_exception_reset()函數(shù)在arch/arm/arm-m/src/arch.ld.S鏈接文件中也被定位了入口函數(shù)

其實(shí)現(xiàn)在arch/arm/arm-m/src/arch_handlers.c:

 noreturn  void arch_exception_reset(void) {     
 extern noreturn void __main(void);     
 __main(); 
 }

__main在c運(yùn)行時(shí)調(diào)用main函數(shù),對于M核實(shí)現(xiàn)來說,arch/arm/arm-m/src/arch_main.c中有main()函數(shù)

int main(void)
{
////初始化 ARM Cortex-M 系列芯片的 Configuration Control Register (CCR)。
//其中,通過設(shè)置 SCB_CCR_DIV_0_TRP_Msk 來啟用除以零的異常處理
arch_init_ccr();

//scp 入口及應(yīng)用函數(shù)
return fwk_arch_init(&arch_init_driver);
}

scp 入口及應(yīng)用為fwk_arch_init函數(shù),在framework/src/fwk_arch.c中

int fwk_arch_init(const struct fwk_arch_init_driver *driver)
{
//scp 框架初始化,完成module_table、module_config_table所有模塊信息的初始化
//scp/module目錄下的模塊的初始化
fwk_module_init();

//這里構(gòu)建了一個(gè)全局的fwk_io_stdin、 fwk_io_stdout, 在后面的終端輸出有用
status = fwk_io_init();

//初始化日志輸出方式
status = fwk_log_init();

//中斷gic初始化
status = fwk_arch_interrupt_init(driver->interrupt);

//所有模塊初始化,開始任務(wù)
status = fwk_module_start();

//循環(huán)等待處理隊(duì)列事件
__fwk_run_main_loop();
}

2.2 module初始化

fwk_module_init函數(shù),在framework/src/fwk_module.c中實(shí)現(xiàn)

在系統(tǒng)構(gòu)建章節(jié)中module_table和module_config_table是由配置文件Firmware.cmake生成的fwk_module_list.c中定義。

module見module介紹章節(jié)

module_config_table就是模塊的上下文信息

void fwk_module_init(void)
{
for (uint32_t i = 0U; i < ?(uint32_t)FWK_MODULE_IDX_COUNT; i++) {
//獲取模塊的上下文信息
struct fwk_module_context *ctx = &fwk_module_ctx.module_ctx_table[i];

fwk_id_t id = FWK_ID_MODULE(i);
const struct fwk_module *desc = module_table[i];
const struct fwk_module_config *config = module_config_table[i];

//給模塊上下文信息賦值
*ctx = (struct fwk_module_context){
.id = id,
.desc = desc,
.config = config,
};

//初始化模塊的鏈表
fwk_list_init(&ctx->delayed_response_list);

if (config->elements.type == FWK_MODULE_ELEMENTS_TYPE_STATIC) {
size_t notification_count = 0;

#ifdef BUILD_HAS_NOTIFICATION
notification_count = desc->notification_count;
#endif

fwk_module_init_element_ctxs(
ctx, config->elements.table, notification_count);
}

#ifdef BUILD_HAS_NOTIFICATION
if (desc->notification_count > 0) {
fwk_module_init_subscriptions(
&ctx->subscription_dlist_table, desc->notification_count);
}
#endif
}
}

2.3 中斷初始化

static int fwk_arch_interrupt_init(int (*interrupt_init_handler)(
const struct fwk_arch_interrupt_driver **driver))
{
const struct fwk_arch_interrupt_driver *driver;

status = interrupt_init_handler(&driver);

/* Initialize the interrupt management component */
status = fwk_interrupt_init(driver);

return FWK_SUCCESS;
}

interrupt_init_handler是入?yún)⒒卣{(diào)函數(shù),對應(yīng)為arch_init_driver

static const struct fwk_arch_init_driver arch_init_driver = {
.interrupt = arch_nvic_init,
};

在arch_nvic_init中有*driver =&arch_nvic_driver;

static const struct fwk_arch_interrupt_driver arch_nvic_driver = {
.global_enable = global_enable,
.global_disable = global_disable,
.is_enabled = is_enabled,
.enable = enable,
.disable = disable,
.is_pending = is_pending,
.set_pending = set_pending,
.clear_pending = clear_pending,
.set_isr_irq = set_isr_irq,
.set_isr_irq_param = set_isr_irq_param,
.set_isr_nmi = set_isr_nmi,
.set_isr_nmi_param = set_isr_nmi_param,
.set_isr_fault = set_isr_fault,
.get_current = get_current,
.is_interrupt_context = is_interrupt_context,
};

拿到driver的值后,執(zhí)行fwk_interrupt_init(driver);

int fwk_interrupt_init(const struct fwk_arch_interrupt_driver *driver)
{
//校驗(yàn)driver
fwk_interrupt_driver = driver;
initialized = true;

return FWK_SUCCESS;
}

fwk_interrupt_driver 全局變量用于中斷處理。

模塊使用中斷時(shí),需要調(diào)用對外接口在framework/include/fwk_interrupt.h中,

例如開啟中斷fwk_interrupt_enable函數(shù)的實(shí)現(xiàn):

int fwk_interrupt_enable(unsigned int interrupt)
{
if (!initialized) {
return FWK_E_INIT;
}

return fwk_interrupt_driver->enable(interrupt);
}

2.4 module啟動

fwk_module_start()在framework/src/fwk_module.c中定義

int fwk_module_start(void)
{
//初始化任務(wù)列表
status = __fwk_init(FWK_MODULE_EVENT_COUNT);
fwk_module_ctx.stage = MODULE_STAGE_INITIALIZE;

//從功能方面初始化所有module
fwk_module_init_modules();
fwk_module_ctx.stage = MODULE_STAGE_BIND;

//調(diào)用模塊.bind回調(diào)函數(shù)完成所有模塊的綁定。(此處共進(jìn)行兩輪調(diào)用fwk_module_bind_module(round=0 1),
//每輪都將分別綁定模塊module和模塊的元素element)
for (bind_round = 0; bind_round <= FWK_MODULE_BIND_ROUND_MAX;
bind_round++) {
status = fwk_module_bind_modules(bind_round);
if (status != FWK_SUCCESS) {
return status;
}
}
fwk_module_ctx.stage = MODULE_STAGE_START;

//啟動模塊
status = start_modules();
fwk_module_ctx.initialized = true;

return FWK_SUCCESS;
}

fwk_module_init_modules函數(shù)調(diào)用fwk_module_init_module對每個(gè)模塊進(jìn)行功能初始化

//初始化模塊元素上下文(element_ctxs),
//調(diào)用模塊的config->elements.generator,獲取element信息,加入模塊上下文表
elements = config->elements.generator(ctx->id);
fwk_module_init_element_ctxs(ctx, elements, notification_count);

//調(diào)用模塊的init函數(shù),傳入element_count,config->dat
status = desc->init(ctx->id, ctx->element_count, config->data);

//初始化模塊元素(element),調(diào)用模塊回調(diào)函數(shù).element_init將模塊element->data配置信息導(dǎo)入到模塊內(nèi)部
fwk_module_init_elements(ctx);

start_modules函數(shù)調(diào)用fwk_module_start_module對每個(gè)模塊進(jìn)行啟動

module = fwk_mod_ctx->desc;

//調(diào)用模塊.start回調(diào)函數(shù)
module->start(fwk_mod_ctx->id);

例如在juno_rom的.start回調(diào)函數(shù)函數(shù)中,通過event和notification機(jī)制,到達(dá)juno_rom模塊的相應(yīng)回調(diào)函數(shù),在juno_rom中,通過ctx.bootloader_api->load_image()調(diào)用mod_bootloader的api,從安全內(nèi)存拷貝到指定位置,在該bootloader模塊api中加載跳轉(zhuǎn)scp_ramfw。(注mod_bootloader_boot為匯編實(shí)現(xiàn),依賴arm指令)。在product/juno/module/juno_rom/src/mod_juno_rom.c中:

const struct fwk_module module_juno_rom = {
.type = FWK_MODULE_TYPE_SERVICE,
.event_count = (unsigned int)MOD_JUNO_ROM_EVENT_COUNT,
.notification_count = (unsigned int)MOD_JUNO_ROM_NOTIFICATION_COUNT,
.init = juno_rom_init,
.bind = juno_rom_bind,
.start = juno_rom_start,
.process_event = juno_rom_process_event,
.process_notification = juno_rom_process_notification,
};

2.5 運(yùn)行狀態(tài)機(jī)

scp-firmware在完成了所有的初始化操作后,進(jìn)入死循環(huán),處理隊(duì)列里面的事件或者休眠等待事件到來。

noreturn void __fwk_run_main_loop(void)
{
for (;;) {
fwk_process_event_queue();
if (fwk_log_unbuffer() == FWK_SUCCESS) {
fwk_arch_suspend();
}
}
}

fwk_process_event_queue主要處理三個(gè)重要的鏈表:free_event_queue, event_queue, isr_event_queue所有的操作都是圍繞這三個(gè)隊(duì)列展開。

void fwk_process_event_queue(void)
{
for (;;) {
while (!fwk_list_is_empty(&ctx.event_queue)) {
process_next_event();
}

if (!process_isr()) {
break;
}
}
}

event_queue中根據(jù)target_id找到對應(yīng)module,然后調(diào)用module->process_event進(jìn)行處理,詳細(xì)見module中說明。

process_next_event中調(diào)用duplicate_event會處理free_event_queue隊(duì)列中的事件

process_isr從中斷isr_event_queue隊(duì)列中取到事件,然后加入到event_queue中

3.module對外接口

在scp代碼中,所有的功能都由一個(gè)個(gè)模塊提供。每個(gè)模塊以api枚舉及其結(jié)構(gòu)體的方式對外提供該模塊的功能,并在模塊通用結(jié)構(gòu)體fwk_module中提供,例如

module/scmi_power_domain/src/mod_scmi_power_domain.c中,

/* SCMI Power Domain Management Protocol Definition */
const struct fwk_module module_scmi_power_domain = {
.api_count = 1,
.type = FWK_MODULE_TYPE_PROTOCOL,
.init = scmi_pd_init,
.bind = scmi_pd_bind,
.start = scmi_pd_start,
.process_bind_request = scmi_pd_process_bind_request,
.event_count = (unsigned int)SCMI_PD_EVENT_IDX_COUNT,
.process_event = scmi_pd_process_event,
#ifdef BUILD_HAS_MOD_DEBUG
.process_notification = scmi_pd_process_notification,
#endif
};

.init(模塊初始化)

.bind(獲取綁定別的模塊的api)

.process_bind_request(被其他模塊依賴的api的獲取并綁定請求函數(shù))等通用接口。

.start模塊啟動

.process_event事件處理

.process_notification通知處理

初始化模塊:

模塊在初始化時(shí)由fwk_module.c 中fwk_module_start函數(shù),調(diào)用回調(diào)函數(shù).init,.bind,.start

?模塊初始化:調(diào)用模塊API的init()函數(shù)指針

?元素初始化:調(diào)用框架模塊API的element_init()函數(shù)指針

?后初始化:元素初始化后,模塊交互之前的一些可選處理操作

?綁定:模塊必須綁定好才能調(diào)用對方的api

?開始

運(yùn)行時(shí):

一旦運(yùn)行前階段成功完成,固件將開始處理模塊或中斷引發(fā)的事件。默認(rèn)情況下,固件將永遠(yuǎn)循環(huán)等待新事件在運(yùn)行前階段結(jié)束時(shí)處理,但當(dāng)事件列表為空時(shí),可以在處理未決事件后返回。

模塊配置:

模塊初始化的時(shí)候,模塊配置被讀入存放到模塊上下文中:

const struct fwk_module_config *config = module_config_table[i];

//給模塊上下文信息賦值
*ctx = (struct fwk_module_context){
.id = id,
.desc = desc,
.config = config,
};

在module_config_table在fwk_module_list.c中定義,這里以config_juno_ppu為例:

const struct fwk_module_config *module_config_table[FWK_MODULE_IDX_COUNT] = {
&config_juno_ppu,

struct fwk_module_config config_juno_ppu = {
.data =
&(struct mod_juno_ppu_config){
.timer_alarm_id = FWK_ID_SUB_ELEMENT_INIT(
FWK_MODULE_IDX_TIMER,
0,
JUNO_PPU_ALARM_IDX),
},

.elements = FWK_MODULE_DYNAMIC_ELEMENTS(get_element_table),
};

#define FWK_MODULE_DYNAMIC_ELEMENTS(GENERATOR)
{
.type = FWK_MODULE_ELEMENTS_TYPE_DYNAMIC,
.generator = (GENERATOR),
}

如果類型為FWK_MODULE_ELEMENTS_TYPE_STATIC ,框架使用表指針中給出的靜態(tài)表來訪問產(chǎn)品為模塊提供的元素表。

如果類型為 FWK_MODULE_ELEMENTS_TYPE_DYNAMIC ,則框架使用生成器函數(shù)指針。

get_element_table對應(yīng)一個(gè)配置結(jié)構(gòu)體數(shù)組:

static struct fwk_element element_table[] = {
[JUNO_PPU_DEV_IDX_BIG_SSTOP] = {
.name = "",
.data = &(const struct mod_juno_ppu_element_config) {
.reg_base = PPU_BIG_SSTOP_BASE,
.timer_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_TIMER, 0),
.pd_type = MOD_PD_TYPE_CLUSTER,
},
},
....

enum juno_ppu_idx {
JUNO_PPU_DEV_IDX_BIG_CPU0,
JUNO_PPU_DEV_IDX_BIG_CPU1,
JUNO_PPU_DEV_IDX_BIG_SSTOP,

JUNO_PPU_DEV_IDX_LITTLE_CPU0,
JUNO_PPU_DEV_IDX_LITTLE_CPU1,
JUNO_PPU_DEV_IDX_LITTLE_CPU2,
JUNO_PPU_DEV_IDX_LITTLE_CPU3,
JUNO_PPU_DEV_IDX_LITTLE_SSTOP,

JUNO_PPU_DEV_IDX_GPUTOP,
JUNO_PPU_DEV_IDX_SYSTOP,
JUNO_PPU_DEV_IDX_DBGSYS,

JUNO_PPU_DEV_IDX_COUNT,
};

struct fwk_element結(jié)構(gòu)體表示元素,里面有名字,子元素個(gè)數(shù)和數(shù)據(jù)

元素:

元素表示由模塊擁有或管理的資源。每個(gè)元素將表示模塊與之交互和/或負(fù)責(zé)的對象。

例如,驅(qū)動程序類型的模塊可能具有表示它所控制的硬件設(shè)備的元素。因?yàn)樵嘏渲脭?shù)據(jù)靈活多變,使用通用的方式const void *data實(shí)現(xiàn)。

子元素表示由元素?fù)碛谢蚬芾淼馁Y源。子元素僅由它們的索引和/或標(biāo)識符表示。

索引和標(biāo)識符:

由于框架設(shè)計(jì)為模塊化,因此需要一種標(biāo)準(zhǔn)化方法來識別和引用模塊、元素、子元素、事件、通知和 API。該框架為此定義了兩個(gè)組件:indices和identifiers。

indices:

模塊索引由構(gòu)建系統(tǒng)為每個(gè)固件生成,并放在fwk_module_idx.h頭文件中。

enum fwk_module_idx {
FWK_MODULE_IDX_JUNO_PPU = 0,
FWK_MODULE_IDX_JUNO_ROM = 1,
......

identifiers:

標(biāo)識符有一個(gè)類型,這決定了標(biāo)識符中包含的信息。在內(nèi)部,標(biāo)識符始終包含模塊的索引,并且可能包含在該模塊的上下文中標(biāo)識項(xiàng)目的附加索引。也在fwk_module_idx.h頭文件中,有宏和變量兩部分定義,值是一樣的:

#define FWK_MODULE_ID_JUNO_PPU FWK_ID_MODULE(FWK_MODULE_IDX_JUNO_PPU)
#define FWK_ID_MODULE(MODULE_IDX) ((fwk_id_t)FWK_ID_MODULE_INIT(MODULE_IDX))
#define FWK_ID_MODULE_INIT(MODULE_IDX)
{
.common = {
.type = (uint32_t)__FWK_ID_TYPE_MODULE,
.module_idx = (uint32_t)MODULE_IDX,
},
}

static const fwk_id_t fwk_module_id_juno_ppu = FWK_MODULE_ID_JUNO_PPU_INIT;
#define FWK_MODULE_ID_JUNO_PPU_INIT FWK_ID_MODULE_INIT(FWK_MODULE_IDX_JUNO_PPU)

可用的標(biāo)識符類型有:

?模塊:僅由模塊索引組成

?元素:由模塊索引和模塊內(nèi)元素的索引組成

?子元素:由模塊索引、模塊內(nèi)元素的索引和該元素?fù)碛械淖釉氐乃饕M成。

?API:由模塊索引和模塊提供的API的索引組成

?事件:由模塊索引和模塊可能產(chǎn)生的事件的索引組成

?通知:由模塊索引和模塊可能生成的通知索引組成。

日志:

日志記錄功能定義并實(shí)現(xiàn)了該組件的公共接口。該接口的文檔可以在 fwk_log.h 中找到。

#include
FWK_LOG_ERR("[ROM] ERROR: Failed to turn on LITTLE cluster.");
# define FWK_LOG_ERR(...) fwk_log_printf(__VA_ARGS__)

fwk_log_printf()函數(shù)在framework/src/fwk_log.c中定義。

4. event事件

255e9582-192e-11ee-962d-dac502259ad0.png

模塊可以給自己或者別的模塊發(fā)送event事件,事件的參數(shù)是結(jié)構(gòu)化消息structfwk_event。

static int juno_rom_process_event(
const struct fwk_event *event,
struct fwk_event *resp)
{

truct fwk_event {
struct fwk_slist_node slist_node;
fwk_id_t source_id;
fwk_id_t target_id;
uint32_t cookie;
bool is_response;
bool response_requested;
bool is_notification;
bool is_delayed_response;
fwk_id_t id;
alignas(max_align_t) uint8_t params[FWK_EVENT_PARAMETERS_SIZE];
};

該事件包含一個(gè)response_requested 屬性,該屬性指示源實(shí)體是否期望對其事件的響應(yīng)。為了響應(yīng)這個(gè)事件,接收實(shí)體填寫響應(yīng)參數(shù),框架發(fā)出一個(gè)事件,該事件以發(fā)出原始事件的實(shí)體為目標(biāo)。

事件的is_response屬性用于指示新生成的事件是對原始事件的響應(yīng)。

例如在juno_rom固件初始化時(shí),初始化juno_rom模塊,product/juno/module/juno_rom/src/mod_juno_rom.c

會執(zhí)行.start回調(diào)函數(shù)函數(shù)juno_rom_start(),給自己發(fā)了一個(gè)event,如下:

static int juno_rom_start(fwk_id_t id)
{
struct fwk_event event = {
.source_id = fwk_module_id_juno_rom,
.target_id = fwk_module_id_juno_rom,
.id = mod_juno_rom_event_id_run,
};
.....
return fwk_put_event(&event);
}

#define fwk_put_event(event)
_Generic((event), struct fwk_event *
: __fwk_put_event, struct fwk_event_light *
: __fwk_put_event_light)(event)

fwk_put_event把event分為兩類,fwk_event_light 是輕量級的攜帶不攜帶額外數(shù)據(jù)參數(shù)。這里我們用fwk_event 則處理函數(shù)為:

__fwk_put_event
--》put_event(event,
intr_state, FWK_EVENT_TYPE_STD);
--》fwk_list_push_tail(&ctx.event_queue,
&allocated_event->slist_node);

固件狀態(tài)機(jī)運(yùn)行的時(shí)候會循環(huán)執(zhí)行framework/src/fwk_core.c中process_next_event()函數(shù)

static void process_next_event(void)
{
ctx.current_event = event = FWK_LIST_GET(
fwk_list_pop_head(&ctx.event_queue), struct fwk_event, slist_node);

module = fwk_module_get_ctx(event->target_id)->desc;
process_event = event->is_notification ? module->process_notification :
module->process_event;

status = process_event(event, &async_response_event);

這里找到模塊juno_rom,然后取出其event處理函數(shù)process_event并執(zhí)行,實(shí)際執(zhí)行的是juno_rom_process_event(),其發(fā)了一條通知消息如下:

static int juno_rom_process_event(
const struct fwk_event *event,
struct fwk_event *resp)
{
....
/* Send SYSTOP ON notification */
systop_on_event =
(struct fwk_event){ .response_requested = true,
.id = mod_juno_rom_notification_id_systop,
.source_id = FWK_ID_NONE };

notification_params = (void *)systop_on_event.params;
notification_params->state = (unsigned int)MOD_PD_STATE_ON;

//發(fā)notification消息
status = fwk_notification_notify(&systop_on_event, &ctx.notification_count);
if (!fwk_expect(status == FWK_SUCCESS)) {
return FWK_E_PANIC;
}

//通過ctx.bootloader_api->load_image()調(diào)用mod_bootloader的api,從安全內(nèi)存拷貝到指定位置,
//在該bootloader 模塊api中加載跳轉(zhuǎn)scp_ramfw。
if (ctx.notification_count == 0) {
return deferred_setup();
}

fwk_notification_notify的解釋見notification章節(jié)

5.motificaiont通知

notification涉及到兩個(gè)模塊的通信,跟event的區(qū)別是:

?event是一個(gè)模塊發(fā)給另外一個(gè)模塊或者發(fā)給自己,比較確定

?notification是發(fā)給訂閱了這個(gè)模塊的所有模塊,算廣播,需要先進(jìn)行訂閱

notification接口:

?fwk_notification_subscribe//訂閱指定模塊指定通知

?fwk_notification_unsubscribe//取消訂閱通知

?fwk_notification_notify//向訂閱該通知的模塊發(fā)送通知

在實(shí)現(xiàn)上notification使用event的消息傳遞機(jī)制,只在發(fā)消息和處理消息的時(shí)候做微小改動。

例如上面例子中使用fwk_notification_notify()函數(shù)發(fā)送的通知

int fwk_notification_notify(struct fwk_event *notification_event,unsigned int *count){
send_notifications(notification_event, count);

通知的參數(shù)沿用event的struct fwk_event ,發(fā)送通知的時(shí)候,需要先找到訂閱鏈表,然后進(jìn)行過濾

static void send_notifications(struct fwk_event *notification_event,
unsigned int *count)
{
//根據(jù)id和source_id找到訂閱的鏈表
subscription_dlist = get_subscription_dlist(notification_event->id,
notification_event->source_id);
notification_event->is_response = false;
notification_event->is_notification = true;

for (node = fwk_list_head(subscription_dlist); node != NULL;
node = fwk_list_next(subscription_dlist, node)) {
subscription = FWK_LIST_GET(node,
struct __fwk_notification_subscription, dlist_node);

//對比源id如果相同就進(jìn)行發(fā)送
if (!fwk_id_is_equal(
subscription->source_id, notification_event->source_id)) {
continue;
}

notification_event->target_id = subscription->target_id;

status = __fwk_put_notification(notification_event);
if (status == FWK_SUCCESS) {
(*count)++;
}
}
}

get_subscription_dlist函數(shù)中source_id 決定是模塊上下文還是元素上下文

.id = mod_juno_rom_notification_id_systop,
.source_id = FWK_ID_NONE };

static const fwk_id_t mod_juno_rom_notification_id_systop =
FWK_ID_NOTIFICATION_INIT(
FWK_MODULE_IDX_JUNO_ROM,
MOD_JUNO_ROM_NOTIFICATION_IDX_SYSTOP);

拿到subscription_dlist訂閱列表后,就進(jìn)行過濾發(fā)送通知

int __fwk_put_notification(struct fwk_event *event)
{
event->is_response = false;
event->is_notification = true;

return put_event(event, UNKNOWN_STATE, FWK_EVENT_TYPE_STD);
}

這里就使用了event進(jìn)行實(shí)現(xiàn)。然后系統(tǒng)狀態(tài)機(jī)在處理event的時(shí)候,

static void process_next_event(void)
{
ctx.current_event = event = FWK_LIST_GET(
fwk_list_pop_head(&ctx.event_queue), struct fwk_event, slist_node);

module = fwk_module_get_ctx(event->target_id)->desc;
process_event = event->is_notification ? module->process_notification :
module->process_event;

根據(jù)is_notification 就可以知道是notification 了,然后調(diào)用process_notification 進(jìn)行處理

6.模塊綁定

一個(gè)模塊或元素可以綁定到另一個(gè)模塊或模塊內(nèi)的元素。目標(biāo)是相同的 - 獲取指向可在后續(xù)階段使用的 API 的指針。當(dāng)嘗試綁定到模塊內(nèi)的元素(而不是模塊本身)時(shí),主要區(qū)別在于接收和處理綁定請求的模塊能夠根據(jù)目標(biāo)元素更改其行為。例如,可以允許請求綁定的模塊僅綁定到處理請求的模塊內(nèi)的元素子集。

思路:A模塊要與B模塊通信,A模塊的全局變量要拿到B模塊的回調(diào)函數(shù)

A模塊在初始化的時(shí)候,會調(diào)用自己的bind函數(shù),

bind--》fwk_module_bind--》B模塊的process_bind_request()函數(shù),從而拿到api

25fd5cda-192e-11ee-962d-dac502259ad0.png

scmi_power_domain模塊調(diào)用scmi模塊的api函數(shù)示例圖

scmi_pd_ctx.scmi_api賦值為scmi模塊的處理函數(shù),在.bind = scmi_pd_bind中,

static int scmi_pd_bind(fwk_id_t id, unsigned int round)
{
status = fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_SCMI),
FWK_ID_API(FWK_MODULE_IDX_SCMI, MOD_SCMI_API_IDX_PROTOCOL),
&scmi_pd_ctx.scmi_api);

fwk_module_bind調(diào)用依賴模塊提供的process_bind_request函數(shù)來獲取依賴模塊的api,并綁定。

int fwk_module_bind(fwk_id_t target_id, fwk_id_t api_id, const void *api)
{
fwk_mod_ctx = fwk_module_get_ctx(target_id);

status = fwk_mod_ctx->desc->process_bind_request(
fwk_module_ctx.bind_id, target_id, api_id, (const void **)api);

target_id是FWK_MODULE_IDX_SCMI,對應(yīng)SCMI模塊,fwk_mod_ctx 是SCMI模塊的上下文

/* SCMI module definition */
const struct fwk_module module_scmi = {
.api_count = (unsigned int)MOD_SCMI_API_IDX_COUNT,
.event_count = 1,
#ifdef BUILD_HAS_NOTIFICATION
.notification_count = (unsigned int)MOD_SCMI_NOTIFICATION_IDX_COUNT,
#endif
.type = FWK_MODULE_TYPE_SERVICE,
.init = scmi_init,
.element_init = scmi_service_init,
.bind = scmi_bind,
.start = scmi_start,
.process_bind_request = scmi_process_bind_request,
.process_event = scmi_process_event,
#ifdef BUILD_HAS_NOTIFICATION
.process_notification = scmi_process_notification,
#endif
};

scmi_process_bind_request調(diào)用到

static int scmi_process_bind_request(fwk_id_t source_id, fwk_id_t target_id,
fwk_id_t api_id, const void **api)
{
unsigned int api_idx;
struct scmi_service_ctx *ctx;
enum mod_scmi_api_idx api_id_type;

api_idx = fwk_id_get_api_idx(api_id);

api_id_type = (enum mod_scmi_api_idx)api_idx;

switch (api_id_type) {
case MOD_SCMI_API_IDX_PROTOCOL:
if (!fwk_id_is_type(target_id, FWK_ID_TYPE_MODULE)) {
return FWK_E_SUPPORT;
}

if (scmi_ctx.protocol_count >= scmi_ctx.config->protocol_count_max) {
return FWK_E_NOMEM;
}

scmi_ctx.protocol_table[PROTOCOL_TABLE_RESERVED_ENTRIES_COUNT +
scmi_ctx.protocol_count++].id = source_id;
*api = &scmi_from_protocol_api;
break;

到此scmi_power_domain模塊拿到了scmi模塊的處理函數(shù),放入&scmi_pd_ctx.scmi_api

幾個(gè)關(guān)鍵的模塊間api調(diào)用示例:

mod_scmi_power_domain模塊中scmi消息收發(fā):

?scmi模塊綁定各個(gè)scmi協(xié)議模塊的protocol_api,根據(jù)id來找到此模塊api,執(zhí)行該protocol;

?scmi_power_domain模塊綁定scmi模塊的api,通過調(diào)用scmi_api(mod_scmi.c中)來回復(fù)該protocol;

mod_scmi.c/transport_api調(diào)用mod_smt中的transport相關(guān)功能來完成scmi協(xié)議的transport層(scmi 數(shù)據(jù)收發(fā)及解析);

mod_smt.c/driver_api調(diào)用scmi更下一級的channel來產(chǎn)生中斷(scmi消息通知中斷產(chǎn)生和處理)。





審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • ARM處理器
    +關(guān)注

    關(guān)注

    6

    文章

    360

    瀏覽量

    41517
  • 耦合器
    +關(guān)注

    關(guān)注

    8

    文章

    710

    瀏覽量

    59473
  • FreeRTOS
    +關(guān)注

    關(guān)注

    12

    文章

    483

    瀏覽量

    61727
  • 狀態(tài)機(jī)
    +關(guān)注

    關(guān)注

    2

    文章

    489

    瀏覽量

    27395
  • MCP
    MCP
    +關(guān)注

    關(guān)注

    0

    文章

    252

    瀏覽量

    13824

原文標(biāo)題:ARM SCP入門-framework框架代碼分析

文章出處:【微信號:OS與AUTOSAR研究,微信公眾號:OS與AUTOSAR研究】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    ARM SCP入門-簡介和代碼下載編譯

    在現(xiàn)代SoC芯片中CPU只能說是皇帝,掌握資源命脈的還得是太上皇 SCP 。
    發(fā)表于 07-02 10:33 ?5597次閱讀
    <b class='flag-5'>ARM</b> <b class='flag-5'>SCP</b><b class='flag-5'>入門</b>-簡介和<b class='flag-5'>代碼</b>下載編譯

    嵌入式框架Zorb Framework搭建方案

    Zorb Framework是一個(gè)基于面向?qū)ο蟮乃枷雭泶罱ㄒ粋€(gè)輕量級的嵌入式框架
    的頭像 發(fā)表于 11-05 17:08 ?1326次閱讀
    嵌入式<b class='flag-5'>框架</b>Zorb <b class='flag-5'>Framework</b>搭建方案

    嵌入式框架Zorb Framework狀態(tài)機(jī)的實(shí)現(xiàn)

    Zorb Framework是一個(gè)基于面向?qū)ο蟮乃枷雭泶罱ㄒ粋€(gè)輕量級的嵌入式框架。
    的頭像 發(fā)表于 11-29 09:33 ?908次閱讀
    嵌入式<b class='flag-5'>框架</b>Zorb <b class='flag-5'>Framework</b>狀態(tài)機(jī)的實(shí)現(xiàn)

    ARM入門基礎(chǔ)好書

    `ARM入門基礎(chǔ)好書本文從學(xué)習(xí)者的角度出發(fā),分別描述了下面幾部分內(nèi)容:ARM編程的基本知識,BOOT代碼流程和功能分析,OS中斷程序的編寫舉
    發(fā)表于 10-31 16:52

    .NET Micro Framework簡介

    的方式實(shí)現(xiàn)了源代碼完全開放。  1.哪些領(lǐng)域可以采用.NET Micro Framework技術(shù)?  .NET Micro Framework技術(shù)可以應(yīng)用到:Sideshow、遠(yuǎn)程控制、智能家電
    發(fā)表于 05-27 16:03

    .Net Micro Framework 快速入門

    ?  .NET Micro Framework對存儲器和處理器的要求更低。開發(fā)人員可以在低功耗、低成本的ARM7、ARM9、Blackfin和Cortex-M3處理器上使用該框架(不需
    發(fā)表于 05-29 10:56

    [資料分享]+Android框架揭秘

    框架入門教材使用。 三、看看目錄,了解其主要內(nèi)容 《android框架揭秘》第1章 android framework概要 11.1 android源代碼組成 21.2 通過啟動過程
    發(fā)表于 09-26 09:47

    Labview Actor Framework 框架使用例子(反饋式蒸發(fā)器)

    工作中遇到老外的項(xiàng)目,用的是Actor Framework框架,所以最近這段時(shí)間學(xué)習(xí)了下。但網(wǎng)上這塊資料非常少,就算是有也是非常簡單的介紹。后面看到這篇博客http://blog.csdn.net
    發(fā)表于 11-17 19:36

    Android Framework電源子系統(tǒng)的分析

    系列文章解讀&說明:Android Framework 電源子系統(tǒng) 的分析主要分為以下部分:Android Framework 電源子系統(tǒng)(01)PowerManagerService啟動
    發(fā)表于 12-31 06:51

    CH372DLL.dll在.NET Framework 4.7.2及以上版本框架會閃退如何解決?

    問題:CH372DLL.dll 在 .NET Framework 3.5 及以下版本框架工作正常,.NET Framework 4.7.2 及以上版本框架會閃退請問如何解決
    發(fā)表于 07-19 06:18

    ARM入門 Study ARM Step by Step

    ARM入門 Study ARM Step by Step本文從學(xué)習(xí)者的角度出發(fā),分別描述了下面幾個(gè)部分內(nèi)容:ARM編程的基本知識,BOOT代碼
    發(fā)表于 02-10 10:57 ?193次下載

    ARM處理器的啟動代碼分析與設(shè)計(jì)

    ARM處理器的啟動代碼分析與設(shè)計(jì)
    發(fā)表于 09-25 08:27 ?12次下載
    <b class='flag-5'>ARM</b>處理器的啟動<b class='flag-5'>代碼</b>的<b class='flag-5'>分析</b>與設(shè)計(jì)

    關(guān)于嵌入式應(yīng)用框架(EAF)的分析

    EAF是Embedded Application Framework 的縮寫,即嵌入式應(yīng)用框架。嵌入式應(yīng)用框架是 Application framework的一種, 是在嵌入式領(lǐng)域的應(yīng)
    發(fā)表于 01-01 09:50 ?1400次閱讀

    ARM SCP入門-AP與SCP通信

    當(dāng)Linux想要關(guān)機(jī)或者休眠的時(shí)候,這涉及到整個(gè)系統(tǒng)電源狀態(tài)的變化,為了安全性Linux內(nèi)核沒有權(quán)利去直接執(zhí)行了,需要陷入到EL3等級去執(zhí)行,可以參考之前文章ARM ATF入門-安全固件軟件介紹和代碼運(yùn)行
    的頭像 發(fā)表于 07-16 11:25 ?4663次閱讀
    <b class='flag-5'>ARM</b> <b class='flag-5'>SCP</b><b class='flag-5'>入門</b>-AP與<b class='flag-5'>SCP</b>通信

    framework框架流程 模塊綁定

    framework框架負(fù)責(zé)固件的通用流程實(shí)現(xiàn),包括系統(tǒng)初始化,module初始化,中斷服務(wù)提供,event服務(wù)提供等。這樣module就可以專注于自己功能和對外交互api的實(shí)現(xiàn)。SCP
    的頭像 發(fā)表于 11-02 17:24 ?517次閱讀
    <b class='flag-5'>framework</b><b class='flag-5'>框架</b>流程 模塊綁定