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 master core re-programs the wall clock to the next wake event. Its work queue operates in master mode. Work queues running on other cores are attached to the shared time source on CAVS SMP platforms; these are configured to the slave mode. On other SMP platforms where multiple independent time sources are available, all queues can be configured in master 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 master/slave 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