Work Queues

On CAVS platforms, the wall clock is used as a time source for multiple work queues (one work queue instance per active core).

Since enough comparators are not available, all instances register to a shared interrupt where one comparator is used to wake up all. The primary core re-programs the wall clock to the next wake event. Its work queue operates in primary mode. Work queues running on other cores are attached to the shared time source on CAVS SMP platforms; these are configured to the secondary mode. On other SMP platforms where multiple independent time sources are available, all queues can be configured in primary mode.

Synchronous SysTick on All Cores

The shared time source aligns work scheduling on all cores as all synchronously wake up on the same periodic event via a system tick timer, or SysTick.

SysTick periods are configurable. While no resolution is guaranteed, the default value of 1ms is acceptable for works that are typically scheduled on this system, specifically low latency works that are enabled and can be run on multiple cores in sync. For ultra low latency configurations, the SysTick period can be configured to a value of < 1ms.

An HD/A DMA running in circular buffer mode (@ dai) is already registered in the work queue with a specified timeout period of 1ms. Other DMAs can be switched from their individual interrupt sources (buffer completion) to work queues, thus making pipelines scheduling fully systick aligned.

In the case of more complex topologies, pipelines that start/terminate with a component other then dai can be also driven by work queues.

Note

Work queue primary/secondary mode vs. independent mode configurable by CONFIG @ compile time. The work queue min tick (1ms/0.33ms/1us) is configurable @ run-time so that the current mode is still fully supported.

scale max 800 height



skinparam rectangle {

   backgroundColor<<dai>> #6fccdd

   backgroundColor<<dma>> #f6ed80

   backgroundColor<<stream>> #d6d6de

   borderColor<<stream>> #d6d6de

   borderColor<<ppl>> #a1a1ca



   backgroundColor<<event>> #f05772

   stereotypeFontColor<<event>> #ffffff

   fontColor<<event>> #ffffff



   backgroundColor<<cpu>> #f0f0f0

}



together {

   ' ssp dais

   rectangle "ssp-dai #0" as ssp_0 <<dai>>

   rectangle "ssp-dai #1" as ssp_1 <<dai>>

   ' hda dais

   rectangle "hda-dai #0" as hda_0 <<dai>>

}



rectangle "core #0" <<cpu>> {



   together {

      rectangle "hda-dma\nhost output #0" as hda_dma_ho_0 <<dma>>

      rectangle "hda-dma\nhost input #0" as hda_dma_hi_0 <<dma>>

   }

   together {

      rectangle "dw-dma #0" as dw_dma_0 <<dma>>

      rectangle "dw-dma #1" as dw_dma_1 <<dma>>

   }



   rectangle "playback #0" <<stream>> {



      rectangle "ppl #0" as ppl_0 <<ppl>> {

         rectangle "host comp" as host_0

         rectangle "vol" as vol_0

         rectangle "mix-in" as mix_in_0



         host_0 --> vol_0

         vol_0 --> mix_in_0

      }



      rectangle "ppl #1" as ppl_1 <<ppl>> {

         rectangle "mix-out" as mix_out_1

         rectangle "dai comp" as dai_1



         mix_out_1 --> dai_1

      }

      mix_in_0 --> mix_out_1

   }

   hda_dma_ho_0 --> host_0



   dai_1 --> dw_dma_0

   dw_dma_0 --> ssp_0



   rectangle "capture #0" <<stream>> {

      rectangle "ppl #3" as ppl_3 <<ppl>> {

         rectangle "host comp" as host_3

         rectangle "dai comp" as dai_3

         host_3 <-- dai_3

      }

   }

   hda_dma_hi_0 <-- host_3

   dai_3 <-- dw_dma_1

   dw_dma_1 <-- ssp_1



   ' now let's show who's driving

   rectangle "work queue #0\n@core_0" as wq_0 <<event>>

   wq_0 .[#green]> mix_in_0

   wq_0 .[#green]> dw_dma_0 : new

   wq_0 .[#green]> dw_dma_1 : new

}



rectangle "core #1" <<cpu>> {



   rectangle "hda-dma\nhost output #1" as hda_dma_ho_1 <<dma>>

   rectangle "hda-dma\nlink input #0" as hda_dma_li_0 <<dma>>





   rectangle "playback #1" <<stream>> {

      rectangle "ppl #2" as ppl_2 <<ppl>> {



         rectangle "host comp" as host_2

         rectangle "dai comp" as dai_2



         host_2 --> dai_2

      }

   }

   hda_dma_ho_1 --> host_2

   dai_2 --> hda_dma_li_0

   hda_dma_li_0 --> hda_0



   rectangle "work queue #1\n@core_1" as wq_1 <<event>>

   wq_1 .[#green]> hda_dma_li_0

}

Figure 10 Work queue dependecies for CAVS SMP

participant ipc

participant "core 0" as core_0

participant "core 1" as core_1

participant wallclk



ipc -> core_0 : new pipeline 0 (@core 0)

   activate core_0

   core_0 -> wallclk : timer_register()

   core_0 -> core_0 : work_schedule(ppl 0 dai, +1ms)

   core_0 -> wallclk : work_set_timer(period = +1ms)

ipc <-- core_0

deactivate core_0



wallclk -> core_0 : cb() @ next ms

   activate core_0

   core_0 -> core_0 : ppl 0 dai cb()

      activate core_0

         core_0 -> core_0 : ppl 0 copy

         core_0 -> core_0 : work_schedule(ppl 0 dai, +1ms)

      deactivate core_0

   core_0 -> wallclk : work_set_timer()

   deactivate core_0



ipc -> core_0 : new pipeline 1 (@core 1)

   core_0 -> core_1

   activate core_1

      core_1 -> wallclk : timer_register()

      core_1 -> core_1 : work_schedule(ppl 1 dai)

   core_0 <-- core_1

   deactivate core_1



wallclk -> core_0 : cb() @ next ms

   activate core_0

   activate core_1

   core_0 -> core_0 : ppl 0 dai cb()

      activate core_0

         core_0 -> core_0 : ppl 0 copy

         core_0 -> core_0 : work_schedule(ppl 0 dai, +1ms)

      deactivate core_0

   core_1 -> core_1 : ppl 1 dai cb()

      activate core_1

         core_1 -> core_1 : ppl 1 copy

         core_1 -> core_1 : work_schedule(ppl 1 dai, +1ms)

      deactivate core_1

   deactivate core_1

   core_0 -> wallclk : work_set_timer()

   deactivate core_0

Figure 11 Work queue flow for CAVS SMP