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

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

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

電源管理入門-6 CPUFreq

yzcdx ? 來源:OS與AUTOSAR研究 ? 2023-10-16 17:07 ? 次閱讀

之前章節(jié)介紹的電源管理都都直接下電,不用電當(dāng)然能節(jié)能,但是還有比較溫柔的方法就是通過調(diào)節(jié)電壓頻率。比如經(jīng)常的一個(gè)說法:CPU太熱了跑不動了,快給降頻下。頻率就干活的速度,干活太快,CPU都要燒了,太熱了,費(fèi)電啊。但是在戶外的設(shè)備,環(huán)境溫度過高還是要考慮遮陽、通風(fēng)來應(yīng)對,但降頻也可以降低溫度,但是會引起卡頓啊。

一般電壓和頻率是成對出現(xiàn)的,也叫OPP(Operating Performance Points),對其進(jìn)行調(diào)節(jié)也叫DVFS(Dynamic Voltage and Frequency Scaling),下面就來揭開這些技術(shù)的神秘面紗。

1. 整體介紹

1.1 DVFS

03a73854-6bf9-11ee-939d-92fbcf53809c.png

DVFS(Dynamic Voltage and Frequency Scaling)即動態(tài)電壓頻率調(diào)整。這項(xiàng)技術(shù)可以根據(jù)芯片運(yùn)行的應(yīng)用程序的計(jì)算需求制定策略,動態(tài)調(diào)整電壓和頻率:

  • 在不需要高性能時(shí),降低電壓和頻率,以降低功耗;
  • 在需要高性能時(shí),提高電壓和頻率,以提高性能,從而達(dá)到兼顧性能而又節(jié)能的目的。

DVFS技術(shù)利用了CMOS芯片的特性:CMOS芯片的能量消耗正比于電壓的平方和時(shí)鐘頻率:

  • 減少能量消耗需要降低電壓和頻率。
  • 僅僅降低時(shí)鐘頻率并不節(jié)約能量,因?yàn)闀r(shí)鐘頻率的降低會帶來任務(wù)執(zhí)行時(shí)間的增加。調(diào)節(jié)電壓需要以相同的比例調(diào)節(jié)頻率以滿足信號傳播延遲要求。然而不管是電壓調(diào)節(jié)還是頻率調(diào)節(jié),都會造成系統(tǒng)性能的損失,并增加系統(tǒng)的響應(yīng)延遲。
  • DVFS技術(shù)是以延長任務(wù)執(zhí)行時(shí)間為代價(jià)來達(dá)到減少系統(tǒng)能量消耗的目的,體現(xiàn)了功耗與性能之間的權(quán)衡??梢酝ㄟ^減少時(shí)鐘頻率來降低通用處理器功耗的。

1.2 Linux 軟件流程框圖

CPUFreq系統(tǒng)流程:

03d32504-6bf9-11ee-939d-92fbcf53809c.png

  1. 用戶app可以使用/sys/devices/system/cpu/cpu0/cpufreq/下的接口文件設(shè)置cpu頻率
  2. 設(shè)置頻率的時(shí)候會調(diào)用相關(guān)governor的函數(shù),主要包括查詢、設(shè)置等
  3. governor負(fù)責(zé)采集與系統(tǒng)負(fù)載有關(guān)的信號,計(jì)算當(dāng)前的系統(tǒng)負(fù)載。根據(jù)系統(tǒng)的當(dāng)前負(fù)載,根據(jù)調(diào)節(jié)策略預(yù)測系統(tǒng)在下一時(shí)間段需要的性能。將預(yù)測的性能轉(zhuǎn)換成需要的頻率和電壓在cpufreq table中選擇一個(gè),進(jìn)行調(diào)整芯片的時(shí)鐘和電壓設(shè)置。
  4. governor需要設(shè)置的時(shí)候會調(diào)用cpufreq core的接口cpufreq_driver->target_index進(jìn)行設(shè)置
  5. driver會繼續(xù)調(diào)用opp驅(qū)動clk_set_rate(clk, freq)接口進(jìn)行寄存器設(shè)置,讓電壓頻率生效 另外:動態(tài)策略的governor會自動收集系統(tǒng)中的各種信號進(jìn)行動態(tài)調(diào)節(jié)

DVFS調(diào)節(jié)策略 一味的降頻降壓當(dāng)然是不能降低功耗的,因?yàn)榈皖l下運(yùn)行可能使系統(tǒng)處理任務(wù)的時(shí)長增加,從而整體上可能增加了功耗。所以DVFS的核心是動態(tài)調(diào)整的策略,其目的是根據(jù)當(dāng)時(shí)的系統(tǒng)負(fù)載實(shí)時(shí)調(diào)整,從而提供滿足當(dāng)時(shí)性能要求的最低功率,也就達(dá)到了最低功耗。

需要統(tǒng)計(jì)出這些模塊的負(fù)載情況,基本的策略當(dāng)然是工作負(fù)載增加則升頻升壓,工作負(fù)載降低則降頻降壓。工作負(fù)載的粗略模型是在一個(gè)時(shí)間窗口內(nèi),統(tǒng)計(jì)模塊工作的時(shí)間長度,設(shè)定不同閾值,高閾值對應(yīng)高電壓高頻率,低閾值對應(yīng)低電壓低頻率。每次統(tǒng)計(jì)值穿過閾值邊界,觸發(fā)DVFS轉(zhuǎn)換。

03db8cbc-6bf9-11ee-939d-92fbcf53809c.png

> 在調(diào)整頻率和電壓時(shí),要特別注意調(diào)整的順序: > - 當(dāng)頻率由高到低調(diào)整時(shí),應(yīng)該先降頻率,再降電壓; > - 相反,當(dāng)升高頻率時(shí),應(yīng)該先升電壓,再升頻率。

2. 相關(guān)代碼介紹

2.1 整體代碼框架

內(nèi)核目前有一套完整的代碼支持DVFS,具體可參考內(nèi)核下drivers/cpufreq/。

03ff6fe2-6bf9-11ee-939d-92fbcf53809c.png

image.png
  1. cpufreq core:是cpufreq framework的核心模塊,和kernel其它framework類似,主要實(shí)現(xiàn)三類功能:
  • 向上,以sysfs的形式向用戶空間提供統(tǒng)一的接口,以notifier的形式向其它driver提供頻率變化的通知。
  • 內(nèi)部,抽象調(diào)頻調(diào)壓的公共邏輯和接口,主要圍繞struct cpufreq_driver、struct cpufreq_policy和struct cpufreq_governor三個(gè)數(shù)據(jù)結(jié)構(gòu)進(jìn)行。包括:圍繞結(jié)構(gòu)struct cpufreq_governor提供governor框架,用于實(shí)現(xiàn)不同的頻率調(diào)整機(jī)制;圍繞struct cpufreq_policy實(shí)現(xiàn)的一些功能等。
  • 向下:提供CPU頻率和電壓控制的驅(qū)動框架,封裝通用操作接口給驅(qū)動,方便底層驅(qū)動的開發(fā);
  1. cpufreq governor:負(fù)責(zé)調(diào)頻調(diào)壓的各種策略,每種governor計(jì)算頻率的方式不同,根據(jù)提供的頻率范圍和參數(shù)(閾值等),計(jì)算合適的頻率。

  2. cpufreq driver:負(fù)責(zé)平臺相關(guān)的調(diào)頻調(diào)壓機(jī)制的實(shí)現(xiàn),基于cpu subsystem driver、OPP、clock driver、regulator driver等模塊,提供對CPU頻率和電壓的控制。kernel中實(shí)現(xiàn)了比較通用的驅(qū)動模塊cpufreq-dt.c

  3. cpufreq stats:負(fù)責(zé)調(diào)頻信息和各頻點(diǎn)運(yùn)行時(shí)間等統(tǒng)計(jì),提供每個(gè)cpu的cpufreq有關(guān)的統(tǒng)計(jì)信息。

2.2 用戶態(tài)接口

cpufreq相關(guān)驅(qū)動模塊加載后,會在各cpu下創(chuàng)建:/sys/devices/system/cpu/cpuX/cpufreq接口

041b8934-6bf9-11ee-939d-92fbcf53809c.png

這是一個(gè)軟鏈接:cpufreq -> ../cpufreq/policy0

0432c9be-6bf9-11ee-939d-92fbcf53809c.png

0456ce7c-6bf9-11ee-939d-92fbcf53809c.png

image.png

前綴是scaling的屬性文件表示軟件可調(diào)節(jié)的幾種屬性,前綴是cpuinfo的屬性文件表示硬件支持的幾種屬性。cpuinfo是scaling的子集,因?yàn)檐浖O(shè)置范圍在硬件支持范圍內(nèi)。 scaling_governor 可以手動修改設(shè)置:

echo ondemand > /sys/devices/system/cpu/cpu0/scaling_governor

一般系統(tǒng)啟動默認(rèn)為performance,支持5種模式,可以通過make menuconfig配置。

目前DVFS支持調(diào)頻調(diào)壓策略主要就是上面支持的5種:

  1. userspace(用戶定義的) 使用用戶在/sys 節(jié)點(diǎn)scaling_setspeed設(shè)置的頻率運(yùn)行。 最早的 cpufreq 子系統(tǒng)通過 userspace governor 為用戶提供了這種靈活性。系統(tǒng)將變頻策略的決策權(quán)交給了用戶態(tài)應(yīng)用程序,并提供了相應(yīng)的接口供用戶態(tài)應(yīng)用程序調(diào)節(jié) CPU 運(yùn)行頻率使用。 (可以使用Dominik 等人開發(fā)了cpufrequtils 工具包 )
  2. performancecpu(突出性能) 按照支持的最高頻率運(yùn)行
  3. ondemand(按需的) 系統(tǒng)負(fù)載小時(shí)以低頻率運(yùn)行,系統(tǒng)負(fù)載提高時(shí)按需提高頻率 userspace是內(nèi)核態(tài)的檢測,效率低。而ondemand正是人們長期以來希望看到的一個(gè)完全在內(nèi)核態(tài)下工作并且能夠以更加細(xì)粒度的時(shí)間間隔對系統(tǒng)負(fù)載情況進(jìn)行采樣分析的governor。
  4. conservative(保守的) 跟ondemand方式類似, 不同之處在于提高頻率時(shí)漸進(jìn)提高,而ondemand是跳變提高,ondemand比conservative先進(jìn),是conservative的改良版本。 ondemand governor 的最初實(shí)現(xiàn)是在可選的頻率范圍內(nèi)調(diào)低至下一個(gè)可用頻率。這種降頻策略的主導(dǎo)思想是盡量減小對系統(tǒng)性能的負(fù)面影響,從而不會使得系統(tǒng)性能在短時(shí)間內(nèi)迅速降低以影響用戶體驗(yàn)。但是在 ondemand governor 的這種最初實(shí)現(xiàn)版本在社區(qū)發(fā)布后,大量用戶的使用結(jié)果表明這種擔(dān)心實(shí)際上是多余的, ondemand governor在降頻時(shí)對于目標(biāo)頻率的選擇完全可以更加激進(jìn)。因此最新的 ondemand governor 在降頻時(shí)會在所有可選頻率中一次性選擇出可以保證 CPU 工作在 80% 以上負(fù)荷的頻率,當(dāng)然如果沒有任何一個(gè)可選頻率滿足要求的話則會選擇 CPU 支持的最低運(yùn)行頻率。大量用戶的測試結(jié)果表明這種新的算法可以在不影響系統(tǒng)性能的前提下做到更高效的節(jié)能。在算法改進(jìn)后, ondemand governor 的名字并沒有改變,而 ondemand governor 最初的實(shí)現(xiàn)也保存了下來,并且由于其算法的保守性而得名conservative 。 Ondemand降頻更加激進(jìn),conservative降頻比較緩慢保守,事實(shí)使用ondemand的效果也是比較好的。
  5. powersavecpu(省電的) 以支持的最低頻率運(yùn)行 CPU會固定工作在其支持的最低運(yùn)行頻率上。因此其和performance這兩種 governors 都屬于靜態(tài) governor ,即在使用它們時(shí) CPU 的運(yùn)行頻率不會根據(jù)系統(tǒng)運(yùn)行時(shí)負(fù)載的變化動態(tài)作出調(diào)整。這兩種 governors 對應(yīng)的是兩種極端的應(yīng)用場景,使用 performance governor 體現(xiàn)的是對系統(tǒng)高性能的最大追求,而使用 powersave governor 則是對系統(tǒng)低功耗的最大追求。
  6. schedutil:通過將自己的調(diào)頻策略注冊到hook,在負(fù)載發(fā)生變化的時(shí)候,會調(diào)用該hook,此時(shí)就可以進(jìn)行調(diào)頻決策或執(zhí)行調(diào)頻動作。前面的調(diào)頻策略都是周期采樣計(jì)算cpu負(fù)載有滯后性,精度也有限,而schedutil可以使用PELT(per entity load tracking)或者WALT(window assist load tracking)準(zhǔn)確的計(jì)算task的負(fù)載。如果支持fast_switch的功能,可以在中斷上下文直接進(jìn)行調(diào)頻。

功耗:performance > ondemand > conservative >powersave

2.3 主要數(shù)據(jù)結(jié)構(gòu)

04691564-6bf9-11ee-939d-92fbcf53809c.png

image.png

2.3.1 驅(qū)動相關(guān)cpufreq_driver

在include/linux/cpufreq.h中,用于描述cpufreq的驅(qū)動,是驅(qū)動工程師最關(guān)注的結(jié)構(gòu)。如下默認(rèn)值:

static struct cpufreq_driver dt_cpufreq_driver = {
        .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
        .verify = cpufreq_generic_frequency_table_verify,
        .target_index = set_target,
        .get = cpufreq_generic_get,
        .init = cpufreq_init,
        .exit = cpufreq_exit,
        .ready = cpufreq_ready,
        .name = "cpufreq-dt",
        .attr = cpufreq_dt_attr,
        .suspend = cpufreq_generic_suspend,
};
  • name,該driver的名字,需要唯一,因?yàn)閏pufreq framework允許同時(shí)注冊多個(gè)driver,用戶可以根據(jù)實(shí)際情況選擇使用哪個(gè)driver。driver的標(biāo)識,就是name。
  • flags,一些flag,具體會在后續(xù)的文章中介紹。 init,driver的入口,由cpufreq core在設(shè)備枚舉的時(shí)候調(diào)用,driver需要根據(jù)硬件情況,填充policy的內(nèi)容。
  • verify,驗(yàn)證policy中的內(nèi)容是否符合硬件要求。它和init接口都是必須實(shí)現(xiàn)的接口。
  • setpolicy,driver需要提供這個(gè)接口,用于設(shè)置CPU core動態(tài)頻率調(diào)整的范圍(即policy)。
  • target、target_index,driver需要實(shí)現(xiàn)這兩個(gè)接口中的一個(gè)(target為舊接口,不推薦使用),用于設(shè)置CPU core為指定頻率(同時(shí)修改為對應(yīng)的電壓)。target_index()接口底層真正用于設(shè)置cpu為指定頻率的接口(同時(shí)修改為對應(yīng)的電壓)

有關(guān)struct cpufreq_driver的API包括:

   1: int cpufreq_register_driver(struct cpufreq_driver *driver_data);
   2: int cpufreq_unregister_driver(struct cpufreq_driver *driver_data);
   3:  
   4: const char *cpufreq_get_current_driver(void);
   5: void *cpufreq_get_driver_data(void);

分別為driver的注冊、注銷。獲取當(dāng)前所使用的driver名稱,以及該driver的私有數(shù)據(jù)結(jié)構(gòu)(driver_data字段)。

2.3.2 策略相關(guān)cpufreq_policy

linux使用cpufreq policy來抽象cpu設(shè)備的調(diào)頻調(diào)壓功能,用于描述不同的policy,包含頻率表、cpuinfo等各種信息,并且每個(gè)policy都會對應(yīng)某個(gè)具體的governor。

min/max frequency,調(diào)頻范圍,對于可以自動調(diào)頻的CPU而言,只需要這兩個(gè)參數(shù)就夠了。 current frequency和governor,對于不能自動調(diào)頻的CPU,需要governor設(shè)置具體的頻率值。下面介紹一下governor。 struct cpufreq_policy不會直接對外提供API。

2.3.3 管理策略cpufreq_governor

不同policy的管理策略,根據(jù)使用場景的不同,會有不同的調(diào)頻調(diào)壓策略。如下一個(gè)governor的默認(rèn)值:

static struct cpufreq_governor cpufreq_gov_userspace = {
        .name                 = "userspace",
        .init                 = cpufreq_userspace_policy_init,
        .exit                 = cpufreq_userspace_policy_exit,
        .start                = cpufreq_userspace_policy_start,
        .stop                 = cpufreq_userspace_policy_stop,
        .limits               = cpufreq_userspace_policy_limits,
        .store_setspeed       = cpufreq_set,
        .show_setspeed        = show_speed,
        .owner                = THIS_MODULE,
};
  • name,該governor的名稱。
  • governor,用于governor狀態(tài)切換的回調(diào)函數(shù)。
  • show_setspeed、store_setspeed,用于提供sysfs “setspeed” attribute文件的回調(diào)函數(shù)。
  • max_transition_latency,該governor所能容忍的最大頻率切換延遲。
  • cpufreq governors主要向具體的governor模塊提供governor的注冊和注銷接口

2.2 初始化流程

04844aaa-6bf9-11ee-939d-92fbcf53809c.png

image.png

2.2.1 governor注冊

cpufreq_register_governor 如果policy中有默認(rèn)的governor,則調(diào)用find_governor,在列表中尋找。cpufreq core定義了一個(gè)全局鏈表變量:cpufreq_governor_list,注冊函數(shù)首先根據(jù)governor的名稱,通過__find_governor()函數(shù)查找該governor是否已經(jīng)被注冊過,如果沒有被注冊過,則把代表該governor的結(jié)構(gòu)體添加到cpufreq_governor_list鏈表中。

系統(tǒng)中可以同時(shí)存在多個(gè)governor,policy通過cpufreq_policy->governor指針和某個(gè)governor相關(guān)聯(lián)。要想一個(gè)governor能夠被使用,首先要把該governor注冊到cpufreq framework中。例如:

static int __init cpufreq_gov_userspace_init(void)
{
        return cpufreq_register_governor(&cpufreq_gov_userspace);
}

#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE
struct cpufreq_governor *cpufreq_default_governor(void)
{
        return &cpufreq_gov_userspace;
}

fs_initcall(cpufreq_gov_userspace_init);

注冊的gov定義為:

static struct cpufreq_governor cpufreq_gov_userspace = {
        .name                = "userspace",
        .init                = cpufreq_userspace_policy_init,
        .exit                = cpufreq_userspace_policy_exit,
        .start                = cpufreq_userspace_policy_start,
        .stop                = cpufreq_userspace_policy_stop,
        .limits                = cpufreq_userspace_policy_limits,
        .store_setspeed        = cpufreq_set,
        .show_setspeed        = show_speed,
        .owner                = THIS_MODULE,
};

2.2.2 cpufreq驅(qū)動發(fā)現(xiàn)注冊

dt_cpufreq_probe()在drivers/cpufreq/cpufreq-dt.c中 系統(tǒng)啟動的時(shí)候平臺驅(qū)動dt_cpufreq_platdrv,會執(zhí)行prob函數(shù)dt_cpufreq_probe()

static struct cpufreq_driver dt_cpufreq_driver = {
        .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
        .verify = cpufreq_generic_frequency_table_verify,
        .target_index = set_target,
        .get = cpufreq_generic_get,
        .init = cpufreq_init,
        .exit = cpufreq_exit,
        .ready = cpufreq_ready,
        .name = "cpufreq-dt",
        .attr = cpufreq_dt_attr,
        .suspend = cpufreq_generic_suspend,
};

cpufreq_register_driver(&dt_cpufreq_driver),在drivers/cpufreq/cpufreq.c中 cpufreq_register_driver為cpufreqdriver注冊的入口,驅(qū)動程序通過調(diào)用該函數(shù)進(jìn)行初始化,并傳入相關(guān)的struct cpufreq_driver,cpufreq_register_driver會調(diào)用subsys_interface_register,入?yún)椋?/p>

static struct subsys_interface cpufreq_interface = {
        .name                = "cpufreq",
        .subsys                = &cpu_subsys,
        .add_dev        = cpufreq_add_dev,
        .remove_dev        = cpufreq_remove_dev,
};

最終執(zhí)行回調(diào)函數(shù)cpufreq_add_dev。

2.2.3 CPU subsys注冊

kernel將cpu都抽象成device,并抽象出cpu_subsys bus,所有cpu都掛載在這個(gè)bus下。每個(gè)bus都包含一個(gè)struct subsys_private結(jié)構(gòu)的成員p,該結(jié)構(gòu)包括一個(gè)interface list成員interfaces和設(shè)備鏈表klist_devices。interface list上的一個(gè)interface通常用于抽象bus下的一個(gè)功能。

cpufreq是CPU device的一類特定功能,也就被抽象為一個(gè)subsys interface(kernel使用struct subsys_interface結(jié)構(gòu)表示)即變量cpufreq_interface,, 掛載在interface list下。cpufreq作為一個(gè)功能掛載到cpu subsys下后會對相應(yīng)的所有設(shè)備即cpu執(zhí)行interface.add_dev()操作,表示對subsys_private支持的設(shè)備都添加這個(gè)功能,在添加這個(gè)功能時(shí)為每個(gè)cpu設(shè)備生成具體的policy結(jié)構(gòu),即struct cpufreq_policy.

04844aaa-6bf9-11ee-939d-92fbcf53809c.png

上圖涉及cpu初始化,在系統(tǒng)啟動的時(shí)候:

//drivers/base/cpu.c
register_cpu
    cpu->dev.bus = &cpu_subsys;
    device_register
        device_add
            bus_add_device
                error = device_add_groups(dev, bus->dev_groups);//向總線注冊設(shè)備
                klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);//向subsys_private

回到cpureq流程中,subsys_interface_register(),在drivers/base/bus.c中

        mutex_lock(&subsys->p->mutex);
        list_add_tail(&sif->node, &subsys->p->interfaces);
        if (sif->add_dev) {
                subsys_dev_iter_init(&iter, subsys, NULL, NULL);
                while ((dev = subsys_dev_iter_next(&iter)))
                        sif->add_dev(dev, sif);
                subsys_dev_iter_exit(&iter);
        }
        mutex_unlock(&subsys->p->mutex);

這里可以看到對于多核,都執(zhí)行了cpufreq_add_dev,會為cpu device創(chuàng)建struct cpufreq_policy結(jié)構(gòu)。 cpufreq_add_dev(),在drivers/cpufreq/cpufreq.c中

static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
{
        struct cpufreq_policy *policy;
        unsigned cpu = dev->id;
        int ret;

        if (cpu_online(cpu)) {
                ret = cpufreq_online(cpu);
                if (ret)
                        return ret;
        }

        /* Create sysfs link on CPU registration */
        policy = per_cpu(cpufreq_cpu_data, cpu);
        if (policy)
                add_cpu_dev_symlink(policy, cpu);

        return 0;
}

3.2.4 CPU上線設(shè)置

cpufreq_online(cpu)在drivers/cpufreq/cpufreq.c中

?cpufreq_policy_alloc()創(chuàng)建policy節(jié)點(diǎn)/sys/devices/system/cpu/cpufreq/*
?cpufreq_driver->init(policy)指向cpufreq_init()
?cpufreq_add_dev_interface()創(chuàng)建sysfs節(jié)點(diǎn)的一些可選屬性
?cpufreq_init_policy()初始化policy的governor

cpufreq_driver->init對應(yīng)cpufreq_init()函數(shù) 這個(gè)函數(shù)會解析cpu信息得到cpu_dev、cpu_clk、opp_table等

        cpu_dev = get_cpu_device(policy->cpu);
        cpu_clk = clk_get(cpu_dev, NULL);
        ret = dev_pm_opp_of_get_sharing_cpus(cpu_dev, policy->cpus);//多CPU共享
        opp_table = dev_pm_opp_set_regulators(cpu_dev, &name, 1);
        
        priv->reg_name = name;
        priv->opp_table = opp_table;
        
        priv->cpu_dev = cpu_dev;
        policy->driver_data = priv;
        policy->clk = cpu_clk;
        
        policy->suspend_freq = dev_pm_opp_get_suspend_opp_freq(cpu_dev) / 1000;
        
        ret = cpufreq_table_validate_and_show(policy, freq_table);

cpufreq_table_validate_and_show()里面找到CPU支持的最大和最小頻率

int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
                                    struct cpufreq_frequency_table *table)
{
        struct cpufreq_frequency_table *pos;
        unsigned int min_freq = ~0;
        unsigned int max_freq = 0;
        unsigned int freq;

        cpufreq_for_each_valid_entry(pos, table) {
                freq = pos->frequency;

                if (!cpufreq_boost_enabled()
                    && (pos->flags & CPUFREQ_BOOST_FREQ))
                        continue;

                pr_debug("table entry %u: %u kHz
", (int)(pos - table), freq);
                if (freq < min_freq)
                        min_freq = freq;
                if (freq > max_freq)
                        max_freq = freq;
        }

        policy->min = policy->cpuinfo.min_freq = min_freq;
        policy->max = policy->cpuinfo.max_freq = max_freq;

        if (policy->min == ~0)
                return -EINVAL;
        else
                return 0;
}

設(shè)置policy的時(shí)候,會讀取cpu的頻率表,賦值給policy->min和policy->max。另外各種governor也用到frequency table。

frequency table是CPU core可以正確運(yùn)行的一組頻率/電壓組合,之所以存在的一個(gè)思考點(diǎn)是:table是頻率和電壓之間的一個(gè)一一對應(yīng)的組合,因此cpufreq framework只需要關(guān)心頻率,所有的策略都稱做“調(diào)頻”策略。而cpufreq driver可以在“調(diào)頻”的同時(shí),通過table取出和頻率對應(yīng)的電壓,進(jìn)行修改CPU core電壓,實(shí)現(xiàn)“調(diào)壓”的功能,這簡化了設(shè)計(jì)。 例如在DTS中:

04c04078-6bf9-11ee-939d-92fbcf53809c.png

04d78fda-6bf9-11ee-939d-92fbcf53809c.png

2.2.5 策略初始化

cpufreq_init_policy(),drivers/cpufreq/cpufreq.c在 使用默認(rèn)策略初始化policy

        /* Update governor of new_policy to the governor used before hotplug */
        gov = find_governor(policy->last_governor);
        if (gov) {
                pr_info("dddd Restoring governor %s for cpu %d
",
                                policy->governor->name, policy->cpu);
        } else {
                gov = cpufreq_default_governor();
                if (!gov)
                        return -ENODATA;
        }

        new_policy.governor = gov;
        
        
        /* set default policy */
        return cpufreq_set_policy(policy, &new_policy);

如果policy中有默認(rèn)的governor,則調(diào)用find_governor,在列表中尋找。cpufreq core定義了一個(gè)全局鏈表變量:cpufreq_governor_list,注冊函數(shù)首先根據(jù)governor的名稱,通過__find_governor()函數(shù)查找該governor是否已經(jīng)被注冊過,如果沒有被注冊過,則把代表該governor的結(jié)構(gòu)體添加到cpufreq_governor_list鏈表中。

系統(tǒng)中可以同時(shí)存在多個(gè)governor,policy通過cpufreq_policy->governor指針和某個(gè)governor相關(guān)聯(lián)。要想一個(gè)governor能夠被使用,首先要把該governor注冊到cpufreq framework中。例如:

fs_initcall(cpufreq_gov_performance_init);
static int __init cpufreq_gov_performance_init(void)
{
        return cpufreq_register_governor(&cpufreq_gov_performance);
}

這里我們默認(rèn)使用default

#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE
struct cpufreq_governor *cpufreq_default_governor(void)
{
        return &cpufreq_gov_performance;
}
#endif
static struct cpufreq_governor cpufreq_gov_performance = {
        .name                = "performance",
        .owner                = THIS_MODULE,
        .limits                = cpufreq_gov_performance_limits,
};

最后調(diào)用cpufreq_set_policy(policy, &new_policy);去設(shè)置policy

2.2.6 governor初始化

cpufreq_set_policy(),在drivers/cpufreq/cpufreq.c中

cpufreq_init_governor->policy->governor->init(policy);

cpufreq_start_governor->policy->governor->start(policy);

在governor初始化和啟動的時(shí)候會發(fā)生:CPUFreq通知 CPUFreq子系統(tǒng)會發(fā)出通知的情況有兩種:CPUFreq的策略變化或者CPU運(yùn)行頻率變化。

在策略變化的過程中,例如cpufreq_set_policy函數(shù)中,會發(fā)送3次通知:

  1. CPUFREQ_ADJUST:所有注冊的notifier可以根據(jù)硬件或者溫度的情況去修改范圍(即policy->min和policy->max);
  2. CPUFREQ_INCOMPATIBLE:除非前面的策略設(shè)定可能會導(dǎo)致硬件出錯,否則被注冊的notifier不能改變范圍等設(shè)定;
  3. CPUFREQ_NOTIFY:所有注冊的notifier都會被告知新的策略已經(jīng)被設(shè)置。

在頻率變化的過程中,例如__cpufreq_notify_transition函數(shù)中,會發(fā)送2次通知:

  1. CPUFREQ_PRECHANGE:準(zhǔn)備進(jìn)行頻率變更;
  2. CPUFREQ_POSTCHANGE:已經(jīng)完成頻率變更。
        /* notification of the new policy */
        blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
                        CPUFREQ_NOTIFY, new_policy);
cpufreq_policy_notifier_list

cpufreq_register_notifier()函數(shù)注冊這個(gè)鏈表

3.2.6 設(shè)置熱插拔計(jì)算機(jī)狀態(tài)的回調(diào)函數(shù)

cpuhp_setup_state_nocalls_cpuslocked(): 參數(shù)說明:

__cpuhp_setup_state_cpuslocked(
CPUHP_AP_ONLINE_DYN, "cpufreq:online", false, 
cpuhp_cpufreq_online,cpuhp_cpufreq_offline, false);

* __cpuhp_setup_state_cpuuslocked—設(shè)置熱插拔計(jì)算機(jī)狀態(tài)的回調(diào)函數(shù)
* @state:要設(shè)置的狀態(tài)
* @invoke:如果為true,啟動函數(shù)將被調(diào)用于cpu,cpu state >= @state
* @startup:啟動回調(diào)函數(shù)
* @teardown: teardown回調(diào)函數(shù)
* @multi_instance:狀態(tài)是為多個(gè)實(shí)例設(shè)置的,然后添加。

2.3 userspace governor

04fb295e-6bf9-11ee-939d-92fbcf53809c.png

image.png

用戶空間監(jiān)控CPUFreq流程圖

### 2.3.1 用戶接口說明 userspace governor是一種用戶可以自己手動調(diào)整自己cpu頻率的governor,即在linux目錄下:/sys/devices/system/cpu/cpu0/cpufreq/,有一個(gè)參數(shù)scaling_setspeed,是這個(gè)governor轉(zhuǎn)有的,其他governor是不能對其進(jìn)行讀寫操作的,只有這個(gè)governor才能這樣做。

0507e694-6bf9-11ee-939d-92fbcf53809c.png

對應(yīng)底層有處理函數(shù),設(shè)置也有處理函數(shù)。

2.3.2 配置說明

默認(rèn)是Performance的策略,我們可以通過make menuconfig選擇,如下:

051fab80-6bf9-11ee-939d-92fbcf53809c.png

保存后在.config中可以看到

CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y

在代碼里面搜索這個(gè)宏,drivers/cpufreq/cpufreq_userspace.c中:

#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE
struct cpufreq_governor *cpufreq_default_governor(void)
{
        return &cpufreq_gov_userspace;
}

2.3.3 回調(diào)函數(shù)介紹

cpufreq_gov_userspace對應(yīng)

static struct cpufreq_governor cpufreq_gov_userspace = {
        .name                = "userspace",
        .init                = cpufreq_userspace_policy_init,
        .exit                = cpufreq_userspace_policy_exit,
        .start                = cpufreq_userspace_policy_start,
        .stop                = cpufreq_userspace_policy_stop,
        .limits                = cpufreq_userspace_policy_limits,
        .store_setspeed        = cpufreq_set,
        .show_setspeed        = show_speed,
        .owner                = THIS_MODULE,
};

可以看到其中有init函數(shù)和start函數(shù)。 cpufreq_userspace_policy_init 申請一個(gè)governor_data

policy->governor_data = setspeed;
cpufreq_userspace_policy_start 設(shè)置policy的cur頻率
*setspeed = policy->cur;
cpufreq_userspace_policy_limits 就是約束性檢查,如果超過max或者小于min進(jìn)行重新設(shè)定

show_setspeed   就是讀scaling_setspeed-當(dāng)前cpu頻率
store_setspeed   就是寫scaling_setspeed,可以用戶控制。改變cpu頻率的時(shí)候會調(diào)用如下函數(shù):

ret = __cpufreq_driver_target(policy, freq, CPUFREQ_RELATION_L);

2.3.4 調(diào)頻調(diào)壓流程

例如輸入命令:

echo 700000 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed

__cpufreq_driver_target->__target_index->cpufreq_driver->target_index

static int set_target(struct cpufreq_policy *policy, unsigned int index)
{
        struct private_data *priv = policy->driver_data;

        return dev_pm_opp_set_rate(priv->cpu_dev,
                                   policy->freq_table[index].frequency * 1000);
}

dev_pm_opp_set_rate()函數(shù)在drivers/base/power/opp/core.c中定義 找到opp_table進(jìn)行調(diào)頻調(diào)壓,opp_table的名字是/cpus/cpu0_opp_table

        opp_table = _find_opp_table(dev);
        
        clk = opp_table->clk;
        freq = clk_round_rate(clk, target_freq);
        if ((long)freq <= 0)
                freq = target_freq;

        old_freq = clk_get_rate(clk);
        
  ret = _generic_set_opp_clk_only(dev, clk, old_freq, freq);

clk_set_rate(clk, freq);在drivers/clk/clk.c中定義

        ret = clk_core_set_rate_nolock(clk->core, rate);

clk的名字是,rate是要設(shè)置的頻率

        /* change the rates */
        clk_change_rate(top);

top的名字為cpu_core0_mux_clk,節(jié)點(diǎn)父子關(guān)系為:

armpll1_912m_cpu_clk->cpu_core0_mux_clk->cpu_core0_div_clk->cpu_core0_clk
首先設(shè)置armpll1_912m_cpu_clk
clk_change_rate()
core->ops->set_parent(core->hw, core->new_parent_index);
set_parent對應(yīng)clk_mux_set_parent()函數(shù)在drivers/clk/clk-mux.c中

static int clk_mux_set_parent(struct clk_hw *hw, u8 index) { struct clk_mux *mux = to_clk_mux(hw);

    val = clk_readl(mux->reg);
    val &= ~(mux->mask << mux->shift);

    val |= index << mux->shift;
    clk_writel(val, mux->reg);
mux->reg值是0x42000020,index是4,clk_readl出來是默認(rèn)值5,需要寫入為4
 

cpu_core0_div_clk進(jìn)行了頻率設(shè)置
clk_change_rate
core->ops->set_rate(core->hw, core->new_rate, best_parent_rate);

const struct clk_ops clk_divider_ops = { .recalc_rate = clk_divider_recalc_rate, .round_rate = clk_divider_round_rate, .set_rate = clk_divider_set_rate, };

clk_divider_set_rate

static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { value = divider_get_val(rate, parent_rate, divider->table, divider->width, divider->flags);

    if (divider->flags & CLK_DIVIDER_HIWORD_MASK) {
            val = div_mask(divider->width) << (divider->shift + 16);
    } else {
            val = clk_readl(divider->reg);
            val &= ~(div_mask(divider->width) << divider->shift);
    }

    val |= (u32)value << divider->shift;
    clk_writel(val, divider->reg);

其中divider->reg為0x42000020,value=0 divider->shift的值為8 clk_readl(divider->reg);讀出來值為4已經(jīng)是要設(shè)置的值了。

2.4 其他governor

2.4.1 ondemand governor

ondemand governor,最終是通過調(diào)頻接口od_dbs_update實(shí)現(xiàn)計(jì)算負(fù)載進(jìn)行調(diào)頻的。

//drivers/cpufreq/cpufreq_ondemand.c
od_dbs_update
        od_update

static void od_update(struct cpufreq_policy *policy)
{
        unsigned int load = dbs_update(policy);//負(fù)載(百分比)(1)
        
        /* Check for frequency increase */
        if (load > dbs_data->up_threshold) {//(2)如果負(fù)載大于策略設(shè)置的閾值,則直接切換到最大頻率
                /* If switching to max speed, apply sampling_down_factor */
                if (policy->cur < policy->max)
                        policy_dbs->rate_mult = dbs_data->sampling_down_factor;
                dbs_freq_increase(policy, policy->max);
        } else {
                /* Calculate the next frequency proportional to load */
                unsigned int freq_next, min_f, max_f;

                min_f = policy->cpuinfo.min_freq;
                max_f = policy->cpuinfo.max_freq;
                freq_next = min_f + load * (max_f - min_f) / 100;
                                //(3)按照負(fù)載百分比,在頻率范圍內(nèi)選擇合適頻率

                /* No longer fully busy, reset rate_mult */
                policy_dbs->rate_mult = 1;

                if (od_tuners->powersave_bias)//(4)
                        freq_next = od_ops.powersave_bias_target(policy,
                                                                 freq_next,
                                                                 CPUFREQ_RELATION_L);

                __cpufreq_driver_target(policy, freq_next, CPUFREQ_RELATION_C);//設(shè)置頻率
        }

(1)計(jì)算負(fù)載函數(shù): od_dbs_update()核心方法是: 當(dāng)前負(fù)載load = 100 * (time_elapsed - idle_time) / time_elapsed idle_time = 本次idle時(shí)間 - 上次idle時(shí)間 time_elapsed = 本次總運(yùn)行時(shí)間 - 上次總運(yùn)行時(shí)間 該函數(shù)返回使用此policy的各個(gè)cpu中的最大負(fù)載。

(2)當(dāng)最大負(fù)載大于策略設(shè)置的最大閾值時(shí),調(diào)用dbs_freq_increase()將頻率設(shè)置在最大頻率。

(3)按照負(fù)載百分比設(shè)置合適頻率 freq_next = min_f + load * (max_f - min_f) / 100;

(4) 表明我們?yōu)榱诉M(jìn)一步節(jié)省電力,我們希望在計(jì)算出來的新頻率的基礎(chǔ)上,再乘以一個(gè)powersave_bias設(shè)定的百分比,作為真正的運(yùn)行頻率,powersave_bias的值從0-1000,每一步代表0.1%

2.4.2 schedutil governor

053eb78c-6bf9-11ee-939d-92fbcf53809c.png

不同的governor的觸發(fā)調(diào)頻調(diào)壓流程不一樣,這里以schedutil governor為例。 CFS負(fù)載變化的時(shí)候或者RT、DL任務(wù)狀態(tài)更新的時(shí)候,就會啟動調(diào)頻。這幾個(gè)scheduler類會調(diào)用cpufreq_update_util函數(shù)(前面注冊進(jìn)來的hook函數(shù))觸發(fā)schedutil工作。每個(gè)cpu最終會回調(diào)到sugov_upate_shared或者sugov_upate_single函數(shù)中的一個(gè)。 由于是從scheduler里直接調(diào)用下來的,最終執(zhí)行調(diào)頻切換時(shí),無論是快速路徑觸發(fā)的簡單寫寄存器,還是慢速路徑觸發(fā)的kthread都不會占用過多時(shí)間或者調(diào)度開銷。

2.4.3 Interactive governor

Interactive 與Conservative相對,快速提升頻率,緩慢降低頻率

  • 優(yōu)點(diǎn): 比Ondemand稍強(qiáng)的性能,較快的響應(yīng)速度
  • 缺點(diǎn): 在不需要時(shí)仍然維持較高的頻率,比Ondemand耗電 Interactive X 基于Interactive改進(jìn),區(qū)分開關(guān)屏狀態(tài)情景
  • 優(yōu)點(diǎn):比Interactive省電
  • 缺點(diǎn):穩(wěn)定性不如Interactive 代碼位置:drivers/cpufreq/cpufreq_interactive.c 首先需要定義一個(gè)cpufreq_governor類型的結(jié)構(gòu)體用來描述interactive governor.
static struct interactive_governor interactive_gov = {
        .gov = {
                .name                        = "interactive",
                .max_transition_latency        = TRANSITION_LATENCY_LIMIT,
                .owner                        = THIS_MODULE,
                .init                        = cpufreq_interactive_init,
                .exit                        = cpufreq_interactive_exit,
                .start                        = cpufreq_interactive_start,
                .stop                        = cpufreq_interactive_stop,
                .limits                        = cpufreq_interactive_limits,
        }
};

后記

本節(jié)代碼有點(diǎn)多,不是調(diào)試這個(gè)可以不用關(guān)注代碼,想深入學(xué)習(xí)還是需要運(yùn)行起來代碼打點(diǎn)log比較好。


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

    關(guān)注

    115

    文章

    6183

    瀏覽量

    144541
  • cpu
    cpu
    +關(guān)注

    關(guān)注

    68

    文章

    10870

    瀏覽量

    211901
  • 電壓頻率
    +關(guān)注

    關(guān)注

    0

    文章

    9

    瀏覽量

    8075

原文標(biāo)題:電源管理入門-6 CPUFreq

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

收藏 人收藏

    評論

    相關(guān)推薦

    電源管理入門-關(guān)機(jī)重啟基礎(chǔ)知識詳解

    當(dāng)我們接觸電源管理的時(shí)候,最簡單的流程就是關(guān)機(jī)重啟,但是仔細(xì)分析其涉及的所有源代碼就會發(fā)現(xiàn),關(guān)機(jī)重啟雖然簡單
    發(fā)表于 09-19 11:41 ?2643次閱讀
    <b class='flag-5'>電源</b><b class='flag-5'>管理入門</b>-關(guān)機(jī)重啟基礎(chǔ)知識詳解

    電源管理入門-什么是OPP?怎么用?

    在SoC內(nèi),某些domain可以運(yùn)行在較低的頻率和電壓下,而其他domain可以運(yùn)行在較高的頻率和電壓下,某個(gè)domain所支持的對的集合被稱為Operating Performance Point,縮寫OPP。
    的頭像 發(fā)表于 11-16 16:49 ?2021次閱讀
    <b class='flag-5'>電源</b><b class='flag-5'>管理入門</b>-什么是OPP?怎么用?

    電源管理入門-Regulator驅(qū)動是什么?Regulator的作用是什么?

    Regulator是Linux系統(tǒng)中電源管理的基礎(chǔ)設(shè)施之一,用于穩(wěn)壓電源管理,是各種驅(qū)動子系統(tǒng)中設(shè)置 電壓的標(biāo)準(zhǔn)接口。
    的頭像 發(fā)表于 11-16 16:51 ?1w次閱讀
    <b class='flag-5'>電源</b><b class='flag-5'>管理入門</b>-Regulator驅(qū)動是什么?Regulator的作用是什么?

    電源管理入門:Thermal熱管理

    管理指的是在電子設(shè)備或系統(tǒng)中通過各種方式控制其溫度來保證其正常工作或延長壽命的過程。其中包括散熱設(shè)計(jì)、溫度監(jiān)測、溫度控制等方面。熱管理的重要性越來越凸顯,尤其在高性能計(jì)算、人工智能等領(lǐng)域的應(yīng)用中更為重要。
    的頭像 發(fā)表于 11-29 10:09 ?4943次閱讀
    <b class='flag-5'>電源</b><b class='flag-5'>管理入門</b>:Thermal熱<b class='flag-5'>管理</b>

    電源管理入門:驅(qū)動Runtime PM管理

    Runtime PM管理也就是設(shè)備驅(qū)動里面的電源管理,即設(shè)備驅(qū)動結(jié)構(gòu)體里面的struct dev_pm_ops,只控制設(shè)備自己的電源。這樣可以在設(shè)備不需要工作的時(shí)候可以進(jìn)入到低功耗狀態(tài)
    的頭像 發(fā)表于 11-29 10:13 ?3157次閱讀
    <b class='flag-5'>電源</b><b class='flag-5'>管理入門</b>:驅(qū)動Runtime PM<b class='flag-5'>管理</b>

    電源管理入門:Power supply子系統(tǒng)

    對于便攜設(shè)備來說,電源管理更加的重要,因?yàn)殡姵仉娏坑邢?,容易電量焦慮。除了省電管理外,還需要對電池進(jìn)行監(jiān)控管理和充放電管理,這樣保護(hù)好電池和
    的頭像 發(fā)表于 11-29 10:15 ?4252次閱讀
    <b class='flag-5'>電源</b><b class='flag-5'>管理入門</b>:Power supply子系統(tǒng)

    電源管理入門:Power Domain管理

    SoC中通常有很多IP,按邏輯可以把幾個(gè)相關(guān)功能的IP劃為一個(gè)電源域。一個(gè)電源域內(nèi)的IP,通常按相同的方式由同一個(gè)硬件模塊PMIC供電,電壓一樣并且電源管理例如休眠喚醒一致。
    的頭像 發(fā)表于 11-29 10:16 ?3458次閱讀
    <b class='flag-5'>電源</b><b class='flag-5'>管理入門</b>:Power Domain<b class='flag-5'>管理</b>

    電源管理入門-芯片設(shè)計(jì)中的電源管理介紹

    SCP直接控制SoC的電源和時(shí)鐘,而AP通過硬件和軟件接口協(xié)同管理。
    的頭像 發(fā)表于 12-06 09:16 ?3324次閱讀
    <b class='flag-5'>電源</b><b class='flag-5'>管理入門</b>-芯片設(shè)計(jì)中的<b class='flag-5'>電源</b><b class='flag-5'>管理</b>介紹

    電源管理入門:Hypervisor中的電源管理

    很多時(shí)候聽說Hypervisor,但是對底層軟件技術(shù)不了解的人感覺挺神秘。本篇文章簡單介紹下Hypervisor的基本概念,另外介紹下電源管理在Hypervisor之上多OS間怎么應(yīng)用。
    的頭像 發(fā)表于 12-06 09:27 ?1467次閱讀
    <b class='flag-5'>電源</b><b class='flag-5'>管理入門</b>:Hypervisor中的<b class='flag-5'>電源</b><b class='flag-5'>管理</b>

    cpufreq的5種模式介紹

    本文檔以iMX6UL為例,簡單介紹cpufreq的5種模式。
    發(fā)表于 01-27 06:27

    IMX8MM CPUFREQ無法工作怎么解決?

    我們使用了compulab的imx8m-mini的模塊(kernel version: 5.10.35),cpufreq模塊無法正常工作,已檢查內(nèi)核配置已啟用
    發(fā)表于 04-20 07:39

    防火墻原理入門

    防火墻原理入門 防火墻能增強(qiáng)機(jī)構(gòu)內(nèi)部網(wǎng)絡(luò)的安全性。防火墻系統(tǒng)決定了哪些內(nèi)部服務(wù)可以被外界訪問;外界的哪些人可以訪問內(nèi)部的服務(wù)以及哪
    發(fā)表于 08-01 10:21 ?1024次閱讀
    防火墻原<b class='flag-5'>理入門</b>

    數(shù)字信號處理入門指南

    數(shù)字信號處理入門指南什么是DSP? 數(shù)字信號處理器(DSP)采集已被數(shù)字化的現(xiàn)實(shí)世界的聲音、音頻、視頻、溫度、壓力或位置等信號,并從數(shù)學(xué)的角度對其進(jìn)
    發(fā)表于 09-15 08:55 ?1347次閱讀
    數(shù)字信號處<b class='flag-5'>理入門</b>指南

    單片機(jī)原理及應(yīng)用ppt入門課程

    微機(jī)原理入門課程ppt,比較簡單,適合新手。。
    發(fā)表于 01-12 17:52 ?0次下載

    電源管理入門-7 DevFreq

    復(fù)雜SoC由多個(gè)子模塊協(xié)同工作組成,在運(yùn)行中并非SoC中的所有模塊都需要始終保持最高性能。為方便起見,將SoC中的子模塊分組為域,從而允許某些域以較低的電壓和頻率運(yùn)行
    的頭像 發(fā)表于 10-16 17:06 ?1504次閱讀
    <b class='flag-5'>電源</b><b class='flag-5'>管理入門</b>-7 DevFreq