色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

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

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

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

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

1. module介紹

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

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

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

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

這些module在framework啟動時候初始化啟動運行。

公共的module比較有通用性,產品自己的module一般是驅動需要進行定制

模塊類型及軟件協議棧:

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

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

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

2.framework框架流程

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

framework框架負責固件的通用流程實現,包括系統初始化,module初始化,中斷服務提供,event服務提供等。這樣module就可以專注于自己功能和對外交互api的實現。SCP framework初始化流程圖如下:

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

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

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

BS_PRODUCT_NAME := juno

BS_FIRMWARE_LIST := scp_romfw
scp_romfw_bypass
scp_ramfw

模塊可以在項目根目錄的 modules/ 目錄下實現,也可以是產品特定的并在product//modules 目錄下實現。

gen_module_code.py腳本會根據

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:包含構成固件的模塊索引的枚舉。fwk_module_idx.h中枚舉中模塊索引的順序保證遵循固件firmware.mk 文件中 BS_FIRMWARE_MODULES列表中模塊名稱的順序。當執行涉及迭代固件中存在的所有模塊的操作時,框架在運行時使用相同的順序,例如 fwk_module.c 中的 init_modules() 函數。

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:包含一個指向模塊描述符的指針表,每個模塊對應一個作為固件一部分構建的模塊。該文件及其內容由框架內部使用,通常不應由其他單元(如模塊)使用。

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文件中可以對配置開關進行聲明,例如:

set(SCP_FIRMWARE_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}")

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

framework.md-固件相關配置文件說明

產品始終包含定義一個或多個固件目標的product.mk文件。

在一個產品中,總會有至少一個固件。

對于每個固件,必須在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)

如果使用雙區域內存配置,則還必須定義FMW_MEM1_BASE和 FMW_MEM1_SIZE 。

Toolchain-*.cmake 中定義image的體系結構目標

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 定義編譯范圍和目標,cmake會使用生成makefile文件。

編譯選項說明:

cmake_readme.md-構建配置選項:

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

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

?SCP_ENABLE_RESOURCE_PERMISSIONS:啟用/禁用資源權限設置。

單獨配置編譯:

配置生效命令:

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下,再繼續編譯。

2.1 平臺初始化

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

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

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

其實現在arch/arm/arm-m/src/arch_handlers.c:

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

__main在c運行時調用main函數,對于M核實現來說,arch/arm/arm-m/src/arch_main.c中有main()函數

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

//scp 入口及應用函數
return fwk_arch_init(&arch_init_driver);
}

scp 入口及應用為fwk_arch_init函數,在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();

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

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

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

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

//循環等待處理隊列事件
__fwk_run_main_loop();
}

2.2 module初始化

fwk_module_init函數,在framework/src/fwk_module.c中實現

在系統構建章節中module_table和module_config_table是由配置文件Firmware.cmake生成的fwk_module_list.c中定義。

module見module介紹章節

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是入參回調函數,對應為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的值后,執行fwk_interrupt_init(driver);

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

return FWK_SUCCESS;
}

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

模塊使用中斷時,需要調用對外接口在framework/include/fwk_interrupt.h中,

例如開啟中斷fwk_interrupt_enable函數的實現:

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)
{
//初始化任務列表
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;

//調用模塊.bind回調函數完成所有模塊的綁定。(此處共進行兩輪調用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函數調用fwk_module_init_module對每個模塊進行功能初始化

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

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

//初始化模塊元素(element),調用模塊回調函數.element_init將模塊element->data配置信息導入到模塊內部
fwk_module_init_elements(ctx);

start_modules函數調用fwk_module_start_module對每個模塊進行啟動

module = fwk_mod_ctx->desc;

//調用模塊.start回調函數
module->start(fwk_mod_ctx->id);

例如在juno_rom的.start回調函數函數中,通過event和notification機制,到達juno_rom模塊的相應回調函數,在juno_rom中,通過ctx.bootloader_api->load_image()調用mod_bootloader的api,從安全內存拷貝到指定位置,在該bootloader模塊api中加載跳轉scp_ramfw。(注mod_bootloader_boot為匯編實現,依賴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 運行狀態機

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

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主要處理三個重要的鏈表:free_event_queue, event_queue, isr_event_queue所有的操作都是圍繞這三個隊列展開。

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

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

event_queue中根據target_id找到對應module,然后調用module->process_event進行處理,詳細見module中說明。

process_next_event中調用duplicate_event會處理free_event_queue隊列中的事件

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

3.module對外接口

在scp代碼中,所有的功能都由一個個模塊提供。每個模塊以api枚舉及其結構體的方式對外提供該模塊的功能,并在模塊通用結構體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的獲取并綁定請求函數)等通用接口。

.start模塊啟動

.process_event事件處理

.process_notification通知處理

初始化模塊:

模塊在初始化時由fwk_module.c 中fwk_module_start函數,調用回調函數.init,.bind,.start

?模塊初始化:調用模塊API的init()函數指針

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

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

?綁定:模塊必須綁定好才能調用對方的api

?開始

運行時:

一旦運行前階段成功完成,固件將開始處理模塊或中斷引發的事件。默認情況下,固件將永遠循環等待新事件在運行前階段結束時處理,但當事件列表為空時,可以在處理未決事件后返回。

模塊配置:

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

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 ,框架使用表指針中給出的靜態表來訪問產品為模塊提供的元素表。

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

get_element_table對應一個配置結構體數組:

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結構體表示元素,里面有名字,子元素個數和數據

元素:

元素表示由模塊擁有或管理的資源。每個元素將表示模塊與之交互和/或負責的對象。

例如,驅動程序類型的模塊可能具有表示它所控制的硬件設備的元素。因為元素配置數據靈活多變,使用通用的方式const void *data實現。

子元素表示由元素擁有或管理的資源。子元素僅由它們的索引和/或標識符表示。

索引和標識符:

由于框架設計為模塊化,因此需要一種標準化方法來識別和引用模塊、元素、子元素、事件、通知和 API。該框架為此定義了兩個組件:indices和identifiers。

indices:

模塊索引由構建系統為每個固件生成,并放在fwk_module_idx.h頭文件中。

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

identifiers:

標識符有一個類型,這決定了標識符中包含的信息。在內部,標識符始終包含模塊的索引,并且可能包含在該模塊的上下文中標識項目的附加索引。也在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)

可用的標識符類型有:

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

?元素:由模塊索引和模塊內元素的索引組成

?子元素:由模塊索引、模塊內元素的索引和該元素擁有的子元素的索引組成。

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

?事件:由模塊索引和模塊可能產生的事件的索引組成

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

日志:

日志記錄功能定義并實現了該組件的公共接口。該接口的文檔可以在 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()函數在framework/src/fwk_log.c中定義。

4. event事件

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

模塊可以給自己或者別的模塊發送event事件,事件的參數是結構化消息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];
};

該事件包含一個response_requested 屬性,該屬性指示源實體是否期望對其事件的響應。為了響應這個事件,接收實體填寫響應參數,框架發出一個事件,該事件以發出原始事件的實體為目標。

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

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

會執行.start回調函數函數juno_rom_start(),給自己發了一個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 是輕量級的攜帶不攜帶額外數據參數。這里我們用fwk_event 則處理函數為:

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

固件狀態機運行的時候會循環執行framework/src/fwk_core.c中process_next_event()函數

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處理函數process_event并執行,實際執行的是juno_rom_process_event(),其發了一條通知消息如下:

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;

//發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()調用mod_bootloader的api,從安全內存拷貝到指定位置,
//在該bootloader 模塊api中加載跳轉scp_ramfw。
if (ctx.notification_count == 0) {
return deferred_setup();
}

fwk_notification_notify的解釋見notification章節

5.motificaiont通知

notification涉及到兩個模塊的通信,跟event的區別是:

?event是一個模塊發給另外一個模塊或者發給自己,比較確定

?notification是發給訂閱了這個模塊的所有模塊,算廣播,需要先進行訂閱

notification接口:

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

?fwk_notification_unsubscribe//取消訂閱通知

?fwk_notification_notify//向訂閱該通知的模塊發送通知

在實現上notification使用event的消息傳遞機制,只在發消息和處理消息的時候做微小改動。

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

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

通知的參數沿用event的struct fwk_event ,發送通知的時候,需要先找到訂閱鏈表,然后進行過濾

static void send_notifications(struct fwk_event *notification_event,
unsigned int *count)
{
//根據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如果相同就進行發送
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函數中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訂閱列表后,就進行過濾發送通知

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進行實現。然后系統狀態機在處理event的時候,

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;

根據is_notification 就可以知道是notification 了,然后調用process_notification 進行處理

6.模塊綁定

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

思路:A模塊要與B模塊通信,A模塊的全局變量要拿到B模塊的回調函數

A模塊在初始化的時候,會調用自己的bind函數,

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

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

scmi_power_domain模塊調用scmi模塊的api函數示例圖

scmi_pd_ctx.scmi_api賦值為scmi模塊的處理函數,在.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調用依賴模塊提供的process_bind_request函數來獲取依賴模塊的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,對應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調用到

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模塊的處理函數,放入&scmi_pd_ctx.scmi_api

幾個關鍵的模塊間api調用示例:

mod_scmi_power_domain模塊中scmi消息收發:

?scmi模塊綁定各個scmi協議模塊的protocol_api,根據id來找到此模塊api,執行該protocol;

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

mod_scmi.c/transport_api調用mod_smt中的transport相關功能來完成scmi協議的transport層(scmi 數據收發及解析);

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





審核編輯:劉清

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • ARM處理器
    +關注

    關注

    6

    文章

    360

    瀏覽量

    41733
  • 耦合器
    +關注

    關注

    8

    文章

    725

    瀏覽量

    59705
  • FreeRTOS
    +關注

    關注

    12

    文章

    484

    瀏覽量

    62157
  • 狀態機
    +關注

    關注

    2

    文章

    492

    瀏覽量

    27538
  • MCP
    MCP
    +關注

    關注

    0

    文章

    254

    瀏覽量

    13904

原文標題:ARM SCP入門-framework框架代碼分析

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

收藏 人收藏

    評論

    相關推薦

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

    在現代SoC芯片中CPU只能說是皇帝,掌握資源命脈的還得是太上皇 SCP
    發表于 07-02 10:33 ?6378次閱讀
    <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是一個基于面向對象的思想來搭建一個輕量級的嵌入式框架
    的頭像 發表于 11-05 17:08 ?1458次閱讀
    嵌入式<b class='flag-5'>框架</b>Zorb <b class='flag-5'>Framework</b>搭建方案

    嵌入式框架Zorb Framework狀態機的實現

    Zorb Framework是一個基于面向對象的思想來搭建一個輕量級的嵌入式框架
    的頭像 發表于 11-29 09:33 ?1167次閱讀
    嵌入式<b class='flag-5'>框架</b>Zorb <b class='flag-5'>Framework</b>狀態機的實現

    ARM入門基礎好書

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

    .NET Micro Framework簡介

    的方式實現了源代碼完全開放。  1.哪些領域可以采用.NET Micro Framework技術?  .NET Micro Framework技術可以應用到:Sideshow、遠程控制、智能家電
    發表于 05-27 16:03

    .Net Micro Framework 快速入門

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

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

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

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

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

    Android Framework電源子系統的分析

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

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

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

    ARM入門 Study ARM Step by Step

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

    ARM處理器的啟動代碼分析與設計

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

    關于嵌入式應用框架(EAF)的分析

    EAF是Embedded Application Framework 的縮寫,即嵌入式應用框架。嵌入式應用框架是 Application framework的一種, 是在嵌入式領域的應
    發表于 01-01 09:50 ?1528次閱讀

    ARM SCP入門-AP與SCP通信

    當Linux想要關機或者休眠的時候,這涉及到整個系統電源狀態的變化,為了安全性Linux內核沒有權利去直接執行了,需要陷入到EL3等級去執行,可以參考之前文章ARM ATF入門-安全固件軟件介紹和代碼運行
    的頭像 發表于 07-16 11:25 ?5365次閱讀
    <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框架負責固件的通用流程實現,包括系統初始化,module初始化,中斷服務提供,event服務提供等。這樣module就可以專注于自己功能和對外交互api的實現。SCP
    的頭像 發表于 11-02 17:24 ?676次閱讀
    <b class='flag-5'>framework</b><b class='flag-5'>框架</b>流程 模塊綁定
    主站蜘蛛池模板: 999精品在线| 我的漂亮朋友在线观看全集免费 | 青青青草国产| 麻豆AV福利AV久久AV| 久久精品久久久| 久久精品免费观看久久| 精品无码国产自产在线观看| 国精产品一区一区三区有| 国产一区二区精品视频| 国产综合在线视频| 国产在线AV一区二区香蕉| 国产永久免费视频| 国内外成人免费在线视频| 精品国产精品人妻久久无码五月天| 国产中的精品AV一区二区| 红桃视频国产AV| 久久国产视频网| 妹妹我要操| 欧美乱妇日本无乱码特黄大片| 欧美在线视频一区| 日日干夜夜啪蕉视频| 无码人妻精品一区二区蜜桃在线看| 性色无码AV久久蜜臀| 亚洲欧美日韩国产手机在线| 诱受H嗯啊巨肉舍友1V1| 2021精品国产综合久久| YY6080A旧里番在线观看| 成人综合在线观看| 国产美女视频一区二区二三区| 好色女博士| 免费观看成人www精品视频在线| 欧美极限扩肛| 手机在线观看你懂的| 亚洲精品伊人久久久久| 中文中幕无码亚洲视频| ebc5恐怖5a26房间| 国产成人无码区免费内射一片色欲 | 人妻洗澡被强公日日澡| 色欲人妻无码AV精品一区二区| 亚洲AV无码一区二区三区牛牛 | 色欲色香天天天综合|