diff --git a/Kconfig b/Kconfig index 9ea7ff644881..1378d255279b 100644 --- a/Kconfig +++ b/Kconfig @@ -127,6 +127,13 @@ config INTERRUPT_LEVEL_5 rsource "src/Kconfig" +config IDC_TASK_BUDGET + int "Cycles budget for IDC task" + depends on MULTICORE + default 8000 + help + Cycles budget for IDC task per systick (see config SYSTICK). + choice prompt "Optimization" default OPTIMIZE_FOR_PERFORMANCE diff --git a/src/drivers/intel/Kconfig b/src/drivers/intel/Kconfig index aad635124ae9..ffbb172130bd 100644 --- a/src/drivers/intel/Kconfig +++ b/src/drivers/intel/Kconfig @@ -47,6 +47,13 @@ config INTEL_DMIC Select this to enable Intel DMIC driver. The DMIC driver provides as DAI the SoC direct attach digital microphones interface. +config INTEL_CAVS_IPC_TASK_BUDGET + int "Intel cycles budget for IPC task" + depends on CAVS + default 8000 + help + Cycles budget for IPC task per systick (see config SYSTICK). + if INTEL_DMIC choice diff --git a/src/drivers/intel/cavs/ipc.c b/src/drivers/intel/cavs/ipc.c index 7d335fce6a73..a689bcfefd7f 100644 --- a/src/drivers/intel/cavs/ipc.c +++ b/src/drivers/intel/cavs/ipc.c @@ -296,8 +296,10 @@ int platform_ipc_init(struct ipc *ipc) ipc_set_drvdata(ipc, NULL); /* schedule */ - schedule_task_init_edf(&ipc->ipc_task, SOF_UUID(ipc_task_uuid), - &ipc_task_ops, ipc, 0, 0); + schedule_task_init_edf_with_budget(&ipc->ipc_task, + SOF_UUID(ipc_task_uuid), + &ipc_task_ops, ipc, 0, 0, + CONFIG_INTEL_CAVS_IPC_TASK_BUDGET); /* configure interrupt */ irq = interrupt_get_irq(PLATFORM_IPC_INTERRUPT, diff --git a/src/idc/idc.c b/src/idc/idc.c index 5f65662e2e5d..363b58af1113 100644 --- a/src/idc/idc.c +++ b/src/idc/idc.c @@ -325,8 +325,10 @@ int idc_init(void) (*idc)->payload = cache_to_uncache((struct idc_payload *)payload); /* process task */ - schedule_task_init_edf(&(*idc)->idc_task, SOF_UUID(idc_cmd_task_uuid), - &ops, *idc, cpu_get_id(), 0); + schedule_task_init_edf_with_budget(&(*idc)->idc_task, + SOF_UUID(idc_cmd_task_uuid), + &ops, *idc, cpu_get_id(), 0, + CONFIG_IDC_TASK_BUDGET); return platform_idc_init(); } diff --git a/src/include/sof/schedule/edf_schedule.h b/src/include/sof/schedule/edf_schedule.h index 5bf552a06969..04dfdd7be663 100644 --- a/src/include/sof/schedule/edf_schedule.h +++ b/src/include/sof/schedule/edf_schedule.h @@ -12,6 +12,7 @@ #include #include #include +#include #define edf_sch_set_pdata(task, data) \ (task->priv_data = data) @@ -20,6 +21,10 @@ struct edf_task_pdata { void *ctx; + uint32_t budget; /**< cycles per systick */ + uint64_t last_systick; /**< last systick in which budget + * was granted to task + */ }; int scheduler_init_edf(void); @@ -28,4 +33,9 @@ int schedule_task_init_edf(struct task *task, uint32_t uid, const struct task_ops *ops, void *data, uint16_t core, uint32_t flags); +int schedule_task_init_edf_with_budget(struct task *task, uint32_t uid, + const struct task_ops *ops, + void *data, uint16_t core, + uint32_t flags, uint32_t cycles_budget); + #endif /* __SOF_SCHEDULE_EDF_SCHEDULE_H__ */ diff --git a/src/schedule/edf_schedule.c b/src/schedule/edf_schedule.c index 41cd4fc0853e..fcb4f6cdeb47 100644 --- a/src/schedule/edf_schedule.c +++ b/src/schedule/edf_schedule.c @@ -55,6 +55,26 @@ static void schedule_edf_task_run(struct task *task, void *data) } } +static inline uint64_t edf_eval_task_deadline(struct task *task, + uint64_t systick) +{ + struct edf_task_pdata *edf_pdata = edf_sch_get_pdata(task); + + if (!edf_pdata->budget) + return task_get_deadline(task); + + /* check if task was already executed in current systick */ + if (edf_pdata->last_systick != systick) { + edf_pdata->last_systick = systick; + return task_get_deadline(task); + } + + /* when there are no other tasks to run, + * task can be executed even if it used systick's budget, + */ + return SOF_TASK_DEADLINE_ALMOST_IDLE; +} + static void edf_scheduler_run(void *data) { struct edf_schedule_data *edf_sch = data; @@ -64,9 +84,20 @@ static void edf_scheduler_run(void *data) struct task *task; uint64_t deadline; uint32_t flags; + uint64_t systick; tr_dbg(&edf_tr, "edf_scheduler_run()"); + timer_disable(cpu_timer_get(), edf_sch, cpu_get_id()); + + /* budget is given per systick, for example if systick is configured + * as 1ms and budget is 8000 cycles, then task will be able to use + * up to 8MCPS (or more if there are no other tasks) + */ + systick = platform_timer_get(timer_get()) / + (clock_ms_to_ticks(PLATFORM_DEFAULT_CLOCK, 1) + * (CONFIG_SYSTICK_PERIOD / 1000)); + irq_local_disable(flags); /* find next task to run */ @@ -77,7 +108,7 @@ static void edf_scheduler_run(void *data) task->state != SOF_TASK_STATE_RUNNING) continue; - deadline = task_get_deadline(task); + deadline = edf_eval_task_deadline(task, systick); if (deadline == SOF_TASK_DEADLINE_NOW) { /* task needs to be scheduled ASAP */ @@ -134,6 +165,15 @@ static int schedule_edf_task(void *data, struct task *task, uint64_t start, int schedule_task_init_edf(struct task *task, uint32_t uid, const struct task_ops *ops, void *data, uint16_t core, uint32_t flags) +{ + return schedule_task_init_edf_with_budget(task, uid, ops, data, + core, flags, 0); +} + +int schedule_task_init_edf_with_budget(struct task *task, uint32_t uid, + const struct task_ops *ops, + void *data, uint16_t core, + uint32_t flags, uint32_t cycles_budget) { struct edf_task_pdata *edf_pdata = NULL; int ret = 0; @@ -182,6 +222,7 @@ int schedule_task_init_edf(struct task *task, uint32_t uid, static int schedule_edf_task_running(void *data, struct task *task) { + struct edf_schedule_data *edf_sch = data; struct edf_task_pdata *edf_pdata = edf_sch_get_pdata(task); uint32_t flags; @@ -192,6 +233,13 @@ static int schedule_edf_task_running(void *data, struct task *task) task_context_set(edf_pdata->ctx); task->state = SOF_TASK_STATE_RUNNING; + /* if budget is not set then it's not budgeted task */ + if (edf_pdata->budget) { + timer_set(cpu_timer_get(), timer_get_system(cpu_timer_get()) + + edf_pdata->budget); + timer_enable(cpu_timer_get(), edf_sch, cpu_get_id()); + } + irq_local_enable(flags); return 0; @@ -278,6 +326,8 @@ int scheduler_init_edf(void) interrupt_register(edf_sch->irq, edf_scheduler_run, edf_sch); interrupt_enable(edf_sch->irq, edf_sch); + timer_register(cpu_timer_get(), edf_scheduler_run, edf_sch); + return 0; } @@ -288,6 +338,8 @@ static void scheduler_free_edf(void *data) irq_local_disable(flags); + timer_unregister(cpu_timer_get(), edf_sch); + /* disable and unregister EDF scheduler interrupt */ interrupt_disable(edf_sch->irq, edf_sch); interrupt_unregister(edf_sch->irq, edf_sch);