diff --git a/.github/actions/create-publish-folder.sh b/.github/actions/create-publish-folder.sh new file mode 100755 index 00000000..1ac8f6c9 --- /dev/null +++ b/.github/actions/create-publish-folder.sh @@ -0,0 +1,9 @@ +#!/bin/bash +# create publish folder for github.io + +mkdir _build/latest +mv -v _build/html/* _build/latest +mv _build/latest _build/html/ +touch _build/html/.nojekyll +mv scripts/publish-README.md _build/html/README.md +mv scripts/publish-index.html _build/html/index.html diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml new file mode 100644 index 00000000..11b29b37 --- /dev/null +++ b/.github/workflows/pull-request.yml @@ -0,0 +1,150 @@ +--- +# Tools that can save round-trips to github and a lot of time: +# +# yamllint -f parsable pull_request.yml +# pip3 install ruamel.yaml.cmd +# yaml merge-expand pull_request.yml exp.yml && +# diff -w -u pull_request.yml exp.yml +# +# github.com also has a powerful web editor that can be used without +# committing. + +name: Build and Deploy + +# yamllint disable-line rule:truthy +on: + push: + branches: + - master + - publish + pull_request: + branches: [master] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# As of January 2021, no YAML anchors :-( +env: + ubuntu_base_deps: doxygen make default-jre graphviz cmake ninja-build + +jobs: + + supported-reqs: + + name: 'Supported scripts/requirements.txt' + runs-on: ubuntu-22.04 + + steps: + - uses: actions/checkout@v3 + + # FIXME: remove this time consuming step once github stops + # providing a broken package index, see + # https://github.com/actions/virtual-environments/issues/1757#issuecomment-777700920 + - name: apt-get update github broken package index + run: sudo apt-get update + + - name: apt-get install + run: sudo apt-get -y install $ubuntu_base_deps + + - name: PATH += .local/bin + run: echo "$HOME/.local/bin" >> $GITHUB_PATH + + - name: 'pip install -r scripts/requirements.txt' + run: pip install -r scripts/requirements.txt + + # No YAML anchors; this is copy/paste + - name: configure SOF doc + run: | + git clone https://github.com/thesofproject/sof + cmake -GNinja -S sof/doc -B _build_doxy + + # TODO: change the (bad) default value for SOF_DOC_BUILD in + # sof-docs/Makefile and remove this command line override + - name: build + run: | + make html VERBOSE=1 SOF_DOC_BUILD=_build_doxy + du -shc _build*/* + + - name: prepare file for deploy + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/publish' }} + run: ./.github/actions/create-publish-folder.sh + + # store the build result to artifact, used for later deploy or + # download for debug + # https://docs.github.com/en/actions/guides/storing-workflow-data-as-artifacts + - name: upload HTML for deploy + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/publish' }} + uses: actions/upload-artifact@v4 + with: + name: html + path: _build/html + + deploy: + needs: supported-reqs + runs-on: ubuntu-22.04 + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/publish' }} + steps: + # download the build result from the same workflow + # https://docs.github.com/en/actions/guides/storing-workflow-data-as-artifacts + - name: download HTML + uses: actions/download-artifact@v4.1.7 + with: + name: html + path: html + + - name: deploy + uses: peaceiris/actions-gh-pages@v3 + with: + personal_token: ${{ secrets.ACTIONS_DEPLOY_KEY }} + publish_dir: ./html/ + publish_branch: master + external_repository: thesofproject/thesofproject.github.io + + lax: + name: "PIP_IGNORE_INSTALLED=0 requirements-lax.txt" + runs-on: ubuntu-22.04 + # Makefile downgrades the Sphinx warnings, they are not errors any more + env: {LAX: 1} + steps: + - uses: actions/checkout@v3 + + # FIXME: remove this time consuming step once github stops + # providing a broken package index, see + # https://github.com/actions/virtual-environments/issues/1757#issuecomment-777700920 + - name: apt-get update github broken package index + run: sudo apt-get update + + - name: apt-get install base dependencies + run: sudo apt-get -y install $ubuntu_base_deps + + - name: apt-get instead of pip + run: sudo apt-get -y install + python3-pip python3-setuptools python3-wheel + python3-sphinx python3-breathe python3-docutils + python3-sphinx-rtd-theme python3-sphinxcontrib.plantuml + + - name: PATH += .local/bin + run: echo "$HOME/.local/bin" >> $GITHUB_PATH + + # should be a no-op + - name: 'pip install -r scripts/requirements-lax.txt' + run: pip install -r scripts/requirements-lax.txt + + - name: config tweaks + run: | + # sphinx-build < 1.7 doesn't support "auto" and we're not sure + # all default modules are "thread-safe" + sed -i -e '/SPHINXBUILD/ s/-j *auto/-j 1/' Makefile + # We don't want plantUML to raise the contribution bar + sed -i -e 's/^\(plantuml_output_format *=\).*/\1 "none"/' conf.py + + # No YAML anchors; this is copy/paste + - name: configure SOF doc + run: | + git clone https://github.com/thesofproject/sof + cmake -GNinja -S sof/doc -B _build_doxy + + - name: build + run: | + make html VERBOSE=1 SOF_DOC_BUILD=_build_doxy + du -shc _build*/* diff --git a/.github/workflows/woke.yml b/.github/workflows/woke.yml new file mode 100755 index 00000000..8338578c --- /dev/null +++ b/.github/workflows/woke.yml @@ -0,0 +1,28 @@ +--- +# Tools that can save round-trips to github and a lot of time: +# +# yamllint -f parsable pull_request.yml +# pip3 install ruamel.yaml.cmd +# yaml merge-expand pull_request.yml exp.yml && +# diff -w -u pull_request.yml exp.yml +# +# github.com also has a powerful web editor that can be used without +# committing. +name: woke manually checker + +# yamllint disable-line rule:truthy +on: + workflow_dispatch: + +jobs: + woke: + name: woke check for all file + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 + - name: woke + uses: get-woke/woke-action@v0 + with: + # Cause the check to fail on any broke rules + fail-on-error: true + woke-args: -c ./rules-woke.yaml diff --git a/.github/workflows/woke_pr.yml b/.github/workflows/woke_pr.yml new file mode 100755 index 00000000..7aa97088 --- /dev/null +++ b/.github/workflows/woke_pr.yml @@ -0,0 +1,35 @@ +--- +# Tools that can save round-trips to github and a lot of time: +# +# yamllint -f parsable pull_request.yml +# pip3 install ruamel.yaml.cmd +# yaml merge-expand pull_request.yml exp.yml && +# diff -w -u pull_request.yml exp.yml +# +# github.com also has a powerful web editor that can be used without +# committing. +name: woke PR reviewdog checker + +# yamllint disable-line rule:truthy +on: + pull_request: + branches: + - master + +jobs: + woke_pr: + name: woke check for patch + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 + - uses: get-woke/woke-action-reviewdog@v0 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + # Change reviewdog reporter if you need [github-pr-check,github-check,github-pr-review]. + reporter: github-pr-review + # Change reporter level if you need. + # GitHub Status Check won't become failure with warning. + level: warning + # Enable this to fail the check when violations are found + fail-on-error: true + woke-args: -c ./rules-woke.yaml diff --git a/.travis.yml b/.travis.yml deleted file mode 100755 index b398a46a..00000000 --- a/.travis.yml +++ /dev/null @@ -1,68 +0,0 @@ -dist: bionic - -before_install: - - sudo apt-get update -qq - - sudo apt-get install doxygen make default-jre graphviz cmake ninja-build - - -jobs: - include: - - - name: "Supported scripts/requirements.txt" - - language: python - python: "3.6" - - install: - - pip install -r scripts/requirements.txt - - script: &main_script - - pushd .. && git clone https://github.com/thesofproject/sof.git - && pushd sof/doc && cmake -GNinja . - - popd && popd - - make html VERBOSE=1 - - ls _build - - before_deploy: - - mkdir _build/latest - - mv -v _build/html/* _build/latest - - mv _build/latest _build/html - - touch _build/html/.nojekyll - - mv scripts/publish-README.md _build/html/README.md - - mv scripts/publish-index.html _build/html/index.html - - deploy: - - provider: pages - skip_cleanup: true - github_token: $GITHUB_TOKEN - repo: thesofproject/thesofproject.github.io - on: - branch: publish - local_dir: _build/html/ - target_branch: master - - - - name: "PIP_IGNORE_INSTALLED=0 requirements-lax.txt" - - language: minimal - - # Sphinx warnings are not errors any more - env: LAX=1 - - install: - - sudo apt -y install - python3-pip python3-setuptools python3-wheel - python3-sphinx python3-breathe python3-docutils - python3-sphinx-rtd-theme python3-sphinxcontrib.plantuml - - # Ideally, this pip3 command installs almost nothing. - - PIP_IGNORE_INSTALLED=0 pip3 install --user - -r scripts/requirements-lax.txt - - # sphinx-build < 1.7 doesn't support "auto" and we're not sure - # all default modules are "thread-safe" - - sed -i -e '/SPHINXBUILD/ s/-j *auto/-j 1/' Makefile - # We don't want plantUML to raise the contribution bar - - sed -i -e 's/^\(plantuml_output_format *=\).*/\1 "none"/' conf.py - - script: *main_script diff --git a/.wokeignore b/.wokeignore new file mode 100644 index 00000000..2f1f79d6 --- /dev/null +++ b/.wokeignore @@ -0,0 +1,20 @@ +# The following files can be ignored when running Woke. + +rules-woke.yaml +conf.py +*.pu +tox.ini +Makefile +.travis.yml +.github/workflows/*.yml +index.rst +release.rst +introduction/index.rst +maintainers/merge_rights.rst +contribute/process/bug-tracking.rst +getting_started/setup/setup_up_2_board.rst +developer_guides/virtualization/files/q-v6.sh +developer_guides/topology/topology.rst +developer_guides/fuzzing/testbench_afl_fuzzing.rst + + diff --git a/CODEOWNERS b/CODEOWNERS index 613b7d64..6339b6f0 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,5 +1,8 @@ -#This file identifies people who are automatically notified when Pull Requests are made for /sof-docs. -#At this time, the following people are notified and are expected to review the PRs: -#Liam Girdwood (technical review) and Deb Taylor (grammatical/style review). +# This file identifies people who are automatically notified when Pull Requests are made for /sof-docs. +# At this time, the following people are notified and are expected to review the PRs: +# Liam Girdwood (technical review), # Deb Taylor (grammatical/style review), +# Marcin Maka (technical review) and Michal Wasko (technical review). -* @lgirdwood @deb-intel @intelkevinputnam +# So if a pull request only touches javascript files, only these owners + +* @lgirdwood @deb-intel @intelkevinputnam @greg-intel @mmaka1 @mwasko diff --git a/Makefile b/Makefile index ae05e89c..64785f0d 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ # Minimal makefile for Sphinx documentation # +# You can override these defaults from the command line. ifeq ($(VERBOSE),1) Q = @@ -8,7 +9,7 @@ else Q = @ endif -# You can set these variables from the command line. +SOF_DOC_BUILD = ../sof/build_doxygen/ SPHINXBUILD = sphinx-build SPHINXPROJ = "SOF Project" SOURCEDIR = . @@ -38,18 +39,22 @@ help: # Keep doxygen optional not to burden "drive-by" .rst contributors with # extra dependencies. -APIS_CMAKE := ../sof/doc/build.ninja +APIS_CMAKE := ${SOF_DOC_BUILD}/build.ninja apidocs: ifeq (${APIS_CMAKE},$(wildcard ${APIS_CMAKE})) - ninja -C ../sof/doc $${VERBOSE:+-v} doc + ninja -C ${SOF_DOC_BUILD} $${VERBOSE:+-v} doc else - # To build doxygen APIs too run this first: - # cmake -GNinja -S ../sof/doc -B ../sof/doc + # To include doxygen APIs run this first: + # cmake -GNinja -S ../sof/doc -B ${SOF_DOC_BUILD} + # Note this will make the build CONSIDERABLY LONGER! + # Conversely, you can have _instant builds from scratch_ + # by disabling UML diagrams in the conf.py file. endif html: apidocs $(SPHINXBUILD) -j auto -t $(DOC_TAG) -b html \ -d $(BUILDDIR)/doctrees $(SOURCEDIR) $(BUILDDIR)/html $(SPHINXOPTS) \ +-D breathe_projects.'SOF Project'="${SOF_DOC_BUILD}"/doxygen/xml \ $(ERROROPTS) $(O) # Reminder: to see _all_ warnings you must "make clean" first. @@ -59,7 +64,7 @@ $(ERROROPTS) $(O) clean: rm -fr $(BUILDDIR) ifeq (${APIS_CMAKE},$(wildcard ${APIS_CMAKE})) - ninja -C ../sof/doc $${VERBOSE:+-v} doc-clean clean + ninja -C ${SOF_DOC_BUILD} $${VERBOSE:+-v} doc-clean clean endif # Copy material over to the GitHub pages staging repo diff --git a/algos/demux/demux.rst b/algos/demux/demux.rst index 89b55d2d..e36da7af 100644 --- a/algos/demux/demux.rst +++ b/algos/demux/demux.rst @@ -23,29 +23,28 @@ Configuration ============= The component configuration defines how audio channels are copied from -input to output stream. As the ASoC/SOF audio stream can have up to 8 -audio channels, a stream-to-stream specific 8 x 8 routing matrix +input to output streams. As the ASoC/SOF audio stream can have up to 8 +audio channels, a stream-to-stream specific 8x8 routing matrix defines the channel mapping from input to output. Because every stream is fully configurable, we have a matrix for all multiplexer input -streams or all demultiplexer output streams. The 8 x 8 binary matrix takes up +streams or all demultiplexer output streams. The 8x8 binary matrix takes up to 64 bits and is controlled with eight unsigned char values. +.. note:: + The mux/demux component can't mix channels. If you try to set up mixing in the configuration matrix, you will get an error in the component initialization phase. + .. figure:: images/mux.png Example of multiplexer configuration matrices with 2 input streams. - In this artificial mux example, Input stream 1’s channel 1 is copied - to both output channels; Input stream 2’s single channel is copied - to output channel 1. + In this artificial mux example, the first input stream's channel 1 is copied to the output stream's channel 1. The second input stream's channel 2 is copied to the output stream's channel 2. If the streams have only 2 channels, the matrix values outside the 2x2 square don't have any effect. .. figure:: images/demux.png Example of demultiplexer configuration matrices with 2 output streams. - In this artificial demux example, Input stream’s channel 1 is copied to - all output streams channels (1 channel to 4 identical channels in 2 - output streams). + In this artificial demux example, the input stream's channel 1 is copied to both channels of the first output stream and the input stream's channel 2 is copied to both channels of the second output stream. -Using a routing matrix means also that this component can leave out -channels or mix multiple channels into one. +.. note:: + The demux matrix configuration is opposite to the mux configuration: the input channel is the matrix column and the output is the row. Topology ======== diff --git a/algos/demux/images/demux.png b/algos/demux/images/demux.png index 3d87dc79..ee3cf873 100644 Binary files a/algos/demux/images/demux.png and b/algos/demux/images/demux.png differ diff --git a/algos/demux/images/mux.png b/algos/demux/images/mux.png index 65df01bd..bb867516 100644 Binary files a/algos/demux/images/mux.png and b/algos/demux/images/mux.png differ diff --git a/algos/eq/Picture_FIR_equalized_response.png b/algos/eq/Picture_FIR_equalized_response.png new file mode 100644 index 00000000..9407914b Binary files /dev/null and b/algos/eq/Picture_FIR_equalized_response.png differ diff --git a/algos/eq/Picture_FIR_impulse_response.png b/algos/eq/Picture_FIR_impulse_response.png new file mode 100644 index 00000000..8abbb95d Binary files /dev/null and b/algos/eq/Picture_FIR_impulse_response.png differ diff --git a/algos/eq/Picture_FIR_response.png b/algos/eq/Picture_FIR_response.png new file mode 100644 index 00000000..9cbc688f Binary files /dev/null and b/algos/eq/Picture_FIR_response.png differ diff --git a/algos/eq/Picture_FIR_response_absolute.png b/algos/eq/Picture_FIR_response_absolute.png new file mode 100644 index 00000000..35b615f0 Binary files /dev/null and b/algos/eq/Picture_FIR_response_absolute.png differ diff --git a/algos/eq/Picture_FIR_right_channel_equalized.png b/algos/eq/Picture_FIR_right_channel_equalized.png new file mode 100644 index 00000000..bf58810c Binary files /dev/null and b/algos/eq/Picture_FIR_right_channel_equalized.png differ diff --git a/algos/eq/Picture_IIR_FIR_target_vs_achieved_response.png b/algos/eq/Picture_IIR_FIR_target_vs_achieved_response.png new file mode 100644 index 00000000..cbac1eec Binary files /dev/null and b/algos/eq/Picture_IIR_FIR_target_vs_achieved_response.png differ diff --git a/algos/eq/Picture_iir_absolute_response.png b/algos/eq/Picture_iir_absolute_response.png new file mode 100644 index 00000000..5995dfc5 Binary files /dev/null and b/algos/eq/Picture_iir_absolute_response.png differ diff --git a/algos/eq/Picture_iir_filter_response_vs_ideal_target.png b/algos/eq/Picture_iir_filter_response_vs_ideal_target.png new file mode 100644 index 00000000..26a4e27b Binary files /dev/null and b/algos/eq/Picture_iir_filter_response_vs_ideal_target.png differ diff --git a/algos/eq/Picture_iir_impulse_response.png b/algos/eq/Picture_iir_impulse_response.png new file mode 100644 index 00000000..308368af Binary files /dev/null and b/algos/eq/Picture_iir_impulse_response.png differ diff --git a/algos/eq/Picture_iir_poles_and_zeros.png b/algos/eq/Picture_iir_poles_and_zeros.png new file mode 100644 index 00000000..968a48f8 Binary files /dev/null and b/algos/eq/Picture_iir_poles_and_zeros.png differ diff --git a/algos/eq/Picture_iir_simulated_left_and_channel_responses.png b/algos/eq/Picture_iir_simulated_left_and_channel_responses.png new file mode 100644 index 00000000..37c13898 Binary files /dev/null and b/algos/eq/Picture_iir_simulated_left_and_channel_responses.png differ diff --git a/algos/eq/Picture_imported_frequency_response_for_iir.png b/algos/eq/Picture_imported_frequency_response_for_iir.png new file mode 100644 index 00000000..ed42b918 Binary files /dev/null and b/algos/eq/Picture_imported_frequency_response_for_iir.png differ diff --git a/algos/eq/Picture_raw_frequency_response.png b/algos/eq/Picture_raw_frequency_response.png new file mode 100644 index 00000000..d4bc8ff2 Binary files /dev/null and b/algos/eq/Picture_raw_frequency_response.png differ diff --git a/algos/eq/Picture_response_with_smoothing.png b/algos/eq/Picture_response_with_smoothing.png new file mode 100644 index 00000000..b9d1c7b5 Binary files /dev/null and b/algos/eq/Picture_response_with_smoothing.png differ diff --git a/algos/eq/Picture_right_channel_FIR_absolute_response.png b/algos/eq/Picture_right_channel_FIR_absolute_response.png new file mode 100644 index 00000000..50450729 Binary files /dev/null and b/algos/eq/Picture_right_channel_FIR_absolute_response.png differ diff --git a/algos/eq/Picture_right_channel_response.png b/algos/eq/Picture_right_channel_response.png new file mode 100644 index 00000000..e995b01a Binary files /dev/null and b/algos/eq/Picture_right_channel_response.png differ diff --git a/algos/eq/Picture_simulated_IIR_FIR_frequency_response.png b/algos/eq/Picture_simulated_IIR_FIR_frequency_response.png new file mode 100644 index 00000000..edd732a2 Binary files /dev/null and b/algos/eq/Picture_simulated_IIR_FIR_frequency_response.png differ diff --git a/algos/eq/Picture_simulated_left_and_right_channel_responses.png b/algos/eq/Picture_simulated_left_and_right_channel_responses.png new file mode 100644 index 00000000..79f22238 Binary files /dev/null and b/algos/eq/Picture_simulated_left_and_right_channel_responses.png differ diff --git a/algos/eq/Picture_speaker_meas.jpg b/algos/eq/Picture_speaker_meas.jpg new file mode 100644 index 00000000..414a3139 Binary files /dev/null and b/algos/eq/Picture_speaker_meas.jpg differ diff --git a/algos/eq/Picture_tested_speaker_frequency_response.png b/algos/eq/Picture_tested_speaker_frequency_response.png new file mode 100644 index 00000000..ec1cb738 Binary files /dev/null and b/algos/eq/Picture_tested_speaker_frequency_response.png differ diff --git a/algos/eq/equalizers_tuning.rst b/algos/eq/equalizers_tuning.rst new file mode 100644 index 00000000..45335c60 --- /dev/null +++ b/algos/eq/equalizers_tuning.rst @@ -0,0 +1,924 @@ +.. _equalizers_tuning: + +Equalizers, IIR and FIR +####################### + +.. contents:: + :depth: 3 + +Introduction +************ + +Frequency response is the system output level specific to a +frequency. It can be measured in acoustical, electrical analog, or +digital domain. Standards such as AES17 [1]_ define how it is measured +and reported. + +The frequency response between e.g. 20 Hz and 20 kHz, that +is typical human max. range, is measured by sweeping signal generator +frequency and observing and recording the system output level into a +curve. + +The speaker frequency responses can rarely be optimized by mechanical +and acoustical design in the mass market devices. The industrial +design and miniaturization typically limit the performance. The +non-flat frequency response is a form of linear distortion. It causes +the sound reproduction to be unnatural in a way that could be called +thin, dark, etc. Such systematic issues in speaker frequency response +can be improved with equalization. + +Equalization is a simple technique that creates by signal processing +in an open loop pre-defined opposite linear distortion into signal to +cancel the linear distortion caused by speaker. However the cancel +cannot be perfect since the fixed equalization response need to be in +practice common for all production devices. Optimizing for one device +could cause another device to fail if the characteristic at that +frequency would differ. Hence the equalization can address only +systematic issues in the frequency response. Also when applying +equalization the system performance is impacted. Equalization nearly +always reduces achievable peak sound pressure level (SPL) and reduces +system dynamic range (DR). When tuning the equalization the trade-offs +need to be considered. + +The document describes speaker equalization. Microphones equalization +is similar but measurements are done in opposite domain: Acoustical -> +digital. In both cases use of calibrated reference microphone is +needed. + +Preparations +************ + +The device should allow remote ssh without password for the automatic +scripts to work. Since the developers have usually their public and +private keys setup only this is needed. Find out the IP address from +ifconfig command output on your device. + +.. code-block:: bash + + ssh-copy-id -i ~/.ssh/id_rsa.pub user@aa.bb.cc.dd + +For ssh to work with low delay the development PC and tuned device +should be on the same local IP network. You can check that remote +playback works to DUT with example command: + +.. code-block:: bash + + ssh user@aa.bb.cc.dd "aplay -l" + +Since the tests are done with low-level ALSA aplay and arecord +utilities it is recommended to temporarily rename in DUT the audio +servers to disable them. Kill manually the processes or reboot to +avoid them continue running. The audio servers can be disabled from OS +system control in more elegant way but it is harder to remember how to +do it and restore to normal vs. the brute force way. + +.. code-block:: bash + + cd /usr/bin + sudo mv pulseaudio pulseaudio.disabled + sudo mv pipewire pipewire.disabled + + +Frequency response measurement +****************************** + +Note: More professional audio analyzer systems are recommended to be +used for final tuning. The procedures described in this document are +for coarse initial settings. Final tuning, especially if dependence +to regulations and standards need to be done with care in professional +environment with calibrated measurement equipment. + +To measure speakers an omnidirectional USB measurement microphone is +recommended, e.g. UMM6 [2]_ or UMIK-1 [3]_. Such microphones are +inexpensive and do not necessarily have a flat frequency response but +the manufacturers provide a serial number based downloadable +calibration file for them. The calibration can be applied to these +measurements in SOF as well by referencing the downloaded calibration +data to measurement script. + +Next step up are analog condenser measurement microphones with a +high-end USB sound card that can provide the 48V phantom voltage. But +analog microphones add more calibration consideration for analog +level. The measurement microphones can be also calibrated for absolute +level with dedicated microphone calibrators those can output into the +sealed compartment a reference 94 dBSPL tone. + +The tools for measurement and EQ design are in located in directory +$SOF_WORKSPACE/sof/tools/tune/eq. The test setup is such that the DUT +device plays back the measurement wav file via ssh commands and the +development PC connected USB microphone captures the output. To +achieve this the configuration files for playback and capture need to +be edited. + +The capture device UMM6 is hw:3.0 (card 3, device 0), this can be seen +from output of arecord command on a the development PC example. We +also know that this device supports one capture channel. + +.. code-block:: bash + + arecord -l + **** List of CAPTURE Hardware Devices **** + card 0: PCH [HDA Intel PCH], device 0: ALC257 Analog [ALC257 Analog] + Subdevices: 1/1 + Subdevice #0: subdevice #0 + card 1: Ultra [Fast Track Ultra], device 0: USB Audio [USB Audio] + Subdevices: 1/1 + Subdevice #0: subdevice #0 + card 2: Audio [ThinkPad Dock USB Audio], device 0: USB Audio [USB Audio] + Subdevices: 1/1 + Subdevice #0: subdevice #0 + card 3: UMM6 [UMM-6], device 0: USB Audio [USB Audio] + Subdevices: 1/1 + Subdevice #0: subdevice #0 + +The settings file + +.. code-block:: bash + + $ cat mls_rec_config.txt + %% Recording device configuration + + rec.ssh = 0; % Set to 1 for remote capture + rec.user = ''; % Set to user@domain for ssh + rec.dir = '/tmp'; % Directory for temporary files + rec.dev = 'hw:3,0'; % Audio capture device + rec.nch = 1; % Number audio capture channels to use + + % Use '' if calibration is not needed. Otherwise set to + % e.g. '1234567.txt'. Such calibration data format is supported for + % some reasonably priced measurement microphones. The ASCII text + % calibration data file is the measured frequency response of the used + % microphone. Lines in the beginning those start with character " are + % treated as comment. The successive lines should be + % number pairs. Their unit must be Hz and dB. + rec.cal = ''; + +Similarly check with remote aplay command the playback devices and +then edit the playback settings. + +.. code-block:: bash + + ssh user@aa.bb.cc.dd "aplay -l" + **** List of PLAYBACK Hardware Devices **** + card 0: sofglkda7219max [sof-glkda7219max], device 0: Speakers (*) [] + Subdevices: 1/1 + Subdevice #0: subdevice #0 + card 0: sofglkda7219max [sof-glkda7219max], device 1: Headset (*) [] + Subdevices: 1/1 + Subdevice #0: subdevice #0 + card 0: sofglkda7219max [sof-glkda7219max], device 5: HDMI1 (*) [] + Subdevices: 1/1 + Subdevice #0: subdevice #0 + card 0: sofglkda7219max [sof-glkda7219max], device 6: HDMI2 (*) [] + Subdevices: 1/1 + Subdevice #0: subdevice #0 + card 0: sofglkda7219max [sof-glkda7219max], device 7: HDMI3 (*) [] + Subdevices: 1/1 + Subdevice #0: subdevice #0 + +On the DUT the speakers are provided by device hw:0,0. It's known that +there's two playback channels in the device. + +.. code-block:: bash + + $ cat mls_play_config.txt + play.ssh = 1; % Set to use remote ssh commands + play.user = 'user@aa.bb.cc.dd'; % Set user@domain for ssh + play.dir = '/tmp'; % directory for temporary files + play.dev = 'hw:0,0'; % Audio device for playback + play.nch = 2; % Number of playback channels to test + +Next the measurement orientation and measurement microphone place is +considered. A notebook could be placed on top of a table symmetrically +where the measurement microphone location should be symmetrical to +display center axis. The microphone location could be near the center +of user’s ears. If the measurement microphone capture is too silent or +disturbed by ambient noise the microphone should be placed closer into +near field. + +.. figure:: Picture_speaker_meas.jpg + :width: 600 + + On-axis measurement position for bottom located speakers. + +Since this example device is a convertible type with a near 360 degree +display hinge there are several usage orientations. It was chosen to +measure the speakers from about their firing axis. Since the response +is impacted by orientation this was felt as safest choice. It also +gave the flattest looking frequency response. + +The MLS measurement tolerates some noise but the more silent the +environment is the better it is. An anechoic chamber would be ideal +naturally. The used MLS signal sets stress for the speakers so start +with a low volume setting with “alsamixer -Dhw:0”. Find the speaker +playback volume control PGA or volume controlin speaker amplifier and +start with e.g. 50%. + +Start Octave and launch the measurement + +.. code-block:: octave + + [f, m] = mls_freq_resp('DUT'); + +If the script warns about too silent audio increase the volume and/or +bring the microphone closer to the device. If the device has small +speakers and test signal playback sounds like at being near to their +capability limit, it is best to ignore the warning. The speakers may +permanently damage if the playback is too loud. + +If problems the script contains a self test for quick integrity +check. The self test measures a recursive filter that simulates a +non-flat response. The measurement and theoretical response that’s +computed directly from filter coefficients should match. + +.. code-block:: octave + + [f, m] = mls_freq_resp('selftest'); + +The test signal contains two chirps and a few times repeated +pseudo-random numbers sequence. The chirps are used to locate and +extract the MLS part. The MLS sequence has such a characteristic the +the correlation with itself is minimal. The sufficient length of the +sequence is used to suppress room reverberation from the +measurement. It provides nearly similar measured frequency responses +as achieved in anechoic conditions. As in anechoic chamber the setup +should be as much as possible like free-field. The desk/stand where +the device is measured should be away from reflecting surfaces. + +This MLS measurement would naturally also benefit from doing in +anechoic chamber since the MLS technique cannot eliminate all reverb +impact form measurement. Though usually in chambers there’s +professional equipment available like Audio Precision ® and other. If +such are available this measurement step with SOF can be avoided and +continued from next section for data import for tuning. + +.. figure:: Picture_raw_frequency_response.png + :width: 600 + + Frequency response measurement. The first channel is aligned to 0 + dB at 1 kHz. The second channel is shown with true offset + vs. first. + +After a successful measurement a plot with frequency (Hz) and +magnitude (dB) as x and y axis will be shown. The variable f will +contain the frequency response and variable m the magnitude. If the +number of measured channels was larger than 1 the m is a matrix. The +result can be saved for equalizer design into a .mat file. + +.. code-block:: octave + + save example_dut.mat f m + +Equalizer design +**************** + +It can be seen from the picture that the output of speakers is weak at +below 200 Hz. There’s two resonances, first at about 700 Hz and second +at about 5 kHz (better visible on table orientation). The response is +within -10 .. +10 dB in about 300 - 13000 Hz range. The equalization +should not be applied outside these frequencies to avoid a large loss +of SPL. It can be also seen that the left and right speaker have +slightly different frequency response. + +Next the measurement data is imported to SOF. It can be done by load +of previously saved file or importing e.g. in MS Excel format from +other equipment. The matrix columns for frequency and channel specific +levels need to be known. + +The tool in SOF is a set of functions to be used in user created +script. Therefore programming knowledge is needed. The benefit of +using script is the procedure is easy to repeat and documented by +itself. + +FIR equalizer +************* + +The finite impulse response (FIR) filter type has the advantages that +design for any finite time impulse response / frequency response is +simple and robust. The filters do not oscillate by design so the +rounding errors do not appear as noise. The rounding of coefficients +into a fixed word length only impairs slightly the response but the +effect can be usually ignored. Therefore the FIR equalizers especially +when used with 24 and 32 bit audio format are compatible with studio +like 24 bit audio quality. + +Due finite response (often limited by DSP resources) the FIR filters +are not practical for lowest frequencies unless very long filters are +used. The longer the filter is the more DSP RAM and MCPS the +processing consumes. However FIR filters are great for mid and high +frequencies equalization. The next example equalizes those frequencies +for the previously done measurement. + +The initial script for tuning is shown below. Alternatively for other +equipment the data import could be done in Excel format and use +function xlsread(); to read a matrix and then extract the frequency +and magnitude columns. + +.. code-block:: octave + + %% Load measurement data, variable f and m + load example_dut.mat; + + %% EQ settings + eq1 = eq_defaults(); % Get defaults + eq1.fs = 48e3; % Set sample rate + eq1.norm_type = 'loudness'; % Normalize criteria can be loudness/peak/1k + eq1.norm_offs_db = -3; % Offset in dB to normalize, -3dB loudness + eq1.logsmooth_plot = 1.0; % Smooth over 1.0 octaves + eq1.logsmooth_eq = 1.0; % Smooth over 1.0 octaves + eq1.enable_fir = 1; % By default both FIR and IIR disabled + eq1.fir_beta = 3.0; % Lower beta is more accurate but be careful + eq1.fir_length = 90; % Minimize this vs. fmin/fmax choice + eq1.fir_autoband = 0; % Select manually frequency limits + eq1.fmin_fir = 700; % Equalization starts from 800 Hz + eq1.fmax_fir = 13e3; % Equalization ends at 13 kHz + eq1.fir_minph = 1; % Check result carefully if 1 is used, 0 is safe + eq2 = eq1; % Copy settings to second EQ + + %% Design left channel EQ + eq1.raw_f = f; % Measurement Hz + eq1.raw_m_db = m(:,1); % Measurement dB, left ch + eq1 = eq_compute(eq1); + eq_plot(eq1, 10); + +The run of this script creates these plots. Note the choice of 1.0 +octaves smoothing for both plotting and EQ target derivation. It’s +best to start carefully with such a high amount of smoothing to avoid +to equalize highly uncertain details of frequency response. + +The smoothed version of the response becomes very flat in the +equalized version. However the simulated raw response still contains a +lot of ripple especially at high bands. It’s an industry standard to +use ⅓ octaves smoothing since it quite well matches human ear +psycho-acoustics. Therefore ⅓ octaves should be the smallest feasible +width of octaves smoothing to use. + +.. figure:: Picture_response_with_smoothing.png + :width: 600 + + Imported frequency response with and without octaves + smoothing. Note that the strong 1.0 octaves wide smoothing + “flattens” most of the narrow (high Q) resonances and leaves the + two mentioned resonances at 700 Hz and 4 kHz. + +.. figure:: Picture_FIR_right_channel_equalized.png + :width: 600 + + Simulated frequency response after equalization + +.. figure:: Picture_FIR_response.png + :width: 600 + + Frequency response of equalizer. The blue curve is the ideal + inverse response including the smoothing. The red curve is the band + limited and filter design parameters constrained actual EQ + response. The y-axis is offset in such way that 1 kHz frequency is + shifted to 0 dB. Try the impact of filter length to see how it + impacts the accuracy and find a fair compromise. + +.. figure:: Picture_FIR_response_absolute.png + :width: 600 + + Frequency response of equalizer. This curve shows the absolute gain + of the equalization. It can be seen that the normalization of + loudness (-3 dB) does some fairly high gain above 10 kHz. The + attenuation of frequencies below 200 Hz may or may not be + sufficient to give signal headroom for this boost. Need to watch + out for distortion in playback, if observed the loudness need to be + decreased. + +.. figure:: Picture_FIR_impulse_response.png + :width: 600 + + Impulse response of equalizer. The chosen minimum phase + non-symmetrical impulse response can be seen in the shape. A linear + phase response would have symmetrical pre- and post oscillation in + the impulse response. + +Add of right channel measurement import and EQ design is done by +adding these lines to above script. + + +.. code-block:: octave + + %% Design right channel EQ + eq2.raw_f = f; % Measurement Hz + eq2.raw_m_db = m(:,2); % Measurement dB, right ch + eq2 = eq_compute(eq2); + eq_plot(eq2, 20); + +The resulting EQ can be seen from these plots. If the left and right +channel results are different need to know if it is due to +non-symmetrical mechanics. If there’s designed non-symmetry it’s safe +to go ahead and design different EQ for left and right channels. If +the hardware is symmetrical then it is likely to better to equalize +e.g. average response of left and right instead. + +Note: The left and right responses are quite similar. The mechanics & +acoustics is likely symmetrical so a common EQ could be the best +choice. The average of left and right response could be suitable to +use. However in this in this case the design is done as stereo for +tutorial purpose. + +.. figure:: Picture_right_channel_response.png + :width: 600 + + Import right channel frequency response. + + +.. figure:: Picture_FIR_right_channel_equalized.png + :width: 600 + + Simulated response of equalizer. + +.. figure:: Picture_right_channel_FIR_absolute_response.png + :width: 600 + + Frequency response of the right channel filter. Notice the difference to left channel filter. + +The next step is to check the stereo EQ design. The left and right +channels should as equalized have similar loudness. Since the SOF tool +currently does not add much help to multi-channel design this step +needs some additional own code. + +.. code-block:: octave + + %% Stereo EQ + figure(30); + l_ch = eq1.m_db+eq1.fir_eq_db; + r_ch = eq2.m_db+eq2.fir_eq_db; + semilogx(eq1.f, l_ch, eq2.f, r_ch); + grid on; + axis([100 20e3 -20 10]); + xlabel('Frequency (Hz)'); + ylabel('Magnitude (dB)'); + + %% Calculate level offset at 1 - 4 kHz from RMS + idx0 = find(eq1.f < 4e3); + idx = find(eq1.f(idx0) > 1e3); + l_lev = 20*log10(sqrt(mean(10.^(l_ch(idx)/10)))); + r_lev = 20*log10(sqrt(mean(10.^(r_ch(idx)/10)))); + fprintf('L ch level %3.1f dB\n', l_lev); + fprintf('R ch level %3.1f dB\n', r_lev); + delta_lev = l_lev-r_lev; + fprintf('delta %3.1f dB\n', delta_lev); + +The plot shows the raw data plus EQ impact. Since the offset is hard +to judge from the non-smoothed plot (the smoothed data is +unfortunately for this purpose 1 kHz, 0 dB aligned) the offset is +computed from RMS level difference in 1 - 4 kHz band. In this example +the difference was 0.2 dB. The offset is next added to right channel +align. + +.. code-block:: octave + + %% Design right channel EQ + eq2.norm_offs_db = -3 + 0.2; % Offset in dB to normalize, -3dB plus L-R + eq2.raw_f = f; % Measurement Hz + eq2.raw_m_db = m(:,2); % Measurement dB, right ch + eq2 = eq_compute(eq2); + eq_plot(eq2, 20); + + +.. figure:: Picture_simulated_left_and_right_channel_responses.png + :width: 600 + + Simulated frequency responses of left and right speaker channels. + +The complete tuning script is shown below for completeness. It can be a +starting point for your own stereo speaker equalizer design case! + + +.. code-block:: octave + + %% Load measurement data, variable f and m + load example_dut.mat; + + %% EQ settings + eq1 = eq_defaults(); % Get defaults + eq1.fs = 48e3; % Set sample rate + eq1.norm_type = 'loudness'; % Normalize criteria can be loudness/peak/1k + eq1.norm_offs_db = -3; % Offset in dB to normalize, -3dB loudness + eq1.logsmooth_plot = 1.0; % Smooth over 1.0 octaves + eq1.logsmooth_eq = 1.0; % Smooth over 1.0 octaves + eq1.enable_fir = 1; % By default both FIR and IIR disabled + eq1.fir_beta = 3.0; % Lower beta is more accurate but be careful + eq1.fir_length = 90; % Minimize this vs. fmin/fmax choice + eq1.fir_autoband = 0; % Select manually frequency limits + eq1.fmin_fir = 700; % Equalization starts from 800 Hz + eq1.fmax_fir = 13e3; % Equalization ends at 20 kHz + eq1.fir_minph = 1; % Check result carefully if 1 is used, 0 is safe + eq2 = eq1; % Copy settings to second EQ + + %% Design left channel EQ + eq1.raw_f = f; % Measurement Hz + eq1.raw_m_db = m(:,1); % Measurement dB, left ch + eq1 = eq_compute(eq1); + eq_plot(eq1, 10); + + %% Design right channel EQ + eq2.norm_offs_db = -3 + 0.2; % Offset in dB to normalize, -3dB plus L-R + eq2.raw_f = f; % Measurement Hz + eq2.raw_m_db = m(:,2); % Measurement dB, right ch + eq2 = eq_compute(eq2); + eq_plot(eq2, 20); + + %% Stereo EQ + figure(30); + l_ch = eq1.m_db+eq1.fir_eq_db; + r_ch = eq2.m_db+eq2.fir_eq_db; + semilogx(eq1.f, l_ch, eq2.f, r_ch); + grid on; + axis([100 20e3 -20 10]); + xlabel('Frequency (Hz)'); + ylabel('Magnitude (dB)'); + + %% Calculate level offset at 1 - 4 kHz from RMS + idx0 = find(eq1.f < 4e3); + idx = find(eq1.f(idx0) > 1e3); + l_lev = 20*log10(sqrt(mean(10.^(l_ch(idx)/10)))); + r_lev = 20*log10(sqrt(mean(10.^(r_ch(idx)/10)))); + fprintf('L ch level %3.1f dB\n', l_lev); + fprintf('R ch level %3.1f dB\n', r_lev); + delta_lev = l_lev-r_lev; + fprintf('delta %3.1f dB\n', delta_lev); + +IIR equalizer +************* + +Infinite impulse response (IIR) filter is the other main filter type +for equalization. Here it’s described after FIR because despite the +simpler look (much lower filter orders needed) using them needs more +expertise. An IIR design can fail fatally if not used with care and +plenty of testing. Therefore it is recommended to use simple low order +filters and do the more complex response manipulation with FIR. The +risks of IIR are in stability (unwanted loud oscillation), noise, and +loss of SNR due to scaling need. However IIR filters are great for +enhancing frequency response at lowest frequencies and generally doing +stronger adjustment. + +The tool in SOF does not support automatic design. Instead the design +is manual with parametric first and second order blocks. The second +order blocks are called often bi-quads. The parametric blocks are +specified by their type (high-pass, low-pass, low-shelf, high-shelf, +peak/notch). The shelving and peaking filters are second order. The +high-pass and low-pass filters can be first or second order. Therefore +the parametric blocks are called with abbreviations HP1, HP2, LP1, +LP2, LS2, HS2, and PN2. All parametric blocks have a resonant +frequency parameter in Hz. The shelving filters and peaking filters +have also gain in Decibels as parameter. Finally the peaking filter +has a Q-value parameter. The higher the Q-value is the narrower is the +resonance. The syntax for describing parametric EQ is shown below: + +.. code-block:: octave + + eq1.peq = [ eq1.PEQ_HP2 200 0 0 ; ... + eq1.PEQ_PN2 750 -5.0 1.3 ; ... + eq1.PEQ_PN2 5000 -4.0 0.6 ; ... + ]; + + +The example can be equalized with IIR only. First, since there is very +little output from the speaker below 200 Hz we can with second order +high-pass suppress the not audible frequencies from output. It +increases the headroom for equalization a lot since typical music and +speech content has large energy there. Then, a peaking EQ is set to +attenuate the 750 Hz region by 5 dB and Q-value 1.3 for flatter +response. Finally, a peaking filter is set to attenuate the wide bump +at 5 kHz by 4 dB and Q-value 0.6. The resulting EQ is 6th order. It +also could be possible to boost the low frequencies at 400 Hz a bit +with a low-shelf but it is not done here to keep filter order +low. Boost at low frequencies creates risk for signal clipping while +the achievable bandwidth extension is not large. + +.. figure:: Picture_imported_frequency_response_for_iir.png + :width: 600 + + Simulated frequency response. The difference in parametric + low-order IIR can be seen as more remaining small ripple in the + smoothed equalized response vs. FIR. + +.. figure:: Picture_iir_filter_response_vs_ideal_target.png + :width: 600 + + IIR filter response vs. ideal target. + +.. figure:: Picture_iir_absolute_response.png + :width: 600 + + Absolute response. The loudness normalize suggests a fairly high + gain for the filter since a lot of loudness is lost due to suppress + of lowest frequencies. Need to be careful with this. + +.. figure:: Picture_iir_poles_and_zeros.png + :width: 600 + + Poles and zeros plot. In recursive filters the poles (x) need to be + inside unit circle for stable design. This plot is for 64 bit float + coefficients, fixed scaled coefficients could have issues even if + this looks OK. + +.. figure:: Picture_iir_impulse_response.png + :width: 600 + + Impulse response. The main purpose of this to do another stability + check. A stable filter decays to zero while an unstable design + might remain oscillation at steady or increasing amplitude. + +The right channel is tuned similarly. The resulting non-smoothed +left/right balance corrected responses and the complete code for +tuning are shown below. + + +.. figure:: Picture_iir_simulated_left_and_channel_responses.png + :width: 600 + + Simulated frequency responses of left and right speakers with + IIR equalizer. + + +.. code-block:: octave + + %% Load measurement data, variable f and m + load example_dut.mat; + + %% EQ settings + eq1 = eq_defaults(); % Get defaults + eq1.fs = 48e3; % Set sample rate + eq1.norm_type = 'loudness'; % Normalize criteria can be loudness/peak/1k + eq1.norm_offs_db = -3; % Offset in dB to normalize, -3 dB loudness + eq1.logsmooth_plot = 1.0; % Smooth over 1.0 octaves + eq1.logsmooth_eq = 1.0; % Smooth over 1.0 octaves + eq1.enable_iir = 1; % By default both FIR and IIR disabled + eq2 = eq1; % Copy settings to second EQ + + %% Design left channel EQ + eq1.raw_f = f; % Measurement Hz + eq1.raw_m_db = m(:,1); % Measurement dB, left ch + eq1.peq = [ eq1.PEQ_HP2 200 0 0 ; ... + eq1.PEQ_PN2 750 -5.0 1.3 ; ... + eq1.PEQ_PN2 5000 -4.0 0.6 ; ... + ]; + eq1 = eq_compute(eq1); + eq_plot(eq1, 10); + + %% Design right channel EQ + eq2.norm_offs_db = -3 + 0.1; % Offset in dB to normalize, -3dB plus L-R + eq2.raw_f = f; % Measurement Hz + eq2.raw_m_db = m(:,2); % Measurement dB, right ch + eq2.peq = [ eq2.PEQ_HP2 200 0 0 ; ... + eq2.PEQ_PN2 750 -5.0 1.4 ; ... + eq2.PEQ_PN2 4500 -4.0 0.6 ; ... + ]; + eq2 = eq_compute(eq2); + eq_plot(eq2, 20); + + %% Stereo EQ + figure(30); + l_ch = eq1.m_db+eq1.iir_eq_db; + r_ch = eq2.m_db+eq2.iir_eq_db; + semilogx(eq1.f, l_ch, eq2.f, r_ch); + grid on; + axis([100 20e3 -20 20]); + xlabel('Frequency (Hz)'); + ylabel('Magnitude (dB)'); + + %% Calculate level offset at 1 - 4 kHz from RMS + idx0 = find(eq1.f < 4e3); + idx = find(eq1.f(idx0) > 1e3); + l_lev = 20*log10(sqrt(mean(10.^(l_ch(idx)/10)))); + r_lev = 20*log10(sqrt(mean(10.^(r_ch(idx)/10)))); + fprintf('L ch level %3.1f dB\n', l_lev); + fprintf('R ch level %3.1f dB\n', r_lev); + delta_lev = l_lev-r_lev; + fprintf('delta %3.1f dB\n', delta_lev); + +Combined IIR and FIR +******************** + +The EQ tool can support use of both types simultaneously. The IIR type +is applied first and the impact is subtracted from the target. This +allows the FIR to fine tune the response where IIR could not match +fully the target. + +For this example the IIR high shelf is left out because FIR can do it +efficiently. Instead of boosting at 2 kHz this script tests +attenuation at 700 Hz to flatten and extend a bit the flat frequency +response region down. + +Note: In current version the norm_offs_db parameter impacts both FIR +and IIR part by the given amount. Therefore the level adjust need to +be entered as 0.5*adjust. + +.. figure:: Picture_IIR_FIR_target_vs_achieved_response.png + :width: 600 + + Right channel equalization filters. The red solid plot is the combined IIR and FIR response + that matches well the smoothed target response in solid blue. The dashed yellow and purple + lines show the IIR and FIR responses. + +.. figure:: Picture_simulated_IIR_FIR_frequency_response.png + :width: 600 + + Simulated raw frequency response + +Exporting coefficients to SOF +***************************** + +The coefficients can be exported into a format for m4 topology for +automatic boot time setup. The topology file can include the m4 +scripts instead of the default “flat” response coefficients. It is +also possible to set up an equalizer with .txt or .bin format blob in +device run-time with sof-ctl utility to test the response and iterate +the design. + +The complete script for equalizers tuning and coefficients export for +the previous example is shown below. + +.. code-block:: octave + + %% Load measurement data, variable f and m + load example_dut.mat; + + %% EQ settings + eq1 = eq_defaults(); % Get defaults + eq1.fs = 48e3; % Set sample rate + eq1.norm_type = 'loudness'; % Normalize criteria can be loudness/peak/1k + eq1.norm_offs_db = -3; % Offset in dB to normalize, -3dB loudness + eq1.logsmooth_plot = 1.0; % Smooth over 1.0 octaves + eq1.logsmooth_eq = 1.0; % Smooth over 1.0 octaves + eq1.enable_fir = 1; % By default both FIR and IIR disabled + eq1.enable_iir = 1; % Enable too + eq1.fir_beta = 3.0; % Lower beta is more accurate but be careful + eq1.fir_length = 40; % Minimize this vs. fmin/fmax choice + eq1.fir_autoband = 0; % Select manually frequency limits + eq1.fmin_fir = 700; % Equalization starts from 800 Hz + eq1.fmax_fir = 13e3; % Equalization ends at 13 kHz + eq1.fir_minph = 1; % Check result carefully if 1 is used, 0 is safe + eq2 = eq1; % Copy settings to second EQ + + %% Design left channel EQ + eq1.raw_f = f; % Measurement Hz + eq1.raw_m_db = m(:,1); % Measurement dB, left ch + eq1.peq = [ eq1.PEQ_HP2 200 0 0 ; ... + eq1.PEQ_PN2 750 -5.0 1.3 ; ... + ]; + eq1 = eq_compute(eq1); + eq_plot(eq1, 10); + + %% Design right channel EQ + eq2.norm_offs_db = -3 + 0.1; % Offset in dB to normalize, -4dB plus L-R + eq2.raw_f = f; % Measurement Hz + eq2.raw_m_db = m(:,2); % Measurement dB, right ch + eq2.peq = [ eq2.PEQ_HP2 200 0 0 ; ... + eq2.PEQ_PN2 750 -5.0 1.4 ; ... + ]; + eq2 = eq_compute(eq2); + eq_plot(eq2, 20); + + %% Stereo EQ + figure(30); + l_ch = eq1.m_db+eq1.tot_eq_db; + r_ch = eq2.m_db+eq2.tot_eq_db; + semilogx(eq1.f, l_ch, eq2.f, r_ch); + grid on; + axis([100 20e3 -20 10]); + xlabel('Frequency (Hz)'); + ylabel('Magnitude (dB)'); + + %% Calculate level offset at 1 - 4 kHz from RMS + idx0 = find(eq1.f < 4e3); + idx = find(eq1.f(idx0) > 1e3); + l_lev = 20*log10(sqrt(mean(10.^(l_ch(idx)/10)))); + r_lev = 20*log10(sqrt(mean(10.^(r_ch(idx)/10)))); + fprintf('L ch level %3.1f dB\n', l_lev); + fprintf('R ch level %3.1f dB\n', r_lev); + delta_lev = l_lev-r_lev; + fprintf('delta %3.1f dB\n', delta_lev); + + %% Export FIR + fir_ascii_fn = 'dut_spk_fir.txt'; + fir_tplg_fn = 'dut_spk_fir.m4'; + fir_eq1_quant = eq_fir_blob_quant(eq1.b_fir); + fir_eq2_quant = eq_fir_blob_quant(eq2.b_fir); + channels_in_config = 2; % Setup max 2 channels EQ + assign_response = [0 1]; % Switch to response #0 and #1 + num_responses = 2; % Two responses + fir_bm = eq_fir_blob_merge(channels_in_config, ... + num_responses, ... + assign_response, ... + [fir_eq1_quant fir_eq2_quant]); + fir_bp = eq_fir_blob_pack(fir_bm); + eq_alsactl_write(fir_ascii_fn, fir_bp); + eq_tplg_write(fir_tplg_fn, fir_bp, 'FIR'); + + %% Export IIR + iir_ascii_fn = 'dut_spk_iir.txt'; + iir_tplg_fn = 'dut_spk_iir.m4'; + iir_eq1_quant = eq_iir_blob_quant(eq1.p_z, eq1.p_p, eq1.p_k); + iir_eq2_quant = eq_iir_blob_quant(eq2.p_z, eq2.p_p, eq2.p_k); + iir_bm = eq_iir_blob_merge(channels_in_config, ... + num_responses, ... + assign_response, ... + [iir_eq1_quant iir_eq2_quant]); + iir_bp = eq_iir_blob_pack(iir_bm); + eq_alsactl_write(iir_ascii_fn, iir_bp); + eq_tplg_write(iir_tplg_fn, iir_bp, 'IIR'); + +Testing the response with sof-ctl +********************************* + +The sof-ctl tool is practical for testing new EQ settings and iterate +the design without need to reboot the device. The pre-requisite is that +the DUT runs for speaker path a topology that contains the IIR and FIR +equalizers. + +First the numids of the equalizers are found out with amixer +command. The lines with prompt $ are user entered commands and other +text shown is command output. + +.. code-block:: bash + + $ amixer -Dhw:0 controls | grep EQIIR + numid=66,iface=MIXER,name='EQIIR1.0 EQIIR' + + $ amixer -Dhw:0 controls | grep EQFIR + numid=67,iface=MIXER,name='EQFIR1.0 EQFIR' + +The numids are in this device 66 and 67 for IIR and FIR. Next the +exported ALSA binary controls are passed to equalizers with sof-ctl: + +.. code-block:: bash + + $ ./sof-eqctl -n 66 -s dut_spk_iir.txt + Applying configuration "dut_spk_iir.txt" into device hw:0 control numid=66. + + 4607827,0,196,50331648,0,0,0,0,196,2,2,0,0,0,0,0,1,2,2,0,0,0,0,3260252783,2107733822, + 528275171,3238416955,528275171,0,16384,3324016838,2034846530,497901563,3275128193, + 526872106,4294967293,20454,2,2,0,0,0,0,3260252783,2107733822,528275171,3238416955, + 528275171,0,16384,3317002057,2041827532,500647939,3271629404,527641448,4294967293,20551 + + Success. + + $ ./sof-eqctl -n 67 -s dut_spk_fir.txt + Applying configuration "dut_spk_fir.txt" into device hw:0 control numid=67. + + 4607827,0,244,50331648,0,0,0,0,244,131074,0,0,0,0,65536,44,0,0,0,0,3801503801,233243489, + 4293068324,74908123,1901269,7733144,6422742,4290772934,17039467,1114313,4293328827, + 4291756033,4289658785,4291297224,4293459912,589833,4294115318,4294246391,4294442989, + 1310731,13,0,44,0,0,0,0,3785054386,221118972,13436579,74515002,8520459,10551247,10944817, + 4292018112,23789790,4291559609,4293984167,4288479207,4290576265,4293394406,131047,1179673, + 4293853177,4293853167,4294901744,851980,6,0 + + Success. + + + +.. figure:: Picture_tested_speaker_frequency_response.png + :width: 600 + + The response is simple to test acoustically by re-running + mls_freq_resp(); The overall response is now much more flat and is + very similar to previously shown simulated response. + + +Using the EQ settings in topology +********************************* + +The generated .m4 suffix files for FIR and IIR can be included or +embedded into topology m4 scripts. There are a few examples of such +topologies in $SOF_WORKSPACE/sof/tools/topology/topology1/development. +The CMakeLists.txt file builds e.g. topologies +sof-cml-rt1011-rt5682-eq.tplg and sof-hda-generic-2ch-loud.tplg those +can be used as example. + +The playback pipeline is set with -DSPKPROC=eq-iir-eq-fir-volume +or -DHSPROC=eq-iir-eq-fir-volume to contain the equalizers and volume +control components. The macros -DHSPROC_FILTER1=eq_iir_coef_pass.m4 +and -DHSPROC_FILTER2=eq_fir_coef_pass.m4 are flat default responses. + +Setting -DHSPROC_FILTER1=dut_spk_iir.m4 and +-DHSPROC_FILTER2=dut_spk_fir.m4 would set the just exported equalizer +tuning to be applied at device boot. + +Note: Unfortunately the SOF topology1 equalizers definitions at top +CMakeLists.txt are not very systematic and there may be bugs with some +platforms triggered by small topology changes. The new topology needs +extensive testing for all audio endpoints (that other existing filters +are not modified) and preferably manual inspection of topology .conf +file that the m4 parsed output matches expectation. + +The development now focuses to to topology2 and hopefully this part +can be cleaned up and made easier for product audio tuning. + +References +********** + +.. [1] AES17-2020: AES standard method for digital audio engineering - Measurement of digital audio equipment, + https://www.aes.org/publications/standards/search.cfm?docID=21 + +.. [2] Dayton audio UMM-6 USB measurement microphone, + https://www.daytonaudio.com/product/1116/umm-6-usb-measurement-microphone + +.. [3] MiniDSP UMIK-1 USB measurement microphone, + https://www.minidsp.com/products/acoustic-measurement/umik-1 diff --git a/algos/index.rst b/algos/index.rst index 271ce774..6cd9fb9c 100644 --- a/algos/index.rst +++ b/algos/index.rst @@ -17,14 +17,14 @@ build pipelines. "Acoustical Echo Cancellation (mockup)", "Attenuates speaker originated acoustical coupling in microphone capture signal", "Yes", "N/A", "Planned", "1.6" "Asynchronous sample rate conversion", "Converts between common sample rates and connects pipelines with different clock domains", "Yes", "Xtensa HiFi3", "Upstream", "1.5" "Channel selector", "Copies the selected channel from the source buffer to the sink buffer", "Yes", "N/A", "Upstream", "1.4" - "Crossover", "Splits up audio into at most four different bands for individual processing", "Yes", "No", "Planned", "" - "DCBlocker", "Simple highpass filter to remove DC components from audio", "Yes", "N/A", "Planned", "" + "Crossover", "Splits up audio into at most four different bands for individual processing", "Yes", "(Possible via IIR)", "Upstream", "1.5" + "DCBlocker", "Simple highpass filter to remove DC components from audio", "Yes", "N/A", "Upstream", "1.4" "Demultiplexer", "Copies PCM sample frames from one source buffer to multiple sink buffers with configurable channels", "Yes", "N/A", "Upstream", "1.4" - "Dynamic Range Processor", "Compresses and expands an audio signal to bring out quiet sounds and dampening loud sounds", "Yes", "Yes", "Planned", "" + "Dynamic Range Processor", "Compresses and expands an audio signal to bring out quiet sounds and dampening loud sounds", "Yes", "Yes", "In Progress", "1.7 (expected)" "FIR equalizer", "Enhances frequency response with a finite impulse response filter, e.g. improve speaker sound", "Yes", "Xtensa HiFi3", "Upstream", "1.4" "IIR equalizer", "Enhances frequency response with an infinite impulse response filter, e.g. cancel DC component or improve speaker sound", "Yes", "Xtensa HiFi3", "Upstream", "1.4" "Mixer", "Sums with unity gain and saturation source buffers of multiple pipelines to a single output sink buffer", "Yes", "No", "Upstream", "1.0" - "Multi-microphone beamformer", "Enhances directivity of microphone array towards steer direction and attenuates diffuse noise", "Yes", "Yes", "Planned", "1.6" + "Multi-microphone beamformer", "Enhances directivity of microphone array towards steer direction and attenuates diffuse noise", "Yes", "Yes", "Upstream", "1.6" "PCM converter", "Not a dedicated component but provides for DAI and host components conversion between PCM formats e.g. S16_LE, S24_LE, and S32_LE", "Yes", "Xtensa HiFi3", "Upstream", "1.5" "Sample rate conversion", "Converts between common sample rates to connect multi-rate synchronous pipelines", "Yes", "Xtensa HiFi3", "Upstream", "1.3" "Volume", "Provides real-time stream gain controls to the user", "Yes", "Xtensa HiFi3", "Upstream", "1.0" @@ -37,5 +37,7 @@ Further information on specific algorithms is forthcoming. .. toctree:: :maxdepth: 1 - src/sample_rate_conversion demux/demux.rst + eq/equalizers_tuning + src/sample_rate_conversion + tdfb/time_domain_fixed_beamformer diff --git a/algos/tdfb/beamformer_delay_and_sum.png b/algos/tdfb/beamformer_delay_and_sum.png new file mode 100644 index 00000000..7ec8d1b7 Binary files /dev/null and b/algos/tdfb/beamformer_delay_and_sum.png differ diff --git a/algos/tdfb/circular_array.png b/algos/tdfb/circular_array.png new file mode 100644 index 00000000..52357cc3 Binary files /dev/null and b/algos/tdfb/circular_array.png differ diff --git a/algos/tdfb/circular_di.png b/algos/tdfb/circular_di.png new file mode 100644 index 00000000..c0cfe27c Binary files /dev/null and b/algos/tdfb/circular_di.png differ diff --git a/algos/tdfb/circular_filters.png b/algos/tdfb/circular_filters.png new file mode 100644 index 00000000..3f6e4010 Binary files /dev/null and b/algos/tdfb/circular_filters.png differ diff --git a/algos/tdfb/circular_polar.png b/algos/tdfb/circular_polar.png new file mode 100644 index 00000000..deb9ff95 Binary files /dev/null and b/algos/tdfb/circular_polar.png differ diff --git a/algos/tdfb/circular_spatial.png b/algos/tdfb/circular_spatial.png new file mode 100644 index 00000000..80e266a3 Binary files /dev/null and b/algos/tdfb/circular_spatial.png differ diff --git a/algos/tdfb/circular_wng.png b/algos/tdfb/circular_wng.png new file mode 100644 index 00000000..3925e11d Binary files /dev/null and b/algos/tdfb/circular_wng.png differ diff --git a/algos/tdfb/line_array.png b/algos/tdfb/line_array.png new file mode 100644 index 00000000..69bcaade Binary files /dev/null and b/algos/tdfb/line_array.png differ diff --git a/algos/tdfb/lshape_array.png b/algos/tdfb/lshape_array.png new file mode 100644 index 00000000..69aa4484 Binary files /dev/null and b/algos/tdfb/lshape_array.png differ diff --git a/algos/tdfb/lshape_array_rot.png b/algos/tdfb/lshape_array_rot.png new file mode 100644 index 00000000..8d1157df Binary files /dev/null and b/algos/tdfb/lshape_array_rot.png differ diff --git a/algos/tdfb/rectangular_array.png b/algos/tdfb/rectangular_array.png new file mode 100644 index 00000000..76a9fe3a Binary files /dev/null and b/algos/tdfb/rectangular_array.png differ diff --git a/algos/tdfb/time_domain_fixed_beamformer.rst b/algos/tdfb/time_domain_fixed_beamformer.rst new file mode 100644 index 00000000..ff4cf762 --- /dev/null +++ b/algos/tdfb/time_domain_fixed_beamformer.rst @@ -0,0 +1,563 @@ +.. _time-domain-fixed-beamformer: + +Time Domain Fixed Beamformer (TDFB) +################################### + +.. contents:: + :depth: 3 + +Introduction +************ + +The beamformer is a pre-processing component for microphones. It +improves microphone signal-to-noise capturing by providing spatial +noise suppression for ambient noise. The non-correlated self-noise of the +microphones and electronics can be mitigated by summing two or +more microphones into an output channel stream. + +The beamformer's operation is easiest to understand with a delay-and-sum +beamformer type for a line array shape. The microphones are assumed to +be in far-field of the sound source. At a sufficient distance, the +spherical waves such as from a person's mouth appears as planar. The waves +propagate at a slightly temperature-dependent speed of 340 m/s. The +beamformer can sum the microphones outputs in-phase for the look +direction. The direction is called the azimuth angle. + +The beamformer can also, if desired, be set up to do the opposite to +null the signal from a specified angle by delaying the signal for an +opposite phase sum. + +.. figure:: beamformer_delay_and_sum.png + + Example delay-and-sum beamformer with two microphones at a 50 mm + distance. The sound waves arrive at an 18 degree azimuth angle. + +In the above example, the plane waves arrive from source at an 18 degrees +azimuth angle versus the normal line array axis. The task is to +determine the needed delay values for delay elements D\ :sub:`1` and +D\ :sub:`2`. Since the first microphone receives the wave before the +second microphone, the signal from the first microphone must be delayed +by D\ :sub:`1` before the summing operation. The Delay value of D\ +:sub:`2` is set to zero. + +The needed delay value is the sound propagation time equivalent length +of edge **a** in the formed right triangle with edges **a**, **b**, and **c**. +The lengths of the edges are time values that are computed from the +microphone's known distance, speed of sound, and azimuth angle. + +The length of edge **c** is + +:math:`t_c = \frac{d}{v} = \frac{50~mm}{340~m/s} \approx 147~us` + +The angle between edges **a** and **c** is 90 - az. Therefore, the arrival +time difference t\ :sub:`a` to apply for D\ :sub:`1` with an 18 degree steer +angle (az) is + +:math:`t_a = t_c \cos (90 - azimuth) \approx 45~us` + +The different az angles shows that the delay to apply varies +between 0 (az = 0) and 147 us (az = 90). For negative azimuth angles, +the applied delays for D\ :sub:`1` and D\ :sub:`2` are swapped. + +Such a delay is typically applied by an all-pass digital filter. The beam +patterns for line shape one-dimensional arrays have a rotational +symmetrical beam pattern. In the above example with D\ :sub:`1` and +D\ :sub:`2` set, the array would also pass the waveform from an 180 - az +direction. The beam shape resembles a bent ellipse for broadside. A +3D cone-like beam pattern is possible only for end-fire angles of +90 +or -90 degrees. A 2D array like a circular shape can provide a 360 +degree steerable cone in an azimuth plane. + +Analog directional microphones, such as a 3D cardioid shape for an +end-fire angle, are actually single or dual diaphragm microphones with +tuned acoustical ports or analog all-pass electronics that achieve +similar additional delays for delay-and-sum. Due to their large mechanical +size, they are common only in studio equipment. Consumer electronics +such as notebooks form factors can fortunately provide various-shaped +microphone arrays while the studio microphone-like approach is +impossible. + +Beamformer types +**************** + +Main beamformer types are fixed and adaptive. The implementations +can be in time or frequency domain. + +The fixed beamformer has a simple time domain with a pre-defined look angle +(azimuth, elevation). The audio source is not tracked automatically. Audio +waveforms from other angles are attenuated. The beam shape is not +particularly narrow (with a low microphone count such as 2 -4) so there's no +need to track the subject; we automatically know the approximate angle for +the use case. + +Adaptive beamformers usually seek to minimize the output signal +while unblocking the configured pass direction. This differs from the fixed +beamformer, where the assumed or theoretical noise characteristic is +pre-programmed. There is no delay to adapt (same performance from the +beginning) or risk for mis-adaptation (desired signal corrupts), but +the practical performance is somewhat limited in a theoretical noise field. +The time domain implementation is low-latency with no added delay for signal +framing for the transform domain. It can compute nearly any number of stream +frames due to no block size constraints. The filter bank adds a small delay, +such as 2 -10 ms, that depends on the configuration. + +The fixed beamformer must be configured for every type of +microphone array geometry. The beam can be steered by applying a new +programming filter (with presets in a later version of TDFB) if the +capture subject angle has changed based on camera face recognition or +acoustical direction of the arrival estimation. Also, quick beam direction +switching for some array geometries is also possible by rotating the input channels at the algorithm input. + +Microphone array geometries +*************************** + +Line +==== + +In the line array, microphone locations form a straight line. As shown in the +figure below, microphone numbers correspond to audio channels at the +beamformer input. In stereo, audio channel 1 is the left channel. + +The array size is described by microphones count and the space between +two neighboring microphones. In the example below, the spacing of the four +microphones is 30 mm. The steer azimuth angle is 90 degrees. The beam +direction for positive angles (0 to 90) travels towards microphone 1. The +beam direction towards the last microphone has a negative angle +(0 to -90). + +.. figure:: line_array.png + :width: 600 + + Line array with four microphones. + +The code to create the above design is below. The Octave GUI must +be started from the TDFB ``tune`` directory: + + +.. code-block:: bash + + cd $SOF_WORKSPACE/sof/tools/tune/tdfb + octave --gui & + +In the Octave shell, enter the following commands or create a short script +(such as ``ex_line.m``) and run it. Remember to end each line with a +semicolon to avoid long prints of internal data structures. + +.. code-block:: octave + + bf = bf_defaults(); % Get defaults + bf.array = 'line'; % Calculate xyz coordinates for line array + bf.mic_n = 4; % four microphones + bf.mic_d = 30e-3; % 30 mm spacing + bf.steer_az = 90; % Azimuth angle 90 deg + bf = bf_design(bf); + +The above design is simplified and lacks the output files definition; it +assumes a default of four microphones to one output channel configuration +but it creates the plots for geometry and theoretical characteristics. + +Circular +======== + +In the circular array, microphones are at an equal radius with equal +angular spacing. The microphones are numbered counterclockwise when +viewing the array from above (positive z-axis). + +The azimuth angle (-180 to +180) is at 90 degrees in our example. A 0 +degree angle points exactly towards microphone 1. The circular array is +two-dimensional. If the elevation angle (-90 to 90 degrees) is set to a +non-zero value, the look direction can be tilted up or down. A positive +elevation angle tilts the beam upwards. + +.. figure:: circular_array.png + :width: 600 + + Circular array with six microphones. + +This design was created using commands, as shown below. The plot_box is +optional; it only zooms the plot axis to a 150 mm wide cube. + +.. code-block:: octave + + bf = bf_defaults(); % Get defaults + bf.array = 'circular'; % Calculate xyz coordinates for line array + bf.mic_n = 6; % six microphones + bf.mic_r = 30e-3; % 30 mm radius + bf.steer_az = 90; % Azimuth angle 90 deg + bf.plot_box = 150e-3; + bf = bf_design(bf); + +The view can be rotated as a normal 3D plot. In Matlab, mouse rotation is +available. In Octave, the command view() can be used to view the array from +another angle. + +.. code-block:: octave + + figure(1) + v = view() + view(130, 30) + +The azimuth view was rotated by 180 degrees (-50 to +130). The view has +no impact on the beamformer design. + +Rectangular +=========== + +A rectangular array is shown below. The numbering of microphones for +the first row is the same as for the line array. The number continues from +the left-most microphone of the next row. + + +.. figure:: rectangular_array.png + :width: 600 + + Rectangular array with six microphones. + +The code for the design is as follows: + +.. code-block:: octave + + bf = bf_defaults(); % Get defaults + bf.array = 'rectangle'; % Calculate xyz coordinates for rectangular array + bf.mic_nxy = [3 2]; % of 3 x 2 + bf.mic_dxy = [30e-3 30e-3]; % Same x and y spacing + bf.plot_box = 150e-3; + bf = bf_design(bf); + + +L-shape +======= + +The L-shape array is much like the rectangular array but only the left and +bottom edge of the microphones rectangle is populated. + +.. figure:: lshape_array.png + :width: 600 + + L-shape array with four microphones. + +It is produced by the following: + +.. code-block:: octave + + bf = bf_defaults(); % Get defaults + bf.array = 'lshape'; % Calculate xyz coordinates for rectangular array + bf.mic_nxy = [3 2]; % of 3 x 2 + bf.mic_dxy = [30e-3 30e-3]; % Same x and y spacing + bf.steer_az = 90; % Azimuth angle 90 deg + bf.plot_box = 150e-3; + bf = bf_design(bf); + + +Arbitrary XYZ +============= + +All microphone coordinates can be defined manually. The following +example shows a tetrahedron shape with four microphones. The microphones +order is as they are presented in the design script. + +.. figure:: xyz_array.png + :width: 600 + + XYZ array with four microphones. + +The tetrahedron shape is made with the following script: + +.. code-block:: octave + + bf = bf_defaults(); % Get defaults + bf.array = 'xyz'; % Enter xyz directly, note that script centers it + bf.plot_box = 100e-3; % Small 100 mm plot box + bf.steer_az = 90; % Steer array to 90 deg azimuth + + % Coordinates from https://en.wikipedia.org/wiki/Tetrahedron + s = 30e-3/sqrt(8/3); % Scale to 30 mm + bf.mic_x = [ sqrt(8/9) -sqrt(2/9) -sqrt(2/9) 0] * s; + bf.mic_y = [ 0 sqrt(2/3) -sqrt(2/3) 0] * s; + bf.mic_z = [-sqrt(1/3) -sqrt(1/3) -sqrt(1/3) 1] * s; + + bf = bf_design(bf); + +Note that the beamformer design is totally unaware of the surface effects +of the object. The design equations assume that the microphones "float" in +free space. Particularly, a 3D array will be impacted by device mechanics +so custom design equations may be needed. + +Rotation of the array +===================== + +Change the array orientation by changing the X, Y, and Z axis rotation +angle in the ``array_angle``. The following example rotates the array like +it would be on a notebook display lid corner at a 60 degree angle. The steer +azimuth is set to 0 degrees towards the notebook user. The plot view angle +is changed also. + +.. code-block:: octave + + bf = bf_defaults(); % Get defaults + bf.array = 'lshape'; % Calculate xyz coordinates for rectangular array + bf.mic_nxy = [3 2]; % of 3 x 2 + bf.mic_dxy = [30e-3 30e-3]; % Same x and y spacing + bf.steer_az = 0; % Azimuth angle 90 deg + bf.array_angle = [180 60 0]; % Array rotation angles for xyz + bf.plot_box = 150e-3; + bf = bf_design(bf); + figure(1) + view(140,30) + + +.. figure:: lshape_array_rot.png + :width: 600 + + Rotated L-shape array. + +Filter bank design procedure +**************************** + +.. note:: + The following procedure is based on equations published in "Superdirective Microphone Arrays" by Joerg Bitzer and K. Uwe Simmer. It is available in book "Microphone Arrays" by Michael Brandstein and Darren Ward (Springer 2001). + +The filter bank design procedure is located in the ``bf_design.m`` file. +Briefly, the design is done entirely in the FFT frequency domain with a +default of 512 bins. The conversion to a time domain FIR filter bank for the +desired filter length is done with an IFFT and kaiser window. The longer +the filters, the less they deviate from the super-directive frequency domain +design. + +The procedure starts with computing the x, y, z coordinates of the +virtual sound source at the specified azimuth (``steer_az``) and elevation +(``steer_el``) angles. The point is by default 5m radius away which is +enough for far-field with planar sound waves that have typical array +dimensions but can be altered (``steer_r``). Near-field (less than +1m) design may suffer from a lack of sound level compensation for +microphone channels. + +The noise field is assumed to be a theoretical homogeneous type; a +coherence matrix is formed with knowledge of the microphone's +geometry. The super-directive design is a set of coefficients that +minimize the noise power spectral density of filtered and summed +microphone signals but provides a distortion-less response towards the +look direction. The used design equations compute a Minimum Variance +Distortion-less Response (MVDR) beamformer. The details are found in the +``bf_design.m`` script and the above-mentioned book. + +The elegance of the frequency domain design is that the equations can +be solved per each single frequency bin in the FFT domain. Since the +process is potentially numerically unstable, a diagonal loading factor is +added to the coherence matrix prior to inversion. The parameters is ``mu_db``. It defaults to -50 dB but smaller or larger values can be tested for best +results. Smaller than default values need to be used with care. The self +noise of the microphones, via white noise gain (WNG), could even get boosted +with near zero diagonal load designs. Large diagonal load improves the +robustness of the design but may compromise other characteristic-like beam +patterns or diffuse noise field suppression. + +After solving the equation for all frequencies, the filters for each +microphone channel are converted to a time domain with IFFT and window +function. The window function shortens the impulse responses to the +desired length. The windowing naturally changes the characteristics so +different filter lengths (fir_beta) should be tested. + + +Design examples +*************** + +Circular array +============== + +In reference to the earlier circular array design example, note that the +design creates several plot windows in addition to the geometry and steer +direction plot. The following examples below show the beam pattern +characteristics. The polar plot shows only frequencies 1, 2, 3, and 4 kHz. +The colorful frequency vs. angle shows a more detailed view for the same but +with all frequencies up to Nyquist Fs/2. + +Notice that the beam patterns are different for different frequencies. A +beamformer type exists for constant directivity but the performance against +diffuse noise is not as good. The narrower beam towards higher frequencies +in super-directive achieves the higher ambient noise suppression. + +At frequencies above 5 kHz, side lobes pass the signal as well as the main +beam. Those are caused by spatial aliasing. The wave length of audio gets +smaller than the array microphones distance. The array dimensions must be +decreased if spatial aliasing needs to be avoided. In most cases, some of it +can be tolerated. + +In the look direction beam, some attenuation exists at lowest and highest +frequencies. The response can be made more flat by increasing the filter +length from the default 64 (``fir_length``). + +.. figure:: circular_polar.png + :width: 600 + + Polar response of the circular array. + +.. figure:: circular_spatial.png + :width: 600 + + Frequency vs. angle response of the circular array. + +The performance of the array and beamformer can also be characterized +with White Noise Gain (WNG) and Directivity Index (DI) plots. The WNG +plot shows the amount of attenuation the design provides for uncorrelated +noise. For example, self-noise of the microphones is an uncorrelated noise +type. The directivity index shows the attenuation of noise that arrives from +other directions than the steer direction. The noise that arrives from +surrounding noise sources and reflects from walls and other surfaces and is +correlated is called *diffuse field noise*. + +The impact of diagonal load ``mu_db`` in an example range of -100 to -20 can +be tried and seen best in these plots. A near zero diagonal load with a +-200 dB value makes the directivity even negative at some frequencies. Such +beamformer design would boost noise at those frequencies! + +.. figure:: circular_wng.png + :width: 600 + + White noise gain of the circular array. + +.. figure:: circular_di.png + :width: 600 + + Directivity index of the circular array. + +Finally, the FIR coefficients plot can be checked for a sane-looking result. +The plot below shows a typical symmetrical FIR impulse response. + +.. figure:: circular_filters.png + :width: 600 + + Filter coefficients for the circular array. + +Line array +========== + +The circular arrays have nearly identical beam patterns in any direction. As +an exercise, compare the beam patterns of a 4 mic line array to a 0 degrees +azimuth steer vs. 90 or -90 degrees. + +Limitations +=========== + +The above examples defaulted to N microphones to a single channel output. +However, due to a current limitation in the SOF pipeline, the PCM and DAI +must have the same word length. This limitation will be addressed in a future +SOF release. + +As a workaround, the beamformer can duplicate its output channel to +the needed number of channels; there can also be several beams in the +design for different output channels. The latter is actually preferred +for the generic stereo capture PCM in typical notebooks. The typical array +dimensions do not provide much subjective stereo sensation. + +Dual mono example +----------------- + +A complete dual mono 0 degree azimuth beamformer can be designed and +exported with a script. The beam characteristic is a 50 mm spaced pair but +the ``num_output_channels`` and ``output_channel_mix`` settings alter the +TDFB output mixer configuration. + +.. code-block:: octave + + bf = bf_defaults(); % Get defaults + bf.array = 'line'; % Calculate xyz coordinates for line array + bf.mic_n = 2; % two microphones + bf.mic_d = 50e-3; % 50 mm spacing + bf.fs = 16e3; % 16 kHz rate + bf.steer_az = 0; % 0 degree azimuth + + % Two output channels + bf.num_output_channels = 2; + + % Mix filter 1 output to channels 0 and 1 (2^0 + 2^1 = 3) + % Mix filter 2 output to channels 0 and 1 (2^0 + 2^1 = 3) + bf.output_channel_mix = [3 3]; + + bf = bf_filenames_helper(bf); + bf = bf_design(bf); + bf_export(bf); + +Example with two beams +---------------------- + +The following example creates a -10 degree beam for the left channel and a ++10 degree azimuth beam for the right channel. It's quite suitable for notebooks with an emphasis on user direction (and opposite due to rotational +symmetry of line array) and still have a noticeable channel separation. + +The procedure uses ``bf_merge()`` to combine bf1 and bf2 designs. The +different ``out_channel_mix`` vectors sum the filters to the proper +channels. The filenames are redefined to avoid overwriting the single beam +files. + +.. code-block:: octave + + % Get defaults + bf1 = bf_defaults(); + bf1.fs = 48e3; + + % Setup array + bf1.array='line'; + bf1.mic_n = 2; + bf1.mic_d = 50e-3; + + % Copy settings for bf2 + bf2 = bf1; + + % Design beamformer 1 (left) + bf1.steer_az = -10; + bf1.input_channel_select = [0 1]; % Input two channels + bf1.output_channel_mix = [1 1]; % Mix both filters to channel 2^0 + bf1.fn = 10; % Figs 10.... + bf1 = bf_filenames_helper(bf1); + bf1 = bf_design(bf1); + + % Design beamformer 2 (right) + bf2.steer_az = +10; + bf2.input_channel_select = [0 1]; % Input two channels + bf2.output_channel_mix = [2 2]; % Mix both filters to channel 2^1 + bf2.fn = 20; % Figs 20.... + bf2 = bf_filenames_helper(bf2); + bf2 = bf_design(bf2); + + % Merge two beamformers into single description, set file names + bfm = bf_merge(bf1, bf2); + bfm.sofctl_fn = fullfile(bfm.sofctl_path, 'coef_line2_50mm_pm10deg_48khz.txt'); + bfm.tplg_fn = fullfile(bfm.tplg_path, 'coef_line2_50mm_pm10deg_48khz.txt'); + + % Export files for topology and sof-ctl + bf_export(bfm); + +.. figure:: two_beams_left.png + :width: 600 + + Beam pattern for the left channel. + +.. figure:: two_beams_right.png + :width: 600 + + Beam pattern for the right channel. + +Simulation +********** + +Measurement in an anechoic chamber is recommended for validation. A quick +check, however, is available to validate the configuration blob and C code +version TDFB operation. + +The script ``tdbf_test.m`` performs a beam patten test. To test your own beamformer design, the proper file name must be edited to ``test-placback.m`` +(currently it is ``coef_line2_50mm_pm90deg_48khz.m4``) and the test +topologies must be regenerated. + +.. code-block:: bash + + cd $SOF_WORKSPACE/sof/ + scripts/build-tools.sh -t + scripts/rebuild-testbench.sh + cd cd tools/test/audio + octave --gui & + tdfb_test + +This simulation is empirical and executed with testbench. The previous +``bf_design()`` call for the array created the sine rotation, diffuse +field, and random field waveform data files that the simulation run +used. The theoretical and simulated beam patterns should match. diff --git a/algos/tdfb/two_beams_left.png b/algos/tdfb/two_beams_left.png new file mode 100644 index 00000000..5663aa2b Binary files /dev/null and b/algos/tdfb/two_beams_left.png differ diff --git a/algos/tdfb/two_beams_right.png b/algos/tdfb/two_beams_right.png new file mode 100644 index 00000000..a773888a Binary files /dev/null and b/algos/tdfb/two_beams_right.png differ diff --git a/algos/tdfb/xyz_array.png b/algos/tdfb/xyz_array.png new file mode 100644 index 00000000..fe775342 Binary files /dev/null and b/algos/tdfb/xyz_array.png differ diff --git a/architectures/dsp/index.rst b/architectures/dsp/index.rst deleted file mode 100644 index 8cac2ec6..00000000 --- a/architectures/dsp/index.rst +++ /dev/null @@ -1,95 +0,0 @@ -.. _architecture-dsp: - -DSP Architecture -################ - -Currently SOF has support for the Cadence Xtensa DSP architecture in UP and SMP -modes in the upstream code base today. - -The diagram below shows the high-level firmware architecture with the -Baytrail platform integration as an example. The firmware is divided into four -main sections: - -#. **Generic microkernel.** The microkernel manages and abstracts the - DSP hardware for the rest of the system. It also exports C APIs for - memory allocation, scheduling work, event notifications, and power - management. - -#. **Audio components.** The audio components can be used to form an - audio processing pipeline from the host DMA buffer to the DSP digital - audio interface. Audio components will have a source and sink buffer - where they will usually transform or route audio data as part of their - processing. - -#. **Audio task.** The audio task manages the audio pipelines at run - time; it manages the transportation of data from source to sink - component within the pipeline. The pipelines are currently statically - defined in the firmware, but infrastructure is now in place to allow the - dynamic creation of pipelines from Linux userspace. - -#. **Platform drivers.** The platform drivers are used to control any - external IP to the DSP IP. This will usually be things like DMA engines - or DAI (Digital Audio Interface) controllers. These drivers are used by - the audio components and pipelines to send/receive data to/from the host - and external codecs. - - .. figure:: ../images/fw-arch-diag.png - :align: center - :alt: SOF Architecture - :width: 800px - - `Sound Open Firmware Architecture using Intel Baytrail Platform` - - -Each section above is well insulated from the other sections by partitioning -code into separate directories and by using DSP and platform agnostic generic -APIs for orchestration between the sections. - -Adding a new DSP architecture to SOF -==================================== - -This is not yet a guide for architecure porting, but in general are two ways to -add support for new DSP architectures to SOF. - -#. Write a new Hardware Astraction Layer (HAL) for your DSP. - -#. Use an existing RTOS that supports your DSP architecture as a HAL for SOF. - -Both methods require a working compiler for the new DSP architecture and -preferrably an emulation environment or hardware debugger to help with the -bringup and debug. - -Method 1 - New HAL ------------------- - -The main work in adding the new architecture HAL is duplicating and porting the -src/arch directory to your new architecture. The code in the architecture -directory mainly deals with architecture abstraction and initialization of any -architecture IP like MMU, IRQs and caches alongside providing optimized -versions of some common C functions (memcpy, memset, etc) for that architecture. -Adding a new architecture also usually means adding a new host platform too. - -Method 2 - Use existing RTOS ----------------------------- - -This method involves creating a HAL by wrapping the RTOS functions used by SOF -as thinly as possible (i.e. to compile out). It also means removing unused code -from the SOF build in order to use the RTOS version if desireable i.e. -allocator, schedulers, messaging etc. The final stage is to link the SOF audio -code to the RTOS. - - -Vendor Specific Architecture Information -======================================== - -Architecture details of any vendor specific code and flows. This is architecture -specific to a single vendor that falls outside the scope of the high level -generic SOF architecture. - -Intel ------ - -.. toctree:: - :maxdepth: 1 - - intel/index diff --git a/architectures/firmware/index.rst b/architectures/firmware/index.rst new file mode 100644 index 00000000..86c79454 --- /dev/null +++ b/architectures/firmware/index.rst @@ -0,0 +1,12 @@ +.. _architecture-firmware: + +Firmware Architecture +##################### + +.. toctree:: + :maxdepth: 1 + + sof-common/index + sof-xtos/index + sof-zephyr/index + intel/index \ No newline at end of file diff --git a/architectures/firmware/intel/ace/iadk_modules.rst b/architectures/firmware/intel/ace/iadk_modules.rst new file mode 100644 index 00000000..2410faa7 --- /dev/null +++ b/architectures/firmware/intel/ace/iadk_modules.rst @@ -0,0 +1,48 @@ +.. _iadk-modules: + +IADK Modules Adapter +#################### + +IADK Modules +============ + +An IADK module is a software component that can be represented by a processing +block with some input pins and output pins capable to transport a digital +signal into the block or out of the block. Processing is applied on an input +signal or a combination of input signals, some input signals may only be used +as reference signals that influence the processing on other input signals. +The result of the processing is written into the output signals. The behavior +of the block can be controlled using a configuration parameter interface. + +An IADK module communicates with base firmware and other modules through +ProcessingModuleInterface API and access base firmware services via +System Service API. + + +IADK Module Adapter +=================== + +The IADK Module Adapter is an extension to SOF component infrastructure that +allows to integrate modules developed under IADK (Intel Audio Development Kit) +Framework. +IADK modules uses uniform set of interfaces and are linked into separate +library. These modules are loaded in runtime through Library Manager and then +after registration into SOF component infrastructure are interfaced through +module adapter API. +Since IADK modules uses ProcessingModuleInterface API to control/data transfer +and SystemService API to use base FW services from internal module code, there +is a communication shim layer defined. + +The SOF IADK Module Adapter is designed to interact with IADK modules without +their code modification. Therefore C++ function, structures and variables +definition are here kept with original form from IADK Framework. +This provides binary compatibility with already developed 3rd party modules. + +There are three entities in IADK Module Adapter Package: + * System Agent - A mediator to allow the custom module to interact with the + base SOF FW. It calls IADK module entry point and provides all necessary + information to connect both sides of ProcessingModuleInterface and + System Service. + * System Service - exposes of SOF base FW services to the module. + * Processing Module Adapter - SOF base FW side of ProcessingModuleInterface + API diff --git a/architectures/firmware/intel/ace/index.rst b/architectures/firmware/intel/ace/index.rst new file mode 100644 index 00000000..8f2507bd --- /dev/null +++ b/architectures/firmware/intel/ace/index.rst @@ -0,0 +1,13 @@ +.. _ace-architecture-intel: + +Intel ACE Architecture +###################### + +The details below are specific to Intel products with an audio DSP ACE +architecture using SOF. Intel ACE is a next generation of Intel Audio DSP +solution that replaces Intel cAVS architecture. + +.. toctree:: + :maxdepth: 1 + + iadk_modules diff --git a/architectures/dsp/intel/cavs-boot/apollolake/apl-boot-ldr.rst b/architectures/firmware/intel/cavs/cavs-boot/apollolake/apl-boot-ldr.rst similarity index 94% rename from architectures/dsp/intel/cavs-boot/apollolake/apl-boot-ldr.rst rename to architectures/firmware/intel/cavs/cavs-boot/apollolake/apl-boot-ldr.rst index 2b6c68c5..04fd5d0e 100644 --- a/architectures/dsp/intel/cavs-boot/apollolake/apl-boot-ldr.rst +++ b/architectures/firmware/intel/cavs/cavs-boot/apollolake/apl-boot-ldr.rst @@ -1,7 +1,7 @@ .. _apl-boot-ldr: -Apollolake Boot Loader -###################### +Apollo Lake Boot Loader +####################### * Additional HPSRAM memory initialization. * L2 cache disabled in ``boot_entry`` (enabled by default by APL ROM). diff --git a/architectures/dsp/intel/cavs-boot/apollolake/apl-boot-rom.rst b/architectures/firmware/intel/cavs/cavs-boot/apollolake/apl-boot-rom.rst similarity index 99% rename from architectures/dsp/intel/cavs-boot/apollolake/apl-boot-rom.rst rename to architectures/firmware/intel/cavs/cavs-boot/apollolake/apl-boot-rom.rst index 037b5718..5070e745 100644 --- a/architectures/dsp/intel/cavs-boot/apollolake/apl-boot-rom.rst +++ b/architectures/firmware/intel/cavs/cavs-boot/apollolake/apl-boot-rom.rst @@ -1,7 +1,7 @@ .. _apl-boot-rom: -Apollolake Boot ROM -################### +Apollo Lake Boot ROM +#################### Progress of the boot process is reflected by the status information updated by the ROM in an SRAM area called *FW Registers*. It is available to the host diff --git a/architectures/dsp/intel/cavs-boot/apollolake/images/apl-rom-flow.pu b/architectures/firmware/intel/cavs/cavs-boot/apollolake/images/apl-rom-flow.pu similarity index 100% rename from architectures/dsp/intel/cavs-boot/apollolake/images/apl-rom-flow.pu rename to architectures/firmware/intel/cavs/cavs-boot/apollolake/images/apl-rom-flow.pu diff --git a/architectures/dsp/intel/cavs-boot/apollolake/index.rst b/architectures/firmware/intel/cavs/cavs-boot/apollolake/index.rst similarity index 62% rename from architectures/dsp/intel/cavs-boot/apollolake/index.rst rename to architectures/firmware/intel/cavs/cavs-boot/apollolake/index.rst index 043d6503..21ee69cb 100644 --- a/architectures/dsp/intel/cavs-boot/apollolake/index.rst +++ b/architectures/firmware/intel/cavs/cavs-boot/apollolake/index.rst @@ -1,7 +1,7 @@ .. _cavs-boot-apl: -Apollolake Boot Process -####################### +Apollo Lake Boot Process +######################## .. toctree:: :maxdepth: 1 diff --git a/architectures/dsp/intel/cavs-boot/cavs-dsp-boot-overview.rst b/architectures/firmware/intel/cavs/cavs-boot/cavs-dsp-boot-overview.rst similarity index 98% rename from architectures/dsp/intel/cavs-boot/cavs-dsp-boot-overview.rst rename to architectures/firmware/intel/cavs/cavs-boot/cavs-dsp-boot-overview.rst index e846bbc4..24fde842 100644 --- a/architectures/dsp/intel/cavs-boot/cavs-dsp-boot-overview.rst +++ b/architectures/firmware/intel/cavs/cavs-boot/cavs-dsp-boot-overview.rst @@ -16,7 +16,7 @@ There are two main DSP boot flows: IPC Communication with DSP ROM ****************************** -Once the master DSP core (#0) is powered up and reset by the host driver, an +Once the primary DSP core (#0) is powered up and reset by the host driver, an IPC communication with the DSP ROM is required in order to set the boot options (see Boot Path Control Messages for details and list of platforms that require this step). It is a one-way message that does not require a response diff --git a/architectures/dsp/intel/cavs-boot/images/boot-dsp.pu b/architectures/firmware/intel/cavs/cavs-boot/images/boot-dsp.pu similarity index 100% rename from architectures/dsp/intel/cavs-boot/images/boot-dsp.pu rename to architectures/firmware/intel/cavs/cavs-boot/images/boot-dsp.pu diff --git a/architectures/dsp/intel/cavs-boot/images/boot-ldr-flow.pu b/architectures/firmware/intel/cavs/cavs-boot/images/boot-ldr-flow.pu similarity index 100% rename from architectures/dsp/intel/cavs-boot/images/boot-ldr-flow.pu rename to architectures/firmware/intel/cavs/cavs-boot/images/boot-ldr-flow.pu diff --git a/architectures/dsp/intel/cavs-boot/images/loading-bins.pu b/architectures/firmware/intel/cavs/cavs-boot/images/loading-bins.pu similarity index 100% rename from architectures/dsp/intel/cavs-boot/images/loading-bins.pu rename to architectures/firmware/intel/cavs/cavs-boot/images/loading-bins.pu diff --git a/architectures/dsp/intel/cavs-boot/images/write-bin.pu b/architectures/firmware/intel/cavs/cavs-boot/images/write-bin.pu similarity index 100% rename from architectures/dsp/intel/cavs-boot/images/write-bin.pu rename to architectures/firmware/intel/cavs/cavs-boot/images/write-bin.pu diff --git a/architectures/dsp/intel/cavs-boot/index.rst b/architectures/firmware/intel/cavs/cavs-boot/index.rst similarity index 66% rename from architectures/dsp/intel/cavs-boot/index.rst rename to architectures/firmware/intel/cavs/cavs-boot/index.rst index 098f12f2..54ba2ef6 100644 --- a/architectures/dsp/intel/cavs-boot/index.rst +++ b/architectures/firmware/intel/cavs/cavs-boot/index.rst @@ -4,9 +4,9 @@ Booting up CAVS ADSP #################### Intel has several generations of audio DSP. "CAVS" versions relate to the audio -DSP in Skylake Core and Apollolake Atom platforms onwards. +DSP in Skylake Core and Apollo Lake Atom platforms onwards. -Baytrail, Cherrytrail, Braswell, Haswell and Broadwell audio DSPs have a simpler +Bay Trail, Cherry Trail, Braswell, Haswell, and Broadwell audio DSPs have a simpler boot flow using memory copy and not authentication. diff --git a/architectures/dsp/intel/images/idc-send-message.pu b/architectures/firmware/intel/cavs/images/idc-send-message.pu similarity index 100% rename from architectures/dsp/intel/images/idc-send-message.pu rename to architectures/firmware/intel/cavs/images/idc-send-message.pu diff --git a/architectures/dsp/intel/index.rst b/architectures/firmware/intel/cavs/index.rst similarity index 54% rename from architectures/dsp/intel/index.rst rename to architectures/firmware/intel/cavs/index.rst index cd96cc5a..4f3ac9a7 100644 --- a/architectures/dsp/intel/index.rst +++ b/architectures/firmware/intel/cavs/index.rst @@ -1,9 +1,10 @@ -.. _architecture-intel: +.. _cavs-architecture-intel: -Intel DSP Architecture -###################### +Intel cAVS Architecture +####################### -The details below are specific to Intel products with an audio DSP using SOF. +The details below are specific to Intel products with an audio DSP cAVS +architecture using SOF. .. toctree:: :maxdepth: 1 diff --git a/architectures/dsp/intel/smp/index.rst b/architectures/firmware/intel/cavs/smp/index.rst similarity index 80% rename from architectures/dsp/intel/smp/index.rst rename to architectures/firmware/intel/cavs/smp/index.rst index 49a6753f..5aff7fc0 100644 --- a/architectures/dsp/intel/smp/index.rst +++ b/architectures/firmware/intel/cavs/smp/index.rst @@ -1,7 +1,7 @@ .. _architecture-intel-smp: -Intel Architecture -################## +Intel SMP Architecture +###################### Description *********** @@ -44,19 +44,21 @@ This structure contains pointers to the XTOS data along with struct idc *idc; }; -``struct core_context`` is allocated by master core for slave cores before -slave core boot. Address of the ``struct core_context`` is written into -``THREADPTR`` processor register, which can later be retrieved by slave core +``struct core_context`` is allocated by primary core for secondary cores before +secondary core boot. Address of the ``struct core_context`` is written into +``THREADPTR`` processor register, which can later be retrieved by the secondary core after boot. Every core has its own instance of ``THREADPTR``, so ``struct core_context`` address can be read anytime at any place of the code. Communication between cores *************************** -Master core can communicate with slave cores by sending messages using -IDC mechanism. This mechanism is pretty much the same as IPC. +Primary core can communicate with secondary cores by sending messages using +the IDC mechanism. This mechanism is pretty much the same as IPC. Important data can be sent in two 32-bit IDC registers. Cores use interrupts to register for the incoming messages. .. uml:: ../images/idc-send-message.pu +.. comment "master" has been replaced with "primary" +.. comment "slave" has been replaced with "secondary" \ No newline at end of file diff --git a/architectures/firmware/intel/index.rst b/architectures/firmware/intel/index.rst new file mode 100644 index 00000000..c7a5c4ea --- /dev/null +++ b/architectures/firmware/intel/index.rst @@ -0,0 +1,14 @@ +.. _vendor-specific: + +Vendor-Specific Architecture Information +######################################## + +Architecture details of any vendor specific code and flows. This is architecture +specific to a single vendor that falls outside the scope of the high level +generic SOF architecture. + +.. toctree:: + :maxdepth: 1 + + cavs/index + ace/index \ No newline at end of file diff --git a/developer_guides/firmware/components/component-mgmt-api.rst b/architectures/firmware/sof-common/components/component-mgmt-api.rst similarity index 100% rename from developer_guides/firmware/components/component-mgmt-api.rst rename to architectures/firmware/sof-common/components/component-mgmt-api.rst diff --git a/architectures/firmware/sof-common/components/component-module-api.rst b/architectures/firmware/sof-common/components/component-module-api.rst new file mode 100644 index 00000000..15560b87 --- /dev/null +++ b/architectures/firmware/sof-common/components/component-module-api.rst @@ -0,0 +1,42 @@ +.. _apps-comp-world: + +Component & Module Interfaces +############################# + +Introduction of the Module Adapter, an intermediate layer which provides common +code for different module API adapters, created multi-level sequences of calls +to functions and this mechanism is very expensive during run-time processing +with regards to additional cycles consumed for parameter translation and copying +as well as the additional memory for extra buffers, contexts, and the call +stack. The `module_adapter` translates the `comp_ops` interface required by the +existing infrastructure (pipelines etc.) into the `module_interface`. Then +appropriate adapter translates the `module_interface` into the final module +interface like `Cadence Codec API` or `IADK ProcessingModuleInterface`. These +dependencies are illustrated in the next figure. + +.. uml:: images/comp-module-api.pu + :caption: Component & Module API + +Maintenance of two base component (alias module) interfaces is expensive and +also confusing for the developers who wants to create a module that provides SOF +native module API. It is unclear whether this should be the `comp_ops` or the +`module_interface`. The latter is much more convenient since it is tailored for +the audio processing modules while the `comp_ops` is a multipurpose interface +cluttered with many optional operations required for *dai-comp* modules only. + +Therefore the `module_interface` should become the only SOF native module +interface that the rest of underlying infrastructure would interact with +directly. The `comp_ops` would become obsolete and eventually would be removed +from the SOF. + +The cost of extra memory required at the moment for intermediate audio data +buffers allocated inside the `module_adapter` layer (see the *Preparation Flow* +figure below) as well as cost of extra cycles required to copy the data to/from +the intermediate buffers (see the *Processing Flow* figure below) could be +avoided by removing the `comp_ops` as well. + +.. uml:: images/comp-prepare-flow.pu + :caption: Preparation Flow + +.. uml:: images/comp-copy-flow.pu + :caption: Processing Flow diff --git a/developer_guides/firmware/components/component-overview.rst b/architectures/firmware/sof-common/components/component-overview.rst similarity index 98% rename from developer_guides/firmware/components/component-overview.rst rename to architectures/firmware/sof-common/components/component-overview.rst index 4a9c97a9..edfd4a50 100644 --- a/developer_guides/firmware/components/component-overview.rst +++ b/architectures/firmware/sof-common/components/component-overview.rst @@ -83,6 +83,10 @@ Handle the component device state .. uml:: images/comp-dev-states.pu :caption: Component Device States +.. note:: + + COMP_STATE_SUSPEND is not used currently. + Refer to :c:func:`comp_set_state` in :ref:`component-api` for details. Implement the component API (comp_ops) diff --git a/architectures/firmware/sof-common/components/images/comp-copy-flow.pu b/architectures/firmware/sof-common/components/images/comp-copy-flow.pu new file mode 100644 index 00000000..0f015622 --- /dev/null +++ b/architectures/firmware/sof-common/components/images/comp-copy-flow.pu @@ -0,0 +1,42 @@ +actor pipeline +box "Module Adapter\no-- comp_ops" + participant "module_adapter" as module_adapter +end box +box "IADK Module Adapter\no-- module_interface" + participant "iadk_adapter" as iadk_adapter +end box +box "IADK Module\no-- ProcessingModuleInterface" + participant iadk_module +end box + +pipeline -> module_adapter : (1) ops->module_adapter_copy() + activate module_adapter + + module_adapter -> module_adapter : find min bytes\nto process + + note left of module_adapter + This logic is WRONG for some modules!! + end note + + module_adapter -> module_adapter : copy input from sources\nto internal buffers + + module_adapter -> module_adapter : module_process() + activate module_adapter +note left of module_adapter +Why all those extra internal calls +used only once?? +end note + + module_adapter -> iadk_adapter : (2) ops->process() + activate iadk_adapter + iadk_adapter -> iadk_module : (3) processing + module_adapter <-- iadk_adapter + deactivate iadk_adapter + + deactivate module_adapter + + module_adapter -> module_adapter : module_adapter_process_output() + activate module_adapter + module_adapter -> module_adapter : copy output from internal buffers\ntosinks + deactivate module_adapter +pipeline <-- module_adapter diff --git a/architectures/firmware/sof-common/components/images/comp-dev-states.pu b/architectures/firmware/sof-common/components/images/comp-dev-states.pu new file mode 100644 index 00000000..85d91941 --- /dev/null +++ b/architectures/firmware/sof-common/components/images/comp-dev-states.pu @@ -0,0 +1,17 @@ +hide empty description +[*] -right-> COMP_STATE_INIT : start of comp_ops.new() + +COMP_STATE_INIT --> COMP_STATE_READY : end of comp_ops.new() + +COMP_STATE_READY ---> COMP_STATE_PREPARE : COMP_TRIGGER_PREPARE + +COMP_STATE_PREPARE --> COMP_STATE_ACTIVE : COMP_TRIGGER_START + +COMP_STATE_ACTIVE --> COMP_STATE_PREPARE : COMP_TRIGGER_STOP +COMP_STATE_PAUSED --> COMP_STATE_PREPARE : COMP_TRIGGER_STOP + +COMP_STATE_ACTIVE -> COMP_STATE_PAUSED : COMP_TRIGGER_PAUSE + +COMP_STATE_PAUSED --> COMP_STATE_ACTIVE : COMP_TRIGGER_RELEASE + +COMP_STATE_PREPARE --> COMP_STATE_READY : COMP_TRIGGER_RESET diff --git a/developer_guides/firmware/components/images/comp-driver.pu b/architectures/firmware/sof-common/components/images/comp-driver.pu similarity index 100% rename from developer_guides/firmware/components/images/comp-driver.pu rename to architectures/firmware/sof-common/components/images/comp-driver.pu diff --git a/architectures/firmware/sof-common/components/images/comp-module-api.pu b/architectures/firmware/sof-common/components/images/comp-module-api.pu new file mode 100644 index 00000000..5f7ef971 --- /dev/null +++ b/architectures/firmware/sof-common/components/images/comp-module-api.pu @@ -0,0 +1,160 @@ +scale 1024 width + +component "pipelines" { + class pipeline + hide pipeline methods + hide pipeline attributes +} +component "component" { + + class comp_driver <> { + } + hide comp_driver methods + hide comp_driver attributes + + class comp_dev <> { + state + position + frames + pipeline + min_sink_bytes + min_source_bytes + task + size + period + ... + } + hide comp_dev methods + + interface buffer + hide buffer methods + hide buffer attributes + + interface comp_ops { + create() : comp_dev* + free(comp_dev*) + params(params) + dai_get_hw_params(params, dir) + dai_config(dai_config, dai_spec_config) + cmd(int cmd, void *data) + trigger(int cmd) + prepare() + reset() + copy() + position() + get_attribute() + set_attribute() + dai_ts_config() + dai_ts_start() + dai_ts_stop() + unbind() + get_large_config() + set_large_config() + } + hide comp_ops attributes + + + comp_driver -> comp_dev : creates + comp_dev *-right- comp_ops +} +pipeline -> comp_ops : calls + +component "module_adapter" { + + class module_adapter <> { + ops : comp_ops = + .create = adapter_shim_new + .prepare = module_adapter_prepare + .params = module_adapter_params + .copy = module_adapter_copy + + adapter_shim_new() + module_adapter_prepare() + module_adapter_params() + module_adapter_copy() + } + + interface module_interface { + init(processing_module*) + prepare(processing_module*) + process(processing_module*) + set_configuration() + get_configuration() + set_processing_mode() + get_processing_mode() + reset() + free() + } + hide module_interface attributes + + class processing_module <> { + stream_params + sink_buffer_list + period_bytes + deep_buff_bytes + output_buffer_size + input_buffers[] + output_buffers[] + } + hide processing_module methods + + module_adapter -left-> processing_module : creates + module_adapter -> module_interface : calls + +} +module_adapter -up-|> comp_ops + +component "cadence adapter" { + class cadence_codec { + cadence_codec_init() + cadence_codec_prepare() + cadence_codec_process() + cadence_codec_set_configuration() + cadence_codec_reset() + cadence_codec_free() + } + hide cadence_codec attributes + + interface "Cadence Codec API" as cadence_codec_api + hide cadence_codec_api methods + hide cadence_codec_api attributes + + cadence_codec -> cadence_codec_api : calls +} +cadence_codec -up-|> module_interface + +component "custom extensions" { + class "mp3 codec" as mp3_codec + hide mp3_codec methods + hide mp3_codec attributes + + class "aac codec" as aac_codec + hide aac_codec methods + hide aac_codec attributes +} +mp3_codec -up-|> cadence_codec_api +aac_codec -up-|> cadence_codec_api + +component "IADK adapter" { + class adp_interface { + intel_modules_init() + intel_modules_prepare() + intel_modules_process() + } + hide adp_interface attributes + + interface ProcessingModuleInterface <> { + Init() + Delete() + Process() + Reset() + SetProcessingMode() + GetProcessingMode() + SetConfiguration(config_id, fragment_pos, data_in, data_out) + GetConfiguration(config_id, fragment_pos, data_out) + } + hide ProcessingModuleInterface attributes + + adp_interface -> ProcessingModuleInterface : calls +} +adp_interface -up-|> module_interface diff --git a/developer_guides/firmware/components/images/comp-new-flow.pu b/architectures/firmware/sof-common/components/images/comp-new-flow.pu similarity index 100% rename from developer_guides/firmware/components/images/comp-new-flow.pu rename to architectures/firmware/sof-common/components/images/comp-new-flow.pu diff --git a/architectures/firmware/sof-common/components/images/comp-prepare-flow.pu b/architectures/firmware/sof-common/components/images/comp-prepare-flow.pu new file mode 100644 index 00000000..960b8623 --- /dev/null +++ b/architectures/firmware/sof-common/components/images/comp-prepare-flow.pu @@ -0,0 +1,22 @@ +actor pipeline +box "Module Adapter\no-- comp_ops" + participant "module_adapter" as module_adapter +end box +box "IADK Module Adapter\no-- module_interface" + participant "iadk_adapter" as iadk_adapter +end box +box "IADK Module\no-- ProcessingModuleInterface" + participant iadk_module +end box + +pipeline -> module_adapter : (1) ops->module_adapter_prepare() + activate module_adapter + module_adapter -> module_adapter : module_prepare() + activate module_adapter + module_adapter -> iadk_adapter : (2) ops->prepare() + activate iadk_adapter + iadk_adapter -> iadk_module : (3) preparation + module_adapter <-- iadk_adapter + deactivate iadk_adapter + module_adapter -> module_adapter : alloc buf descriptors + module_adapter -> module_adapter : alloc buffers diff --git a/developer_guides/firmware/components/images/component-mgmt-api.pu b/architectures/firmware/sof-common/components/images/component-mgmt-api.pu similarity index 100% rename from developer_guides/firmware/components/images/component-mgmt-api.pu rename to architectures/firmware/sof-common/components/images/component-mgmt-api.pu diff --git a/developer_guides/firmware/components/index.rst b/architectures/firmware/sof-common/components/index.rst similarity index 83% rename from developer_guides/firmware/components/index.rst rename to architectures/firmware/sof-common/components/index.rst index a78e796e..76db7fdd 100644 --- a/developer_guides/firmware/components/index.rst +++ b/architectures/firmware/sof-common/components/index.rst @@ -8,3 +8,4 @@ Components component-overview component-mgmt-api + component-module-api diff --git a/architectures/firmware/sof-common/index.rst b/architectures/firmware/sof-common/index.rst new file mode 100644 index 00000000..6e093246 --- /dev/null +++ b/architectures/firmware/sof-common/index.rst @@ -0,0 +1,12 @@ +.. _sof-common: + +SOF Common Architecture +####################### + +Architecture chapters and details that are common both for SOF Legacy and SOF +with Zephyr. + +.. toctree:: + :maxdepth: 1 + + components/index diff --git a/developer_guides/firmware/drivers/dai/images/dai-ops.pu b/architectures/firmware/sof-xtos/drivers/dai/images/dai-ops.pu similarity index 100% rename from developer_guides/firmware/drivers/dai/images/dai-ops.pu rename to architectures/firmware/sof-xtos/drivers/dai/images/dai-ops.pu diff --git a/developer_guides/firmware/drivers/dai/images/dai-set-config.pu b/architectures/firmware/sof-xtos/drivers/dai/images/dai-set-config.pu similarity index 100% rename from developer_guides/firmware/drivers/dai/images/dai-set-config.pu rename to architectures/firmware/sof-xtos/drivers/dai/images/dai-set-config.pu diff --git a/developer_guides/firmware/drivers/dai/index.rst b/architectures/firmware/sof-xtos/drivers/dai/index.rst similarity index 100% rename from developer_guides/firmware/drivers/dai/index.rst rename to architectures/firmware/sof-xtos/drivers/dai/index.rst diff --git a/developer_guides/firmware/drivers/dma/images/dma-ops.pu b/architectures/firmware/sof-xtos/drivers/dma/images/dma-ops.pu similarity index 100% rename from developer_guides/firmware/drivers/dma/images/dma-ops.pu rename to architectures/firmware/sof-xtos/drivers/dma/images/dma-ops.pu diff --git a/developer_guides/firmware/drivers/dma/images/dma-transfer.pu b/architectures/firmware/sof-xtos/drivers/dma/images/dma-transfer.pu similarity index 100% rename from developer_guides/firmware/drivers/dma/images/dma-transfer.pu rename to architectures/firmware/sof-xtos/drivers/dma/images/dma-transfer.pu diff --git a/developer_guides/firmware/drivers/dma/index.rst b/architectures/firmware/sof-xtos/drivers/dma/index.rst similarity index 100% rename from developer_guides/firmware/drivers/dma/index.rst rename to architectures/firmware/sof-xtos/drivers/dma/index.rst diff --git a/developer_guides/firmware/drivers/dma/intel/hda-dma.rst b/architectures/firmware/sof-xtos/drivers/dma/intel/hda-dma.rst similarity index 100% rename from developer_guides/firmware/drivers/dma/intel/hda-dma.rst rename to architectures/firmware/sof-xtos/drivers/dma/intel/hda-dma.rst diff --git a/developer_guides/firmware/drivers/dma/intel/images/hda-host-output.pu b/architectures/firmware/sof-xtos/drivers/dma/intel/images/hda-host-output.pu similarity index 100% rename from developer_guides/firmware/drivers/dma/intel/images/hda-host-output.pu rename to architectures/firmware/sof-xtos/drivers/dma/intel/images/hda-host-output.pu diff --git a/developer_guides/firmware/drivers/dma/intel/images/hda-link.pu b/architectures/firmware/sof-xtos/drivers/dma/intel/images/hda-link.pu similarity index 100% rename from developer_guides/firmware/drivers/dma/intel/images/hda-link.pu rename to architectures/firmware/sof-xtos/drivers/dma/intel/images/hda-link.pu diff --git a/developer_guides/firmware/drivers/dma/intel/images/hda-start-flow.pu b/architectures/firmware/sof-xtos/drivers/dma/intel/images/hda-start-flow.pu similarity index 100% rename from developer_guides/firmware/drivers/dma/intel/images/hda-start-flow.pu rename to architectures/firmware/sof-xtos/drivers/dma/intel/images/hda-start-flow.pu diff --git a/developer_guides/firmware/drivers/dma/intel/images/hda-stop-flow.pu b/architectures/firmware/sof-xtos/drivers/dma/intel/images/hda-stop-flow.pu similarity index 100% rename from developer_guides/firmware/drivers/dma/intel/images/hda-stop-flow.pu rename to architectures/firmware/sof-xtos/drivers/dma/intel/images/hda-stop-flow.pu diff --git a/developer_guides/firmware/drivers/dma/intel/index.rst b/architectures/firmware/sof-xtos/drivers/dma/intel/index.rst similarity index 100% rename from developer_guides/firmware/drivers/dma/intel/index.rst rename to architectures/firmware/sof-xtos/drivers/dma/intel/index.rst diff --git a/developer_guides/firmware/drivers/images/device-disco.pu b/architectures/firmware/sof-xtos/drivers/images/device-disco.pu similarity index 100% rename from developer_guides/firmware/drivers/images/device-disco.pu rename to architectures/firmware/sof-xtos/drivers/images/device-disco.pu diff --git a/developer_guides/firmware/drivers/images/device-probe.pu b/architectures/firmware/sof-xtos/drivers/images/device-probe.pu similarity index 100% rename from developer_guides/firmware/drivers/images/device-probe.pu rename to architectures/firmware/sof-xtos/drivers/images/device-probe.pu diff --git a/developer_guides/firmware/drivers/images/device-remove.pu b/architectures/firmware/sof-xtos/drivers/images/device-remove.pu similarity index 100% rename from developer_guides/firmware/drivers/images/device-remove.pu rename to architectures/firmware/sof-xtos/drivers/images/device-remove.pu diff --git a/developer_guides/firmware/drivers/index.rst b/architectures/firmware/sof-xtos/drivers/index.rst similarity index 100% rename from developer_guides/firmware/drivers/index.rst rename to architectures/firmware/sof-xtos/drivers/index.rst diff --git a/architectures/firmware/sof-xtos/images/edf-scheduler-deps.pu b/architectures/firmware/sof-xtos/images/edf-scheduler-deps.pu new file mode 100644 index 00000000..89bd4137 --- /dev/null +++ b/architectures/firmware/sof-xtos/images/edf-scheduler-deps.pu @@ -0,0 +1,6 @@ +class "edf_schedule_data" as edf { + - list : list + - clock + - irq +} +hide edf methods diff --git a/architectures/firmware/sof-xtos/images/edf-scheduler-flow.pu b/architectures/firmware/sof-xtos/images/edf-scheduler-flow.pu new file mode 100644 index 00000000..db1b44db --- /dev/null +++ b/architectures/firmware/sof-xtos/images/edf-scheduler-flow.pu @@ -0,0 +1,21 @@ +actor client as c + +participant edf_scheduler as edf +participant interrupt_driver as int + +-> edf : scheduler_init() + activate edf + edf -> int : interrupt_register(edf_scheduler_run) + deactivate edf +<-- edf +... +c -> edf : schedule_task(&task) + activate edf + edf -> int : interrupt_set() + deactivate edf +c <-- edf + +edf <- int : edf_scheduler_run() + activate edf + edf -> edf : schedule_task_running() + edf -> edf : schedule_task_complete() diff --git a/architectures/images/fw-arch-diag.png b/architectures/firmware/sof-xtos/images/fw-arch-diag.png similarity index 100% rename from architectures/images/fw-arch-diag.png rename to architectures/firmware/sof-xtos/images/fw-arch-diag.png diff --git a/architectures/firmware/sof-xtos/images/ll-scheduler-deps.pu b/architectures/firmware/sof-xtos/images/ll-scheduler-deps.pu new file mode 100644 index 00000000..5ff8f402 --- /dev/null +++ b/architectures/firmware/sof-xtos/images/ll-scheduler-deps.pu @@ -0,0 +1,30 @@ +class "ll_schedule_data" as lsd { + - tasks : list + - num_tasks + - pcd + - domain +} +hide lsd methods + +class "ll_schedule_domain" as lsdom { + - last_tick + - lock + - total_num_tasks + - num_clients + - ticks_per_ms + - type + - clk + - synchronous + - priv_data + - registered : array + - enabled : array + + domain_register() + + domain_unregister() + + domain_enable() + + domain_disable() + + domain_set() + + domain_clear() + + domain_is_pending() +} + +lsd *-- lsdom : contains diff --git a/architectures/firmware/sof-xtos/images/ll-scheduler-flow.pu b/architectures/firmware/sof-xtos/images/ll-scheduler-flow.pu new file mode 100644 index 00000000..fafdd382 --- /dev/null +++ b/architectures/firmware/sof-xtos/images/ll-scheduler-flow.pu @@ -0,0 +1,26 @@ +actor client as c + +participant ll_scheduler as ls +participant ll_schedule_domain as lsd + +-> lsd : domain_init +<-- lsd : domain + +-> ls : scheduler_init(&domain) +<-- ls +... +c -> ls : schedule_task(&task) + activate ls + ls -> lsd : domain_register(schedule_ll_tasks_run) + ls -> lsd : domain_enable() + deactivate ls +c <-- ls +... +ls <- lsd : schedule_ll_tasks_run() + activate ls + ls -> lsd : domain_disable() + loop schedule_ll_is_pending() + ls -> ls : schedule_ll_tasks_execute() + end loop + ls -> lsd : domain_enable() + deactivate ls diff --git a/developer_guides/firmware/images/memory-zones.dot b/architectures/firmware/sof-xtos/images/memory-zones.dot similarity index 97% rename from developer_guides/firmware/images/memory-zones.dot rename to architectures/firmware/sof-xtos/images/memory-zones.dot index 868c1f8e..e4541abf 100644 --- a/developer_guides/firmware/images/memory-zones.dot +++ b/architectures/firmware/sof-xtos/images/memory-zones.dot @@ -2,7 +2,7 @@ digraph memory_zones { compound = true; node [shape = record]; rankdir = LR; - size=3; + size=5; clients [label = "applications |kernel diff --git a/developer_guides/firmware/images/runtime-zone.dot b/architectures/firmware/sof-xtos/images/runtime-zone.dot similarity index 97% rename from developer_guides/firmware/images/runtime-zone.dot rename to architectures/firmware/sof-xtos/images/runtime-zone.dot index ffa0e506..e2824216 100644 --- a/developer_guides/firmware/images/runtime-zone.dot +++ b/architectures/firmware/sof-xtos/images/runtime-zone.dot @@ -2,7 +2,7 @@ digraph runtime_zone { compound = true; node [shape = record]; rankdir = LR; - size=2.5; + size=4; subgraph cluster_rt_0 { label = "RUNTIME HEAP #0"; diff --git a/architectures/firmware/sof-xtos/images/scheduler-ops.pu b/architectures/firmware/sof-xtos/images/scheduler-ops.pu new file mode 100644 index 00000000..80bf07b1 --- /dev/null +++ b/architectures/firmware/sof-xtos/images/scheduler-ops.pu @@ -0,0 +1,19 @@ +class "scheduler_ops" { + .. Mandatory .. + + schedule_task() + + schedule_task_cancel() + + schedule_task_free() + .. Optional .. + + schedule_task_running() + + schedule_task_complete() + + reschedule_task() + + scheduler_free() + + scheduler_run() +} + +enum schedule_types { + SOF_SCHEDULE_EDF + SOF_SCHEDULE_LL_TIMER + SOF_SCHEDULE_LL_DMA +} +hide schedule_types methods diff --git a/developer_guides/firmware/images/system-zone.dot b/architectures/firmware/sof-xtos/images/system-zone.dot similarity index 98% rename from developer_guides/firmware/images/system-zone.dot rename to architectures/firmware/sof-xtos/images/system-zone.dot index d18a7c8b..06768cb9 100644 --- a/developer_guides/firmware/images/system-zone.dot +++ b/architectures/firmware/sof-xtos/images/system-zone.dot @@ -2,7 +2,7 @@ digraph system_zone { compound = true; node [shape = record]; rankdir = LR; - size=4; + size=7; subgraph cluster_sys_0 { label = "SYS HEAP #0"; diff --git a/architectures/firmware/sof-xtos/index.rst b/architectures/firmware/sof-xtos/index.rst new file mode 100644 index 00000000..1063889e --- /dev/null +++ b/architectures/firmware/sof-xtos/index.rst @@ -0,0 +1,15 @@ +.. _sof-xtos: + +SOF with XTOS Architecture +########################## + +.. toctree:: + :maxdepth: 1 + + overview + mem-mgmt + pm-runtime/index + schedulers + drivers/index + pipelines/index + kd_integration/index diff --git a/developer_guides/firmware/kd_integration/images/kd-component-diagram.pu b/architectures/firmware/sof-xtos/kd_integration/images/kd-component-diagram.pu similarity index 100% rename from developer_guides/firmware/kd_integration/images/kd-component-diagram.pu rename to architectures/firmware/sof-xtos/kd_integration/images/kd-component-diagram.pu diff --git a/developer_guides/firmware/kd_integration/images/kd-e2e-sequence-diagram.pu b/architectures/firmware/sof-xtos/kd_integration/images/kd-e2e-sequence-diagram.pu similarity index 100% rename from developer_guides/firmware/kd_integration/images/kd-e2e-sequence-diagram.pu rename to architectures/firmware/sof-xtos/kd_integration/images/kd-e2e-sequence-diagram.pu diff --git a/developer_guides/firmware/kd_integration/images/kd-state-diagram.pu b/architectures/firmware/sof-xtos/kd_integration/images/kd-state-diagram.pu similarity index 100% rename from developer_guides/firmware/kd_integration/images/kd-state-diagram.pu rename to architectures/firmware/sof-xtos/kd_integration/images/kd-state-diagram.pu diff --git a/developer_guides/firmware/kd_integration/images/kd-timing-diagram.pu b/architectures/firmware/sof-xtos/kd_integration/images/kd-timing-diagram.pu similarity index 100% rename from developer_guides/firmware/kd_integration/images/kd-timing-diagram.pu rename to architectures/firmware/sof-xtos/kd_integration/images/kd-timing-diagram.pu diff --git a/developer_guides/firmware/kd_integration/index.rst b/architectures/firmware/sof-xtos/kd_integration/index.rst similarity index 100% rename from developer_guides/firmware/kd_integration/index.rst rename to architectures/firmware/sof-xtos/kd_integration/index.rst diff --git a/developer_guides/firmware/kd_integration/kd-integration.rst b/architectures/firmware/sof-xtos/kd_integration/kd-integration.rst similarity index 100% rename from developer_guides/firmware/kd_integration/kd-integration.rst rename to architectures/firmware/sof-xtos/kd_integration/kd-integration.rst diff --git a/architectures/firmware/sof-xtos/mem-mgmt.rst b/architectures/firmware/sof-xtos/mem-mgmt.rst new file mode 100644 index 00000000..e96b955a --- /dev/null +++ b/architectures/firmware/sof-xtos/mem-mgmt.rst @@ -0,0 +1,102 @@ +.. _kernel-mem-mgmt: + +Memory Management +################# + +Heap Memory Zones +***************** + +The heap has three different zones from where memory can be allocated: + +System Zone + Fixed size heap where allocation always succeeds and is never freed. Used + by any initialization code that will never give up the memory. + +Runtime Zone + Main and larger heap zone where allocations are not guaranteed to succeed. + Memory can be freed here. + +Buffer Zone + Largest heap zone intended for audio buffers. See platform/memory.h for + heap size configuration and mappings. + +.. graphviz:: images/memory-zones.dot + :caption: Memory Zones + +System Zone +*********** +The system zone receives a series of allocations during the system +initialization phase. Since no memory is freed until the system (core) goes +down, the allocation mechanism might be simple, ensuring that a sufficient +offset to the beginning of free space left is maintained. + +.. graphviz:: images/system-zone.dot + :caption: System Zone + +All system-level components (schedulers, work queues, etc.) allocate their +memory blocks from the system heap. Separation between the system heap and +runtime heap(s) might be further hardened in case an access control for user +mode vs. kernel mode is supported by the architecture/platform. + +Extensions for SMP Architectures +================================ + +Each CPU (core) might own a dedicated system heap. The memory assigned for +system heaps is distributed asymmetrically on CAVS platforms: a large heap +for the primary core (#0) and smaller ones for other cores (#1+). + +When a core goes down, the entire heap can be freed by moving back the free +space offset to the beginning of the heap. + +The heap can be aligned with memory bank(s) to provide better control over +power consumption. Once a core goes down, memory banks allocated for +its system heap can be powered off as well. + +Runtime Zone +************ + +* Provides flexible ``malloc``/``free`` operations. + +* Since the runtime zone is separated from the system zone, adjustment + and complex usage scenarios do not interface with system allocations. + +.. graphviz:: images/runtime-zone.dot + :caption: Runtime Zone + +Buffer Zone +*********** + +Information is forthcoming. + +Shared Data +************* + +Shared data refers to a piece of memory that can be accessed by different +DSP cores. Data can be declared as shared in one of two ways, depending on +its type: + +* Static global variables are marked with the ``SHARED_DATA`` definition. + +* Heap data is allocated with the ``SOF_MEM_FLAG_SHARED`` flag. + +To keep data synchronized, commit very read and write access to the shared +data by using the dedicated ``platform_shared_commit`` function. Note that +read-only access does not exist and that shared data must be synchronized +even after just reading. + +Both the ``SHARED_DATA`` macro and the ``platform_shared_commit`` function +are platform-specific and can be implemented differently on different +platforms. Two general approaches can be used, based on available hardware +support: + +1. Platform uses L1 cache, but also supports uncached memory regions: + + * ``SHARED_DATA`` puts data into a dedicated firmware section that is accessed using uncache. + + * ``platform_shared_commit`` does nothing. + +2. Platform uses L1 cache and doesn't support uncached memory regions: + + * ``SHARED_DATA`` does nothing. + + * ``platform_shared_commit`` writebacks and invalidates cache. diff --git a/architectures/firmware/sof-xtos/overview.rst b/architectures/firmware/sof-xtos/overview.rst new file mode 100644 index 00000000..94b44a8a --- /dev/null +++ b/architectures/firmware/sof-xtos/overview.rst @@ -0,0 +1,46 @@ +.. _sof-legacy-overview: + +Overview +########## + +Currently SOF has support for the Cadence Xtensa DSP architecture in UP and SMP +modes in the upstream code base. + +The diagram below shows the high-level firmware architecture with the +Bay Trail platform integration as an example. The firmware is divided into four +main sections: + +#. **Generic microkernel.** The microkernel manages and abstracts the + DSP hardware for the rest of the system. It also exports C APIs for + memory allocation, scheduling work, event notifications, and power + management. + +#. **Audio components.** The audio components can be used to form an + audio processing pipeline from the host DMA buffer to the DSP digital + audio interface. Audio components will have a source and sink buffer + where they will usually transform or route audio data as part of their + processing. + +#. **Audio task.** The audio task manages the audio pipelines at run + time; it manages the transportation of data from source to sink + component within the pipeline. The pipelines are currently statically + defined in the firmware, but infrastructure is now in place to allow the + dynamic creation of pipelines from Linux userspace. + +#. **Platform drivers.** The platform drivers are used to control any + external IP to the DSP IP. This will usually be things like DMA engines + or DAI (Digital Audio Interface) controllers. These drivers are used by + the audio components and pipelines to send/receive data to/from the host + and external codecs. + + .. figure:: ./images/fw-arch-diag.png + :align: center + :alt: SOF Architecture + :width: 800px + + `Sound Open Firmware Architecture using Intel Bay Trail Platform` + + +Each section above is well insulated from the other sections by partitioning +code into separate directories and by using DSP and platform agnostic generic +APIs for orchestration between the sections. diff --git a/developer_guides/firmware/pipelines/images/ppl-new.pu b/architectures/firmware/sof-xtos/pipelines/images/ppl-new.pu similarity index 100% rename from developer_guides/firmware/pipelines/images/ppl-new.pu rename to architectures/firmware/sof-xtos/pipelines/images/ppl-new.pu diff --git a/developer_guides/firmware/pipelines/images/ppl-op-downstream.pu b/architectures/firmware/sof-xtos/pipelines/images/ppl-op-downstream.pu similarity index 100% rename from developer_guides/firmware/pipelines/images/ppl-op-downstream.pu rename to architectures/firmware/sof-xtos/pipelines/images/ppl-op-downstream.pu diff --git a/developer_guides/firmware/pipelines/images/ppl-operations.pu b/architectures/firmware/sof-xtos/pipelines/images/ppl-operations.pu similarity index 100% rename from developer_guides/firmware/pipelines/images/ppl-operations.pu rename to architectures/firmware/sof-xtos/pipelines/images/ppl-operations.pu diff --git a/developer_guides/firmware/pipelines/images/ppl-params.pu b/architectures/firmware/sof-xtos/pipelines/images/ppl-params.pu similarity index 100% rename from developer_guides/firmware/pipelines/images/ppl-params.pu rename to architectures/firmware/sof-xtos/pipelines/images/ppl-params.pu diff --git a/developer_guides/firmware/pipelines/images/ppl-reset.pu b/architectures/firmware/sof-xtos/pipelines/images/ppl-reset.pu similarity index 100% rename from developer_guides/firmware/pipelines/images/ppl-reset.pu rename to architectures/firmware/sof-xtos/pipelines/images/ppl-reset.pu diff --git a/developer_guides/firmware/pipelines/images/ppl-struct.pu b/architectures/firmware/sof-xtos/pipelines/images/ppl-struct.pu similarity index 100% rename from developer_guides/firmware/pipelines/images/ppl-struct.pu rename to architectures/firmware/sof-xtos/pipelines/images/ppl-struct.pu diff --git a/developer_guides/firmware/pipelines/images/ppl-task.pu b/architectures/firmware/sof-xtos/pipelines/images/ppl-task.pu similarity index 100% rename from developer_guides/firmware/pipelines/images/ppl-task.pu rename to architectures/firmware/sof-xtos/pipelines/images/ppl-task.pu diff --git a/developer_guides/firmware/pipelines/index.rst b/architectures/firmware/sof-xtos/pipelines/index.rst similarity index 100% rename from developer_guides/firmware/pipelines/index.rst rename to architectures/firmware/sof-xtos/pipelines/index.rst diff --git a/developer_guides/firmware/pm-runtime/images/pm-dsp-core-idle.pu b/architectures/firmware/sof-xtos/pm-runtime/images/pm-dsp-core-idle.pu similarity index 100% rename from developer_guides/firmware/pm-runtime/images/pm-dsp-core-idle.pu rename to architectures/firmware/sof-xtos/pm-runtime/images/pm-dsp-core-idle.pu diff --git a/developer_guides/firmware/pm-runtime/images/pm-dsp-core-init.pu b/architectures/firmware/sof-xtos/pm-runtime/images/pm-dsp-core-init.pu similarity index 100% rename from developer_guides/firmware/pm-runtime/images/pm-dsp-core-init.pu rename to architectures/firmware/sof-xtos/pm-runtime/images/pm-dsp-core-init.pu diff --git a/developer_guides/firmware/pm-runtime/index.rst b/architectures/firmware/sof-xtos/pm-runtime/index.rst similarity index 100% rename from developer_guides/firmware/pm-runtime/index.rst rename to architectures/firmware/sof-xtos/pm-runtime/index.rst diff --git a/developer_guides/firmware/pm-runtime/intel/images/dsp-core-lps-cavs-d0-d0i3-d0.pu b/architectures/firmware/sof-xtos/pm-runtime/intel/images/dsp-core-lps-cavs-d0-d0i3-d0.pu similarity index 100% rename from developer_guides/firmware/pm-runtime/intel/images/dsp-core-lps-cavs-d0-d0i3-d0.pu rename to architectures/firmware/sof-xtos/pm-runtime/intel/images/dsp-core-lps-cavs-d0-d0i3-d0.pu diff --git a/developer_guides/firmware/pm-runtime/intel/pm-dsp-core-cavs.rst b/architectures/firmware/sof-xtos/pm-runtime/intel/pm-dsp-core-cavs.rst similarity index 100% rename from developer_guides/firmware/pm-runtime/intel/pm-dsp-core-cavs.rst rename to architectures/firmware/sof-xtos/pm-runtime/intel/pm-dsp-core-cavs.rst diff --git a/developer_guides/firmware/pm-runtime/pm-dsp-core.rst b/architectures/firmware/sof-xtos/pm-runtime/pm-dsp-core.rst similarity index 100% rename from developer_guides/firmware/pm-runtime/pm-dsp-core.rst rename to architectures/firmware/sof-xtos/pm-runtime/pm-dsp-core.rst diff --git a/architectures/firmware/sof-xtos/schedulers.rst b/architectures/firmware/sof-xtos/schedulers.rst new file mode 100644 index 00000000..17d751d2 --- /dev/null +++ b/architectures/firmware/sof-xtos/schedulers.rst @@ -0,0 +1,69 @@ +.. _schedulers_xtos: + +Schedulers +########## + +Scheduler Registration +********************** + +The Schedule API is an abstract layer that allows for scheduler +registration, task creation, and scheduling. New schedulers can be added by +extending a list of pre-defined schedule types. Currently supported types +are: ``SOF_SCHEDULE_EDF``, ``SOF_SCHEDULE_LL_TIMER`` and ``SOF_SCHEDULE_LL_DMA``. Every newly-added scheduler should implement at least +a mandatory subset of ``scheduler_ops``. + +.. uml:: images/scheduler-ops.pu + :caption: Scheduler operations + +The ``scheduler_init`` function must called in order to register the +scheduler with a given ``type``, ``scheduler_ops``, and the custom +scheduler's data. Scheduling is as simple as initializing a task with ``schedule_task_init`` and passing such an object later on to scheduler +operations. + +Low Latency Scheduler +********************* + +The low latency scheduler executes all registered tasks concurrently based +on their initial priorities and periods of execution. This task chain is a *critical section* which removes any possibility of a system interrupt +preemption. Thus, every client of the scheduler should be aware of the +task's expected DSP utilization and try not to register long-running +processings which can lead to system instability. + +The low latency scheduler requires a low latency schedule domain in order to +be initialized. Each domain includes a different type of interrupt source +that runs the scheduler. Three domains are supported: timer, DMA multiple +channels, and DMA single channel. The timer domain is a simple timer-based +interrupt that occurs after a specified number of cycles. Schedulers for the +DMA multiple channels domain run after every channel interrupt. DMA single +channels run only on interrupts coming from one of the channels. The +appropriate DMA channel is selected based on the order of task registration +and also the task's period. + +Note that even though the domains are shared among all DSP cores, the low +latency schedulers are instantiated per core. + +.. uml:: images/ll-scheduler-deps.pu + :caption: Low latency scheduler dependencies + +.. uml:: images/ll-scheduler-flow.pu + :caption: Basic low latency scheduler flow + +EDF Scheduler +************* + +The EDF scheduler executes all registered tasks based on their deadlines. +Every EDF task has its own private stack which allows for full preemption +support. The task with an earlier deadline can easily pause the execution of +the task with a higher deadline, execute first, and return to the preempted +task after that. Since EDF tasks run on a passive irq level, they can be +preempted by every interrupt. + +The EDF scheduler is instantiated per core. + +.. uml:: images/edf-scheduler-deps.pu + :caption: EDF scheduler structure + +.. uml:: images/edf-scheduler-flow.pu + :caption: Basic EDF scheduler flow + + diff --git a/architectures/firmware/sof-zephyr/app_layer/images/app_layer_diagram.pu b/architectures/firmware/sof-zephyr/app_layer/images/app_layer_diagram.pu new file mode 100644 index 00000000..dea00068 --- /dev/null +++ b/architectures/firmware/sof-zephyr/app_layer/images/app_layer_diagram.pu @@ -0,0 +1,68 @@ +@startuml +allowmixing + +scale max 1280 width + +package "SOF" { + + package "Application layer" as APP_CUSTOMIZATION { + + package "Example Loadable Module" as LOADABLE_MODULE { + component "3rd Party Post-Processing" as PROCESSING_3RD_PARTY + component "WoV" as WOV_MODULE + component "ACA" as ACA_MODULE + component "Other modules" as OTHER_MODULES + + PROCESSING_3RD_PARTY -[hidden]right- WOV_MODULE + WOV_MODULE -[hidden]right- ACA_MODULE + ACA_MODULE -[hidden]right- OTHER_MODULES + } + + package "Built-in Module" as BUILTIN_MODULE { + component "Copier" as COPIER + component "SRC" as SRC + component "Mixers" as MIXERS + component "History Buffer/KPB" as HISTORY_BUFFER + component "Probe" as PROBE + + COPIER -[hidden]right- SRC + SRC -[hidden]right- MIXERS + MIXERS -[hidden]right- HISTORY_BUFFER + HISTORY_BUFFER -[hidden]right- PROBE + } + + BUILTIN_MODULE -[hidden]down- LOADABLE_MODULE + } + + package "System Services" as SYS_SERVICES { + + interface "System Services" as SS + + package "Media Processing Pipelines Services extension" as KERNEL_EXTENSION { + component "Communication" as COMMUNICATION + component "Pipelines and Component Infrastructure" as PIPELINE_COMPONENT_INFRASTRUCTURE + component "AVS Scheduling" as AVS_SCHEDULERS + + COMMUNICATION -[hidden]right- PIPELINE_COMPONENT_INFRASTRUCTURE + PIPELINE_COMPONENT_INFRASTRUCTURE -[hidden]right- AVS_SCHEDULERS + } + + package "Zephyr" as ZEPHYR { + component "Services" as SERVICES + } + + SS -[hidden]down- KERNEL_EXTENSION + SS -[hidden]down- ZEPHYR + + KERNEL_EXTENSION -[hidden]right- ZEPHYR + } + + APP_CUSTOMIZATION -[hidden]down- SYS_SERVICES + BUILTIN_MODULE .down. SS + PROCESSING_3RD_PARTY .down. SS + WOV_MODULE .down. SS + ACA_MODULE .down. SS + OTHER_MODULES .down. SS +} + +@enduml \ No newline at end of file diff --git a/architectures/firmware/sof-zephyr/app_layer/index.rst b/architectures/firmware/sof-zephyr/app_layer/index.rst new file mode 100644 index 00000000..fba32384 --- /dev/null +++ b/architectures/firmware/sof-zephyr/app_layer/index.rst @@ -0,0 +1,51 @@ +.. _app_layer: + +Application Layer +################# + +Application Layer represents the built-in FW modules (processing components), loadable FW +modules and example templates with application libraries required for +modules integration. Application layer content is assumed to be open source +and only proprietary 3rd party components should remain private. + +.. uml:: images/app_layer_diagram.pu + :caption: Application Layer + +Modules in Application Layer +**************************** + +The built-in modules are built together with base firmware and they have +direct access to all firmware drivers and service APIs. When built-in module +is enabled in configuration, it is guaranteed to exist in firmware binary. + +The loadable modules are built separately from base firmware and they are +loaded dynamically as a separate binary, depending on the host audio +configuration. To build, use LMDK(Loadable Module Development Kit) +which is standalone kit containing all required files. + +All the application layer module access base firmware services are via the System +Services ABI. + +**NOTE:** The built-in modules are utility components provided by the base +firmware/kernel. + +Examples of built-in modules: + +* Audio built-in components: Copiers, Mixers, Volume, SRC, etc. + +Example how to build a loadable module: + +* Example Up-Down-Mixer build using :ref:`lmdk_user_guide` + +Probe +===== + +The probe module is special module in FW infrastructure that allows to inject +or extract data from a specified probe point. The traditional client +platforms use HDA DMAs to transfer data in and out of such module. + +Loadable Modules +================ + +The loadable modules are build into separate binaries from the main SOF build. To communicate with them +is used native system agent and to control is used module api :ref:`apps-comp-world`. diff --git a/architectures/firmware/sof-zephyr/images/overview_diagram.pu b/architectures/firmware/sof-zephyr/images/overview_diagram.pu new file mode 100644 index 00000000..8ee7f089 --- /dev/null +++ b/architectures/firmware/sof-zephyr/images/overview_diagram.pu @@ -0,0 +1,42 @@ +@startuml +allowmixing + +scale max 1280 width + +package "SOF" { + + package "Application layer - user space" as APPLICATION_LAYER { + component "3rd party algorithms" - private" as 3RD_PARTY_ALGOS + component "Loadable libraries" as LOADABLE_COMPONENTS + component "Built-in Processing components" as BUILTIN_COMPONENTS + + BUILTIN_COMPONENTS -[hidden]right- LOADABLE_COMPONENTS + LOADABLE_COMPONENTS -[hidden]right- 3RD_PARTY_ALGOS + } + + package "Kernel space" { + + package "Media Processing Pipelines layer - kernel extension" as KERNEL_EXTENSION { + component "Communication" as COMMUNICATION + component "Pipelines and Component Infrastructure" as PIPELINE_COMPONENT_INFRASTRUCTURE + component "AVS Scheduling" as AVS_SCHEDULERS + + COMMUNICATION -[hidden]right- PIPELINE_COMPONENT_INFRASTRUCTURE + PIPELINE_COMPONENT_INFRASTRUCTURE -[hidden]right- AVS_SCHEDULERS + } + + package "Zephyr RTOS layer" as RTOS { + component "Services" as SERVICES + component "SoC HAL" as SOC + component "Drivers" as DRIVERS + + SERVICES --[hidden]right-- SOC + SOC --[hidden]right-- DRIVERS + } + + APPLICATION_LAYER -[hidden]down- KERNEL_EXTENSION + KERNEL_EXTENSION -[hidden]down- RTOS + } +} + +@enduml diff --git a/architectures/firmware/sof-zephyr/images/sof_lib.pu b/architectures/firmware/sof-zephyr/images/sof_lib.pu new file mode 100644 index 00000000..b819e2e4 --- /dev/null +++ b/architectures/firmware/sof-zephyr/images/sof_lib.pu @@ -0,0 +1,39 @@ +component "app/mpp" as app + +component lib <> { + interface cpu + interface dai + interface dma + interface pm_runtime + + component dai_mng + dai_mng -up- dai + component dma_mng + dma_mng -up- dma + component pm_runtime_impl + pm_runtime_impl -up- pm_runtime +} + +app .down.> dai : uses +app .down.> dma : uses +app .down.> pm_runtime : uses +app .down.> cpu : uses + +component "arch/xtensa/lib" as arch_xtensa { + component arch_cpu +} +arch_cpu -up- cpu + +component vendor { + component platform { + component dai_init + dai_init .up.> dai_mng : initialize + component dma_init + dma_init .up.> dma_mng : initialize + component platform_pm_runtime + } + component drivers + drivers .up.> dma : uses +} +arch_cpu .down.> platform +pm_runtime_impl .down.> platform_pm_runtime diff --git a/architectures/firmware/sof-zephyr/images/sof_lib_zephyr.pu b/architectures/firmware/sof-zephyr/images/sof_lib_zephyr.pu new file mode 100644 index 00000000..d39e6c48 --- /dev/null +++ b/architectures/firmware/sof-zephyr/images/sof_lib_zephyr.pu @@ -0,0 +1,53 @@ +component "app/mpp" as app + +component lib <> { + interface cpu + interface dai + interface dma + interface pm_runtime + + ' component dai_mng + ' dai_mng -up- dai + ' component dma_mng + ' dma_mng -up- dma + ' component pm_runtime_impl + ' pm_runtime_impl -up- pm_runtime +} + +app .down.> dai : uses +app .down.> dma : uses +app .down.> pm_runtime : uses +app .down.> cpu : uses + +component "lib-zephyr" as lib_zephyr { + component "cpu-flows" as cpu_flows + cpu_flows -up- cpu +} + +component "zephyr" as zephyr { + component "api" as zephyr_api +} + +cpu_flows .down.> zephyr_api : uses +zephyr_api -up- dai +zephyr_api -up- dma +zephyr_api -up- pm_runtime + +' component "arch/xtensa/lib" as arch_xtensa { +' component arch_cpu +' } +' arch_cpu -up- cpu + +' component vendor { +' component platform { +' component dai_init +' dai_init .up.> dai_mng : initialize +' component dma_init +' dma_init .up.> dma_mng : initialize +' component platform_pm_runtime +' } +' component drivers +' drivers .up.> dma : uses +' } +' arch_cpu .down.> platform +' pm_runtime_impl .down.> platform_pm_runtime diff --git a/architectures/firmware/sof-zephyr/index.rst b/architectures/firmware/sof-zephyr/index.rst new file mode 100644 index 00000000..e89e5913 --- /dev/null +++ b/architectures/firmware/sof-zephyr/index.rst @@ -0,0 +1,13 @@ +.. _sof-zephyr: + +SOF with Zephyr Architecture +############################ + +.. toctree:: + :maxdepth: 1 + + overview + app_layer/index + mpp_layer/index + rtos_layer/index + zephyr_api_integration diff --git a/architectures/firmware/sof-zephyr/mpp_layer/async_messaging.rst b/architectures/firmware/sof-zephyr/mpp_layer/async_messaging.rst new file mode 100644 index 00000000..77813193 --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/async_messaging.rst @@ -0,0 +1,44 @@ +.. _async_msg: + +Asynchronous Messaging Service +############################## + +Asynchronous Messaging Service (AMS) is designed to exchange sporadic / +asynchronous events between firmware components, such as key phrase detection. +It can also be optionally selected in the firmware build configuration. The +service exposes an external interface to the host and is API accessible from +firmware components. + +**NOTE:** The AMS integration is currently a work in progress; it might not be +fully functional in SOF main branch. + +Asynchronous messages are one-way from producer to all consumers and allows to: + + - direct asynchronous communication between components (1:1) + - sending one asynchronous message to many components (1:N) + - producing asynchronous messages by many modules where 1 is receiving (M:1) + - producing asynchronous messages by many modules where many are receiving (M:N) + +Messages are exchanged over IDC protocol and shared memory with multi-core +support. Message producers and consumers can be run on different cores. + +Development guide: :ref:`async_messaging_best_practices` + +.. TODO: Add link to AMS interface generated from code + +Asynchronous Messaging Flows +**************************** + +Producer and consumer on the same core +====================================== + +.. uml:: images/async_messaging/flow_prod_cons_same_core.pu + :caption: Asynchronous Messaging example with WoV producer and custom module consumer running on single core + +Producer on primary core, consumer on secondary core +==================================================== + +.. uml:: images/async_messaging/flow_prod_primary_cons_secondary_core.pu + :caption: Asynchronous Messaging example with WoV producer on primary core and custom module consumer running on secondary core + +.. TODO: Port additional async messaging uml flows from internal FAS documentation diff --git a/architectures/firmware/sof-zephyr/mpp_layer/dp_scheduling.rst b/architectures/firmware/sof-zephyr/mpp_layer/dp_scheduling.rst new file mode 100644 index 00000000..c6adf7ff --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/dp_scheduling.rst @@ -0,0 +1,614 @@ +DP a.k.a. "Data processing" with EDF scheduling +************************************************ + +DP a.k.a. "Data processing" is an async scheduling method of data processing modules. Each module works in a separate, preemptible thread with lower priority than LL thread. It allows processing with periods longer than 1ms, on-demand processing, etc. + +Unlike in LL "low latency" method where a module started every 1ms cycle and all of LL modules together MUST finish processing 1ms, DP works async and gets CPU when a module is "ready for processing", what means: + + - on each module's input buffer there's at least IBS bytes of data and in each module's output buffer there's at least OBS bytes of free space + + OR + + - a module declared readiness by itself by an optional API call "is_ready_to_process" + +Critical part is that the module **must** finish processing before its **deadline**. A deadline is a time when the modules must provide a data chunk in order to keep next module(s) in the pipeline working. + +To ensure that all modules provide data on time - as long as CPU is not overloaded - regardless of modules' processing times and processing periods, a Earliest Deadline First (EDF) scheduling is used. https://en.wikipedia.org/wiki/Earliest_deadline_first_scheduling + +A list of all DP tasks, regardless on core the task is on, is to be iterated every time the situation of DP readiness or deadline timing may change, that include: + + - finish of processing of LL pipeline (on any core) + - finish of processing of any DP module (on any core) + +during the iteration, the following will be checked: + + - Readiness of each DP module. As mentioned before, module "is ready" when declared readiness by itself an API call or when it has at least IBS of data on each input and at least OBS free space on each out + - deadline calculation of each DP module. LFTs and Deadlines are not constant, they may change when a module consume/produce a portion of data. Therefore all LFTs and Deadlines must be re-calculated + +DEADLINE CALCULATIONS +====================== + +The most critical part is to calculate deadlines. Lets go from the beginning, there are some definitions: + +**def: buffers' Latest Feeding Time (LFT)** + +LFT is the latest time when **a buffer** must be fed with a portion of data allowing its data consumer to work and finish in its specific time + +LFT is a parameter specific to a buffer and can be calculated based on: + + - current amount of data in the buffer + - data reciever's consumption rate and period + - data source production rate and period + - data reciever's module's LST - latest start time + +so, in high level LFT is a sum of: + + - Latest start time (LST) of the data consumer (LST is defined later) + + - estimated time the consumer will drain the current data from the buffer: ``number_of_ms_in_buffer / consumer_period`` + + i.e. if there's 5ms of data in the buffer and period of the consumer is 2ms, the calculated time is ``4ms`` + + - correction for multiple source cycle + + in case the producer period < consumer period the LFT time needs to be corrected, as the producer must process more than once to provide enough data. The correction will be calculated as: ``producer_LPT * required_number_of_cycles`` where LPT is longest processing time, explained later + + ``correction = producer_LPT * ((consumer_period - number_of_ms_in_buffer) / producer_period)`` + + if correction is < 0, it should be set to zero. Note that in case producer_period >= consumer_period correction is always 0 + +finally: ``LFT = LST(consumer) + estimated_drain_time - correction`` + +**def: DP module's DEADLINE** + +a DEADLINE is the latest moment **a module** must finish processing to feed all target buffers before their LFTs. +Calculation is simple: + + - module's deadline is the nearest LFT of all target buffers + +in case te LFT of the buffer cannot be calculated - that may happen during pipeline startup or if there's no output buffer, i.e. a module like speech recognition - deadline should be set to "moment when module becomes ready + modules's period" + +**def: DP module's Longest Processing Time (LPT)** + +LPT is the longest time the module may process a portion of data, assuming it is scheduled 100% of CPU time. **LPT cannot be measured in runtime** as processing may change from cycle to cycle, etc. It can, however, be estimated based on: + +- declared (by a module vendor) number of CPU cycles required for processing. This declaration should be done separately for all combination of input/output data formats, platform, CPU type, using of HiFi etc. and either included in manifest od provided in an IPC call +- If declaration is not available, we can take "a period" as an approximation of longest possible processing time. "A period" is a value calculated using IBS and data consumption rate of a module. A module cannot possibly processing longer than its period, because it would never provide data in time (if LPT = period that means a module required 100% of CPU for processing, so it is really the worst possible case) + +*Example:* if a data rate is 48samples/msec and OBS = 480samples, the "worst case" period should be calculated 10ms + +*NOTE:* in case of sampling freq like 44.1 a round up should taken - if ration is 44.1 samples per mlisecond, 45 samples should be used for calculations + +The "worst case approximation", however a correct, is assuming that a module is a heavy one and it requires 100% of CPU time. Using it may lead to unnecessary buffering, see "delayed start" section below. + +**def: DP module's latest start time (LST)** + +LST is the latest time when **a module** must start processing a portion of data in order to meet its deadline. It can be calculated as: +``deadline - LPT`` When a module is in the middle of processing, its LST may be negative. In that case 0 should be taken to all futhure calculations. + +**Based on an above, it is clear that we do need to calculate first a deadline of the very latest module in a chain, than go back and calculate LFTs and deadline of each module separately** + +Fortunate is that the last module of a pipeline is almost always an LL module (usually DAI). For LL module deadline always is "NOW", so it is very easy to calculate LFTs for its input buffer(s). note: in case of data rates like 44.1, which cannot be divided to 1ms, a round up to 45 should be used: + + - LL module always start in 1ms periods + - LL module always consume constant number of bytes in a cycle (with an exception for frequencies like 44.1, a round up 45KHz should be taken for calculations) + + so ``LFT = NOW + number of data chunks in buffer * 1ms`` + +"NOW" in all of the calculations is "last start of LL scheduler". It makes all calculations simpler, as in the examples below (calculating CPU cycles would require taking extra care for 32bit overflows or use slow 64bit operations). Also all modules have the same timestamp as "NOW", regardless of moment in the cycle the deadlines are calculated. + +If a module is in the middle of processing, it should not release data from input buffer till the processing is finished, so the input buffer should be considered as it was at the moment the processing started, otherwise deadlines may be miscalculated. + +In case of pipeline like: + +.. uml:: images/dp_scheduling/pic1_chains.pu + +there are 2 separate deadline calculation chains: DP4 than DP3, and (independent) DP2 than DP1. **Also note that deadlines and other parameters may change, so re-calculation of all parameters should occur reasonable frequently and include all DP modules, regardless of a core it is run on** + +End of stream +============= + +When a SP module is in the middle of processing when a pipeline is stopping, it should finish processing its current chunk of data. Unformtunately there's no way to interrupt ongoing processing without risk of memory leaks etc. Therefore IPC stopping a pipeline should wait till all DP modules finish processing. + +EXAMPLE1 +========= +*data source period is longer or equal to data consumer period* +Note that in the example CPU load is very close to 100%, yet deadline calculation and EDF scheduling allow to keep the processing on time. + +for simplification lets assume: + + - the pipeline is in stable state (processing for a while, not in startup) + - no DP is currently processing + - whole CPU is dedicated to DP, like if LL is on core 0 and DPs on core 1 + +**0ms time:** + +.. uml:: images/dp_scheduling/example1.pu + +Pipeline state: + + - DP1 is ready for processing + - DP2 is ready for processing + + calculate deadlines: + + - ``buf3 LFT = 15 periods of LL2`` ==> ``DP2 deadline = 15ms`` + - ``DP2 LST = 15ms (DP2 deadline) - 9ms (DP2 LPT) = 6ms`` + - ``buf2 LFT = 6ms (DP2 LST) + 10ms (1 period in buf2) = 16ms`` ==> ``DP1 deadline = 16ms`` + + DP2 will be scheduled as it has earliest deadline, will process for 9ms + +**9ms time, DP2 finished processing but not yet released data from BUF2:** + +.. uml:: images/dp_scheduling/example1_1.pu + +Pipeline state: + + - DP1 is ready for processing + - DP2 just finished processing + + calculate deadlines: + + - ``buf3 LFT = 6 periods of LL2`` ==> ``DP2 deadline = 6ms`` + - ``DP2 LST = 6ms(DP2 deadline) - 9ms (DP2 LPT) = -3ms`` LST is negative, 0 should be used ``DP2 LST = 0`` + - ``buf2 LFT = 6ms (DP2 LST) + 10ms (1 period in buf2) = 16ms`` ==> ``DP1 deadline = 16ms`` + + DP1 will be scheduled + +**9ms time, DP2 released data from BUF2:** + +.. uml:: images/dp_scheduling/example1_2.pu + +Pipeline state: + + - DP1 is ready for processing + - DP2 is not ready for processing + + calculate deadlines: + + - ``buf3 LFT = 16 periods of LL2`` ==> ``DP2 deadline = 16ms`` + - ``DP2 LST = 16ms(DP2 deadline) - 9ms (DP2 LPT) = 7ms`` + - ``buf2 LFT = 7ms (DP2 LST) = 7ms`` ==> ``DP1 deadline = 7ms`` + + DP1 will be scheduled, will run for 5ms + +**14ms time, DP1 finished processing and released data from BUF1:** + +.. uml:: images/dp_scheduling/example1_3.pu + +Pipeline state: + + - DP1 is not ready for processing + - DP2 is ready for processing + + calculate deadlines: + + - ``buf3 LFT = 11 periods of LL2`` ==> ``DP2 deadline = 11ms`` + - ``DP2 LST = 11ms(DP2 deadline) - 9ms (DP2 LPT) = 2ms`` + - ``buf2 LFT = 2ms (DP2 LST) + 100ms (10 periods in buf2) = 102ms`` ==> ``DP1 deadline = 102ms`` + + DP2 will be scheduled + +**100ms time, 86ms passed, DP2 processed 9 times, is in the middle of 10th processing, having 5ms left:** + +.. uml:: images/dp_scheduling/example1_4.pu + +Pipeline state: + + - DP1 is ready for processing + - DP2 is ready for processing + + calculate deadlines: + + - ``buf3 LFT = 15 periods of LL2`` ==> ``DP2 deadline = 15ms`` + - ``DP2 LST = 15ms(DP2 deadline) - 9ms (DP2 LPT) = 6ms`` + - ``buf2 LFT = 6ms (DP2 LST) + 10ms (1 period in buf2) = 16ms`` ==> ``DP1 deadline = 16ms`` + + DP2 will be scheduled + +**105ms time, DP2 finished processing and released data from BUF2:** + +.. uml:: images/dp_scheduling/example1_5.pu + +Pipeline state: + + - DP1 is ready for processing + - DP2 is not ready for processing + + calculate deadlines: + + - ``buf3 LFT = 20 periods of LL2`` ==> ``DP2 deadline = 20ms`` + - ``DP2 LST = 20ms(DP2 deadline) - 9ms (DP2 LPT) = 11ms`` + - ``buf2 LFT = 11ms (DP2 LST) + 0ms = 11ms`` ==> ``DP1 deadline = 11ms`` + + DP1 will be scheduled + +EXAMPLE2 +========= +*data source period is shorter than data consumer period* + +**0ms time:** + +.. uml:: images/dp_scheduling/example2.pu + +Pipeline state: + + - DP1 is ready for processing + - DP2 is not ready for processing + + calculate deadlines: + + - ``buf3 LFT = 18 periods of LL2`` ==> ``DP2 deadline = 18ms`` + - ``DP2 LST = 18ms (DP2 deadline) - 10ms (DP2 LPT) = 8ms`` + - ``buf2 LFT = 8ms(DP2 LST) + 0 (0 complete periods of DP2 in buf2) = 8ms`` correction for multiple source cycle is = 0 + - ``DP1 deadline = 8ms`` + + DP1 will be scheduled + +**2ms time:** + +.. uml:: images/dp_scheduling/example2_1.pu + +Pipeline state: + + - DP1 is not ready for processing + - DP2 is ready for processing + + calculate deadlines: + + - ``buf3 LFT = 16 periods of LL2`` ==> ``DP2 deadline = 16ms`` + - ``DP2 LST = 16ms (DP2 deadline) - 10ms (DP2 LPT) = 6ms`` + - ``buf2 LFT = 6ms(DP2 LST) + 20 (1 complete periods of DP2 in buf2) = 26ms`` correction for multiple source cycle is < 0, so 0 is used + - ``DP1 deadline = 26ms`` + + DP2 will be scheduled + +**5ms time:** + +.. uml:: images/dp_scheduling/example2_1a.pu + +Pipeline state: + + - DP1 is ready for processing + - DP2 is in the middle of processing, still has to keep processing for 7ms. According to rules, the module should not release data from input buffer till the processing is finished, so buf2 still contains 20ms samples + + calculate deadlines: + + - ``buf3 LFT = 13 periods of LL2`` ==> ``DP2 deadline = 13ms`` + - ``DP2 LST = 13ms (DP2 deadline) - 10ms (DP2 LPT) = 3ms`` + - ``buf2 LFT = 3ms(DP2 LST) + 20 (1 complete periods of DP2 in buf2) = 23ms`` correction for multiple source cycle is < 0, so 0 is used + - ``DP1 deadline = 23ms`` + + DP2 will be scheduled and will keep processing for 7ms + +**12ms time, before releasing data from buf2 and acking data in buf3** + +.. uml:: images/dp_scheduling/example2_2a.pu + +Pipeline state: + + - DP1 is ready for processing + - DP2 finished processing, but not yet released data from buf2, so buf2 still contains 20ms samples + + calculate deadlines: + + - ``buf3 LFT = 6 periods of LL2`` ==> ``DP2 deadline = 6ms`` + - ``DP2 LST = 6ms (DP2 deadline) - 10ms (DP2 LPT) = -4ms`` LST is negative, so 0 should be used + - ``buf2 LFT = 0ms(DP2 LST) + 20ms (1 complete periods of DP2 in buf2) = 20ms`` correction for multiple source cycle is < 0, so 0 is used + - ``DP1 deadline = 20ms`` + + DP2 will be release data + +**12ms time, after releasing data from buf2 and acking data in buf3** + +.. uml:: images/dp_scheduling/example2_2.pu + +Pipeline state: + + - DP1 is ready for processing + - DP2 is not ready for processing + + calculate deadlines: + + - ``buf3 LFT = 26 periods of LL2`` ==> ``DP2 deadline = 26ms`` + - ``DP2 LST = 26ms (DP2 deadline) - 10ms (DP2 LPT) = 16ms`` + - ``buf2 LFT = 16ms(DP2 LST) + 0 (0 complete periods of DP2 in buf2) - 8ms correction (4 periods of DP2 * 2ms DP1 LPT) = 8ms`` + - ``DP1 deadline = 8ms`` + + DP1 will be scheduled + +**14ms time:** + +.. uml:: images/dp_scheduling/example2_3.pu + +Pipeline state: + + - DP1 is ready for processing + - DP2 is not ready for processing + + calculate deadlines: + + - ``buf3 LFT = 24 periods of LL2`` ==> ``DP2 deadline = 24ms`` + - ``DP2 LST = 24ms (DP2 deadline) - 10ms (DP2 LPT) = 14ms`` + - ``buf2 LFT = 14ms(DP2 LST) + 0 (0 complete periods of DP2 in buf2) - 6ms correction (3 periods of DP2 * 2ms DP1 LPT) = 8ms`` + - ``DP1 deadline = 8ms`` + + DP1 will be scheduled + +**16ms time:** + +.. uml:: images/dp_scheduling/example2_4.pu + +Pipeline state: + + - DP1 is ready for processing + - DP2 is not ready for processing + + calculate deadlines: + + - ``buf3 LFT = 22 periods of LL2`` ==> ``DP2 deadline = 22ms`` + - ``DP2 LST = 22ms (DP2 deadline) - 10ms (DP2 LPT) = 12ms`` + - ``buf2 LFT = 12ms(DP2 LST) + 0 (0 complete periods of DP2 in buf2) - 4ms correction (2 periods of DP2 * 2ms DP1 LPT) = 8ms`` + - ``DP1 deadline = 8ms`` + + DP1 will be scheduled + +**18ms time:** + +.. uml:: images/dp_scheduling/example2_5.pu + +Pipeline state: + + - DP1 is not ready for processing + - DP2 is not ready for processing + + calculate deadlines - however pointless at when no DP is ready: + + - ``buf3 LFT = 20 periods of LL2`` ==> ``DP2 deadline = 20ms`` + - ``DP2 LST = 20ms (DP2 deadline) - 10ms (DP2 LPT) = 10ms`` + - ``buf2 LFT = 10ms(DP2 LST) + 0 (0 complete periods of DP2 in buf2) - 2ms correction (2 periods of DP2 * 2ms DP1 LPT) = 8ms`` + - ``DP1 deadline = 8ms`` + + no DP will be scheduled + +**20ms time:** + +.. uml:: images/dp_scheduling/example2_6.pu + +Pipeline state: + + - DP1 is ready for processing + - DP2 is not ready for processing + + calculate deadlines - however pointless at when no DP is ready: + + - ``buf3 LFT = 18 periods of LL2`` ==> ``DP2 deadline = 18ms`` + - ``DP2 LST = 18ms (DP2 deadline) - 10ms (DP2 LPT) = 8ms`` + - ``buf2 LFT = 8ms(DP2 LST) + 0 (0 complete periods of DP2 in buf2) - 2ms correction (2 periods of DP2 * 2ms DP1 LPT) = 6ms`` + - ``DP1 deadline = 6ms`` + + DP1 will be scheduled + +**22ms time:** + +.. uml:: images/dp_scheduling/example2_7.pu + +Pipeline state: + + - DP1 is not ready for processing + - DP2 is ready for processing + + calculate deadlines + + - ``buf3 LFT = 16 periods of LL2`` ==> ``DP2 deadline = 16ms`` + - ``DP2 LST = 16ms (DP2 deadline) - 10ms (DP2 LPT) = 6ms`` + - ``buf2 LFT = 6ms(DP2 LST) +20 (1 complete period of DP2 in buf2) = 26ms`` correction for multiple source cycle is = 0 + - ``DP1 deadline = 26ms`` + + DP2 will be scheduled + + +STARTUP +========== + +Special case is "pipeline startup". When a pipeline is starting, deadlines cannot be calculated as all the modules are already late and deadlines are in the past. According to deadline calculation rules, the deadline is set to time when the module becomes ready + module's LPT. + +If a module finishes processing before its LPT. it is not guaranteed that it will do it again in any of next cycles. If it happens, the data should be held in the buffer till LPT passes. This prevents underruns in case any of future processing takes longer. This mechanism is called "delayed start". The module should stay in "delayed start" state till the next module becomes ready for the first time. + +Delayed start makes EDF scheduling possible and ensures that even when CPU load close to 100% every module have enough processing time to finish within its deadline. + +Example of a pipeline startup and 100% cpu usage +================================================ + +**0ms time:** + +.. uml:: images/dp_scheduling/example3.pu + +Pipeline state: + + - DP1 is not ready for processing, in startup delay state + - DP2 is not ready for processing, in startup delay state + + calculate deadlines + + - dedline of DP2 can't be calculated + - dedline of DP1 can't be calculated + + no DP will be scheduled + +**5ms time:** + +.. uml:: images/dp_scheduling/example3_1.pu + +Pipeline state: + + - DP1 is ready for processing, in startup delay state + - DP2 is not ready for processing, in startup delay state + + calculate deadlines + + - deadline for DP2 cant be calculated + - deadline of DP1 is fixed to 2ms (NOW + DP1 LPT) - because DP2 deadline cannot be calculated + + DP1 will be scheduled + +**7ms time:** + +.. uml:: images/dp_scheduling/example3_2.pu + +Pipeline state: + + - DP1 is not ready for processing, in startup delay state + - DP2 is not ready for processing, in startup delay state + + calculate deadlines + + - deadline for DP2 cant be calculated + - deadline for DP1 cant be calculated + + no DP will be scheduled + +**10ms time:** + +.. uml:: images/dp_scheduling/example3_3.pu + +Pipeline state: + + - DP1 is ready for processing, in startup delay state + - DP2 is not ready for processing, in startup delay state + + calculate deadlines + + - deadline for DP2 cant be calculated + - deadline of DP1 is fixed to 2ms (NOW + DP1 LPT) - because DP2 deadline cannot be calculated + + DP1 will be scheduled + +**12ms time:** + +.. uml:: images/dp_scheduling/example3_4.pu + +Pipeline state: + + - DP1 is not ready for processing, leaving startup delay state + - DP2 is ready for processing, in startup delay state + + calculate deadlines + + - deadline for DP2 is fixed to 6ms (NOW + DP2 LPT) + - ``DP2 LST = 6ms (DP2 deadline) - 6ms (DP2 LPT) = 0ms`` + - ``buf2 LFT = 0ms(DP2 LST) + 10ms (1 complete period of DP2 in buf2) = 10ms`` correction for multiple source cycle is = 0 + - ``DP1 deadline = 14ms`` + + DP2 will be scheduled + +**15ms time:** + +.. uml:: images/dp_scheduling/example3_5.pu + +Pipeline state: + + - DP1 is ready for processing + - DP2 is the middle for processing, 3ms left, in startup delay state + + calculate deadlines + + - deadline for DP2 was fixed to 6ms 3ms ago, so now it is 3ms + - ``DP2 LST = 3ms (DP2 deadline) - 6ms (DP2 LPT) = -3ms`` 0 will be used + - ``buf2 LFT = 0(DP2 LST) +10 (1 complete period of DP2 in buf2) = 10ms`` correction for multiple source cycle is = 0 + - ``DP1 deadline = 10ms`` + + DP2 will be scheduled + +**17ms time:** + +.. uml:: images/dp_scheduling/example3_6.pu + +Pipeline state: + + - DP1 is ready for processing + - DP2 is not ready for processing, leaving startup delay state + + calculate deadlines + + - ``buf3 LFT = 10 periods of LL2`` ==> ``DP2 deadline = 10ms`` + - ``DP2 LST = 10ms (DP2 deadline) - 6ms (DP2 LPT) = 4ms`` + - ``buf2 LFT = 4ms(DP2 LST) + 0 (0 complete periods of DP2 in buf2) - 2ms correction (2 periods of DP2 * 2ms DP1 LPT) = 2ms`` + - ``DP1 deadline = 2ms`` + + DP1 will be scheduled + +**19ms time:** + +.. uml:: images/dp_scheduling/example3_7.pu + +Pipeline state: + + - DP1 is ready for processing + - DP2 is not ready for processing + + calculate deadlines + + - ``buf3 LFT = 8 periods of LL2`` ==> ``DP2 deadline = 8ms`` + - ``DP2 LST = 10ms (DP2 deadline) - 6ms (DP2 LPT) = 2ms`` + - ``buf2 LFT = 2ms(DP2 LST) + 0 (0 complete periods of DP2 in buf2) - 0ms correction (0 periods of DP2 * 2ms DP1 LPT) = 2ms`` + - ``DP1 deadline = 2ms`` + + DP1 will be scheduled + + +Example of a 2 pipelines, one running and one in startup and 100% cpu usage +============================================================================ + +pipeline1 is running, DP use 80% of CPU, pipeline2 is starting. Calculating of DP1/DP2 LST and BUF1/BUF3 LFT makes no sense as they're connected to LLs + +**0ms time:** + +.. uml:: images/dp_scheduling/example4.pu + +Pipeline state: + + - DP1 is ready for processing + - DP2 is not ready for processing + + calculate deadlines + + - ``buf2 LFT = 10 periods of LL2`` ==> ``DP1 deadline = 10ms`` + - DP2 deadline cannot be calculated + + DP1 will be scheduled + +**5ms time:** + +.. uml:: images/dp_scheduling/example4_1.pu + +Pipeline state: + + - DP1 is in the middle of processing, 3ms left + - DP2 is ready for processing, in startup delay state + + calculate deadlines + + - ``buf2 LFT = 5 periods of LL2`` ==> ``DP1 deadline = 5ms`` + - DP2 deadline is fixed to ``DP2 period = 1ms`` + + DP1 will be preempted, DP2 will be scheduled + +**6ms time:** + +.. uml:: images/dp_scheduling/example4_2.pu + +Pipeline state: + + - DP1 is in the middle of processing, 3ms left + - DP2 is not ready for processing, leaving startup delay state + + calculate deadlines + + - ``buf2 LFT = 4 periods of LL2`` ==> ``DP1 deadline = 4ms`` + - ``buf4 LFT = 5 periods of LL2`` ==> ``DP2 deadline = 5ms`` + + + DP2 will be scheduled + + diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/async_messaging/flow_prod_cons_same_core.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/async_messaging/flow_prod_cons_same_core.pu new file mode 100644 index 00000000..876bea23 --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/async_messaging/flow_prod_cons_same_core.pu @@ -0,0 +1,83 @@ +@startuml + +box "DSP #0 (primary)" #LightBlue + participant "DSP #0: AMS" as ams + participant "DSP #0: KR" as kr + participant "DSP #0: Custom module" as custom_module +end box + +box "Shared SRAM" + participant "AMS database" as ams_db +end box + +... + +group Register KEY_PHRASE_DETECTED producer + group Get Message ID + kr -> ams: am_service_get_message_type_id(KEY_PHRASE_DETECTED UUID) + activate ams + ams -> ams_db: Find ID for KEY_PHRASE_DETECTED message + activate ams_db + alt If no KEY_PHRASE_DETECTED is found + ams -> ams_db: Assign ID to KEY_PHRASE_DETECTED message + return + end + return + end + + kr -> ams: am_service_register_producer(message_id) + activate ams + return +end + +... + +group Register KEY_PHRASE_DETECTED consumer + group Get Message ID + custom_module-> custom_module + end + + custom_module -> ams: am_service_register_consumer(message_id, callback) + activate ams + ams -> ams_db: Add KEY_PHRASE_DETECTED message consumer + return +end + +... + +group Send Async Message + kr -> ams: am_service_send_message(lp_kpd_id, message) + activate ams + + ams -> ams_db: Get KEY_PHRASE_DETECTED consumers + loop Until all consumer are called + ams -> ams_db: Get AMS consumer Processor ID + + alt AMS Consumer Processor ID != Current Processor ID + ams -> ams: Forward AMS message to the consumer's core + else AMS Consumer Processor ID == Current Processor ID + ams -> ams_db: Get AMS consumer callback + ams -> ams: Call consumer AMS callback + end + end + return +end + +... + +group Unregister KEY_PHRASE_DETECTED consumer + custom_module -> ams: am_service_unregister_consumer(message_id, callback) + activate ams + ams -> ams_db: Remove KEY_PHRASE_DETECTED message consumer + return +end + +... + +group Unregister KEY_PHRASE_DETECTED producer + kr -> ams: am_service_unregister_producer(message_id) + activate ams + return +end + +@enduml diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/async_messaging/flow_prod_primary_cons_secondary_core.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/async_messaging/flow_prod_primary_cons_secondary_core.pu new file mode 100644 index 00000000..60b9a7db --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/async_messaging/flow_prod_primary_cons_secondary_core.pu @@ -0,0 +1,153 @@ +@startuml + +scale max 1280 width + +box "DSP #0 (primary)" #LightBlue + participant "DSP #0: IXC Service" as ixc_service_dsp_0 + participant "DSP #0: IDC" as idc_dsp_0 + participant "DSP #0: AMS" as ams_dsp_0 + participant "DSP #0: KR" as kr_dsp_0 +end box + +box "DSP #1 (secondary)" #LightGreen + participant "DSP #1: IDC" as idc_dsp_1 + participant "DSP #1: Scheduler" as scheduler_dsp_1 + participant "DSP #1: IDC Task" as idc_dsp_1 + participant "DSP #1: IXC Service" as ixc_service_dsp_1 + participant "DSP #1: AMS" as ams_dsp_1 + participant "DSP #1: Custom module" as custom_module_dsp_1 +end box + +box "Shared SRAM" + participant "AMS database" as ams_db +end box + +... + +group Register KEY_PHRASE_DETECTED producer + group Get Message ID + kr_dsp_0 -> ams_dsp_0: am_service_get_message_type_id(KEY_PHRASE_DETECTED UUID) + activate ams_dsp_0 + ams_dsp_0 -> ams_db: Find ID for KEY_PHRASE_DETECTED message + activate ams_db + alt If no KEY_PHRASE_DETECTED is found + ams_dsp_0 -> ams_db: Assign ID to KEY_PHRASE_DETECTED message + end + return + return + end + + kr_dsp_0 -> ams_dsp_0: am_service_register_producer(message_id) + activate ams_dsp_0 + return +end + +... + +group Register KEY_PHRASE_DETECTED consumer + group Get Message ID + custom_module_dsp_1 -> ams_dsp_1: am_service_get_message_type_id(KEY_PHRASE_DETECTED UUID) + activate ams_dsp_1 + + ams_dsp_0 -> ams_db: Find ID for KEY_PHRASE_DETECTED message + activate ams_db + alt If no KEY_PHRASE_DETECTED is found + ams_dsp_0 -> ams_db: Assign ID to KEY_PHRASE_DETECTED message + end + return + end + + custom_module_dsp_1 -> ams_dsp_1: am_service_register_consumer(message_id) + activate ams_dsp_1 + ams_dsp_1 -> ams_db: Add KEY_PHRASE_DETECTED message consumer + return +end + +... + +group Send Async Message + kr_dsp_0 -> ams_dsp_0: am_service_send_message(message_id, message) + activate ams_dsp_0 + ams_dsp_0 -> ams_db: Get KEY_PHRASE_DETECTED consumers + + note left of ams_db + "External" means the consumers located on the other DSP cores. + "Internal" means the consumers located on the same DSP core. + end note + + loop Until all consumer are called + ams_dsp_0 -> ams_db: Get AMS consumer Processor ID + + alt AMS Consumer Processor ID != Current Processor ID + alt If a first time AMS consumer on this DSP core + alt If it is a first external AMS consumer + loop Until AMS message slot is reserved or limit of tries is reached + ams_dsp_0 -> ams_db: Reserve AMS message slot + end + + ams_dsp_0 -> ams_db: Increment a 'core use count' for AMS slot + ams_dsp_0 -> ams_db: Set MOVEMENT_REPORT message + ams_dsp_0 -> ams_dsp_0: Flush/Invalidate L1 cache + end + + ams_dsp_0 -> ixc_service_dsp_0: Send FORWARD_AMS_MESSAGE(ams_message_slot_id) IDC to the consumer's core + activate ixc_service_dsp_0 + ixc_service_dsp_0 -> idc_dsp_0: IDC Interrupt + activate idc_dsp_0 + idc_dsp_1 -> scheduler_dsp_1: Add/unblock IDC task + return + return + end + else AMS Consumer Processor ID == Current Processor ID + ams_dsp_0 -> ams_db: Get AMS consumer callback + ams_dsp_0 -> ams_dsp_0: Call consumer AMS callback + end + end + return + + ... + + scheduler_dsp_1 -> idc_dsp_1: Execute task + activate idc_dsp_1 + idc_dsp_1 -> ixc_service_dsp_1: Process IDC message + activate ixc_service_dsp_1 + ixc_service_dsp_1 -> ams_dsp_1: FORWARD_AMS_MESSAGE(ams_message_slot_id) + activate ams_dsp_1 + ams_dsp_1 -> ams_db: Get KEY_PHRASE_DETECTED consumers + loop Until all consumer are called + ams_dsp_1 -> ams_db: Get AMS consumer Processor ID + + alt AMS Consumer Processor ID == Current Processor ID + ams_dsp_1 -> ams_db: Get AMS consumer callback + alt If Custom module consumer + ams_dsp_1 -> custom_module_dsp_1: Call AMS Custom module callback + activate custom_module_dsp_1 + return + else + ams_dsp_1 -> ams_dsp_0: Call consumer AMS callback + end + end + end + return + return + return +end + +... + +group Unregister KEY_PHRASE_DETECTED produce + kr_dsp_0 -> ams_dsp_0: am_service_unregister_producer(message_id) + activate ams_dsp_0 + return +end + +... + +group Unregister KEY_PHRASE_DETECTED consumer + custom_module_dsp_1 -> ams_dsp_1: am_service_unregister_consumer(message_id) + activate ams_dsp_1 + ams_dsp_1 -> ams_db: Remove KEY_PHRASE_DETECTED message consumer + return +end + +@enduml diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example1.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example1.pu new file mode 100644 index 00000000..969188bb --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example1.pu @@ -0,0 +1,34 @@ +@startuml +left to right direction +(LL1) as mod1 #f6ed80 + +rectangle "BUF1\n\n100ms of data\n" as buf1 + +(DP1) as mod2 #ADD1B2 + +note bottom of mod2 + period 100ms + LPT: 5ms +end note + +rectangle "BUF2\n\n10ms of data\n" as buf2 + +(DP2) as mod3 #ADD1B2 + +note bottom of mod3 + period 10ms + LPT: 9ms +end note + +rectangle "BUF3\n\n15ms of data\n" as buf3 + +(LL2) as mod4 #f6ed80 + + +mod1--> buf1 +buf1 --> mod2 +mod2-->buf2 +buf2 --> mod3 +mod3-->buf3 +buf3 --> mod4 +@enduml \ No newline at end of file diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example1_1.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example1_1.pu new file mode 100644 index 00000000..b8f65e24 --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example1_1.pu @@ -0,0 +1,34 @@ +@startuml +left to right direction +(LL1) as mod1 #f6ed80 + +rectangle "BUF1\n\n109ms of data\n" as buf1 + +(DP1) as mod2 #ADD1B2 + +note bottom of mod2 + period 100ms + LPT: 5ms +end note + +rectangle "BUF2\n\n10ms of data\n" as buf2 + +(DP2) as mod3 #ADD1B2 + +note bottom of mod3 + period 10ms + LPT: 9ms +end note + +rectangle "BUF3\n\n6ms of data\n" as buf3 + +(LL2) as mod4 #f6ed80 + + +mod1--> buf1 +buf1 --> mod2 +mod2-->buf2 +buf2 --> mod3 +mod3-->buf3 +buf3 --> mod4 +@enduml \ No newline at end of file diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example1_2.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example1_2.pu new file mode 100644 index 00000000..9b8798ba --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example1_2.pu @@ -0,0 +1,34 @@ +@startuml +left to right direction +(LL1) as mod1 #f6ed80 + +rectangle "BUF1\n\n109ms of data\n" as buf1 + +(DP1) as mod2 #ADD1B2 + +note bottom of mod2 + period 100ms + LPT: 5ms +end note + +rectangle "BUF2\n\n0ms of data\n" as buf2 + +(DP2) as mod3 #ADD1B2 + +note bottom of mod3 + period 10ms + LPT: 9ms +end note + +rectangle "BUF3\n\n16ms of data\n" as buf3 + +(LL2) as mod4 #f6ed80 + + +mod1--> buf1 +buf1 --> mod2 +mod2-->buf2 +buf2 --> mod3 +mod3-->buf3 +buf3 --> mod4 +@enduml \ No newline at end of file diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example1_3.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example1_3.pu new file mode 100644 index 00000000..5dd8d956 --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example1_3.pu @@ -0,0 +1,40 @@ +@startuml +left to right direction +(LL1) as mod1 #f6ed80 + +rectangle "BUF1\n\n14ms of data\n" as buf1 +note bottom of buf1 + there was 109ms + LL1 produced 5ms + DP1 consumed 100ms + 109+5-100 = 14ms +end note + +(DP1) as mod2 #ADD1B2 + +note bottom of mod2 + period 100ms + LPT: 5ms +end note + +rectangle "BUF2\n\n100ms of data\n" as buf2 + +(DP2) as mod3 #ADD1B2 + +note bottom of mod3 + period 10ms + LPT: 9ms +end note + +rectangle "BUF3\n\n11ms of data\n" as buf3 + +(LL2) as mod4 #f6ed80 + + +mod1--> buf1 +buf1 --> mod2 +mod2-->buf2 +buf2 --> mod3 +mod3-->buf3 +buf3 --> mod4 +@enduml \ No newline at end of file diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example1_4.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example1_4.pu new file mode 100644 index 00000000..03ed3355 --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example1_4.pu @@ -0,0 +1,42 @@ +@startuml +left to right direction +(LL1) as mod1 #f6ed80 + +rectangle "BUF1\n\n100ms of data\n" as buf1 + +(DP1) as mod2 #ADD1B2 + +note bottom of mod2 + period 100ms + LPT: 5ms +end note + +rectangle "BUF2\n\n10ms of data\n" as buf2 + +(DP2) as mod3 #ADD1B2 + +note bottom of mod3 + period 10ms + LPT: 9ms +end note + +rectangle "BUF3\n\n15ms of data\n" as buf3 + +note bottom of buf3 + there was 11ms + DP2 produced 90ms + LL2 consumed 86ms + 11+90-86 = 15ms +end note + + +(LL2) as mod4 #f6ed80 + + +mod1--> buf1 +buf1 --> mod2 +mod2-->buf2 +buf2 --> mod3 +mod3-->buf3 +buf3 --> mod4 +@enduml \ No newline at end of file diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example1_5.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example1_5.pu new file mode 100644 index 00000000..098fb792 --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example1_5.pu @@ -0,0 +1,41 @@ +@startuml +left to right direction +(LL1) as mod1 #f6ed80 + +rectangle "BUF1\n\n105ms of data\n" as buf1 + +(DP1) as mod2 #ADD1B2 + +note bottom of mod2 + period 100ms + LPT: 5ms +end note + +rectangle "BUF2\n\n0ms of data\n" as buf2 + +(DP2) as mod3 #ADD1B2 + +note bottom of mod3 + period 10ms + LPT: 9ms +end note + +rectangle "BUF3\n\n20ms of data\n" as buf3 + +note bottom of buf3 + there was 15ms + DP2 produced 10ms + LL2 consumed 5ms + 15+10-5 = 20ms +end note + +(LL2) as mod4 #f6ed80 + + +mod1--> buf1 +buf1 --> mod2 +mod2-->buf2 +buf2 --> mod3 +mod3-->buf3 +buf3 --> mod4 +@enduml \ No newline at end of file diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example2.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example2.pu new file mode 100644 index 00000000..7677f06d --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example2.pu @@ -0,0 +1,34 @@ +@startuml +left to right direction +(LL1) as mod1 #f6ed80 + +rectangle "BUF1\n\n5ms of data\n" as buf1 + +(DP1) as mod2 #ADD1B2 + +note bottom of mod2 + period 5ms + LPT: 2ms +end note + +rectangle "BUF2\n\n15ms of data\n" as buf2 + +(DP2) as mod3 #ADD1B2 + +note bottom of mod3 + period 20ms + LPT: 10ms +end note + +rectangle "BUF3\n\n18ms of data\n" as buf3 + +(LL2) as mod4 #f6ed80 + + +mod1--> buf1 +buf1 --> mod2 +mod2-->buf2 +buf2 --> mod3 +mod3-->buf3 +buf3 --> mod4 +@enduml \ No newline at end of file diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example2_1.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example2_1.pu new file mode 100644 index 00000000..83dfc838 --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example2_1.pu @@ -0,0 +1,40 @@ +@startuml +left to right direction +(LL1) as mod1 #f6ed80 + +rectangle "BUF1\n\n2ms of data\n" as buf1 +note bottom of buf1 + there was 5ms + LL1 produced 2ms + DP1 consumed 5ms + 5-5+2 = 2ms +end note + +(DP1) as mod2 #ADD1B2 + +note bottom of mod2 + period 5ms + LPT: 2ms +end note + +rectangle "BUF2\n\n20ms of data\n" as buf2 + +(DP2) as mod3 #ADD1B2 + +note bottom of mod3 + period 20ms + LPT: 10ms +end note + +rectangle "BUF3\n\n16ms of data\n" as buf3 + +(LL2) as mod4 #f6ed80 + + +mod1--> buf1 +buf1 --> mod2 +mod2-->buf2 +buf2 --> mod3 +mod3-->buf3 +buf3 --> mod4 +@enduml \ No newline at end of file diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example2_1a.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example2_1a.pu new file mode 100644 index 00000000..8e1c2874 --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example2_1a.pu @@ -0,0 +1,34 @@ +@startuml +left to right direction +(LL1) as mod1 #f6ed80 + +rectangle "BUF1\n\n5ms of data\n" as buf1 + +(DP1) as mod2 #ADD1B2 + +note bottom of mod2 + period 5ms + LPT: 2ms +end note + +rectangle "BUF2\n\n20ms of data\n" as buf2 + +(DP2) as mod3 #ADD1B2 + +note bottom of mod3 + period 20ms + LPT: 10ms +end note + +rectangle "BUF3\n\n13ms of data\n" as buf3 + +(LL2) as mod4 #f6ed80 + + +mod1--> buf1 +buf1 --> mod2 +mod2-->buf2 +buf2 --> mod3 +mod3-->buf3 +buf3 --> mod4 +@enduml \ No newline at end of file diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example2_2.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example2_2.pu new file mode 100644 index 00000000..0e7440cf --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example2_2.pu @@ -0,0 +1,34 @@ +@startuml +left to right direction +(LL1) as mod1 #f6ed80 + +rectangle "BUF1\n\n12ms of data\n" as buf1 + +(DP1) as mod2 #ADD1B2 + +note bottom of mod2 + period 5ms + LPT: 2ms +end note + +rectangle "BUF2\n\n0ms of data\n" as buf2 + +(DP2) as mod3 #ADD1B2 + +note bottom of mod3 + period 20ms + LPT: 10ms +end note + +rectangle "BUF3\n\n26ms of data\n" as buf3 + +(LL2) as mod4 #f6ed80 + + +mod1--> buf1 +buf1 --> mod2 +mod2-->buf2 +buf2 --> mod3 +mod3-->buf3 +buf3 --> mod4 +@enduml \ No newline at end of file diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example2_2a.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example2_2a.pu new file mode 100644 index 00000000..20f6e42e --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example2_2a.pu @@ -0,0 +1,34 @@ +@startuml +left to right direction +(LL1) as mod1 #f6ed80 + +rectangle "BUF1\n\n12ms of data\n" as buf1 + +(DP1) as mod2 #ADD1B2 + +note bottom of mod2 + period 5ms + LPT: 2ms +end note + +rectangle "BUF2\n\n20ms of data\n" as buf2 + +(DP2) as mod3 #ADD1B2 + +note bottom of mod3 + period 20ms + LPT: 10ms +end note + +rectangle "BUF3\n\n6ms of data\n" as buf3 + +(LL2) as mod4 #f6ed80 + + +mod1--> buf1 +buf1 --> mod2 +mod2-->buf2 +buf2 --> mod3 +mod3-->buf3 +buf3 --> mod4 +@enduml \ No newline at end of file diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example2_3.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example2_3.pu new file mode 100644 index 00000000..a976be4f --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example2_3.pu @@ -0,0 +1,40 @@ +@startuml +left to right direction +(LL1) as mod1 #f6ed80 + +rectangle "BUF1\n\n9ms of data\n" as buf1 +note bottom of buf1 + there was 12ms + LL1 produced 2ms + DP1 consumed 5ms + 12+2-5 = 9ms +end note + +(DP1) as mod2 #ADD1B2 + +note bottom of mod2 + period 5ms + LPT: 2ms +end note + +rectangle "BUF2\n\n5ms of data\n" as buf2 + +(DP2) as mod3 #ADD1B2 + +note bottom of mod3 + period 20ms + LPT: 10ms +end note + +rectangle "BUF3\n\n24ms of data\n" as buf3 + +(LL2) as mod4 #f6ed80 + + +mod1--> buf1 +buf1 --> mod2 +mod2-->buf2 +buf2 --> mod3 +mod3-->buf3 +buf3 --> mod4 +@enduml \ No newline at end of file diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example2_4.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example2_4.pu new file mode 100644 index 00000000..4e06b3e5 --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example2_4.pu @@ -0,0 +1,40 @@ +@startuml +left to right direction +(LL1) as mod1 #f6ed80 + +rectangle "BUF1\n\n6ms of data\n" as buf1 +note bottom of buf1 + there was 9ms + LL1 produced 2ms + DP1 consumed 5ms + 9+2-5 = 6ms +end note + +(DP1) as mod2 #ADD1B2 + +note bottom of mod2 + period 5ms + LPT: 2ms +end note + +rectangle "BUF2\n\n10ms of data\n" as buf2 + +(DP2) as mod3 #ADD1B2 + +note bottom of mod3 + period 20ms + LPT: 10ms +end note + +rectangle "BUF3\n\n22ms of data\n" as buf3 + +(LL2) as mod4 #f6ed80 + + +mod1--> buf1 +buf1 --> mod2 +mod2-->buf2 +buf2 --> mod3 +mod3-->buf3 +buf3 --> mod4 +@enduml \ No newline at end of file diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example2_5.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example2_5.pu new file mode 100644 index 00000000..c1efc9c0 --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example2_5.pu @@ -0,0 +1,40 @@ +@startuml +left to right direction +(LL1) as mod1 #f6ed80 + +rectangle "BUF1\n\n3ms of data\n" as buf1 +note bottom of buf1 + there was 6ms + LL1 produced 2ms + DP1 consumed 5ms + 9+2-5 = 3ms +end note + +(DP1) as mod2 #ADD1B2 + +note bottom of mod2 + period 5ms + LPT: 2ms +end note + +rectangle "BUF2\n\n15ms of data\n" as buf2 + +(DP2) as mod3 #ADD1B2 + +note bottom of mod3 + period 20ms + LPT: 10ms +end note + +rectangle "BUF3\n\n20ms of data\n" as buf3 + +(LL2) as mod4 #f6ed80 + + +mod1--> buf1 +buf1 --> mod2 +mod2-->buf2 +buf2 --> mod3 +mod3-->buf3 +buf3 --> mod4 +@enduml \ No newline at end of file diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example2_6.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example2_6.pu new file mode 100644 index 00000000..7677f06d --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example2_6.pu @@ -0,0 +1,34 @@ +@startuml +left to right direction +(LL1) as mod1 #f6ed80 + +rectangle "BUF1\n\n5ms of data\n" as buf1 + +(DP1) as mod2 #ADD1B2 + +note bottom of mod2 + period 5ms + LPT: 2ms +end note + +rectangle "BUF2\n\n15ms of data\n" as buf2 + +(DP2) as mod3 #ADD1B2 + +note bottom of mod3 + period 20ms + LPT: 10ms +end note + +rectangle "BUF3\n\n18ms of data\n" as buf3 + +(LL2) as mod4 #f6ed80 + + +mod1--> buf1 +buf1 --> mod2 +mod2-->buf2 +buf2 --> mod3 +mod3-->buf3 +buf3 --> mod4 +@enduml \ No newline at end of file diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example2_7.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example2_7.pu new file mode 100644 index 00000000..b2a6ca71 --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example2_7.pu @@ -0,0 +1,39 @@ +@startuml +left to right direction +(LL1) as mod1 #f6ed80 + +rectangle "BUF1\n\n2ms of data\n" as buf1 +note bottom of buf1 + there was 5ms + LL1 produced 2ms + DP1 consumed 5ms + 5+2-5 = 2ms +end note +(DP1) as mod2 #ADD1B2 + +note bottom of mod2 + period 5ms + LPT: 2ms +end note + +rectangle "BUF2\n\n20ms of data\n" as buf2 + +(DP2) as mod3 #ADD1B2 + +note bottom of mod3 + period 20ms + LPT: 10ms +end note + +rectangle "BUF3\n\n16ms of data\n" as buf3 + +(LL2) as mod4 #f6ed80 + + +mod1--> buf1 +buf1 --> mod2 +mod2-->buf2 +buf2 --> mod3 +mod3-->buf3 +buf3 --> mod4 +@enduml \ No newline at end of file diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example3.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example3.pu new file mode 100644 index 00000000..92dbf045 --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example3.pu @@ -0,0 +1,34 @@ +@startuml +left to right direction +(LL1) as mod1 #f6ed80 + +rectangle "BUF1\n\n0ms of data\n" as buf1 + +(DP1) as mod2 #ADD1B2 + +note bottom of mod2 + period 5ms + LPT: 2ms +end note + +rectangle "BUF2\n\n0ms of data\n" as buf2 + +(DP2) as mod3 #ADD1B2 + +note bottom of mod3 + period 10ms + LPT: 6ms +end note + +rectangle "BUF3\n\n0ms of data\n" as buf3 + +(LL2) as mod4 #f6ed80 + + +mod1--> buf1 +buf1 --> mod2 +mod2-->buf2 +buf2 --> mod3 +mod3-->buf3 +buf3 --> mod4 +@enduml \ No newline at end of file diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example3_1.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example3_1.pu new file mode 100644 index 00000000..0c83890a --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example3_1.pu @@ -0,0 +1,34 @@ +@startuml +left to right direction +(LL1) as mod1 #f6ed80 + +rectangle "BUF1\n\n5ms of data\n" as buf1 + +(DP1) as mod2 #ADD1B2 + +note bottom of mod2 + period 5ms + LPT: 2ms +end note + +rectangle "BUF2\n\n0ms of data\n" as buf2 + +(DP2) as mod3 #ADD1B2 + +note bottom of mod3 + period 10ms + LPT: 6ms +end note + +rectangle "BUF3\n\n0ms of data\n" as buf3 + +(LL2) as mod4 #f6ed80 + + +mod1--> buf1 +buf1 --> mod2 +mod2-->buf2 +buf2 --> mod3 +mod3-->buf3 +buf3 --> mod4 +@enduml \ No newline at end of file diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example3_2.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example3_2.pu new file mode 100644 index 00000000..c5e7faba --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example3_2.pu @@ -0,0 +1,34 @@ +@startuml +left to right direction +(LL1) as mod1 #f6ed80 + +rectangle "BUF1\n\n2ms of data\n" as buf1 + +(DP1) as mod2 #ADD1B2 + +note bottom of mod2 + period 5ms + LPT: 2ms +end note + +rectangle "BUF2\n\n5ms of data\n" as buf2 + +(DP2) as mod3 #ADD1B2 + +note bottom of mod3 + period 10ms + LPT: 6ms +end note + +rectangle "BUF3\n\n0ms of data\n" as buf3 + +(LL2) as mod4 #f6ed80 + + +mod1--> buf1 +buf1 --> mod2 +mod2-->buf2 +buf2 --> mod3 +mod3-->buf3 +buf3 --> mod4 +@enduml \ No newline at end of file diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example3_3.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example3_3.pu new file mode 100644 index 00000000..64b7accb --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example3_3.pu @@ -0,0 +1,34 @@ +@startuml +left to right direction +(LL1) as mod1 #f6ed80 + +rectangle "BUF1\n\n5ms of data\n" as buf1 + +(DP1) as mod2 #ADD1B2 + +note bottom of mod2 + period 5ms + LPT: 2ms +end note + +rectangle "BUF2\n\n5ms of data\n" as buf2 + +(DP2) as mod3 #ADD1B2 + +note bottom of mod3 + period 10ms + LPT: 6ms +end note + +rectangle "BUF3\n\n0ms of data\n" as buf3 + +(LL2) as mod4 #f6ed80 + + +mod1--> buf1 +buf1 --> mod2 +mod2-->buf2 +buf2 --> mod3 +mod3-->buf3 +buf3 --> mod4 +@enduml \ No newline at end of file diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example3_4.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example3_4.pu new file mode 100644 index 00000000..858ff446 --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example3_4.pu @@ -0,0 +1,34 @@ +@startuml +left to right direction +(LL1) as mod1 #f6ed80 + +rectangle "BUF1\n\n2ms of data\n" as buf1 + +(DP1) as mod2 #ADD1B2 + +note bottom of mod2 + period 5ms + LPT: 2ms +end note + +rectangle "BUF2\n\n10ms of data\n" as buf2 + +(DP2) as mod3 #ADD1B2 + +note bottom of mod3 + period 10ms + LPT: 6ms +end note + +rectangle "BUF3\n\n0ms of data\n" as buf3 + +(LL2) as mod4 #f6ed80 + + +mod1--> buf1 +buf1 --> mod2 +mod2-->buf2 +buf2 --> mod3 +mod3-->buf3 +buf3 --> mod4 +@enduml \ No newline at end of file diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example3_5.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example3_5.pu new file mode 100644 index 00000000..d9ccd7e6 --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example3_5.pu @@ -0,0 +1,34 @@ +@startuml +left to right direction +(LL1) as mod1 #f6ed80 + +rectangle "BUF1\n\n9ms of data\n" as buf1 + +(DP1) as mod2 #ADD1B2 + +note bottom of mod2 + period 5ms + LPT: 2ms +end note + +rectangle "BUF2\n\n10ms of data\n" as buf2 + +(DP2) as mod3 #ADD1B2 + +note bottom of mod3 + period 10ms + LPT: 6ms +end note + +rectangle "BUF3\n\n0ms of data\n" as buf3 + +(LL2) as mod4 #f6ed80 + + +mod1--> buf1 +buf1 --> mod2 +mod2-->buf2 +buf2 --> mod3 +mod3-->buf3 +buf3 --> mod4 +@enduml \ No newline at end of file diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example3_6.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example3_6.pu new file mode 100644 index 00000000..f43be350 --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example3_6.pu @@ -0,0 +1,34 @@ +@startuml +left to right direction +(LL1) as mod1 #f6ed80 + +rectangle "BUF1\n\n12ms of data\n" as buf1 + +(DP1) as mod2 #ADD1B2 + +note bottom of mod2 + period 5ms + LPT: 2ms +end note + +rectangle "BUF2\n\n0ms of data\n" as buf2 + +(DP2) as mod3 #ADD1B2 + +note bottom of mod3 + period 10ms + LPT: 6ms +end note + +rectangle "BUF3\n\n10ms of data\n" as buf3 + +(LL2) as mod4 #f6ed80 + + +mod1--> buf1 +buf1 --> mod2 +mod2-->buf2 +buf2 --> mod3 +mod3-->buf3 +buf3 --> mod4 +@enduml \ No newline at end of file diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example3_7.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example3_7.pu new file mode 100644 index 00000000..5591eaae --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example3_7.pu @@ -0,0 +1,40 @@ +@startuml +left to right direction +(LL1) as mod1 #f6ed80 + +rectangle "BUF1\n\n9ms of data\n" as buf1 +note bottom of buf1 + there was 12ms + LL1 procuded 2ms + DP1 consumed 5ms + 12 + 2 - 5 = 9ms +end note + +(DP1) as mod2 #ADD1B2 + +note bottom of mod2 + period 5ms + LPT: 2ms +end note + +rectangle "BUF2\n\n5ms of data\n" as buf2 + +(DP2) as mod3 #ADD1B2 + +note bottom of mod3 + period 10ms + LPT: 6ms +end note + +rectangle "BUF3\n\n8ms of data\n" as buf3 + +(LL2) as mod4 #f6ed80 + + +mod1--> buf1 +buf1 --> mod2 +mod2-->buf2 +buf2 --> mod3 +mod3-->buf3 +buf3 --> mod4 +@enduml \ No newline at end of file diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example4.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example4.pu new file mode 100644 index 00000000..dfb6cd24 --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example4.pu @@ -0,0 +1,38 @@ +@startuml +left to right direction + +package pipeline2{ + (LL3) #f6ed80 + rectangle "BUF3\n\n0ms of data\n" as buf3 + (DP2) #ADD1B2 + note bottom of DP2 + period 5ms + LPT: 1ms + end note + rectangle "BUF4\n\n0ms of data\n" as buf4 + (LL4) #f6ed80 +} + +package pipeline1{ + (LL1) #f6ed80 + rectangle "BUF1\n\n10ms of data\n" as buf1 + (DP1) #ADD1B2 + note bottom of DP1 + period 10ms + LPT: 8ms + end note + rectangle "BUF2\n\n10ms of data\n" as buf2 + (LL2) #f6ed80 + } + +LL1 --> buf1 +buf1 --> DP1 +DP1 --> buf2 +buf2 --> LL2 + +LL3 --> buf3 +buf3 --> DP2 +DP2 --> buf4 +buf4 --> LL4 + +@enduml \ No newline at end of file diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example4_1.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example4_1.pu new file mode 100644 index 00000000..627e730b --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example4_1.pu @@ -0,0 +1,38 @@ +@startuml +left to right direction + +package pipeline2{ + (LL3) #f6ed80 + rectangle "BUF3\n\n5ms of data\n" as buf3 + (DP2) #ADD1B2 + note bottom of DP2 + period 5ms + LPT: 1ms + end note + rectangle "BUF4\n\n0ms of data\n" as buf4 + (LL4) #f6ed80 +} + +package pipeline1{ + (LL1) #f6ed80 + rectangle "BUF1\n\n15ms of data\n" as buf1 + (DP1) #ADD1B2 + note bottom of DP1 + period 10ms + LPT: 8ms + end note + rectangle "BUF2\n\n5ms of data\n" as buf2 + (LL2) #f6ed80 + } + +LL1 --> buf1 +buf1 --> DP1 +DP1 --> buf2 +buf2 --> LL2 + +LL3 --> buf3 +buf3 --> DP2 +DP2 --> buf4 +buf4 --> LL4 + +@enduml \ No newline at end of file diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example4_2.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example4_2.pu new file mode 100644 index 00000000..9e5a49b0 --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/example4_2.pu @@ -0,0 +1,38 @@ +@startuml +left to right direction + +package pipeline2{ + (LL3) #f6ed80 + rectangle "BUF3\n\n1ms of data\n" as buf3 + (DP2) #ADD1B2 + note bottom of DP2 + period 5ms + LPT: 1ms + end note + rectangle "BUF4\n\n5ms of data\n" as buf4 + (LL4) #f6ed80 +} + +package pipeline1{ + (LL1) #f6ed80 + rectangle "BUF1\n\n16ms of data\n" as buf1 + (DP1) #ADD1B2 + note bottom of DP1 + period 10ms + LPT: 8ms + end note + rectangle "BUF2\n\n4ms of data\n" as buf2 + (LL2) #f6ed80 + } + +LL1 --> buf1 +buf1 --> DP1 +DP1 --> buf2 +buf2 --> LL2 + +LL3 --> buf3 +buf3 --> DP2 +DP2 --> buf4 +buf4 --> LL4 + +@enduml \ No newline at end of file diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/pic1_chains.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/pic1_chains.pu new file mode 100644 index 00000000..0dc0256b --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/dp_scheduling/pic1_chains.pu @@ -0,0 +1,16 @@ +left to right direction +(LL1) as mod1 +(DP1) as mod2 #ADD1B2 +(DP2) as mod3 #ADD1B2 +(LL2) as mod4 +(DP3) as mod5 #7D3CFF +(DP4) as mod6 #7D3CFF +(LL3) as mod7 + + +mod1-->mod2 +mod2-->mod3 +mod3-->mod4 +mod4-->mod5 +mod5-->mod6 +mod6-->mod7 diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/lib_manager/library_manager_delete_instance.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/lib_manager/library_manager_delete_instance.pu new file mode 100644 index 00000000..6a827e38 --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/lib_manager/library_manager_delete_instance.pu @@ -0,0 +1,56 @@ +@startuml + +box "Host" #LightGreen + participant "Driver" as host_driver +end box + +box "SOF" #LightSkyBlue + participant "IPC4 Handler" as ipc4_handler + participant "Component Manager" as component_manager + participant "Library Manager" as library_manager +end box + +box "Zephyr RTOS" #LightBlue + participant "Memory Management Driver" as memory_management_driver +end box + +host_driver -> ipc4_handler: SOF_IPC4_MOD_DELETE_INSTANCE + activate ipc4_handler + ipc4_handler -> component_manager: Free comp_driver + activate component_manager + alt IADK module + component_manager -> library_manager: Deinitialize comp_driver \nwith Processing Module Adapter + activate library_manager + library_manager -> component_manager: return status + deactivate library_manager + else SOF module + component_manager -> library_manager: Deinitialize comp_driver + activate library_manager + library_manager -> component_manager: return status + deactivate library_manager + end alt + component_manager -> library_manager: Free comp_driver resources + activate library_manager + library_manager -> memory_management_driver: Free/Unmap L2 memory for code and rodata + activate memory_management_driver + memory_management_driver -> library_manager + deactivate memory_management_driver + library_manager -> memory_management_driver: Free/Unmap L2 memory for bss + activate memory_management_driver + memory_management_driver -> library_manager + deactivate memory_management_driver + loop Search library for shared module + library_manager -> library_manager: Check if shared module exists and is loaded + library_manager -> memory_management_driver: Free/Unmap L2 memory for shared module + activate memory_management_driver + memory_management_driver -> library_manager + deactivate memory_management_driver + end loop + library_manager -> component_manager: return status + deactivate library_manager + component_manager -> ipc4_handler: return status + deactivate component_manager +ipc4_handler -> host_driver: Complete IPC request +deactivate ipc4_handler + +@enduml diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/lib_manager/library_manager_init_instance.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/lib_manager/library_manager_init_instance.pu new file mode 100644 index 00000000..9b16f5fd --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/lib_manager/library_manager_init_instance.pu @@ -0,0 +1,65 @@ +@startuml + +box "Host" #LightGreen + participant "Driver" as host_driver +end box + +box "SOF" #LightSkyBlue + participant "IPC4 Handler" as ipc4_handler + participant "Component Manager" as component_manager + participant "Library Manager" as library_manager +end box + +box "Zephyr RTOS" #LightBlue + participant "Memory Management Driver" as memory_management_driver +end box + +host_driver -> ipc4_handler: SOF_IPC4_MOD_INIT_INSTANCE + activate ipc4_handler + ipc4_handler -> library_manager: lib_manager_register_module() + activate library_manager + library_manager -> ipc4_handler: return status + deactivate library_manager + ipc4_handler -> component_manager: Create comp_driver + activate component_manager + component_manager -> library_manager: Allocate L2 memory for module + activate library_manager + library_manager -> memory_management_driver: Map L2 memory + deactivate library_manager + activate memory_management_driver + memory_management_driver -> library_manager + deactivate memory_management_driver + activate library_manager + library_manager -> memory_management_driver: Load module code and rodata \nfrom L3 to L2 memory + activate memory_management_driver + memory_management_driver -> library_manager + deactivate memory_management_driver + library_manager -> memory_management_driver: Initialize L2 memory for bss + activate memory_management_driver + memory_management_driver -> library_manager + deactivate memory_management_driver + loop Search library for shared module + library_manager -> library_manager: Check if shared module exists and is not loaded + library_manager -> memory_management_driver: Allocate/Map L2 memory for shared module + activate memory_management_driver + memory_management_driver -> library_manager + deactivate memory_management_driver + library_manager -> memory_management_driver: Load shared module code and rodata \nfrom L3 to L2 memory + activate memory_management_driver + memory_management_driver -> library_manager + deactivate memory_management_driver + end loop + alt IADK module + component_manager -> library_manager: Create/Initialize comp_driver \nwith IADK Module Adapter + library_manager -> component_manager: return status + else SOF module + component_manager -> library_manager: Create/Initialize comp_driver + library_manager -> component_manager: return status + deactivate library_manager + end alt + component_manager -> ipc4_handler: return status + deactivate component_manager +ipc4_handler -> host_driver: Complete IPC request +deactivate ipc4_handler + +@enduml diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/lib_manager/library_manager_load.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/lib_manager/library_manager_load.pu new file mode 100644 index 00000000..714a5de5 --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/lib_manager/library_manager_load.pu @@ -0,0 +1,49 @@ +@startuml + +box "Host" #LightGreen + participant "Driver" as host_driver +end box + +box "SOF" #LightBlue + participant "IPC4 Handler" as ipc4_handler + participant "Library Manager" as library_manager + participant "MPP Memory Manager" as mpp_memory_manager +end box + +box "ACE Platform" #LightYellow + participant "ROM EXT" as rom_ext +end box + + +host_driver -> ipc4_handler: SOF_IPC4_GLB_LOAD_LIBRARY + activate ipc4_handler + ipc4_handler -> library_manager: lib_manager_load_library() + activate library_manager + library_manager -> library_manager: Parse Manifest \nPrepare Storage Memory + library_manager -> mpp_memory_manager: Allocate L3 memory for library + activate mpp_memory_manager + mpp_memory_manager -> library_manager + deactivate mpp_memory_manager + library_manager -> library_manager: Prepare HDA DMA transfer + host_driver -> library_manager: Transfer library manifest over DMA\nto L3 memory + note right: if SoC does not support L3 memory\nthen L2 memory has to be used + opt if AUTH_API_ENABLED + library_manager -> rom_ext: Verify Manifest + activate rom_ext + rom_ext -> library_manager: result + deactivate rom_ext + end opt + host_driver -> library_manager: Transfer library code over DMA\nto L3 memory + opt if AUTH_API_ENABLED + library_manager -> rom_ext: Verify whole Library + activate rom_ext + rom_ext -> library_manager: result + deactivate rom_ext + end opt + library_manager -> library_manager: Update Library \ndescriptors table + library_manager -> ipc4_handler: return status + deactivate library_manager +ipc4_handler -> host_driver: Complete IPC request +deactivate ipc4_handler + +@enduml diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/mpp_layer_diagram.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/mpp_layer_diagram.pu new file mode 100644 index 00000000..24f8c175 --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/mpp_layer_diagram.pu @@ -0,0 +1,55 @@ +@startuml + +allowmixing + +scale max 1024 width + +package "SOF" { + + package "Media Processing Pipelines layer" as MEDIA_PROCESSING_PIPELINES { + package "MPP Scheduling" as MPP_SCHEDULING { + component "LL Tasks" as LL_TASKS + component "DP Tasks" as DP_TASKS + + DP_TASKS -[hidden]down- LL_TASKS + } + + package "Communication" as COMMUNICATION { + component "IPC Message Processing and common command definitions" as IPC_MESSAGE_PROCESSING + component "Async Messaging" as ASYNC_MESSAGING + + IPC_MESSAGE_PROCESSING -[hidden]right- ASYNC_MESSAGING + } + + package "Pipeline/Component Infrastructure" as PIPELINE_COMPONENT_INFRASTRUCTURE { + component "Pipeline Management" as PIPELINE_MANAGEMENT + component "Host/DAI Gateways" as HOST_DAI_GATEWAYS + component "Processing Component Management" as PROCESSING_COMPONENT_MANAGEMENT + + PIPELINE_MANAGEMENT -[hidden]right- HOST_DAI_GATEWAYS + HOST_DAI_GATEWAYS -[hidden]right- PROCESSING_COMPONENT_MANAGEMENT + } + + COMMUNICATION -[hidden]down- PIPELINE_COMPONENT_INFRASTRUCTURE + COMMUNICATION -[hidden]right- MPP_SCHEDULING + } + + package "Zephyr" as ZEPHYR { + interface "Zephyr Services, SoC HAL and Driver Interfaces" as SS + + component "SoC HAL" as SOC + component "Drivers" as DRIVERS + component "XTHAL" as XTHAL + component "Services" as SERVICES + + SS -[hidden]down- SERVICES + SERVICES -[hidden]right- SOC + SOC -[hidden]right- DRIVERS + DRIVERS -[hidden]right- XTHAL + } + + MEDIA_PROCESSING_PIPELINES -[hidden]down- ZEPHYR + PIPELINE_COMPONENT_INFRASTRUCTURE -[hidden]down- ZEPHYR +} + +@enduml diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/mpp_scheduling/edf_scheduling.diag b/architectures/firmware/sof-zephyr/mpp_layer/images/mpp_scheduling/edf_scheduling.diag new file mode 100644 index 00000000..b9680b22 --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/mpp_scheduling/edf_scheduling.diag @@ -0,0 +1,42 @@ +// FIXME: blockdiag is orphaned and not compatible with Pillow anymore: +// https://github.com/blockdiag/blockdiag/pull/171 + +blockdiag edf_scheduling { + + node_width = 250; + node_height = 120; + default_fontsize = 16; + + Comp_1 -> Comp_2 + comment_1 -> Comp_2 [style=dashed] + Comp_2 -> Comp_3 + comment_2 -> Comp_3 [style=dashed] + Comp_3 -> Comp_4 + comment_3 -> Comp_4 [style=dashed] + Comp_4 -> sink + comment_4 -> sink [style=dashed] + + Comp_1 [label="DP component 1\n + *processing period\n + *compute requirement"] + Comp_2 [label="DP component 2\n + *processing period\n + *compute requirement"] + Comp_3 [label="DP component 3\n + *processing period\n + *compute requirement"] + Comp_4 [label="DP component 4\n + *processing period\n + *compute requirement"] + + sink [label="real time sink", shape=endpoint, fontsize = 16] + + comment_1 [label="DP1 to deliver data let\n + DP2 meet its objective"] + comment_2 [label="DP2 to deliver data let\n + DP3 meet its objective"] + comment_3 [label="DP3 to deliver data let\n + DP4 meet its objective"] + comment_4 [label="DP4 to deliver data\n + to real time-sink"] +} diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/mpp_scheduling/example_DP_secondary_core_timeline.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/mpp_scheduling/example_DP_secondary_core_timeline.pu new file mode 100644 index 00000000..2d3e35c9 --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/mpp_scheduling/example_DP_secondary_core_timeline.pu @@ -0,0 +1,136 @@ +@startuml + +Title DP tasks scheduling on secondary DSP core + +legend +Assumptions: +1) 1ms scheduling +2) No LL tasks assigned to example secondary DSP core +3) DP Task B do not depend on Task A completion +(otherwise, Task B would start on next timer interrupt after A +completion) +end legend + +scale 1 as 150 pixels + +concise "Task B" as Task_B +concise "Task A" as Task_A + +concise "DP task processing" as DP_Processing +robust "DSP" as DSP +concise "Timer interrupt" as Interrupt + + +@Task_A +0 is Busy +1.5 is {-} + +4 is Busy +5.5 is {-} + +8 is Busy +9.5 is {-} + +@0 <-> @4: Task A schedule period (4ms) +@4 <-> @5.5: Task A execution time (1.5ms) + +DP_Processing@0 -[#Orange]> Task_A@0 +DP_Processing@1 -[#Orange]> Task_A@1 +DP_Processing@1.5 -[#Orange]> Task_A@1.5 + + +@Task_B +0 is Busy +2 is {-} + +6 is Busy +8 is {-} + +@0 <-> @6: Task B schedule period (6ms) +@6 <-> @8: Task B execution time (2ms) + +DP_Processing@1.5 -[#Brown]> Task_B@0 +DP_Processing@2 -[#Brown]> Task_B@0.5 +DP_Processing@3 -[#Brown]> Task_B@1.5 +DP_Processing@3.5 -[#Brown]> Task_B@2 + +DSP is Idle +DP_Processing is {-} + +@0 +DP_Processing is "A" + +@0 +Interrupt -[#DarkViolet]> DSP +DSP -> DP_Processing +DSP is "Scheduling" +DP_Processing is "A" + +@1 +Interrupt -[#DarkViolet]> DSP +DSP -> DP_Processing +DP_Processing is "A" + +@1.5 +DP_Processing -> DSP +DSP -> DP_Processing +DP_Processing is "B" + +@2 +Interrupt -[#DarkViolet]> DSP +DSP -> DP_Processing +DP_Processing is "B" + +@3 +Interrupt -[#DarkViolet]> DSP +DSP -> DP_Processing +DP_Processing is "B" + +@3.5 +DP_Processing -> DSP +DSP is Idle +DP_Processing is {-} + +@4 +Interrupt -[#DarkViolet]> DSP +DSP is "Scheduling" +DSP -> DP_Processing +DP_Processing is "A" + +@5 +Interrupt -[#DarkViolet]> DSP +DSP -> DP_Processing +DP_Processing is "A" + +@5.5 +DP_Processing -> DSP +DSP is Idle +DP_Processing is {-} + +@6.001 +Interrupt -[#DarkViolet]> DSP +DSP -> DP_Processing +DSP is "Scheduling" +DP_Processing is "B" + +@7.001 +Interrupt -[#DarkViolet]> DSP +DSP -> DP_Processing +DP_Processing is "B" + +@8.001 +Interrupt -[#DarkViolet]> DSP +DSP -> DP_Processing +DP_Processing is "A" + +@9.001 +Interrupt -[#DarkViolet]> DSP +DSP -> DP_Processing +DP_Processing is "A" + +@9.5 +DP_Processing -> DSP +DSP is Idle +DP_Processing is {-} + +@enduml diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/mpp_scheduling/example_LL_DP_timeline.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/mpp_scheduling/example_LL_DP_timeline.pu new file mode 100644 index 00000000..74a91cce --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/mpp_scheduling/example_LL_DP_timeline.pu @@ -0,0 +1,95 @@ +@startuml + +Title Task scheduling on DSP core + +legend +Assumptions: +1) 1ms scheduling +2) 0.1ms takes LL task execution +3) 0.5ms takes execution of all DP tasks +end legend + +scale 1 as 200 pixels + +concise "DP Tasks Processing" as DP_Processing +concise "LL Tasks Processing" as LL_Processing +robust "DSP" as DSP +concise "Timer Interrupt" as Interrupt + +DSP is Idle + +@DSP +@1.2 <-> @2: Time available for\nDP tasks execution +@2.2 <-> @2.7: Actual execution time\nof DP tasks +@3 <-> @3.2: Actual execution time\nof LL tasks + +@Interrupt +@0 <-> @1 : Schedule period + +@0 +Interrupt -> DSP +DSP -> LL_Processing +DSP is "Scheduling tasks" +LL_Processing is Busy +DP_Processing is {-} + +@+0.2 +DSP -> DP_Processing +LL_Processing is {-} +DP_Processing is Busy + +@+0.5 +DP_Processing -> DSP +DP_Processing is {-} +DSP is Idle + +@1 +Interrupt -> DSP +DSP -> LL_Processing +DSP is "Scheduling tasks" +LL_Processing is Busy + +@+0.2 +DSP -> DP_Processing +LL_Processing is {-} +DP_Processing is Busy + +@+0.5 +DP_Processing -> DSP +DP_Processing is {-} +DSP is Idle + +@2 +Interrupt -> DSP +DSP -> LL_Processing +DSP is "Scheduling tasks" +LL_Processing is Busy + +@+0.2 +DSP -> DP_Processing +LL_Processing is {-} +DP_Processing is Busy + +@+0.5 +DP_Processing -> DSP +DP_Processing is {-} +DSP is Idle + +@3 +Interrupt -> DSP +DSP -> LL_Processing +DSP is "Scheduling tasks" +LL_Processing is Busy + +@+0.2 +DSP -> DP_Processing + +LL_Processing is {-} +DP_Processing is Busy + +@+0.5 +DP_Processing -> DSP +DP_Processing is {-} +DSP is Idle + +@enduml diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/mpp_scheduling/example_multiple_cores_timeline.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/mpp_scheduling/example_multiple_cores_timeline.pu new file mode 100644 index 00000000..1aa419e7 --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/mpp_scheduling/example_multiple_cores_timeline.pu @@ -0,0 +1,108 @@ +@startuml + +Title Tasks scheduling on multiple DSP cores + +legend +Assumptions: +1) 1ms system tick + +Notes: +2) Core #0 has only LL tasks assigned schedule in 1ms period +3) Core #1 has one DP task assigned that is dependent on Core #0 LL tasks data, scheduled in 1ms period +(e.g. multicore pipeline with DP module scheduled on separate core) +4) Core #2 has LL tasks scheduled in 1ms period and DP task scheduled in 2ms period +(e.g. pipeline processing with LL and DP components components where DP component has 2ms scheduling period) +end legend + +scale 1 as 300 pixels + +concise "DSP #2" as DSP_2 +concise "DSP #1" as DSP_1 +concise "DSP #0" as DSP_0 + +concise "Timer interrupt" as Interrupt + +@DSP_0 +0 is "LL proc." +0.5 is {-} + +1 is "LL proc." +1.5 is {-} + +2 is "LL proc." +2.5 is {-} + +3 is "LL proc." +3.5 is {-} + +4 is "LL proc." +4.5 is {-} + +@0 <-> @1: DSP#0 LL schedule period (1ms) + +@DSP_1 +0 is {-} + +1 is "DP proc." +1.6 is {-} + +2 is "DP proc." +2.6 is {-} + +3 is "DP proc." +3.6 is {-} + +4 is "DP proc." +4.6 is {-} +5 is {-} + +@0 <-> @1: delay one period (waiting for first DSP#0 LL data) +@1 <-> @2: DSP#1 DP schedule period (1ms) + +@DSP_2 + +0 is "LL proc." +0.3 is {-} + +1 is "LL proc." +1.3 is {-} + +2 is "LL proc." +2.3 is "DP proc." + +3 is "LL proc." +3.3 is "DP proc." +3.7 is {-} + +4 is "LL proc." +4.3 is "DP proc." + +@0 <-> @1: DSP#2 LL schedule period (1ms) +@2.3 <-> @4.3: DSP#2 DP schedule period (2ms) + +@0 +Interrupt -[#DarkViolet]> DSP_0 +Interrupt -[#DarkViolet]> DSP_1 +Interrupt -[#DarkViolet]> DSP_2 + +@1 +Interrupt -[#DarkViolet]> DSP_0 +Interrupt -[#DarkViolet]> DSP_1 +Interrupt -[#DarkViolet]> DSP_2 + +@2 +Interrupt -[#DarkViolet]> DSP_0 +Interrupt -[#DarkViolet]> DSP_1 +Interrupt -[#DarkViolet]> DSP_2 + +@3 +Interrupt -[#DarkViolet]> DSP_0 +Interrupt -[#DarkViolet]> DSP_1 +Interrupt -[#DarkViolet]> DSP_2 + +@4 +Interrupt -[#DarkViolet]> DSP_0 +Interrupt -[#DarkViolet]> DSP_1 +Interrupt -[#DarkViolet]> DSP_2 + +@enduml diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/mpp_scheduling/example_task_with_budget.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/mpp_scheduling/example_task_with_budget.pu new file mode 100644 index 00000000..c4b27de9 --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/mpp_scheduling/example_task_with_budget.pu @@ -0,0 +1,50 @@ +@startuml + +skinparam maxMessageSize 400 +skinparam BoxPadding 4 + +box "SOF Firmware" #LightBlue + participant "Firmware Manager" + participant "MPP Scheduling" + participant "Zephyr Scheduler" + participant "Zephyr Thread" +end box + +activate "Zephyr Scheduler" + +"Zephyr Scheduler"-> "Zephyr Thread": schedule IPC Task with Budget (TWB) thread\n(MEDIUM_PRIO) +activate "Zephyr Thread" + + "Zephyr Thread"-> "Zephyr Thread": run + "Zephyr Thread"-> "MPP Scheduling": on processing complete + activate "MPP Scheduling" + "MPP Scheduling"-> "Zephyr Thread": k_thread_runtime_stats_get + activate "Zephyr Thread" + return + "MPP Scheduling"-> "MPP Scheduling": update IPC Task with budget\ncycles_consumed_in_sys_tick + return + "Zephyr Thread"-> "Zephyr Thread": suspend TWB Zephyr Thread\n(k_sem_take) +return + +"Zephyr Scheduler"-> "Zephyr Thread": schedule EDF thread\n(LOW_PRIO) +activate "Zephyr Thread" + "Zephyr Thread"-> "Zephyr Thread": run + + activate "Firmware Manager" + "Firmware Manager"-> "Firmware Manager": Host IPC message received + "Firmware Manager"-> "MPP Scheduling": request IPC processing + activate "MPP Scheduling" + "MPP Scheduling"-> "Zephyr Thread": resume IPC TWB Zephyr Thread\n(k_sem_give) + "MPP Scheduling" --> "Firmware Manager" + deactivate "MPP Scheduling" + deactivate "Firmware Manager" + +"Zephyr Thread" --> "Zephyr Scheduler": EDF thread gets preempted +deactivate "Zephyr Thread" + +"Zephyr Scheduler"-> "Zephyr Thread": schedule IPC task with budget thread\n(MEDIUM_PRIO) + activate "Zephyr Thread" + "Zephyr Thread"-> "Zephyr Thread": run + return + +@enduml diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/mpp_scheduling/schedulers_diagram.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/mpp_scheduling/schedulers_diagram.pu new file mode 100644 index 00000000..b7e6895a --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/mpp_scheduling/schedulers_diagram.pu @@ -0,0 +1,60 @@ +@startuml +allowmixing + +scale max 1280 width + +package “RTOS layer” { + + package "SOF kernel extension" as KERNEL_EXTENSION { + package "MPP Scheduling" as MPP_SCHEDULING { + component "LL Tasks" as LL_TASKS + component "DP Tasks" as DP_TASKS + component "Tasks with Budget" as TWB + component "Idle Tasks" as IDLE_TASKS + + LL_TASKS -[hidden]right- DP_TASKS + DP_TASKS -[hidden]right- TWB + TWB -[hidden]right- IDLE_TASKS + } + } + + package "Zephyr" as ZEPHYR_LAYER { + package "Services" as SERVICES { + component "Timing" as TIMING + component "Interrupts" as INTERRUPTS + } + + package "Scheduling" as SCHEDULING { + component "Threads" as THREADS + component "EDF Scheduler" as EDF + component "Time-Slice Scheduler" as TIME_SLICE_SCHEDULING + + THREADS -[hidden]right- EDF + EDF -[hidden]right- TIME_SLICE_SCHEDULING + } + + package "Drivers" as DRIVERS { + component "Timer" as TIMER_DRV + component "Watchdog" as WATCHDOG_DRV + } + + package “SoC HAL” as SOC_HAL { + component "OEM SoC 1" as OEM_SOC_1 + component "OEM SoC 2" as OEM_SOC_2 + component "Other SoCs" as OTHER_SOCS + } + + component "XTHAL" as XTHAL + + SERVICES -[hidden]right- SCHEDULING + SERVICES -[hidden]down- XTHAL + SCHEDULING -[hidden]down- SOC_HAL + SCHEDULING -[hidden]down- DRIVERS + DRIVERS -[hidden]right- SOC_HAL + DRIVERS -[hidden]right- XTHAL + } + + KERNEL_EXTENSION -[hidden]down- ZEPHYR_LAYER +} + +@enduml diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/mpp_scheduling/schedulers_threads_periodic_update.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/mpp_scheduling/schedulers_threads_periodic_update.pu new file mode 100644 index 00000000..5eee1041 --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/mpp_scheduling/schedulers_threads_periodic_update.pu @@ -0,0 +1,38 @@ +@startuml + +scale max 1280 width + +skinparam maxMessageSize 400 +skinparam BoxPadding 4 + +box "SOF Firmware" #LightBlue + participant "MPP Scheduling" + participant "Zephyr Thread" + participant "Timer" +end box + +"Timer" -> "MPP Scheduling": sys_tick callback +activate "MPP Scheduling" + +loop for each Task with Budget + "MPP Scheduling"-> "MPP Scheduling": reset task with budget\ncycles_consumed_in_sys_tick + "MPP Scheduling" -> "Zephyr Thread": k_thread_priority_set(thread, MEDIUM_PRIO) + "MPP Scheduling" -> "Zephyr Thread": k_thread_time_slice_set(thread, slice_ticks = budget) + note right: Reset priority and budget\nto default value + "MPP Scheduling"-> "Zephyr Thread": k_thread_runtime_stats_get(thread) + activate "Zephyr Thread" + return return thread_cycles - absolute number of cycles consumed + "MPP Scheduling"-> "MPP Scheduling": save thread_ref_cycles = thread_cycles as a reference +end + +loop for each DP task + opt if DP task is ready for processing + "MPP Scheduling"-> "MPP Scheduling": re-calculate task deadline + "MPP Scheduling" -> "Zephyr Thread": k_thread_deadline_set(thread, deadline) + "MPP Scheduling" -> "Zephyr Thread": resume thread + end +end + +deactivate "MPP Scheduling" + +@enduml diff --git a/architectures/firmware/sof-zephyr/mpp_layer/images/mpp_scheduling/schedulers_zephyr.pu b/architectures/firmware/sof-zephyr/mpp_layer/images/mpp_scheduling/schedulers_zephyr.pu new file mode 100644 index 00000000..834b28c4 --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/images/mpp_scheduling/schedulers_zephyr.pu @@ -0,0 +1,88 @@ +@startuml + +scale max 1280 width + +skinparam maxMessageSize 400 +skinparam BoxPadding 4 + +box "SOF" #LightBlue + participant "MPP Scheduling" + participant "Zephyr Scheduler" + participant "Zephyr Thread" + participant "Timer" +end box + +"Timer" -> "MPP Scheduling": sys_tick callback +activate "MPP Scheduling" + loop for each core + "MPP Scheduling"-> "Zephyr Scheduler": resume LL Zephyr Thread\n(k_sem_give) + activate "Zephyr Scheduler" + end + + "MPP Scheduling"-> "MPP Scheduling": DP and Task with Budget\nZephyr Threads update + +"Zephyr Scheduler"-> "Zephyr Thread": schedule LL Zephyr Thread\n(context switch) + deactivate "MPP Scheduling" + activate "Zephyr Thread" + "Zephyr Thread"-> "Zephyr Thread": zephyr_ll_run + activate "Zephyr Thread" + + loop for each LL pending task + note left: LL pending tasks are scheduled operations\nthat are waiting for certain circumstances\n(like data arrival) to start processing + opt if task is ready for processing + "Zephyr Thread"-> "Zephyr Thread": move task \nto LL run queue + end + end + + loop for each task in LL queues + "Zephyr Thread"-> "Zephyr Thread": run LL task callback + end + return + + "Zephyr Thread"-> "Zephyr Thread": suspend LL Zephyr Thread\n(k_sem_take) + return + +loop for each Task With Budget (TwB) Zephyr Thread + "Zephyr Scheduler"-> "Zephyr Thread": schedule TwB Zephyr Thread\n(context switch) + activate "Zephyr Thread" + "Zephyr Thread"-> "Zephyr Thread": run + + alt if time_slice (budget) timeout + "Zephyr Thread"-> "Zephyr Scheduler": time_slice timeout + "Zephyr Scheduler"-> "MPP Scheduling": time_slice callback(thread) + activate "MPP Scheduling" + "MPP Scheduling"-> "Zephyr Thread": k_thread_priority_set(thread, LOW_PRIO) + note right: when budget is consumed\nreset time_slice to default\nand lower priority + "MPP Scheduling"-> "Zephyr Thread": k_thread_time_slice_set(thread, slice_ticks = budget) + deactivate "MPP Scheduling" + + else if processing complete (no time_slice timeout) + "Zephyr Thread"-> "MPP Scheduling": on processing complete (thread) + activate "MPP Scheduling" + "MPP Scheduling"-> "Zephyr Thread": k_thread_runtime_stats_get(thread) + activate "Zephyr Thread" + return return thread_cycles - absolute number of cycles consumed by thread + "MPP Scheduling"->"MPP Scheduling": update thread\ncycles_consumed_in_sys_tick += (thread_cycles - thread_ref_cycles) + note right: thread_ref_cycles is a reference number of cycles consumed by thread\nupdated on each sys_tick start and processing complete + "MPP Scheduling"->"MPP Scheduling": update thread_ref_cycles = thread_cycles + return + deactivate "MPP Scheduling" + + "Zephyr Thread" -> "Zephyr Thread": suspend TwB Zephyr Thread\n(k_sem_take) + note left: TwB Threads are expected to be resumed when there is new data for processing\nfor example IPC TwB Thread will be resumed on IPC interrupt + "Zephyr Thread" --> "Zephyr Scheduler" + deactivate "Zephyr Thread" + end +end + +loop for each DP Zephyr Thread + "Zephyr Scheduler"-> "Zephyr Thread": schedule DP Zephyr Thread with earlieast deadline\n(context switch) + note right: TwB Threads with low priority are treated\nas threads with max deadline and will be\nscheduled after DP threads complete processing + activate "Zephyr Thread" + "Zephyr Thread"-> "Zephyr Thread": run + note right: DP thread runs till completion\nor till earlier deadline or\nhigher priority thread is available + return + deactivate "Zephyr Thread" +end + +@enduml diff --git a/architectures/firmware/sof-zephyr/mpp_layer/index.rst b/architectures/firmware/sof-zephyr/mpp_layer/index.rst new file mode 100644 index 00000000..b8542887 --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/index.rst @@ -0,0 +1,17 @@ +.. _mpp_layer: + +Media Processing Pipelines Layer +################################ + +The Media Processing Pipelines (MPP) is a FW infrastructure layer independent to +the HW. The MPP components serve as kernel services extension that is available +to the Application layer. + +.. toctree:: + :maxdepth: 1 + + mpp_overview + mpp_scheduling + async_messaging + lib_manager + dp_scheduling \ No newline at end of file diff --git a/architectures/firmware/sof-zephyr/mpp_layer/lib_manager.rst b/architectures/firmware/sof-zephyr/mpp_layer/lib_manager.rst new file mode 100644 index 00000000..0bb4bccf --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/lib_manager.rst @@ -0,0 +1,58 @@ +.. _lib_manager: + +Loadable Library Manager +######################## + +The Loadable Library Manager is a MPP Layer component responsible for loading +and running loadable components provided in external libraries. It supports SOF +native components as well as IADK cAVS/ACE developed modules through +:doc:`../../intel/ace/iadk_modules`. + +Loading an external library is a feature available only for IPC4 protocol with +command: `SOF_IPC4_GLB_LOAD_LIBRARY`. + +.. uml:: images/lib_manager/library_manager_load.pu + :caption: Library Manager: Load library flow + +In the `SOF_IPC4_GLB_LOAD_LIBRARY` IPC flow the ``lib_manager_load_library()`` api +function loads binary from host driver to DSP memory and updates its internal +structure with library descriptor data. If ``AUTH_API`` Kconfig option is +selected, library manager communicates with platform ROM Extension library to +perform library image verification. In that case only trusted libraries will be +successfully loaded. + +**NOTE:** ``AUTH_API`` Kconfig option is available only for Intel platforms. + +During the `SOF_IPC4_MOD_INIT_INSTANCE` IPC4 protocol call, handler searches +for specific module among build-in components and if not found, verifies +manifests of all already loaded external libraries. When module is found in +external library, it is registered in SOF Firmware ``struct comp_driver_list`` +with ``lib_manager_register_module()`` api function and loaded from L3 memory to +L2 memory. Afterwards module is created with standard component device +operation. + +**NOTE:** If L3 memory is not available, the L2 memory has to be used and there +is no memory load operation required. + +.. uml:: images/lib_manager/library_manager_init_instance.pu + :caption: Init instance flow for loadable module + +External libraries could contain not only processing modules but also shared +library code that could be reused across several external modules. The library +manager searches external library manifest for such entities and loads them +together with first processing module loaded. + +When an external processing module is no longer needed, it could be unloaded +with the IPC4 call `SOF_IPC4_MOD_DELETE_INSTANCE`. The command performs reverse +flow to the previous one. It frees L2 SRAM memory allocated for the processing +module and if it is last one unloaded from given library, it frees also +resources used for all shared libraries loaded previously. + +.. uml:: images/lib_manager/library_manager_delete_instance.pu + :caption: Delete instance flow for loadable module + +In `SOF_IPC4_MOD_INIT_INSTANCE` and `SOF_IPC4_MOD_DELETE_INSTANCE` flows, +particular module could be loaded in more than one instance. Its `.text` and +`.rodata` memory sections are allocated only for the first instance and shared +for all other instances. Also the `.text` and `.rodata` resources are released +only for the last instance of given processing module. diff --git a/architectures/firmware/sof-zephyr/mpp_layer/mpp_overview.rst b/architectures/firmware/sof-zephyr/mpp_layer/mpp_overview.rst new file mode 100644 index 00000000..955a3781 --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/mpp_overview.rst @@ -0,0 +1,94 @@ +.. _mpp_layer_overview: + +Media Processing Pipelines Overview +################################### + +The Media Processing Pipelines (MPP) layer role is to enable SOF specific use +cases that are not supported directly by Zephyr. The MPP is responsible for host +communication, tasks scheduling, pipeline and component management. + +.. uml:: images/mpp_layer_diagram.pu + :caption: Media Processing Pipelines Layer diagram + +Services in Media Processing Pipelines Layer +******************************************** + +Gateways +======== + +The gateways are a key element in SOF data exchange with host, external audio +peripherals and internally between firmware components. They serve as an +abstraction layer for multiple data protocols. + +The typical audio stream (chain of pipelines) starts and ends with I/O gateway. +I/O gateway represents a sink or a source interface that can be read or written +via DMA operations on I/O FIFO (i.e. DMIC, SNDW, etc.) or directly via memory +operations (i.e. memory buffers of IPC Gateway). Host Gateway is unique as it +exposes interface endpoint to the Host driver. + +The stream audio gateways are created as a part of Copier component +configuration. + +.. TODO: Add link to Copier detailed description + +Examples of gateways: + +- DMIC Gateway, +- SoundWire Gateway, +- I2S Gateway, +- HD/A Gateway, +- IPC Gateway, + +.. TODO: Add link to Gateways detailed specification. + +*NOTE:* Not all I/O gateways must be available in all configurations. + +Pipeline Management +=================== + +The Pipeline Management is a host IPC driven service that is used to: + +- create / delete pipeline +- switch pipeline state +- create pipeline processing tasks +- allocate pipeline buffers memory + +.. TODO: Add link to Pipeline Management IPC interface. + +Processing Component Management +=============================== + +Processing Component Management is driven by host IPC requests. It is used to: + +- instantiate / delete components +- configure components +- bind / unbind components into processing paths +- load components into ADSP memory + +.. TODO: Add link to Component Management IPC interface. + +Asynchronous Messaging +====================== + +Asynchronous Messaging Service (AMS) provides functionality to: + +- send asynchronous messages to firmware components or host, +- broadcast messages to multiple consumers, +- asynchronous message exchange between components running on different cores + +.. TODO: Add link to Asynchronous Messaging Service detailed description + +MPP Scheduling +============== + +MPP Scheduling is dedicated to support Media Processing Pipelines services tasks +scheduling. It exposes SOF specific interface that is implemented on top of +Zephyr scheduling API. + +MPP Scheduling features: + +- Low Latency tasks scheduling, +- Data Processing tasks scheduling, +- Tasks with budget scheduling + +.. TODO: Add link to MPP Scheduling detailed description diff --git a/architectures/firmware/sof-zephyr/mpp_layer/mpp_scheduling.rst b/architectures/firmware/sof-zephyr/mpp_layer/mpp_scheduling.rst new file mode 100644 index 00000000..c4ff5631 --- /dev/null +++ b/architectures/firmware/sof-zephyr/mpp_layer/mpp_scheduling.rst @@ -0,0 +1,302 @@ +.. _sof-zephyr_mpp_scheduling: + +MPP Scheduling +############## + +This section describes MPP scheduling flows, task types and their usage in SOF +based on Zephyr API. + +MPP Scheduling defines four task categories: + +- Low Latency audio data processing tasks (LL) - high priority, +- Tasks with Budget (TwB) - medium priority, +- audio Data Processing tasks (DP) - low priority, +- background (idle) priority tasks + +**NOTE:** As of today, only LL tasks has been integrated with Zephyr. TwB, DP +and idle tasks are work in progress (WIP). + +The role of MPP Scheduling is limited to task threads definition, configuration +and state management. The thread scheduling itself is handled by Zephyr. + +MPP Scheduling is designed to: + +- address strict real-time requirements, + + - i.e. to avoid under/overflows on isochronous interfaces such as + I2S, + +- provide predictable latency, +- reduce amount of buffering needed, + +MPP Scheduling defines two tasks categories: + +Task categories characteristic: + +- LL tasks for latency sensitive audio data processing, + + - LL tasks are organized in queues shared between component instances, + - there is one non-preemptive high priority LL Thread assigned to exactly + one core. For example, for HW configuration with 4 cores there will be 4 + LL Threads, + - each queue is statically linked to one LL Thread and all queue tasks will + be processed on a core that LL Thread is assigned to, + - there are multiple queues per LL Thread which represent a priority and + guarantee tasks execution order, + +.. TODO: Add LL tasks, Threads and queues relation diagram, + +- TwB for medium priority processing (e.g., IPC/IDC message handling), + + - each TwB is scheduled as a separate preemptive thread, + - TwB has assigned budget for processing that is refreshed in each sys tick + (`Zephyr Thread time slicing + `__), + - TwB priority is dropped to low when budget is consumed, + +- DP tasks for low priority audio processing, + + - DP tasks are scheduled based on earliest deadline first (EDF) algorithm, + - each DP task is scheduled as a separate preemptive thread, + - DP tasks can be assigned to one of the available cores, + +- idle tasks for background processing, + + - idle tasks are scheduled as separate preemptive threads, + - they have the lowest priority and are scheduled when all other tasks + completed their processing, + - they are used in Fast Mode. For example, in data draining from firmware to + host. + +**NOTE:** Each task is assigned by MPP Scheduling to one core. Tasks are +executed by the assigned core till termination. + +**NOTE:** For Earliest Deadline First (EDF) algorithm description, please refer +to link: +`Wikipedia `__. + +**NOTE:** For Zephyr Scheduling description, please refer to link: +`Zephyr +Scheduling `__. + +.. uml:: images/mpp_scheduling/schedulers_diagram.pu + :caption: SOF MPP Scheduling based on Zephyr + +LL Tasks +******** + +Low Latency Tasks are executed within one of the non-preemptive high priority LL +Threads that runs all ready-to-run tasks till completion during a single cycle. +There is one LL Thread scheduled per core with its own queues and LL tasks to +execute. + +MPP Scheduling adds ready tasks to LL queues at the beginning of each scheduling +period. There are a number of queues to add tasks to. LL Thread iterates over +the queues, and runs all tasks from one queue before moving to the next queue. +Therefore, it is possible to guarantee that some tasks are always run before +others during a cycle. + +There are also two special queues: pre-run queue and post-run queue. Tasks from +pre-run queue are run at the beginning of each cycle (may consider them to have +the highest priority). + +Tasks from post-run queue are run at the end of each cycle (may consider them to +have the lowest priority). + +Example of a pre-run task may be a task registered by the sink driver that +starts the sink at the very beginning of the cycle if data was supplied during +the previous cycles and link has been stopped. + +.. TODO: Evaluate option to add time slice limit for LL thread (set limit it to + 90% to not starve potential IPC communication tasks) + +DP Tasks +******** + +The data processing components are represented as a DP tasks that are scheduled as +separate preemptive threads. DP threads scheduling is done according to EDF +(Earliest Deadline First) algorithm that is part of Zephyr. + +To meet real-time processing criteria algorithm operates by choosing component task +that is closest to its deadline (time when output data is required). + +For playback case algorithm starts from sink and going backward calculates +deadline for data delivery: + + * Time required by component to process data depend on processing period and compute. + * Goal is to process data through chain before real-time sink deadline + +EDF scheduling example + +.. blockdiag:: images/mpp_scheduling/edf_scheduling.diag + +The capture pipelines operate in the same way. + +It is important to consider that EDF assumes preemptive scheduling of the DP +Tasks and lack of dependency between them. + +Task With Budget +**************** + +This is a specialized version of DP task that has pre-allocated MCPS budget +renewed with every system tick. When the task is ready to run, then depending on +the budget left in the current system tick, either MEDIUM_PRIORITY or +LOW_PRIORITY is assigned to task thread. The latter allows for opportunistic +execution if there is no other ready task with a higher priority while the +budget is already spent. + +Examples of tasks with budget: Ipc Task, Idc Task. + +Task with Budget (TWB) has two key parameters assigned: + +- *cycles granted*: the budget per system tick, +- *cycles consumed*: number of cycles consumed in a given system_tick + for task execution + +The number of cycles consumed is being reset to 0 at the beginning of each +system_tick, renewing TWB budget. When the number of cycles consumed exceed +cycles granted, the task is switched from MEDIUM to LOW priority. When the task +with budget thread is created the MPP Scheduling is responsible to set thread +time slice equal to task budget along with setting callback on time slice +timeout. Thread time slicing guarantee that Zephyr scheduler will interrupt +execution when the budget is spent, so MPP Scheduling timeout callback can +re-evaluate task priority. + +If there is a budget left in some system tick (task spent less time or started +executing close to the system tick that preempts execution), it is reset and not +carried over to the next tick. + +**NOTE** The Zephyr Scheduler track time slice budget of the TWB when preempted +and log warning if the budget is significantly exceeded (some long critical +section inside the task’s code might be responsible for this). + +**NOTE** The MPP Scheduling must be notified by TWB on processing complete and +update cycles consumed in the current system tick. This allows to schedule TWB +more than once (if necessary) in the single system tick with MEDIUM_PRIORITY. +The second TWB schedule should be done with modified time slice value, equal to +delta between budget and cycles consumed. + +Scheduling flows +**************** + +Zephyr scheduling +================= + +The presented Zephyr scheduling flow takes place on each core that has +MPP tasks scheduled. + +.. uml:: images/mpp_scheduling/schedulers_zephyr.pu + :caption: Zephyr scheduling of MPP threads flow + + +MPP Data Processing and Task with Budget threads periodic update +================================================================ + +Zoom in to Data Processing (Earliest Deadline First) and Task with Budget +Threads periodic update operations on each system tick start. + +.. uml:: images/mpp_scheduling/schedulers_threads_periodic_update.pu + :caption: DP and TWB threads sys tick update flow + + +Task with budget scheduling +=========================== + +.. uml:: images/mpp_scheduling/example_task_with_budget.pu + :caption: Task with budget example scheduling flow + + +Example timeline of MPP Scheduling on a DSP core +================================================= + +The below diagram shows how scheduling looks like on a DSP core. At the timer +interrupt, LL scheduler runs as the first one and then DP scheduler is executed. + +.. uml:: images/mpp_scheduling/example_LL_DP_timeline.pu + :caption: Example timeline of MPP Scheduling on DSP core with LL and DP tasks scheduling + + +Example timeline of DP tasks scheduling on secondary DSP core +============================================================== + +The below diagram shows a detailed example of how DP tasks are scheduled +on the secondary DSP core. + +.. uml:: images/mpp_scheduling/example_DP_secondary_core_timeline.pu + :caption: Example of DP tasks scheduling on secondary DSP core + + +Example timeline of MPP scheduling on multiple DSP cores +======================================================== + +The below diagram shows how scheduling looks like on many DSP cores. The DP task +deadlines are reevaluated on each core in Timer sys tick callback. + +.. uml:: images/mpp_scheduling/example_multiple_cores_timeline.pu + :caption: Example of MPP Scheduling on many cores - LL and DP tasks scheduling + +Fast Mode +********* + +The Fast Mode is used to process data faster than real time. The processing +faster than real time is only needed for a short time period and it happens i.e. +when firmware performs low power Wake on Voice. In such case SOF firmware is +working in low power mode, performing i.e. key phrase detection algorithm, +accumulating last few seconds of audio samples in history buffer. When a key +phrase detection happens, there is a need to stream the accumulated history to +Host as quickly as possible with optional additional processing on DSP. It is +only possible when a sink interface to Host transfer burst of data from DSP. + +The Fast Mode is an idle low priority task. The task is only executed when other +DP tasks with deadlines has completed their processing and there is still enough +DSP cycles before a next system tick. + +When the Fast Mode task is created by i.e. History Buffer, the component +instance (i.e. History Buffer) needs to provide a list of LL component instances +that will be executed within a Fast Mode thread, similar as it is done with LL +tasks queues and LL Thread. When the Fast Mode thread is executed it will +trigger processing of LL components in similar way as LL Thread does. The Fast +Mode task is executed in the critical section. It will check if there is data +available in an input queue and there is enough space in an output queue. Only +then it will execute a LL component. What is important to note is that the Fast +Mode task does not call processing on the DP components directly. + +As described in the previous sections, the processing on DP components is called +according to EDF algorithm. A periodicity of a component processing is +determined by time needed to fill an input queue using real time source of data. +When an input queue has sufficient amount of data, the processing on DP +component can be called. The input queues for DP components that are on the Fast +Mode task path will be filling much faster than real time as the side effect of +the Fast Mode task execution - LL components will move data to DP component +input queue and out of DP component output queue. As the result, DP component +can be executed much earlier than real time - a DSP task reports “ready to run” +as soon as it has sufficient amount of data in input queue and output queue has +enough space for produced frame. That can lead to starvation of other tasks and +to prevent it a Fast Mode tasks must be scheduled as idle tasks in background. + +Watchdog timer +************** + +Depending on HW configuration there can be a single watchdog timer, watchdog +available for each DSP core or none. + +All DSP cores shall enable watchdog when they are active to monitor health of +subsystem. When one of watchdogs will expire, the entire subsystem will be reset +by Host. + +Watchdog shall be enabled when: + +- DSP core is enabled, +- tasks are assigned to DSP core, + +Watchdog shall be disabled when: + +- DSP core is disabled, +- no tasks are assigned to DSP core, +- DSP core goes to low power state, + +Watchdog timer shall be programmed to value of a few scheduling periods. + +Watchdog timer when enabled shall be updated at every system tick. In case of +primary DSP core, it should be after running LL tasks. In case of secondary HP +DSP cores, it should be on system tick end. \ No newline at end of file diff --git a/architectures/firmware/sof-zephyr/overview.rst b/architectures/firmware/sof-zephyr/overview.rst new file mode 100644 index 00000000..e9969e29 --- /dev/null +++ b/architectures/firmware/sof-zephyr/overview.rst @@ -0,0 +1,12 @@ +.. _sof-zephyr-overview: + +Overview +######## + +New SOF firmware architecture is based on Zephyr RTOS and introduce new IPC4 +Host protocol ABI. In result FW has been re-organized into layers. The +interaction between the components across the layers is limited to the +internally defined interfaces. + +.. uml:: images/overview_diagram.pu + :caption: SOF with Zephyr Architecture overview diff --git a/architectures/firmware/sof-zephyr/rtos_layer/images/kernel_services.pu b/architectures/firmware/sof-zephyr/rtos_layer/images/kernel_services.pu new file mode 100644 index 00000000..f1d19987 --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/images/kernel_services.pu @@ -0,0 +1,57 @@ +@startuml +allowmixing + +scale max 1280 width + +package "SOF" { + package "Kernel Infrastructure" { + interface "Zephyr System Services" as SS + + package "Services" { + component "Memory Manager" as MEMORY_MANAGER + component "Power Manager" as POWER_MANAGER + component "IPC/IDC" as IXC + component "Logging" as LOGGING + component "Debug" as DEBUG + component "Interrupt Handler" as INTERRUPT_HANDLER + } + + SS .down. MEMORY_MANAGER + SS .down. POWER_MANAGER + SS .down. IXC + SS .down. LOGGING + SS .down. DEBUG + SS .down. INTERRUPT_HANDLER + } + + package "Kernel Extension" { + interface "Extended System Services" as ESS + + component "AVS Scheduling" as AVS_Scheduling + + package "Extended Services" as EXTENDED_SERVICES { + component "Firmware Manager" as FIRMWARE_MANAGEMENT + component "Pipeline Management" as PIPELINE_MANAGEMENT + component "Async Messaging" as ASYNC_MESSAGING + component "Processing Component Management" as COMPONENT_MANAGEMENT + component "IPC Message Processing" as IPC_MESSAGE_PROCESSING + } + + ESS .down. FIRMWARE_MANAGEMENT + ESS .down. PIPELINE_MANAGEMENT + ESS .down. ASYNC_MESSAGING + ESS .down. IPC_MESSAGE_PROCESSING + ESS .down. COMPONENT_MANAGEMENT + + AVS_Scheduling -[hidden]down- EXTENDED_SERVICES + } + + package "Loadable modules" { + component "WoV" as WOV + + WOV .down. SS + WOV .down. ESS + } +} + +@enduml \ No newline at end of file diff --git a/architectures/firmware/sof-zephyr/rtos_layer/images/power/dsp_fw_power_states.pu b/architectures/firmware/sof-zephyr/rtos_layer/images/power/dsp_fw_power_states.pu new file mode 100644 index 00000000..c864c743 --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/images/power/dsp_fw_power_states.pu @@ -0,0 +1,14 @@ +@startuml +hide empty description + +state "D0 / D0ix" as D0 { + [*] --> PM_STATE_ACTIVE: Initialization + PM_STATE_ACTIVE -> PM_STATE_RUNTIME_IDLE + PM_STATE_RUNTIME_IDLE -> PM_STATE_ACTIVE + PM_STATE_ACTIVE --> [*] +} + +[*] --> D0: Go to D0 / Power DSP on +D0 --> [*]: Go to D3 / Power DSP Off + +@enduml diff --git a/architectures/firmware/sof-zephyr/rtos_layer/images/power/dx_state_transitions.pu b/architectures/firmware/sof-zephyr/rtos_layer/images/power/dx_state_transitions.pu new file mode 100644 index 00000000..1a1870b2 --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/images/power/dx_state_transitions.pu @@ -0,0 +1,10 @@ +@startuml + +[*] --> D3 +D3 --> D0 +D0 --> D0ix: SET_D0ix(wake = 0) +D0ix --> D0: SET_D0ix(wake = 1) +D0ix --> D3: ENTER_DX_STATE +D0 --> D3: ENTER_DX_STATE + +@enduml diff --git a/architectures/firmware/sof-zephyr/rtos_layer/images/power/flow_disable_d0i3.pu b/architectures/firmware/sof-zephyr/rtos_layer/images/power/flow_disable_d0i3.pu new file mode 100644 index 00000000..9279b09d --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/images/power/flow_disable_d0i3.pu @@ -0,0 +1,19 @@ +@startuml + +box "Host" #LightSkyBlue + participant "Driver" as DRIVER +end box + +box "SOF" #LightBlue + participant "Core #0: Zephyr lib" as sof_zephyr_lib + participant "Zephyr Power Manager" as zephyr_power_manager +end box + +DRIVER -> sof_zephyr_lib: SET_D0ix(prevent_power_gating = 1) IPC request +activate sof_zephyr_lib + sof_zephyr_lib -> zephyr_power_manager: pm_policy_state_lock_get\n(PM_STATE_RUNTIME_IDLE) + activate zephyr_power_manager + return +return SET_D0ix IPC response + +@enduml diff --git a/architectures/firmware/sof-zephyr/rtos_layer/images/power/flow_dsp_idle.pu b/architectures/firmware/sof-zephyr/rtos_layer/images/power/flow_dsp_idle.pu new file mode 100644 index 00000000..123751e5 --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/images/power/flow_dsp_idle.pu @@ -0,0 +1,41 @@ +@startuml + +box "SOF" #LightBlue + participant "Core #N: Zephyr lib" as sof_zephyr_lib + participant "Zephyr Power Manager" as zephyr_power_manager + participant "Zephyr SoC HAL" as soc_hal +end box + +box "Hardware" #LightGreen + participant "Core #N: Control" as core_hw_control +end box + +opt When Core is Idle + + zephyr_power_manager -> sof_zephyr_lib: pm_policy_next_state (PID: Core #N, ticks) + activate sof_zephyr_lib + activate zephyr_power_manager + sof_zephyr_lib -> zephyr_power_manager: pm_policy_state_lock_get\n(PM_STATE_RUNTIME_IDLE) + activate zephyr_power_manager + return + alt if no lock on D0ix state + return PM_STATE_RUNTIME_IDLE + else if there is lock on D0ix state + return PM_STATE_ACTIVE + end + + zephyr_power_manager -> soc_hal: pm_power_state_set\n(power_state, PID: Core #N) + activate soc_hal + alt If power_state is PM_STATE_IDLE + soc_hal -> soc_hal: arch_clear_power_gating_prevent (Core #N) + soc_hal -> core_hw_control: Clear power gating prevent + else if PM_STATE_RUNTIME_ACTIVE + soc_hal -> soc_hal: arch_set_power_gating_prevent (Core #N) + soc_hal -> core_hw_control: Set power gating prevent + end + return + + deactivate zephyr_power_manager +end + +@enduml diff --git a/architectures/firmware/sof-zephyr/rtos_layer/images/power/flow_enable_d0i3.pu b/architectures/firmware/sof-zephyr/rtos_layer/images/power/flow_enable_d0i3.pu new file mode 100644 index 00000000..70b3ea8b --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/images/power/flow_enable_d0i3.pu @@ -0,0 +1,29 @@ +@startuml + +box "Host" #LightSkyBlue + participant "driver" as driver +end box + +box "SOF" #LightBlue + participant "Core #0: Zephyr lib" as sof_zephyr_lib + participant "Zephyr Power Manager" as zephyr_power_manager +end box + +driver -> sof_zephyr_lib: SET_D0ix(prevent_power_gating = 0) IPC request +activate sof_zephyr_lib + sof_zephyr_lib -> zephyr_power_manager: pm_policy_state_lock_put\n(PM_STATE_RUNTIME_IDLE) + activate zephyr_power_manager + return + sof_zephyr_lib -> zephyr_power_manager: pm_policy_state_lock_is_active\n(PM_STATE_RUNTIME_IDLE) + activate zephyr_power_manager + return + + alt if D0ix is still locked + sof_zephyr_lib --> driver: return ERROR + note right: Zephyr PM Policy can be used concurrently\nand there can be more then one lock\non D0ix state + else + sof_zephyr_lib --> driver: return SUCCESS + end + + deactivate sof_zephyr_lib +@enduml diff --git a/architectures/firmware/sof-zephyr/rtos_layer/images/power/flow_primary_core_power_down.pu b/architectures/firmware/sof-zephyr/rtos_layer/images/power/flow_primary_core_power_down.pu new file mode 100644 index 00000000..c904de25 --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/images/power/flow_primary_core_power_down.pu @@ -0,0 +1,56 @@ +@startuml + +scale max 1280 width + +box "Host" #LightSkyBlue + participant "driver" as driver +end box + +box "SOF" #LightBlue + participant "Core #0: Zephyr lib" as sof_zephyr_lib_0 + participant "Zephyr Power Manager" as zephyr_power_manager + participant "Zephyr SoC HAL" as soc_hal +end box + +box "Hardware" #LightGreen + participant "Core #0: Control" as core_hw_control +end box + +opt If D0ix is enabled + driver -> sof_zephyr_lib_0: SET_D0ix(prevent D0ix) IPC request + activate sof_zephyr_lib_0 + sof_zephyr_lib_0 -> zephyr_power_manager: pm_policy_state_lock_get\n(PM_STATE_RUNTIME_IDLE) + activate zephyr_power_manager + return + return +end + +== DSP FW in PM_STATE_ACTIVE == + +driver -> sof_zephyr_lib_0: SET_DX(PID: Core #0, D3) IPC request +activate sof_zephyr_lib_0 + sof_zephyr_lib_0 -> zephyr_power_manager: Read status of secondary cores + activate zephyr_power_manager + return + + alt If any secondary core is powered up + sof_zephyr_lib_0 --> driver: SET_DX(ERROR) IPC response + else else + sof_zephyr_lib_0 -> zephyr_power_manager: pm_power_state_force\n(PM_STATE_SOFT_OFF, PID: Core #0) + activate zephyr_power_manager + zephyr_power_manager -> soc_hal: pm_power_state_set\n(PM_STATE_SOFT_OFF, PID: Core #0) + activate soc_hal + soc_hal -> soc_hal: Save context + soc_hal -> soc_hal: Prepare restore vector + return + return + +return SET_DX(SUCCESS) IPC response +end + +driver -> core_hw_control: clear power register +loop Until Core #0 power is down + driver -> core_hw_control: Read Core #0 power bit +end + +@enduml diff --git a/architectures/firmware/sof-zephyr/rtos_layer/images/power/flow_secondary_core_boot.pu b/architectures/firmware/sof-zephyr/rtos_layer/images/power/flow_secondary_core_boot.pu new file mode 100644 index 00000000..dc8704e9 --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/images/power/flow_secondary_core_boot.pu @@ -0,0 +1,48 @@ +@startuml + +box "Host" #LightSkyBlue + participant "driver" as driver +end box + +box "SOF" #LightBlue + participant "Core #0: Zephyr lib" as sof_zephyr_lib_0 + participant "Core #1: FW Init" as fw_init_1 + participant "Zephyr Power Manager" as zephyr_power_manager + participant "Zephyr SoC HAL" as soc_hal +end box + +box "Hardware" #LightGreen + participant "Core #1: Control" as core_control_1 +end box + +opt If D0ix is enabled + driver -> sof_zephyr_lib_0: SET_D0ix(prevent D0/D0ix) IPC request + activate sof_zephyr_lib_0 + sof_zephyr_lib_0 -> zephyr_power_manager: pm_policy_state_lock_get\n(PM_STATE_RUNTIME_IDLE) + activate zephyr_power_manager + return + return +end + +== DSP FW in PM_STATE_ACTIVE == + +driver -> sof_zephyr_lib_0: SET_DX(PID: Core #1, D0) IPC request +activate sof_zephyr_lib_0 + sof_zephyr_lib_0 -> soc_hal: arch_start_cpu(PID: Core #1) + activate soc_hal + soc_hal -> core_control_1: Set alternate boot vector to FW Init + note right: Cores share the \nsame FW binary\nand the firmware must be\npresent in SRAM. + soc_hal -> core_control_1: Set SPA bit + + core_control_1 -> fw_init_1: Start and jump to alternate boot vector + activate fw_init_1 + fw_init_1 -> fw_init_1: Restore context\nif any saved + + loop Until Core #1 is enabled + soc_hal -> core_control_1: read power register + end + deactivate fw_init_1 + return +return + +@enduml diff --git a/architectures/firmware/sof-zephyr/rtos_layer/images/power/flow_secondary_core_power_down.pu b/architectures/firmware/sof-zephyr/rtos_layer/images/power/flow_secondary_core_power_down.pu new file mode 100644 index 00000000..05d6c6c8 --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/images/power/flow_secondary_core_power_down.pu @@ -0,0 +1,56 @@ +@startuml + +scale max 1280 width + +box "Host" #LightSkyBlue + participant "driver" as driver +end box + +box "SOF" #LightBlue + participant "Core #0: Zephyr lib" as sof_zephyr_lib_0 + participant "Zephyr Power Manager" as zephyr_power_manager + participant "Zephyr SoC HAL" as soc_hal +end box + +box "Hardware" #LightGreen + participant "Core #1: Control" as core_1_control +end box + +opt If D0ix is enabled + driver -> sof_zephyr_lib_0: SET_D0ix(prevent D0ix) IPC request + activate sof_zephyr_lib_0 + sof_zephyr_lib_0 -> zephyr_power_manager: pm_policy_state_lock_get\n(PM_STATE_RUNTIME_IDLE) + activate zephyr_power_manager + return + return +end + +== DSP FW in PM_STATE_ACTIVE == + +driver -> sof_zephyr_lib_0: SET_DX(PID: Core #1, D3) IPC request +activate sof_zephyr_lib_0 + sof_zephyr_lib_0 -> zephyr_power_manager: pm_power_state_force\n(PM_STATE_SOFT_OFF, PID: Core #1) + activate zephyr_power_manager + zephyr_power_manager -> soc_hal: pm_power_state_set\n(PM_STATE_SOFT_OFF, PID: Core #1) + activate soc_hal + soc_hal -> soc_hal: Save context to IMR + return + deactivate zephyr_power_manager + + loop Until Core #1 transition to PM_STATE_SOFT_OFF + sof_zephyr_lib_0 -> zephyr_power_manager: pm_power_state_get(PID: Core #1) + activate zephyr_power_manager + return + end + + sof_zephyr_lib_0 -> soc_hal: arch_stop_cpu(PID: Core #1) + activate soc_hal + soc_hal -> core_1_control: clear power bit + loop Until Core #1 is disabled + soc_hal -> core_1_control: read power register + end + return + +return SET_DX(SUCCESS) IPC response + +@enduml diff --git a/architectures/firmware/sof-zephyr/rtos_layer/images/power/power_components.pu b/architectures/firmware/sof-zephyr/rtos_layer/images/power/power_components.pu new file mode 100644 index 00000000..f111ae24 --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/images/power/power_components.pu @@ -0,0 +1,30 @@ +node sw_driver [ + SW Driver + * DSP power control over IPC protocol +] + +node fw [ + SOF Zephyr Library + + * Exposes interface of SOF with XTOS for Host Power IPC handling + * Executes requested sequence of Zephyr power operations + * Waits and verifies power request completion + + --- + Zephyr Power Management + Generic RTOS Power Management service + + * Manages System Power States + * Manages Power Policies + * Manages Device Runtime Power + + --- + SoC HAL + Hardware specific power control + + * Moves SoC and its resources to power state requested by Zephyr + * Interacts directly with hardware and power registers + * Suppors different power states depending on the target SoC +] + +sw_driver -down-> fw : <> Set Dx - power state transition D0/D3\n<> Set D0ix - power gating override (on/off) diff --git a/architectures/firmware/sof-zephyr/rtos_layer/images/zephyr_kernel_diagram.pu b/architectures/firmware/sof-zephyr/rtos_layer/images/zephyr_kernel_diagram.pu new file mode 100644 index 00000000..f7bf622a --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/images/zephyr_kernel_diagram.pu @@ -0,0 +1,124 @@ +@startuml +allowmixing + +scale max 1280 width + +package "Kernel space" { + + package "Media Processing Pipelines - kernel extension" as MPP_KERNEL_EXTENSION { + interface "Extended System Services" as ESS + + component "Firmware Manager" as FIRMWARE_MANAGER + + package "Communication" as COMMUNICATION { + component "IPC Message Processing" as IPC_MESSAGE_PROCESSING + component "Async Messaging" as ASYNC_MESSAGING + + IPC_MESSAGE_PROCESSING -[hidden]right- ASYNC_MESSAGING + } + + package "Pipeline/Component Infrastructure" as PIPELINE_COMPONENT_INFRASTRUCTURE { + component "Pipeline Management" as PIPELINE_MANAGEMENT + component "Host/DAI Gateways" as HOST_DAI_GATEWAYS + component "Processing Component Management" as PROCESSING_COMPONENT_MANAGEMENT + + PIPELINE_MANAGEMENT -[hidden]right- HOST_DAI_GATEWAYS + HOST_DAI_GATEWAYS -[hidden]right- PROCESSING_COMPONENT_MANAGEMENT + } + + package "AVS Scheduling" as AVS_SCHEDULING { + component "Data Processing (DP) Tasks (EDF based)" as DP_TASKS + component "Low Latency (LL) Tasks" as LL_TASKS + + DP_TASKS -[hidden]right- LL_TASKS + } + + FIRMWARE_MANAGER -[hidden]right- PIPELINE_COMPONENT_INFRASTRUCTURE + FIRMWARE_MANAGER -[hidden]down- COMMUNICATION + COMMUNICATION -[hidden]right- AVS_SCHEDULING + + ESS -[hidden]down- FIRMWARE_MANAGER + ESS -[hidden]down- PIPELINE_COMPONENT_INFRASTRUCTURE + } + + package "Zephyr" as Zephyr_RTOS { + interface "Zephyr System Services" as SS + + package "Schedulers" as SCHEDULERS { + component "RTOS Scheduling" as RTOS_SCHEDULER + } + + package "Services" as SERVICES { + component "Memory Manager" as MEMORY_MANAGER + component "Power Manager" as POWER_MANAGER + component "IPC/IDC" as IXC + component "Logging" as LOGGING + component "Debug" as DEBUG + component "Timer Manager" as TIMER_MANAGER + component "Interrupt Handler" as INTERRUPT_HANDLER + + MEMORY_MANAGER -[hidden]right- POWER_MANAGER + POWER_MANAGER -[hidden]right- IXC + IXC -[hidden]down- LOGGING + LOGGING -[hidden]right- TIMER_MANAGER + TIMER_MANAGER -[hidden]right- INTERRUPT_HANDLER + } + + package "SoC HAL" as SOC { + component "OEM SoC 1" as SOC_1 + component "OEM SoC 2" as SOC_2 + component "Other SoCs" as OTHER_SOCS + + SOC_1 -[hidden]right- SOC_2 + SOC_2 -[hidden]right- OTHER_SOCS + } + + package "Drivers" as DRIVERS { + package "Common Drivers" as COMMON_DRIVERS { + component "GPDMA" as GPDMA + component "Timer" as TIMER + component "SHA-384" as SHA384 + component "Watchdog" as WATCHDOG + component "IPC" as IPC + component "IDC" as IDC + } + + package "Audio Drivers" as AUDIO_DRIVERS{ + component "DMIC" as DMIC + component "I2S" as I2S + component "SDW" as SDW + component "HDA" as HDA + + DMIC -[hidden]right- I2S + I2S -[hidden]right- SDW + SDW -[hidden]right- HDA + } + + package "Sensing Drivers" as SENSING_DRIVERS { + component "I2C" as I2C + component "GPIO" as GPIO + component "I3C" as I3C + component "SPI" as SPI + component "UART" as UART + + I2C -[hidden]right- GPIO + GPIO -[hidden]right- I3C + I3C -[hidden]right- SPI + SPI -[hidden]right- UART + } + } + + component "XTHAL" as XTHAL + + SS -[hidden]down- SCHEDULERS + SS -[hidden]down- SERVICES + SCHEDULERS -[hidden]right- SERVICES + SERVICES -[hidden]right- SOC + SERVICES --[hidden]down-- DRIVERS + DRIVERS -[hidden]down- XTHAL + } + + MPP_KERNEL_EXTENSION --[hidden]down-- Zephyr_RTOS +} + +@enduml diff --git a/architectures/firmware/sof-zephyr/rtos_layer/index.rst b/architectures/firmware/sof-zephyr/rtos_layer/index.rst new file mode 100644 index 00000000..204b057a --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/index.rst @@ -0,0 +1,16 @@ +.. _rtos_layer: + +RTOS Layer Infrastructure +######################### + +The RTOS layer is based on Zephyr that provide generic and scalable open source +solution with options for SW partitioning of product subsystems to run audio +workloads. + +.. toctree:: + :maxdepth: 1 + + zephyr_kernel_overview + memory_management/index + power_management + io_drivers/index diff --git a/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/dmic/dmic_driver.rst b/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/dmic/dmic_driver.rst new file mode 100644 index 00000000..6da57691 --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/dmic/dmic_driver.rst @@ -0,0 +1,44 @@ +.. _dmic_driver: + +DMIC IO Driver +############## + +.. uml:: images/dmic_diagram.pu + :caption: DMIC IO Driver overview + +Gateway Initialization +********************** + +.. uml:: images/dmic_gateway_init.pu + :caption: DMIC Input Gateway Initialization + +DMIC HW is initialized as follows: + + 1. Mute microphones. + 2. Enable clock on microphones (also enable CIC and FIRs). + 3. Wait for clock stabilization (SoC defined delay). + 4. Unmute microphones using a curved ramp until the DC offset is gone and + replaced with the live stream. + +Configuration BLOB +****************** + +DMIC IO Driver is prepared for the configuration BLOB to come in context of any +instance of the DmicInput at any time. The configuration may be rejected if the +current state of PDM controllers and FIFOs is inappropriate. Accepting the +configuration does not always mean that it is immediately programmed to the HW. +The configuration is global, so when sent by an instance of DmicInput while +another instance is already running it is just compared with already programmed +data for the sake of consistency. + +Gateway Release +*************** + +.. uml:: images/dmic_gateway_release.pu + :caption: DMIC Input Gateway Release + +State Transitions +***************** + +.. uml:: images/dmic_gateway_state_transitions.pu + :caption: DMIC Input Gateway State Transition diff --git a/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/dmic/images/dmic_diagram.pu b/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/dmic/images/dmic_diagram.pu new file mode 100644 index 00000000..6dc94a0c --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/dmic/images/dmic_diagram.pu @@ -0,0 +1,30 @@ +@startuml + +hide methods +hide attributes + +component Zephyr { + class DmicDriver + interface dai_dmic_ops + interface dai_driver_api +} + +component MPP { + interface Gateway + + interface IoDriver + class DmicManager + class DmicInput +} + +DmicDriver -up- dai_dmic_ops + +dai_dmic_ops -left-|> dai_driver_api : implements + +DmicManager -up- IoDriver +DmicManager -left-> DmicInput : manages +DmicInput -up- Gateway +DmicInput -down-> dai_dmic_ops : calls +DmicDriver --* DmicInput + +@enduml diff --git a/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/dmic/images/dmic_gateway_init.pu b/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/dmic/images/dmic_gateway_init.pu new file mode 100644 index 00000000..f8a938f4 --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/dmic/images/dmic_gateway_init.pu @@ -0,0 +1,23 @@ +@startuml + +participant "DMIC Manager" as dmic_manager +participant "Zephyr PM Subsystem" as zephyr_pm +participant "DMIC Driver" as dmic_driver + +-> dmic_manager : gateway_allocate() + activate dmic_manager + dmic_manager -> zephyr_pm : pm_runtime_device_get (dmic) + + activate zephyr_pm + zephyr_pm -> zephyr_pm : increase usage count + opt if usage == 1 + zephyr_pm -> dmic_driver : pm_device_resume + activate dmic_driver + return + end + return + + deactivate dmic_manager +<-- dmic_manager + +@enduml diff --git a/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/dmic/images/dmic_gateway_release.pu b/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/dmic/images/dmic_gateway_release.pu new file mode 100644 index 00000000..9afebab9 --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/dmic/images/dmic_gateway_release.pu @@ -0,0 +1,23 @@ +@startuml + +participant "DMIC Manager" as dmic_manager +participant "Zephyr PM Subsystem" as zephyr_pm +participant "DMIC Driver" as dmic_driver + +-> dmic_manager : gateway_release() + activate dmic_manager + dmic_manager -> zephyr_pm : pm_runtime_device_put (dmic) + + activate zephyr_pm + zephyr_pm -> zephyr_pm : decrease usage count + opt if usage == 0 + zephyr_pm -> dmic_driver : pm_device_suspend + activate dmic_driver + return + end + return + + deactivate dmic_manager +<-- dmic_manager + +@enduml diff --git a/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/dmic/images/dmic_gateway_state_transitions.pu b/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/dmic/images/dmic_gateway_state_transitions.pu new file mode 100644 index 00000000..ee592ed6 --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/dmic/images/dmic_gateway_state_transitions.pu @@ -0,0 +1,19 @@ + +participant "Dmic Input\nGateway" as dmic_input +participant "DMA" as dma + +== Dmic Input : STOPPED == + +-> dmic_input : STOPPED + dmic_input -> dma : stop() + +== Dmic Input : PAUSED == + +-> dmic_input : PAUSED + dmic_input -> dma : init_transfer() + dmic_input -> dma : pause() + +== Dmic Input : RUNNING == + +-> dmic_input : RUNNING + dmic_input -> dma : start() diff --git a/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/hda/hda_driver.rst b/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/hda/hda_driver.rst new file mode 100644 index 00000000..3165775c --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/hda/hda_driver.rst @@ -0,0 +1,50 @@ +.. _hda_driver: + +HD-A IO Driver +############## + +.. uml:: images/hda_io_driver_deps.pu + :caption: HD-A IO Driver overview + +HD-A Gateways +************* + +Gateway Node Addressing +======================= + +There are four types of HD-A gateways. Note that the naming convention +(inherited from c-spec) names the data flow direction based on the external +entity's perspective. Therefore, "output" means that data comes to FW from the +external source and "input" means that data is sent from FW to the external +sink. + +.. uml:: images/hda_playback.pu + :caption: HD-A Playback + +.. uml:: images/hda_capture.pu + :caption: HD-A Capture + +HD-A Gateway types: + - HDA-A DMA Source, + + - DMA Host Output, + - DMA Link Input, + + - HDA-A DMA Sink, + + - DMA Host Input, + - DMA Link Output + +HD-A to HDMI +============ + +There following options are available: + + 1. Legacy HD/A (not recommended), + 2. HW chaining in the DSP (depends on HW support), + 3. SW chaining in the DSP (recommended), + 4. Full Copier-...-Copier pipeline (more resources required). + +The most resource-efficient way to do a simple HD/A to HD/A playback via the DSP +is to use the "DMA Chaining" feature. FW provides an IPC command to connect two +HD/A gateways with a simple data copier task running in the LL domain. diff --git a/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/hda/images/hda_capture.pu b/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/hda/images/hda_capture.pu new file mode 100644 index 00000000..37e6a371 --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/hda/images/hda_capture.pu @@ -0,0 +1,15 @@ +@startuml + +component "host capture" as hc + +package FW { + component "Host Input" as hi + component "Link Output" as lo +} + +component "link capture" as lc +hc <- hi +hi <- lo +lo <- lc + +@enduml diff --git a/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/hda/images/hda_io_driver_deps.pu b/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/hda/images/hda_io_driver_deps.pu new file mode 100644 index 00000000..8cfdd590 --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/hda/images/hda_io_driver_deps.pu @@ -0,0 +1,12 @@ + +@startuml +allowmixing + +component "hd-a io driver" as io_drv +component "hd-a gateway" as gateway +component "hd-a dma" as dma + +io_drv -right-> gateway : provides +gateway -down-> dma : use + +@enduml diff --git a/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/hda/images/hda_playback.pu b/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/hda/images/hda_playback.pu new file mode 100644 index 00000000..7988d2b3 --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/hda/images/hda_playback.pu @@ -0,0 +1,15 @@ +@startuml + +component "host playback" as hp + +package FW { + component "Host Output" as ho + component "Link Input" as li +} + +component "link playback" as lp +hp -> ho +ho -> li +li -> lp + +@enduml diff --git a/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/i2s/i2s_driver.rst b/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/i2s/i2s_driver.rst new file mode 100644 index 00000000..ef17ecae --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/i2s/i2s_driver.rst @@ -0,0 +1,123 @@ +.. _i2s_driver: + +I2S IO Driver +############# + +.. uml:: images/i2s_diagram.pu + :caption: I2S IO Driver overview + +Configuration BLOB +****************** + +The Configuration Blob is a build of block structures: + - TDM slot Map, + - I2S base registers, + - MCLK configuration that allows for specifying the ratio for multiple + dividers, + - Aggregation configuration + +The ``I2sConfigurationBlobHeader`` begins with a signature followed by the BLOB +version and size. + +.. code-block:: text + + I2sConfigurationBlobHeader + { + signature and version { 0xEE, BLOB version } + size in bytes + } + +Blob Configuration structure that follows the header depends on the BLOB version. +Currently, only v2.5 is supported with the structure as follows: + +.. code-block:: text + + I2sConfigurationBlob2 + { + I2sConfigurationBlobHeader + TDM slot map ver.2 [I2S_TDM_MAX_SLOT_MAP_COUNT] + I2S base registers + MCLK configuration ver.2 + { + 2.5: Aggregation configuration + } + } + +TDM Time Slots +============== + +TDM time slots are statically assigned to streams by definition coming from +ACPI. A single stream transmits data through time slots of a single time slot +group. For example, 8 TDM time slots may be grouped by the following definition +from ACPI: + +.. code-block:: text + + tsd[0] = 0xFFFFFF43, tsd[1] = 0xFFFFFF01, ... + +where: + - Stream 0 specifies time_slot_group_index = 1, + - Stream 1 uses time_slot_group_index = 0 + +that would mean that the 1st TDM slot is mapped to S0 Ch0; the 0th TDM slot is +mapped to S0 Ch1; the 3rd TDM slot is mapped to S1 Ch0, and 4th TDM slot is +mapped to S1 Ch1. + +.. graphviz:: images/i2s_tdm.dot + :caption: I2S TDM + +Configuring BCLK Clock Input Source +=================================== + +The I2S Link BCLK may be configured to use on the SoC available clock sources. + +Example BCLK clock sources: + + - XTAL Oscillator clock, + - Audio Cardinal clock, + - Audio PLL fixed clock, + - MCLK + +Clock selection is programmed using values provided in the I2S Configuration +BLOB for the MCDSS and MNDSS fields of the MDIVCTRL register. + +Link Synchronization (and Aggregation) +====================================== + +Applies to sync of the streams started together as well as to synchronizing new +stream with already running ones. + +.. note:: The same configuration must be set to all involved I2S ports. Specifically, + all the ports must be driven by the same clock source. Moreover, there might + be clock source SoC limitations. For example, in the TGL the M/N divider has + to be selected for aggregation case. + +.. list-table:: + :widths: 25 25 50 + :header-rows: 1 + + * - Synchronized + - Provider Mode + - Consumer Mode + * - Stream start + - Yes + - Yes + * - BCLK, SFRM + - Yes + - By hooking up to the same I2S provider + +"Single" I2S links may be synchronized and aggregated by sending I2sSyncData to +the I2S IO Driver. + +Loopback mode +====================================== + +The I2S transmitter and receiver share data pins at the IP level. The Tx pin of the transmitter +is connected to the Rx pin of the receiver. This may be used for easy creation of digital +loopbacks (LBM, loopback mode) - the receiver always does see what the sender is sending. + +Please note the following: + + - all the parameters of transmitter and receiver, like number of channels, data rates, and format must match each other + - the lines are connected internally, so LBM mode may be used even if the I2S pins are not physically available + diff --git a/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/i2s/images/i2s_diagram.pu b/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/i2s/images/i2s_diagram.pu new file mode 100644 index 00000000..d49b4d50 --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/i2s/images/i2s_diagram.pu @@ -0,0 +1,18 @@ +@startuml + +hide methods +hide attributes + +class I2sSink --() Gateway +class I2sSource --() Gateway +class I2sDriver --() IoDriver +class I2sChannel +class I2sInputChannel + +I2sChannel --* I2sDriver +I2sInputChannel --|> I2sChannel +I2sOutputChannel --|> I2sChannel +I2sSink --o I2sOutputChannel +I2sSource --o I2sInputChannel + +@enduml diff --git a/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/i2s/images/i2s_tdm.dot b/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/i2s/images/i2s_tdm.dot new file mode 100644 index 00000000..db699bbd --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/i2s/images/i2s_tdm.dot @@ -0,0 +1,38 @@ +digraph G { + node [fontsize=10,shape=record,height=.1]; + splines=false + + subgraph clusterAcpi { + label="tdm_ts_group[8]"; fontsize=10; + tdm_acpi [label="FFFFFF43 |FFFFFF01 |..."]; + } + + subgraph clusterStr0 { + label="Stream 0"; fontsize=10; color="#C4D600"; + + str0_cfg [label="\{ time_slot_group_index=1\}"]; + str0_cfg -> tdm_acpi:acpi1 [style=dotted]; + + str0 [label="L |R |... |

" color="#C4D600"]; + } + + subgraph clusterStr1 { + label="Stream 1"; fontsize=10; color="#FFA300" + + str1_cfg [label="\{ time_slot_group_index=0\}"] + str1_cfg -> tdm_acpi:acpi0 [style=dotted] + + str1 [label="L |R |..." color="#FFA300"] + } + + str [label="<0>R |<1>L |<2> |<3>L |<4>R |<5> |<6> |<7> "] + + str0:l -> str:1 + str0:r -> str:0 + + str1:l -> str:3 + str1:r -> str:4 + + {rank=min; tdm_acpi} + {rank=max; str} +} diff --git a/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/images/io_drivers_diagram.pu b/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/images/io_drivers_diagram.pu new file mode 100644 index 00000000..37ce38ce --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/images/io_drivers_diagram.pu @@ -0,0 +1,16 @@ +frame "SOF" { + component Gateway + component GatewayExtension <> +} + +frame "Zephyr" { + component IoDriver <> + component DMA +} + +Gateway *-right- GatewayExtension + +Gateway -down- IoDriver +Gateway -down- DMA + +GatewayExtension ..> IoDriver diff --git a/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/index.rst b/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/index.rst new file mode 100644 index 00000000..52562049 --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/io_drivers/index.rst @@ -0,0 +1,31 @@ +.. _io_drivers: + +IO Drivers +########## + +The IO Drivers provide access to an IO HW Interfaces connected to the DSP, e.g. +I2S, DMIC, etc. and are managed as a part of the Zephyr RTOS. The Audio IO +Drivers share generic `Zephyr DAI interface `__. +For a full list of IO drivers available on the specific platform, refer to +:ref:`platforms`. HW IO is accessed via the `Gateway` interface inside the FW. +The actual implementation of that interface depends on the underlying HW IO +mechanism. Gateways use the Zephyr DMA interface to transmit the data to/from +the represented HW IO. DMA interface implementation depends on the underlying +DMA method (HDA-DMA, GPDMA, etc.). + +**NOTE:** The introduction of Gateways concept to SOF with Zephyr is a work in +progress. In existing implementation the SOF Host and DAI implementation is +still in use as a substitute of Gateways. + +.. uml:: images/io_drivers_diagram.pu + :caption: IO Drivers diagram + +Drivers +******* + +.. toctree:: + :maxdepth: 1 + + hda/hda_driver + i2s/i2s_driver + dmic/dmic_driver diff --git a/architectures/firmware/sof-zephyr/rtos_layer/memory_management/heap_sharing.rst b/architectures/firmware/sof-zephyr/rtos_layer/memory_management/heap_sharing.rst new file mode 100644 index 00000000..1b7d0347 --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/memory_management/heap_sharing.rst @@ -0,0 +1,58 @@ +Heap sharing +############ + +The memory heap can be: + +- local - used exclusively by a single DSP core, +- shared - higher level memory shared across all DSP cores + +.. uml:: images/heaps.pu + :caption: Memory Heaps + +.. note:: Introduction of MMU will require a separate local application heap per + isolated domain. + +L1 Cache Coherency +****************** + +NOTE: This section applies to Intel systems without L1 cache coherency + +A local heap is used exclusively by a single DSP core. Therefore operations on +the allocated memory buffers do not require explicit L1 cache operations nor +data cache alignment. + +All operations performed on a local heap can be executed by the associated DSP +core only. The *move-to-another-core* operation is not permitted for allocated +buffers. + +A shared heap can be configured in two ways: + +1. To provide uncache aliases of buffer addresses to the clients, +2. To provide cacheable addresses to the clients. + +Option #1 is preferred, since does not require explicit L1 cache operations +when memory is accessed by a DSP core. However, all operations directly access +L2+ memory therefore it is not suitable for a low latency high performance data +processing case. + +Option #2 provides better performance but requires explicit L1 cache operations, +which are difficult to maintain and validate, as well as data cache alignment +for both client buffers and their descriptors, which creates an overhead. This +configuration should be avoided if possible unless a coherent API is available +to share the data. + +However, a one important exception to the shared memory accessed through uncached +alias is a data buffer connected between processing components running on +different cores. Locking and cache operations price could be payed to get much +better performance of accessing the data in the buffer which may be a +significant part of light weight LL processing modules DSP cycle budget. + +Accessing Shared Memory Pool +**************************** + +The data structures needed to manage shared memories are initialized by the +primary core, structure location in memory map is known at the build time and +API is protected by the mutex. + +The mutex uses atomic operation behind and all processors co-managing this +memory heap must support atomics. diff --git a/architectures/firmware/sof-zephyr/rtos_layer/memory_management/images/dynamic_module_load.pu b/architectures/firmware/sof-zephyr/rtos_layer/memory_management/images/dynamic_module_load.pu new file mode 100644 index 00000000..02c386d0 --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/memory_management/images/dynamic_module_load.pu @@ -0,0 +1,77 @@ +@startuml + +box "Host" #LightGreen + participant "Driver" as host_driver +end box + +box "Media Processing Pipelines Layer" #LightSkyBlue + participant "Component Manager" as component_manager + participant "Library Manager" as lib_manager + participant "MPP Memory Manager" as mpp_memory_manager +end box + +box "Zephyr RTOS" #LightBlue + participant "Memory Management Driver" as memory_management_driver +end box + +box "Hardware" #LightGrey + participant "Memory" as hw_memory +end box + +host_driver -> lib_manager: Load Library + activate lib_manager + lib_manager -> mpp_memory_manager: rmalloc(MEM_ZONE_RUNTIME, flags=NULL, MEM_CAPS_LOADABLE_LIBRARY, size) + activate mpp_memory_manager + return address to store library + lib_manager --> host_driver + deactivate lib_manager + +host_driver -> lib_manager: Transfer library over DMA\nto given address + +host_driver -> component_manager: Instantiate Component + activate component_manager + + opt if Component is Loadable and it is first instance + component_manager -> lib_manager: Load component + activate lib_manager + + loop repeat for Component TEXT, RODATA + lib_manager -> lib_manager: read Component virtual address and size from manifest + + lib_manager -> memory_management_driver: sys_mm_drv_map_region(virt*, phys=NULL, size, flags=NULL) + activate memory_management_driver + memory_management_driver -> memory_management_driver: allocate free phys pages + opt power up memory banks for allocated phys pages + memory_management_driver -> hw_memory: power up memory banks + end + memory_management_driver --> lib_manager + deactivate memory_management_driver + + lib_manager -> lib_manager: read Component address offset from library manifest + lib_manager -> lib_manager: mem_copy(virt*, library_store_addr + offset, size) + lib_manager -> memory_management_driver: sys_mm_drv_update_region(virt*, size, flags= CODE / RODATA) + activate memory_management_driver + note right: update region flags to prevent overwrite + return + + end + + opt if Component has BSS + lib_manager -> memory_management_driver: sys_mm_drv_map_region(virt*, phys=NULL, bss_size, flags) + activate memory_management_driver + memory_management_driver -> memory_management_driver: allocate free phys pages + opt power up memory banks for allocated phys pages + memory_management_driver -> hw_memory: power up memory banks + end + memory_management_driver --> lib_manager + deactivate memory_management_driver + end + + lib_manager --> component_manager + deactivate lib_manager + end + + component_manager -> component_manager: Instantiate Component + component_manager --> host_driver + +@enduml diff --git a/architectures/firmware/sof-zephyr/rtos_layer/memory_management/images/heaps.pu b/architectures/firmware/sof-zephyr/rtos_layer/memory_management/images/heaps.pu new file mode 100644 index 00000000..582b21bd --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/memory_management/images/heaps.pu @@ -0,0 +1,31 @@ +scale max 1024 width + +node "DSP Core #0 Memory Block" as core_0 { + node "Application Heap (local)" as app_0 #lightgreen { + component "Pipelines @Core #0" as ppl_0 + component "LL Modules & Tasks @Core #0" as ll_0 + component "DP Modules & Tasks @Core #0" as dp_0 + } + + node "Application Heap (shared)" as app_shared_0 #lightyellow { + component "Shared buffers" + } + + node "System Heap (shared)" as sys_0 #lightblue { + component "Devices" + } +} + +ppl_0 -[hidden]down-> ll_0 +ll_0 -[hidden]down-> dp_0 + +node "DSP Core #1 Memory Block" as core_1 { + node "Application Heap (local)" as app_1 #lightgreen { + component "Pipelines @Core #1" as ppl_1 + component "LL Modules & Tasks @Core #1" as ll_1 + component "DP Modules & Tasks @Core #1" as dp_1 + } +} + +ppl_1 -[hidden]down-> ll_1 +ll_1 -[hidden]down-> dp_1 diff --git a/architectures/firmware/sof-zephyr/rtos_layer/memory_management/images/memory_allocation.pu b/architectures/firmware/sof-zephyr/rtos_layer/memory_management/images/memory_allocation.pu new file mode 100644 index 00000000..82d84bf0 --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/memory_management/images/memory_allocation.pu @@ -0,0 +1,21 @@ +@startuml + +box "SOF" #LightBlue + participant "Component Management" as component_management + participant "MPP Memory Manager" as mpp_memory_manager +end box + +box "Zephyr" #LightGreen + participant "Zephyr Memory Manager" as zephyr_memory_manager +end box + +activate component_management +component_management -> mpp_memory_manager: rmalloc(mem_zone, flags, caps, size) + activate mpp_memory_manager + + mpp_memory_manager -> mpp_memory_manager: find memory heap that\nmatch zone and caps + mpp_memory_manager -> zephyr_memory_manager: k_heap_alloc (heap, size) + activate zephyr_memory_manager + return + mpp_memory_manager --> component_management +@enduml diff --git a/architectures/firmware/sof-zephyr/rtos_layer/memory_management/images/memory_allocation_from_memory_driver.pu b/architectures/firmware/sof-zephyr/rtos_layer/memory_management/images/memory_allocation_from_memory_driver.pu new file mode 100644 index 00000000..a9d10fd7 --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/memory_management/images/memory_allocation_from_memory_driver.pu @@ -0,0 +1,26 @@ +@startuml + +box "SOF" #LightBlue + participant "Library Manager" as library_manager +end box + +box "Zephyr" #LightGreen + participant "Memory Management Driver" as memory_management_driver +end box + +box "Hardware" #LightGrey + participant "Memory" as hw_memory +end box + +activate library_manager + +library_manager -> memory_management_driver: sys_mm_drv_map_region\n(virt*, phys=NULL, size, flags) + activate memory_management_driver + memory_management_driver -> memory_management_driver: allocate memory phys pages + opt if phys memory pages require power up + memory_management_driver -> hw_memory: power up memory banks + end + + return + +@enduml diff --git a/architectures/firmware/sof-zephyr/rtos_layer/memory_management/images/memory_initialization.pu b/architectures/firmware/sof-zephyr/rtos_layer/memory_management/images/memory_initialization.pu new file mode 100644 index 00000000..666e68b1 --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/memory_management/images/memory_initialization.pu @@ -0,0 +1,42 @@ +@startuml + +box "SOF" #LightBlue + participant "MPP Memory Manager" as mpp_memory_manager +end box + +box "Zephyr" #LightGreen + participant "Memory Manager" as zephyr_memory_manager + participant "Memory Management Driver" as memory_management_driver +end box + +box "Hardware" #LightGrey + participant "Memory" as hw_memory +end box + + +-> memory_management_driver: sys_mm_drv_mm_init + activate memory_management_driver + memory_management_driver -> memory_management_driver: read unused_main_mem_start_marker\nfrom linker + note right: The marker is used to\n identify where base firmware\n ends in memory (text, data, bss) + + memory_management_driver -> memory_management_driver: sys_mm_drv_unmap_region(unused_main_mem_start, unused_size) + activate memory_management_driver + opt If architecture support granular memory banks power control + memory_management_driver -> hw_memory: power down unused memory banks + deactivate memory_management_driver + end + + deactivate memory_management_driver + +-> mpp_memory_manager: mpp_mem_init + activate mpp_memory_manager + mpp_memory_manager -> mpp_memory_manager: read memory zones\nbase address and size + loop for each memory region create heap + mpp_memory_manager -> zephyr_memory_manager: k_heap_init\n(heap, mem*, size) + activate zephyr_memory_manager + return + end + + deactivate mpp_memory_manager + +@enduml diff --git a/architectures/firmware/sof-zephyr/rtos_layer/memory_management/images/memory_management_layers.pu b/architectures/firmware/sof-zephyr/rtos_layer/memory_management/images/memory_management_layers.pu new file mode 100644 index 00000000..a2218c96 --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/memory_management/images/memory_management_layers.pu @@ -0,0 +1,60 @@ +@startuml + +allowmixing + +scale max 1024 width + +component SOF { + + package "Zephyr" as ZEPHYR_RTOS { + interface "Zephyr Memory Service interface" as ZMSI + hide ZMSI methods + hide ZMSI attributes + + package "Drivers" as DRIVERS { + component "Memory Management Driver" as MEMORY_MGMT_DRIVER + } + + package "Memory Manager" as ZEPHYR_MEM_MANAGER { + component "Multi Heap" as MULTI_HEAP + component "Memory Heaps" as MEM_HEAPS + component "Memory Blocks Allocator" as MEM_BLOCK_ALLOCATOR + component "Demand Paging" as DEMAND_PAGING + + MULTI_HEAP .[hidden]right. MEM_HEAPS + MEM_HEAPS .[hidden]right. MEM_BLOCK_ALLOCATOR + MEM_BLOCK_ALLOCATOR .[hidden]right. DEMAND_PAGING + } + + component "Device Tree" as DEV_TREE + + ZMSI -[hidden]down- MEM_BLOCK_ALLOCATOR + ZEPHYR_MEM_MANAGER -[hidden]down- DRIVERS + DRIVERS -[hidden]right- DEV_TREE + } + + package "Media Processing Pipelines layer" as MPP_LAYER { + component "Pipeline Manager" as PIPELINE_MANAGER + component "Communication" as COMMUNICATION + component "Component Manager" as COMPONENT_MANAGER + component "MPP Memory Manager" as MPP_MEM_MANAGER + + PIPELINE_MANAGER -[hidden]right- COMMUNICATION + COMMUNICATION -[hidden]right- MPP_MEM_MANAGER + MPP_MEM_MANAGER -[hidden]right- COMPONENT_MANAGER + + } + + package "Application layer" as APP_LAYER { + component "Loadable Components" as LOADABLE_COMPONENTS + component "Built-in Components" as BUILT_IN_COMPONENTS + + BUILT_IN_COMPONENTS -[hidden]right- LOADABLE_COMPONENTS + } + + APP_LAYER -[hidden]down- MPP_LAYER + MPP_LAYER -[hidden]down- ZEPHYR_RTOS + +} + +@enduml diff --git a/architectures/firmware/sof-zephyr/rtos_layer/memory_management/index.rst b/architectures/firmware/sof-zephyr/rtos_layer/memory_management/index.rst new file mode 100644 index 00000000..68d4e180 --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/memory_management/index.rst @@ -0,0 +1,43 @@ +.. _memory_mgmt: + +Memory Management +################# + +Memory Management role is to provide service API for dynamic memory mapping and +allocation from available memory zones. + +Overview +******** + +The memory support functionality is delivered at two levels: + + - Zephyr Memory Management Service, which provides memory drivers, demand + paging, allocators, and heap management, + + - MPP Memory Management - SOF extension, which provides heaps for virtual + memory mapped to physical memory on demand, and declaration of SOF specific + heaps instantiated for various memory zones, + +.. uml:: images/memory_management_layers.pu + :caption: Example of Memory Management layers and interfaces + +Read More +********* + +.. toctree:: + :maxdepth: 1 + + memory_zones + mpp_memory_management + heap_sharing + memory_management_driver + memory_management_flows + +External Links +============== + +- `Zephyr Memory Management Service `__ +- `Memory Blocks Allocator `__ +- `Memory Management driver `__ +- `Heaps Management `__ +- `Demand Paging `__ diff --git a/architectures/firmware/sof-zephyr/rtos_layer/memory_management/memory_management_driver.rst b/architectures/firmware/sof-zephyr/rtos_layer/memory_management/memory_management_driver.rst new file mode 100644 index 00000000..097c450f --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/memory_management/memory_management_driver.rst @@ -0,0 +1,21 @@ +Memory Management Driver +######################## + +The Memory Management Driver (MMD) is part of the Zephyr distributed drivers. +Each SoC may require unique Memory Management driver implementation. The MMD +shall implement common MMD interface that is exposed to kernel services. This +allows for explicit allocation and mapping of individual memory hardware pages +within the physical environment. + +All operations within Memory Management Driver are explicit. Hardware page IDs +represent real physical blocks of hardware memory. + +The MMD is responsible for identification what part of the SoC memory is used by +the base firmware (code, data, bss) and unmap the unused blocks. The unused +memory will be available for dynamic allocation. Base firmware code, read only +data and BSS are mapped in the TLB automatically with corresponding flags (CODE, +RODATA) to prevent incidental modification. + +Memory Management Driver can maintain memory power at a granular level if the +architecture support it. It has possibility to power up selected memory banks on +map requests and power down on unmap, which is a recommended flow. diff --git a/architectures/firmware/sof-zephyr/rtos_layer/memory_management/memory_management_flows.rst b/architectures/firmware/sof-zephyr/rtos_layer/memory_management/memory_management_flows.rst new file mode 100644 index 00000000..aa90ebb2 --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/memory_management/memory_management_flows.rst @@ -0,0 +1,41 @@ +Flows +##### + +Memory initialization +********************* + +Main goal of Memory initialization is to unmap unused memory after firmware load +and create heaps for supported memory zones. + +.. uml:: images/memory_initialization.pu + :caption: Memory initialization flow + +Memory allocation +***************** + +The common memory allocation is expected to use one of the available memory +zones via Zephyr Heap that was created during initialization. + +.. uml:: images/memory_allocation.pu + :caption: Memory allocation example flow + +Memory allocation directly using Memory Management Driver +********************************************************* + +In specific use cases (e.g. Dynamic Component Load) it may be required to +allocate memory directly using Memory Management Driver to control what virtual +address will be mapped to physical memory. + +.. uml:: images/memory_allocation_from_memory_driver.pu + :caption: Example memory allocation using Memory Management Driver + +Dynamic Component Load +********************** + +The loadable components are stored in Loadable Library memory zone and can be +loaded on instantiate request to System memory. The components load to System +memory is optional and integrator can indicate if the components can be executed +directly from the Loadable Library memory zone. + +.. uml:: images/dynamic_module_load.pu + :caption: Dynamic component load and instantiation flow diff --git a/architectures/firmware/sof-zephyr/rtos_layer/memory_management/memory_zones.rst b/architectures/firmware/sof-zephyr/rtos_layer/memory_management/memory_zones.rst new file mode 100644 index 00000000..5be17be8 --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/memory_management/memory_zones.rst @@ -0,0 +1,11 @@ +Memory Zones +############ + +Depending on the memory use case a different memory zone can be used for +allocation. Application and MPP layer components are using memory zones and +capabilities to identify a target memory. The memory zones mapping on physical +addresses is SoC specific. If the SoC support multiple memory types with +different characteristics, then it is up to SoC integrator to decide which +memory will be most suitable for zone mapping. For example, if SoC has access to +slow but large capacity memory then it can map it for Loadable Library memory +zone. diff --git a/architectures/firmware/sof-zephyr/rtos_layer/memory_management/mpp_memory_management.rst b/architectures/firmware/sof-zephyr/rtos_layer/memory_management/mpp_memory_management.rst new file mode 100644 index 00000000..2f079186 --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/memory_management/mpp_memory_management.rst @@ -0,0 +1,19 @@ +MPP Memory Management +##################### + +MPP Memory Management (MPP MM) is a SOF extension running on top of Zephyr +Memory Manager. The reason to create MPP MM was to add support for memory zones, +which are not natively supported by Zephyr. Zephyr by default initialize single +System Heap. + +The MPP MM roles: + + - initialization of Memory Heaps for supported memory zones, + - provide allocator API for memory allocation from different memory zones, + +Memory Heaps initialization is done based on SoC Memory Map that identify start +and end addresses of memory zones. + +.. note:: + Memory zones are expected to be defined as memory sections in a SoC linker script. + diff --git a/architectures/firmware/sof-zephyr/rtos_layer/power_management.rst b/architectures/firmware/sof-zephyr/rtos_layer/power_management.rst new file mode 100644 index 00000000..c394f52c --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/power_management.rst @@ -0,0 +1,158 @@ +.. _power_mgmt: + +Power Management +################ + +The Power Manager is responsible for system and device power management. The +power management behavior can be customized by power policy configuration and +direct power API requests which allows you to adjust system power savings to the +current firmware activity. + +.. uml:: images/power/power_components.pu + :caption: Participants of the Firmware power management. + +`Zephyr Power Management +documentation `__ + +DSP Cores +********* + +Each DSP core can be separately powered up and down. + +The assumption is that DSP #0 is a primary core and is responsible for powering +up all secondary cores. The primary core powers up the secondary cores on Set Dx +IPC request. + +The secondary core shall be powered up prior to any task allocated to it by the SW +driver. + +Power Transitions +***************** + +There are three DSP core power states: + +.. list-table:: + :widths: 5 10 20 + :header-rows: 1 + + * - DSP Power State + - Zephyr Power State + - Notes + * - D0 + - PM_STATE_ACTIVE + - built-in state, no extra mapping required + * - D0i3 + - PM_STATE_RUNTIME_IDLE + - custom mapping in the Device Tree + *d0i3: idle { power-state-name = "runtime-idle" }* + * - D3 + - PM_STATE_SOFT_OFF + - custom mapping in the Device Tree + *d3: off { power-state-name = "soft-off" }* + +A major consumer of power related to the main part of the DSP subsystem +is a source of the clock that is wired to the DSP core and the DSP core itself. +Transitions to lower power states focus on this part. Another power consumer, +a bit less significant, is the L2 SRAM memory embedded in the DSP subsystem. + +The clock source and clock gating is managed by the Power Manager according to +Power Policy configuration settings. + +Memory power is controlled by the Memory Management Driver that is responsible +for memory setup on power state transitions and memory banks power gating on +map/unmap requests (if it is supported by the SoC). + +Other power-related settings are clock gating and power gating of I/Os (I2C, +I3C, GPIO, SPI, UART, DMIC, etc.) and external DSP accelerators (if supported by +the hardware). + +The low power state transition can be triggered either by Zephyr (on CPU idle) +or on the Host IPC request through the Zephyr force power state set request. The +entrance to D0i3 power state can be dynamically locked on SetD0ix IPC request +that configures the Zephyr Power Policy to prevent a selected power state transition. + +More details are in the `Zephyr Power Management +documentation `__ + +.. uml:: images/power/dsp_fw_power_states.pu + :caption: DSP and FW Power States + +.. uml:: images/power/dx_state_transitions.pu + :caption: D3, D0 and D0ix state transitions + +Power Up of Secondary Core (D3 to D0 transition) +================================================ + +The below diagram shows secondary core boot flow: + +.. uml:: images/power/flow_secondary_core_boot.pu + :caption: DSP Secondary Core Boot flow + +Power down of DSP core (D0 to D3 transition) +============================================ + +The below diagram shows a primary core power down flow: + +.. uml:: images/power/flow_primary_core_power_down.pu + :caption: DSP Primary Core Power Down flow + +Power down of Secondary Core (D0 to D3 transition) +================================================== + +The below diagram shows a secondary core power down flow: + +.. uml:: images/power/flow_secondary_core_power_down.pu + :caption: DSP Secondary Core Power Down flow + +Enable D0ix (D0 to D0ix) +======================== + +D0ix is enabled on explicit `SET_D0ix` IPC message with prevent_power_gating bit +set to 0. + +.. uml:: images/power/flow_enable_d0i3.pu + :caption: Enable D0i3 flow + +Disable D0ix (D0ix to D0) +========================= + +D0ix is disabled on explicit `SET_D0ix` IPC message with prevent_power_gating +bit set to 1. + +.. uml:: images/power/flow_disable_d0i3.pu + :caption: Disable D0i3 flow + +DSP idle state +============== + +.. uml:: images/power/flow_dsp_idle.pu + :caption: DSP idle state flow + +DSP Cores Clock Gating +====================== + +DSP clocks, similar to DSP cores, can be separately gated as well. Clock gating +shall be enabled by default for all DSP cores unless there is request to prevent +it. + +.. TODO: Create diagram with DSP power state transitions when either DSP clock + is gate or DSP power is gate. + +**NOTE:** Power and clock gating is controlled via `Set D0ix` IPC message. + +I/O Power and Clock Gating Management +************************************* + +Zephyr is responsible for I/O devices power and clock management. + +The I/O device power is controlled based on usage count. More details can be +found in `Zephyr Device Runtime Power Management +documentation `__ + +The I/O clock gating is configurable in driver power policy. Each driver shall +request the desired clock and clock power gating if it is necessary for I/O, +accelerator, etc. to work correctly. + +For instance, audio I/Os such as I2S associated with audio domain require a high +accuracy XTAL clock and may request it. This clock shall be used for as long as +audio I/Os are active. diff --git a/architectures/firmware/sof-zephyr/rtos_layer/zephyr_kernel_overview.rst b/architectures/firmware/sof-zephyr/rtos_layer/zephyr_kernel_overview.rst new file mode 100644 index 00000000..e8e9ada2 --- /dev/null +++ b/architectures/firmware/sof-zephyr/rtos_layer/zephyr_kernel_overview.rst @@ -0,0 +1,372 @@ +.. _kernel_overview: + +Zephyr based kernel +################### + +Zephyr has been introduced as an IP agnostic solution that replaced existing SOF +audio specific kernel. The Zephyr base kernel has been complemented with SOF Low +Level Drivers, SoC HAL and kernel extensions. The new solution continues +scalable kernel concept and it serves as a generic part of infrastructure that +can be statically and dynamically customized based on usage, compute, and memory +constrains, HW configuration etc. + +As a result of the kernel customization, a firmware infrastructure is produced. +This firmware infrastructure can run on a given processor type and it is tuned +for specified usage. + +For more Zephyr kernel details, see `Zephyr Introduction +documentation `__ + +The Zephyr based kernel consists of the following components: + +- Hardware integration layer: XTHAL, +- Low Level Drivers, + + - DMIC, + - I2S, + - SNDW, + - GPIO, + - I2C, + - I3C, + - timers, + - GPDMA, + - IDC, + - IPC, + - watchdog, + - etc. + +- SoC HAL, + + - OEM SoC specific code, + +- Services: shared resource services, communication services, memory manager, + power manager, interrupt manager, system service, etc. +- Kernel extensions: + + - AVS schedulers, + - Firmware Manager, + - Media Processing Pipeline Components, + +The Zephyr base kernel expectations: + +- it can scale down to meet all KPIs via static and dynamic scaling options, +- Zephyr itself is IP agnostic and shared across other SW and FW projects, +- it is available and maintain under open source license, + +.. uml:: images/zephyr_kernel_diagram.pu + :caption: Zephyr Kernel diagram + +Scaling Options +''''''''''''''' + +Zephyr kernel offers scaling options to adjust to selected HW configuration, +scale down to meet aggressive KPIs on a given platform, scale up to meet +functional requirements. + +The scaling is achieved in two ways: + +- static, kernel components can be selectively enabled in the build process + + - Drivers selected depending on SoC Configuration + - Services and execution frameworks chosen in Zephyr + +- dynamic, not used parts can be unloaded and saved in "backup storage" memory, + that typically has large capacity and high access latency. They will be + loaded again once a specific event will happen + + - It is only applicable to SoCs that support it. + - It is achieved via one of the following mechanisms: + + - Firmware Paging (if present) - Only currently executing modules are in + SRAM. + - Split Firmware into modules - Modules are loaded from "backup storage" + or unloaded on explicit request. No runtime dynamism. + +Handling Project Configuration +'''''''''''''''''''''''''''''' + +Zephyr is prepared to be configured via device tree that describe given SoC +board audio hardware configuration. + +A SoC board device tree allows configuring: + +* HW configuration + + * number of HP DSP cores, + * types of memories available per cores, + * supported clocks, + * number of I/Os, + * number of IPC and IDC interfaces for DSP cores, + * etc. + + * DSP memory space, + * IPC mailbox address, + * etc. + +Low Level Drivers +''''''''''''''''' + +SOF is capable to support hardware with several audio I/Os, sensor I/Os, DSP +accelerators and DMAs which count can be customized per architcture. + +HW resources with low level drivers: + +* Audio I/Os: I2S, DMIC, SNDW, HD/A, +* Sensor I/Os: I2C, I3C, GPIO, UART, SPI, ADC, PWM, +* Common resources: HP GPDMA, IPC, IDC, Timers, SHA-384, Watchdog + +**NOTE:** Not all I/Os are supported in each SoC board. + +.. TODO: add link to supported audio architectures + +Zephyr based firmware provides low level drivers for all these resources. A +specific driver can be enabled during build process. + +SoC HAL +''''''' + +The SoC HAL include implementation and configuration details specific for +selected SoC architecture. The SoC HAL abstraction allow to seamlesly switch +between target SoC configuration builds. + +More details can be found in Zephyr documentation: + +* `Zephyr Board Porting Guide `__ +* `Zephyr Architecture Porting Guide `__ + +Services +'''''''' + +.. uml:: images/kernel_services.pu + :caption: Example of kernel services + +The base Zephyr services provide generic system management functionality for +memory, interrupts, autonomous power control (clock and power gating, clock +management). + +The SOF specific functionality is exposed in a form of an extended kernel +services. The extended services utilize Zephyr base services infrastructure and +low level drivers to supply user space interface for the firmware application +layer components. The user space separation from hardware and low level drivers +significantly increase the firmware security and stability. + +Firmware Management +------------------- + +The firmware manager is a core service that is responsible for: + +- reading HW capabilities (number of cores, memory available, etc.), +- firmware initialization, +- instantiation and initialization of Low Level drivers for the existing HW + components, + + - memory type drivers initialization with size read form capability + registers + - audio drivers for supported interfaces + +- instantiate and initialize Extended Kernel Services + + - component manager + - pipeline manager + - IPC/IDC communication service + - async messaging service + - debug service + +.. TODO: add other components that require initialization by the firmware manager + +Interrupt Management +-------------------- + +The interrupt handler service allows to: + +- enable and disable an interrupt for DSP core, +- register a callback that will be called once a specified interrupt occur, + +For more details, see `Zephyr Interrupts +documentation `__ + +Memory Management +----------------- + +The Memory Manager provides a service to other FW components to allocate a block +out of available memory pools, it provides high level API, scans for unused +memory areas, handles physical memory defragmentation, prefetch and cache +policies. Most of the memory is expected to be paged. + +All allocation requests refer to virtual memory address space, which shall be +continuous. This also applies to DMA buffer allocations, where continuous memory +is guaranteed by either continuous physical memory or VA/PA translation. + +The map of available memory resources is passed to the Memory Manager during +initialization of Memory Manager via firmware infrastructure. + +For more details, see `Zephyr Memory Management +documentation `__. + +Power Management +---------------- + +The power management behavior highly depends on platform that firmware runs on, +and it can be configured during build time. There are platforms that only allow +clock gating and power gating is not applicable. + +The power management interface provides the following functionality: + +- allow and prevent power gating, +- allow and prevent clock gating, +- allow and prevent slower clock, +- allow and prevent XTAL shutdown, + +In all cases, the implementation relies on atomic counter which is incremented +every time when prevent function is called and decremented when allow function +is called. + +.. TODO: Add link to SOF Power Management detailed description with flows + +`Zephyr Power Management documentation +`__. + +IPC and IDC Service +------------------- + +The IPC and IDC Service provides communication channel over IPC or IDC. IPCs are +used for the external communication with Host, other processors within SoC or +other subsystems within PCH. IDCs are used for the internal communication +between processors within SOF subsystem. + +The introduction of SOF with Zephyr is followed with new IPC4 interface and +message formats that replaced IPC3. + +The following types of sequences are supported: + +- request-response initiated by Host, + + - it is synchronous sequence, + - long-running operations shall queue request and send response immediately. + The actual completion information should be sent via one-way asynchronous + notification, + +- one-way asynchronous notification, + +.. TODO: Add link to Communication section (when ready) + +Debugging +--------- + +The Zephyr based kernel provides a few services that helps with debugging FW. + +Logging +~~~~~~~ + +The Logger Service provides a lightweight mechanism to push log entries to all +firmware modules that are based on Zephyr logging infrastructure. + +It is a very useful mechanism to do a first level of debugging. + +.. TODO: Add link to Logger Service section (when ready) +.. TODO: Add link to SOF Enable Logs interface +.. TODO: Add link to SOF status and error codes registers + +Zephyr related documentation: + +- `Zephyr + Logging `__ + +Probes +~~~~~~ + +SOF supports injection and extraction probes. The probes are mainly used to +extract audio data from queues between components. + +The other probe use cases include: + +- injection of audio data to a component input queue - useful during testing + and debugging, +- injection of data to internal probes, +- extraction of data from internal probes i.e. internal component states, + intermediate data, debug information, +- logging - probes can be used as transport for firmware logs, + +.. TODO: Add link to Probe configuration interface (when ready) + +Performance Measurements +~~~~~~~~~~~~~~~~~~~~~~~~ + +The firmware infrastructures support performance measurements to collect +information about DSP cycles or amount of data moved via interfaces. + +.. TODO: Add link to Performance Measurements State firmware interface +.. TODO: Add link to firmware Global Performance Data description + + +Telemetry +~~~~~~~~~ + +Firmware infrastructure supports collection of telemetry events which then can +be read by the Host Software. The modules running in FW infrastructure can push +telemetry events via System Services. + +If the telemetry collection is started, the telemetry events will be written to +a common circular buffer. + +If the telemetry collection is stopped/disabled, the telemetry events will be +dropped at telemetry service level and they will not be written to the telemetry +circular buffer. During transition from started to stopped state, the telemetry +events that are already in the circular buffer will be dropped. + +.. TODO: Add link to SOF Telemetry interface documentation + +.. _schedulers_zephyr: + +Schedulers +---------- + +The scheduling method depends on compute and memory available for firmware +running on processor as well as type of workloads executed on given domain. + +There are following types of schedulers supported in SOF + +- AVS scheduling, + +.. TODO: Add link to Scheduling detailed section + +Async Messaging Service +----------------------- + +Asynchronous Messaging Service (AMS) is mechanism to exchange asynchronous +events between components running in the same firmware infrastructure or running +on the another processor (e.g. between HiFi and Fusion cores). + +The Async Messages can be also injected and extracted via Host Async Message +Gateway module by Host SW. + +.. TODO: Add link to Asynchronous Messaging detailed section + +System Services +--------------- + +The FW components do not know location of driver and service functions in base +firmware library, so they need to access base firmware services via System +Services. + +In SOF with Zephyr the `Zephyr interfaces for +drivers `__ +were adopted. All newly developed drivers must be compliant to this standard and +the legacy ones must be ported to it. + +In Zephyr based firmware, a driver instance is obtained via +``device_get_binding`` function call with a name of a driver instance. There is +no explicit driver initialization call as a driver instance is initialized with +the first call. + +A driver implementation must be ready for using the same hardware instance from +many modules and from many cores (it must be thread-safe implementation). There +can be more than one device instance if there is more than 1 instance of a +hardware (i.e. 2 I2C owner controllers). + +The example functionalities that should be exposed via system services: + +- IPC and IDC, +- Logger Service, +- RTOS scheduler functionalities, like yield, +- Async Messaging Service, diff --git a/architectures/firmware/sof-zephyr/zephyr_api_integration.rst b/architectures/firmware/sof-zephyr/zephyr_api_integration.rst new file mode 100644 index 00000000..fe351051 --- /dev/null +++ b/architectures/firmware/sof-zephyr/zephyr_api_integration.rst @@ -0,0 +1,86 @@ +.. _zephyr-api-integration: + +Zephyr API Integration +###################### + +Most of the interfaces between the application (audio) layer and the kernel are +aggregated inside the part of the legacy SOF architecture called "lib". The +interfaces are exposed by the *lib*, declared in header files in +*src/include/sof/lib* directory. Implementation is located in the *src/lib* +except for platform and architecture specific functions that are delegated to +*platform* and *arch* parts respectively. + +.. uml:: images/sof_lib.pu + :caption: Legacy SOF Lib + +Zephyr replaces *lib* and other architecture and platform specific code, +everything below the *app* & *mpp* layers. + +In order to unify the access to the lower parts from the *app* and *mpp*, the +library header files provides now a definition of unified interface but some +changes are introduced to the original set of APIs and/or the implementation. + +Let's have a look at possible cases. + +**Case #1: New Zephyr API replaces 1:1 legacy SOF lib API** + +If there is a Zephyr version of a SOF legacy API which provides exactly the same +functionality as the original function but has a different name, the Zephyr +function name is used as a replacement in the SOF *app* and *mpp* code. It +causes direct linking and call into the Zephyr code optimizing FW size and +performance when SOF is built with Zephyr. Building with legacy SOF *lib* +requires an implementation or just a simple adapter for the new Zephyr API. It +may or may not slightly increase the size and decrease the performance of the +legacy SOF. + +.. code-block:: c + + // src/include/sof/lib.cpu.h + #ifdef __ZEPHYR__ + #include + #else + // was: static inline int cpu_is_core_enabled(int id) + static inline bool arch_cpu_active(int id) + { + arch_cpu_is_core_enabled(id); + } + #endif /* __ZEPHYR__ */ + +**Case #2: Legacy SOF lib API requires multi-step implementation for Zephyr +configuration** + +There may be a case when SOF legacy API is implemented by a single function +provided by the *arch* or another package and there is no 1:1 API available in +Zephyr to replace that. In this case, the API is implemented in the *lib-zephyr* +part based on the native Zephyr APIs. + +.. code-block:: c + + // src/include/sof/lib/cpu.h + #ifdef __ZEPHYR__ + void cpu_disable_core(int id); + #else + static inline void cpu_disable_core(int id) + { + arch_cpu_disable_core(id); + } + #endif /* __ZEPHYR__ */ + + // src/lib-zephyr/cpu.c + void cpu_disable_core(int id) + { + // ... calls to Zephyr APIs + } + +**Case #3: Legacy SOF lib API is implemented completely inside the lib and does +not have any replacement in Zephyr** + +The agent code might be an example of the library functions that are common and +must be compiled and linked together with either legacy SOF *lib* or +*lib-zephyr*. + +The dependencies between the SOF *lib*, *lib-zephyr*, and *zephyr* are +illustrated in the below figure. + +.. uml:: images/sof_lib_zephyr.pu + :caption: SOF Lib + Zephyr diff --git a/architectures/host/index.rst b/architectures/host/index.rst index 30d8bb2a..a7a3cd13 100644 --- a/architectures/host/index.rst +++ b/architectures/host/index.rst @@ -3,64 +3,7 @@ Host Architecture ################# -SOF Driver Architecture -======================= - -|SOF| can either operate as a standalone firmware or alongside a host OS -driver for configuration and control. The |SOF| OS driver is responsible for -loading firmware, loading configuration and managing firmware use cases. -Currently |SOF| has a driver for the Linux OS. - -The |SOF| driver code is dual licensed GPLv2 and BSD and this means the user -can choose which licence they want to use (either BSD or GPLv2). The driver -stack is designed with maximum resuse so that large portions of it can be -taken and integrated into other OSs or RTOSs. - -.. seealso:: - - Refer to the indepth :ref:`sof_driver_arch` document for more information. - -Linux Driver ------------- - -The Linux ASoC driver is upstream in the Linux kernel from v5.2 onwards. The -architecture for |SOF| is shown in the diagram below. The driver -architecture is split into four layers, like a protocol stack, each with a -different purpose. - -#. **Machine driver.** The ASoC machine driver does all the - machine/board audio hardware integration. It also glues the platform - driver and drivers for any codec(s) together so they appear as a single - ALSA sound card. |SOF| can reuse existing upstream machine drivers (as - only the platform name needs to be changed) or can have bespoke machine - drivers. *Linux OS specific - GPLv2 only.* - -#. **Generic PCM Driver.** The PCM driver creates ALSA PCMs, DAPM, and - kcontrols based on the topology data loaded at run time. The PCM driver - also allocates buffers for DMA and registers with run time PM. It is - architecture and platform generic code. Generic for all **platforms**, - but OS specific - GPLv2 only.* - -#. **Generic IPC driver.** The IPC driver is the messaging bridge - between the host and DSP and defines the messaging ABI and protocol. It - is architecture and platform generic code. *Generic OS - BSD or GPLv2.* - -#. **DSP Platform Driver.** The platform driver is a platform specific - driver that abstracts the low level platform DSP hardware into a common - generic API that is used by the upper layers. This includes code that - will initialize the DSP and boot the firmware. *Generic OS - BSD or GPLv2.* - - - .. figure:: ../images/driver-arch-diag.png - :align: center - :alt: SOF Driver Architecture - :width: 800px - - `Sound Open Firmware Linux Driver Architecture. The right-hand side of - the diagram shows the mailbox/doorbell mechanism and the DSP. - The Linux PCM and IPC drivers can be reused without modification on every - platform. Runtime differentiation can be achived by regenerating - topology data to match device use cases whilst static hardware - differentiation is achieved via the machine driver and/or ACPI / Device - Tree configuration.` +.. toctree:: + :maxdepth: 1 + linux_driver/architecture/sof_driver_arch.rst diff --git a/developer_guides/linux_driver/architecture/images/sof-driver-arch-1.png b/architectures/host/linux_driver/architecture/images/sof-driver-arch-1.png similarity index 100% rename from developer_guides/linux_driver/architecture/images/sof-driver-arch-1.png rename to architectures/host/linux_driver/architecture/images/sof-driver-arch-1.png diff --git a/developer_guides/linux_driver/architecture/images/sof-driver-arch-2.png b/architectures/host/linux_driver/architecture/images/sof-driver-arch-2.png similarity index 100% rename from developer_guides/linux_driver/architecture/images/sof-driver-arch-2.png rename to architectures/host/linux_driver/architecture/images/sof-driver-arch-2.png diff --git a/developer_guides/linux_driver/architecture/sof_driver_arch.rst b/architectures/host/linux_driver/architecture/sof_driver_arch.rst similarity index 94% rename from developer_guides/linux_driver/architecture/sof_driver_arch.rst rename to architectures/host/linux_driver/architecture/sof_driver_arch.rst index 4d478125..61f774f9 100644 --- a/developer_guides/linux_driver/architecture/sof_driver_arch.rst +++ b/architectures/host/linux_driver/architecture/sof_driver_arch.rst @@ -1,7 +1,17 @@ .. _sof_driver_arch: -SOF Driver Architecture -####################### +SOF Linux Driver Architecture +############################# + +|SOF| can either operate as a standalone firmware or alongside a host OS +driver for configuration and control. The |SOF| OS driver is responsible for +loading firmware, loading configuration and managing firmware use cases. +Currently |SOF| has a driver for the Linux OS. + +The |SOF| driver code is dual licensed GPLv2 and BSD and this means the user +can choose which licence they want to use (either BSD or GPLv2). The driver +stack is designed with maximum resuse so that large portions of it can be +taken and integrated into other OSs or RTOSs. .. contents:: :local: @@ -105,7 +115,7 @@ SOF on both the host and the DSP serializes the sending of their IPC messages. T SPI === -IPC messages have the same structure as in the PCI case, but they are sent and received over an SPI bus. The SPI transfer is always initiated by the SPI master, which is the host. Therefore, the DSP cannot send asynchronous messages to the host using only the SPI bus. To overcome this limitation, an additional GPIO line is used by the DSP to trigger an interrupt on the host to request it to read out an IPC message. Support for such devices is still experimental in SOF. Details will be added later. +IPC messages have the same structure as in the PCI case, but they are sent and received over an SPI bus. The SPI transfer is always initiated by the SPI provider, which is the host. Therefore, the DSP cannot send asynchronous messages to the host using only the SPI bus. To overcome this limitation, an additional GPIO line is used by the DSP to trigger an interrupt on the host to request it to read out an IPC message. Support for such devices is still experimental in SOF. Details will be added later. iMX IPC ======= @@ -124,7 +134,13 @@ IPC messages are divided into several groups: global reply, topology, power mana PCM Driver ********** -The SOF PCM driver creates ALSA PCMs, DAPM, and kcontrols based on the `topology `_ data loaded at runtime. The PCM driver also allocates buffers for DMA and registers with runtime PM. It contains architecture- and platform-generic code. The PCM driver implements the low-level functions defined by the ALSA PCM middle layer in ``struct snd_pcm_ops``. These functions implement the platform-generic parts and invoke platform-specific ops to access the hardware. +The SOF PCM driver creates ALSA PCMs, DAPM, and kcontrols based on the +:ref:`topology` data loaded at runtime. The PCM driver also allocates +buffers for DMA and registers with runtime PM. It contains architecture- +and platform-generic code. The PCM driver implements the low-level +functions defined by the ALSA PCM middle layer in ``struct +snd_pcm_ops``. These functions implement the platform-generic parts and +invoke platform-specific ops to access the hardware. When the machine driver is probed and the sound card is registered, the SOF PCM component driver gets probed when the dai links in the sound card are bound to the card. The SOF PCM component probe callback loads the topology file for the DUT. The SOF topology defines the audio processing pipelines, FE DAIs, and the BE DAI configuration for the BE dai links defined in the machine driver. Therefore, it is important to make sure that the DAI link IDs for the BE DAIs are identical in the topology and the machine driver. A mismatch in the DAI links ID will cause the sound card registration to fail. @@ -309,7 +325,7 @@ The SOF HDMI/DP audio codec driver handles DP-MST audio streams transparently, a Kernel Configuration/Kconfig **************************** -Refer to the `README `_ file of the SOF kconfig `repository `_. +Refer to the `README `_ file of the SOF kconfig repository. Debug Options ************* @@ -343,5 +359,6 @@ Force IPC Position Sending position update IPC from the firmware to the host is a generic method to generate period interrupts to meet the requirement from the ALSA IRQ mode (e.g. ``snd_pcm_period_elapsed()``). On some HDA-integrated platforms (e.g. Intel SKL+ ones), this interrupt can be generated using the `HDA `_ period IOC (interrupt on complete) and the real-time buffer pointers can be read back from the DPIB (DMA Pointer In Buffer). On these platforms, the position update IPC is only the fallback choice and is not used by default. -In order to debug issues with IOC/DPIB, the force IPC position kernel debug config can be selected. On Intel SKL- platforms, the stream position update IPC is used whether or not this option is selected. - +In order to debug issues with IOC/DPIB, the force IPC position kernel +debug config can be selected. On Intel SKL- platforms, the stream +position update IPC is used whether or not this option is selected. diff --git a/architectures/index.rst b/architectures/index.rst index 246cb14c..a82a0be0 100644 --- a/architectures/index.rst +++ b/architectures/index.rst @@ -1,6 +1,6 @@ .. _architectures: -Supported Architectures +Architecture ####################### SOF is intended to run on many different hardware architectures and is therefore @@ -8,11 +8,13 @@ not coupled to any particular DSP or host hardware architecture. The SOF |TSC| ensures that any DSP or host architecture specific code is partitioned to reside in architecture-specific directories with generic APIs to common code. -This section outlines the architecture at a high level, however the source code +This section outlines the architecture at a high level; however, the source code should always be consulted for the low level details. .. toctree:: :maxdepth: 2 host/index - dsp/index + firmware/index + + diff --git a/conf.py b/conf.py old mode 100644 new mode 100755 index d5bbb2c5..c64f3493 --- a/conf.py +++ b/conf.py @@ -30,8 +30,13 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. + + +# FIXME: blockdiag is orphaned and not compatible with Pillow anymore: +# https://github.com/thesofproject/sof-docs/issues/472 extensions = ['breathe', 'sphinx.ext.graphviz', 'sphinxcontrib.plantuml', - 'sphinx.ext.todo', 'sphinx.ext.extlinks', + 'sphinx.ext.todo', 'sphinx.ext.extlinks', 'sphinxcontrib.blockdiag', + 'sphinxcontrib.jquery' ] @@ -45,13 +50,24 @@ plantuml = 'java -jar ' + os.path.join(os.path.abspath('.'), 'scripts/plantuml.jar') \ + ' -config ' + os.path.join(os.path.abspath('.'), 'scripts/plantuml.cfg') -# Temporarily set this to "none" for a build without diagrams but ~= 15 -# times faster from scratch. +# More than half of the time building from scratch is consumed by the +# sphinx extension "breathe" that converts doxygen XML. Most of the rest +# is consumed by plantUML here. So you can set the variable below to +# 'none' for an _almost instant_ sphinx build! (with zero UML diagram +# and no doxygen). 'none' requires sphinxcontrib.plantuml>=0.11 but +# pre-0.11 errors can be ignored. (of course don't disable UML when +# you're touching UML stuff) plantuml_output_format = 'svg' # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] +# Fixes "WARNING: Error when parsing function declaration." +c_id_attributes = ["__sparse_cache"] +# Not clear why Sphinx thinks some C files are C++ +cpp_id_attributes = c_id_attributes +# cpp_paren_attributes = ["_ALIAS_OF", "__printf_like"] + # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # @@ -63,14 +79,14 @@ # General information about the project. project = u'SOF Project' -copyright = u'2020, SOF Project.' +copyright = u'2024, SOF Project' author = u'SOF Project developers' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. -version = release = "0.1" +version = release = "2.11.0" # # The short X.Y version. @@ -83,7 +99,7 @@ # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = None +language = 'en' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -116,12 +132,10 @@ sys.stderr.write('Warning: sphinx_rtd_theme missing. Use pip to install it.\n') else: html_theme = "sphinx_rtd_theme" - html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] html_theme_options = { 'canonical_url': '', - 'analytics_id': '', + 'analytics_id': 'GTM-M4BL5NF', 'logo_only': False, - 'display_version': True, 'prev_next_buttons_location': 'None', # Toc options 'collapse_navigation': False, @@ -163,13 +177,18 @@ SOF_GIT = 'https://github.com/thesofproject' +# "/sof/tree/branch/dir" is for directories and "/sof/blob/branch/file" is +# for files. Fortunately github automatically redirects one to the other +# as required. extlinks = { - 'git-sof-master': - (SOF_GIT + '/sof/tree/master/%s', ""), - 'git-sof-docs-master': - (SOF_GIT + '/sof-docs/tree/master/%s', ""), + 'git-sof-mainline': + (SOF_GIT + '/sof/tree/master/%s', None), + 'git-sof-docs-mainline': + (SOF_GIT + '/sof-docs/tree/master/%s', None), + 'git-sof-kconfig': + (SOF_GIT + '/kconfig/tree/master/%s', None), 'git-alsa': - ('https://git.alsa-project.org/?p=%s.git', ""), + ('https://git.alsa-project.org/?p=%s.git', None), } # Add any paths that contain custom static files (such as style sheets) here, @@ -178,7 +197,12 @@ html_static_path = ['static'] def setup(app): - app.add_stylesheet("sof-custom.css") +# add_stylesheet() was renamed to add_css_file() in sphinx 1.8 released +# in September 2018. add_stylesheet() will be removed in sphinx 4.0 + try: + app.add_css_file('sof-custom.css') + except AttributeError: + app.add_stylesheet('sof-custom.css') # Custom sidebar templates, must be a dictionary that maps document names # to template names. diff --git a/contribute/contribute_guidelines.rst b/contribute/contribute_guidelines.rst index d102e4b1..6b3bdfa0 100644 --- a/contribute/contribute_guidelines.rst +++ b/contribute/contribute_guidelines.rst @@ -20,8 +20,7 @@ software continues to be available under the terms that the author desired. The SOF project uses a BSD-3-Clause license, as found in the -`LICENSE `__ -in the project's GitHub repo. +:git-sof-mainline:`LICENCE` file in the project's GitHub repo. A license tells you what rights you have as a developer, as provided by the copyright holder. It is important that the contributor fully diff --git a/contribute/doc_guidelines.rst b/contribute/doc_guidelines.rst index f2aa0d96..caee341b 100644 --- a/contribute/doc_guidelines.rst +++ b/contribute/doc_guidelines.rst @@ -386,7 +386,7 @@ the first non-white space in the preceding line. For example: 1. And for numbered list items, the continuation line should align with the text of the line above. - .. code-block:: + .. code-block:: none The text within a directive block should align with the first character of the directive name. diff --git a/contribute/process/abiprocess.rst b/contribute/process/abiprocess.rst index 376ecb53..91308bb1 100644 --- a/contribute/process/abiprocess.rst +++ b/contribute/process/abiprocess.rst @@ -13,11 +13,10 @@ defined in: - src/include/ipc/ - src/include/user/ -SOF ABI versioning is defined in firmware source code documentation `FW Source kernel/abi.h`_ +SOF ABI versioning is defined in firmware source code documentation: +:git-sof-mainline:`src/include/kernel/abi.h` -.. _FW Source kernel/abi.h: https://github.com/thesofproject/sof/blob/master/src/include/kernel/abi.h#L8 - -Change Process +Change process ************** When a firmware change requires extending or modifying the public @@ -32,7 +31,7 @@ state diagram: .. _ABI Change Tracker: https://github.com/orgs/thesofproject/projects/2 The pull requests are classified in GitHub using the following -official `ABI Change Tracker`_ +official `ABI Change Tracker`_. .. uml:: images/abiprocess.pu :caption: ABI process state diagram @@ -42,6 +41,28 @@ kernel side shall include code that deals with older firmware and topology files. See :ref:`development_tree` for kernel side documentation. +Document modified fields +************************ + +When the interface is extended with a backwards-compatible (MINOR) interface +change, each added or modified interface field must be documented +with a reference to the interface version where the change was +first implemented. + +Some code examples: + +.. code-block:: c + + struct foo { + uint8_t group_id; /**< group ID, 0 means no group (ABI3.17) */ + } __attribute__((packed)); + +.. code-block:: c + + enum bar { + EXT_MAN_ELEM_FOO_DATA = 7, /**< ABI3.18 */ + }; + ABI change approvers ******************** diff --git a/contribute/process/docbuild.rst b/contribute/process/docbuild.rst index 46d23c94..06570e03 100644 --- a/contribute/process/docbuild.rst +++ b/contribute/process/docbuild.rst @@ -124,6 +124,11 @@ creating drawings: drawing syntax into an image. You need to have a Java runtime environment (JRE) installed when generating documentation. +There is a sof-docs Docker image recipe that can be used as an alternative to +installing the documentation tools on local OS. If you choose to use Docker then +please skip the tools installation steps and go directly to +:ref:`run_documentation_processors` + Depending on your Linux version, install the following tools: * For Ubuntu use: @@ -131,14 +136,14 @@ Depending on your Linux version, install the following tools: .. code-block:: bash sudo apt-get install doxygen python3-pip python3-wheel make \ - default-jre graphviz + default-jre graphviz cmake ninja-build * For Fedora use: .. code-block:: bash sudo dnf install doxygen python3-pip python3-wheel make \ - java graphviz + java graphviz cmake ninja-build For either Linux environment, install the remaining python-based tools: @@ -148,16 +153,30 @@ tools: cd ~/thesofproject/sof-docs pip3 install --user -r scripts/requirements.txt -.. note:: The :git-sof-docs-master:`scripts/requirements.txt` file hardcodes +.. note:: The :git-sof-docs-mainline:`scripts/requirements.txt` file hardcodes versions using ``==``, which may not be compatible with your other projects. In that case you can either setup a Python ``virtualenv`` or - try the unsupported :git-sof-docs-master:`scripts/requirements-lax.txt` + try the unsupported :git-sof-docs-mainline:`scripts/requirements-lax.txt` (more details inside this file): .. code-block:: bash PIP_IGNORE_INSTALLED=0 pip3 install --user -r scripts/requirements-lax.txt + The hardcoded package versions might need additional libraries installed + in order to compile them. For example, to resolve the following error: + + .. code-block:: bash + + ERROR: Could not build wheels for pillow, which is required to install pyproject.toml-based projects + + you should install: + + .. code-block:: bash + + sudo apt install libjpeg-dev zlib1g-dev + + For Windows, install the needed tools manually: * Python (3.7+) from https://www.python.org/downloads/ @@ -202,20 +221,25 @@ another ``make html`` and the output layout and style is changed. The ``read-the-docs`` theme is installed as part of the ``requirements.txt`` list above. +.. _run_documentation_processors: + Run documentation processors **************************** The sof-docs directory contains all the .rst source files, extra tools, and Makefile for generating a local copy of the SOF technical documentation. -* Generate the HTML output by using the following commands: +You can generate the HTML documentation by using local build tools (1) or by +Docker image (2) + +1. Generate the HTML output by using the following commands (**local build**): .. code-block:: bash cd thesofproject # API documentation (Doxygen) - cmake -S sof/doc -B sof/doc -GNinja - ninja -C sof/doc -v doc + cmake -S sof/doc -B sof/build_doxygen -GNinja + ninja -C sof/build_doxygen -v doc # UML and reStructuredText make -C sof-docs VERBOSE=1 html @@ -226,7 +250,29 @@ diagrams. When done, view the HTML output with your browser, starting at If your changes are not related to any UML diagram, you can build more than 10 times faster from scratch by temporarily changing the -``plantuml_output_format`` line in :git-sof-docs-master:`conf.py`. +``plantuml_output_format`` line in :git-sof-docs-mainline:`conf.py`. + +2. Generate the HTML output by using the following commands (**Docker build**): + + .. code-block:: bash + + cd thesofproject + # Build both sof (Doxygen) and sof-docs UML and reStruredText + ./sof-docs/scripts/docker_build/docker-build.sh + +The docker build script will copy the sof and sof-docs source code from host +working directory to image, build the documentation and when completed copy back +output to the host (./sof-docs/_build) + +The first docker build run will take more time due to image creation and +installation of all the necessary build tools. Each next build is much faster +and can additionally be speed up by selecting only sof-docs to build: + + .. code-block:: bash + + cd thesofproject + # Re-build only sof-docs + ./sof-docs/scripts/docker_build/docker-build.sh docs Publish content *************** @@ -259,7 +305,7 @@ publishing. .. note:: In some situations it is necessary to clean all the files and build from - the very beginning. To do this, use the ``make clean`` command. + the very beginning. To do this, use the ``make -C sof-docs clean`` command. Installation troubleshooting **************************** diff --git a/developer_guides/add_new_arch.rst b/developer_guides/add_new_arch.rst new file mode 100644 index 00000000..b0ba708c --- /dev/null +++ b/developer_guides/add_new_arch.rst @@ -0,0 +1,33 @@ +.. _add_new_arch: + +Adding a new DSP architecture to SOF +==================================== + +This is not yet a guide for architecure porting, but in general, you can add +support for a new DSP architectures to SOF in the following two ways: + +- Write a new Hardware Abstraction Layer (HAL) for your DSP. +- Use an existing RTOS that supports your DSP architecture as a HAL for SOF. + +Both methods require a working compiler for the new DSP architecture and +preferrably an emulation environment or hardware debugger to help with the +bringup and debug. + +Method 1 - New HAL +------------------ + +The main work in adding the new architecture HAL is duplicating and porting the +src/arch directory to your new architecture. The code in the architecture +directory mainly deals with architecture abstraction and initialization of any +architecture IP like MMU, IRQs and caches alongside providing optimized +versions of some common C functions (memcpy, memset, etc) for that architecture. +Adding a new architecture also usually means adding a new host platform too. + +Method 2 - Use existing RTOS +---------------------------- + +This method involves creating a HAL by wrapping the RTOS functions used by SOF +as thinly as possible (i.e. to compile out). It also means removing unused code +from the SOF build in order to use the RTOS version if desireable i.e. +allocator, schedulers, messaging etc. The final stage is to link the SOF audio +code to the RTOS. diff --git a/developer_guides/debugability/coredump-reader/index.rst b/developer_guides/debugability/coredump-reader/index.rst index 1400a073..1ca6d5d2 100644 --- a/developer_guides/debugability/coredump-reader/index.rst +++ b/developer_guides/debugability/coredump-reader/index.rst @@ -3,6 +3,10 @@ Coredump-reader ############### +NOTE: These instructions do not work with SOF running on Zephyr, +please refer to +https://docs.zephyrproject.org/latest/services/debugging/coredump.html + Tool for processing FW stack dumps. In verbose mode it prints the stack leading to the core dump including DSP registers and function calls. It outputs unwrapped gdb command function call addresses to human readable @@ -30,3 +34,53 @@ We read from dump file into sof-coredump-reader.py, then we pipe its output to x .. code-block:: bash ./sof-coredump-to-gdb.sh sof-apl dump_file + +Usage with Linux SOF Driver +*************************** + +If a core dump occurs after a DSP error, the Linux SOF driver allows +accessing the dump via debugfs. Consider the following example of capturing +the dump file and processing it with coredump-reader: + +.. code-block:: bash + + dut> cat /sys/kernel/debug/sof/exception >dsp-coredump + # transfer file to host + host> sof/tools/coredumper/sof-coredump-reader.py -v -l 4 -i dsp-coredump -o dsp-coredump.gdb + host> xt-gdb sof/build_tlg_xcc/sof --command=dsp-coredump.gdb + [cut] + $1 = "Exception location:" + 0xbe02fb29 is in ipc_glb_debug_message (/home/user/sof/src/ipc/handler-ipc3.c:1371). + [cut] + $2 = "backtrace" + #0 0xbe051b00 in literals () + #1 0xbe04e277 in dump_stack (p=3187705884, addr=0x1cc6c29b, offset=3270769662, limit=380, stack_ptr=0x1) at /home/user//sof/src/arch/xtensa/include/arch/lib/cache.h:79 + #2 0xbe04e2f7 in panic_dump (p=233492486, panic_info=0x0, data=0xbe0a4130) at /home/user/sof/src/arch/xtensa/include/arch/debug/panic.h:45 + #3 0xbe02dfd9 in exception () at /home/user/sof/src/arch/xtensa/init.c:115 + #4 0xbe050a28 in _GeneralException () + #5 0xbe02fb29 in ipc_glb_debug_message (header=394016) at /home/user/sof/src/ipc/handler-ipc3.c:1373 + [cut] + (xt-gdb) info all-registers + pc 0xbe051b00 0xbe051b00 + ar0 0x0 0 + ar1 0xbe00a044 -1107255228 + ar2 0x10000 65536 + +Notes: + +- Coredump-reader only works with the xcc toolchain. + +- If the Linux kernel fails to probe, the exception file cannot be read. + +- To prevent runtime suspend from powering off the DSP and erasing + the exception data, perform one of the following steps: + + - Set the ``CONFIG_SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT`` option in the + kernel to ensure DSP is left powered on if a DSP crash occurs. + + - Disable runtime power management (PM) with a module parameter. + For example, for PCI devices:: + options sof_pci_dev sof_pci_debug=1 + +- The DSP core dump information is also printed to kernel dmesg, but + sof-coredump-reader.py cannot parse this core dump format. diff --git a/developer_guides/debugability/index.rst b/developer_guides/debugability/index.rst index 8c8f5d2a..ed686043 100644 --- a/developer_guides/debugability/index.rst +++ b/developer_guides/debugability/index.rst @@ -11,3 +11,5 @@ Debugability coredump-reader/index probes/index ri-info/index + perf-counters/index + shell/index diff --git a/developer_guides/debugability/logger/index.rst b/developer_guides/debugability/logger/index.rst index 3b18241b..17e41d1c 100644 --- a/developer_guides/debugability/logger/index.rst +++ b/developer_guides/debugability/logger/index.rst @@ -32,7 +32,7 @@ Usage sof-logger -t Get traces from "/sys/kernel/debug/sof/trace" instead of from the default "/sys/kernel/debug/sof/etrace" -p Get traces from stdin instead of the default "/sys/kernel/debug/sof/etrace" -c clock Set the timestamp clock in MHz --e Enable checking the firmware version with the default verification file "/sys/kernel/debug/sof/fw_version" +-n Disable checking the firmware version with the default verification file "/sys/kernel/debug/sof/fw_version" -v ver_file Enable checking the firmware version with the ver_file file instead of the default: "/sys/kernel/debug/sof/fw_version" -s Take a snapshot of state -r Less formatted output for chained log processors @@ -40,15 +40,18 @@ Usage sof-logger -f precision Set timestamp precision -g Hide timestamp -d Dump ldc information +-F filter Update trace filtering -Examples: +Examples +-------- -- Gets traces from the "/sys/kernel/debug/sof/etrace" file; verifies the - fw_version with "/sys/kernel/debug/sof/fw_version" and prints logs to stdout +- Gets traces from the "/sys/kernel/debug/sof/etrace" file; disable compatibility + check between given ldc_file with fw_version saved in default location + and prints logs to stdout .. code-block:: bash - sof-logger -l ldc_file -e + sof-logger -l ldc_file -n - Gets traces from the "/sys/kernel/debug/sof/etrace" file; verifies the fw_version with the ver_file file and prints logs to stdout @@ -71,7 +74,7 @@ Examples: sof-logger -l ldc_file -o out_file -- Get traces from the "/sys/kernel/debug/sof/trace" file and prints logs to +- Get traces from the "/sys/kernel/debug/sof/trace" file and prints logs to stdout .. code-block:: bash @@ -123,3 +126,149 @@ Examples: .. code-block:: bash sof-logger -l ldc_file -d + + +.. note:: + debugfs files used by sof-logger: + + - ``etrace``: direct access to the shared TRACE window of the SOF firmware + - ``trace``: using DMA to stream debug trace information from SOF firmware (on + distribution kernels ``sof_debug=1`` module option for ``snd_sof`` might be + needed if the /sys/kernel/debug/sof/trace file is not present) + +.. note:: + If sof-logger is started (or restarted) while firmware is active, the initial + trace messages might be incomplete. This can happen as current (as of SOF version + 1.9) trace is implemented with a ringbuffer between firmware and the kernel driver. + When sof-logger is started, kernel will always start to read the ringbuffer from + 0 position, independently of firmware state. If firmware write pointer just wrapped + around when sof-logger is started, sof-logger will only show the traces after + the wrap. Firmware write position is also reset whenever firmware is booted, + including runtime suspend and resume. To capture traces over runtime suspend + events, the kernel trace interface will signal end of file at runtime suspend. + When sof-logger notices the end of file marker, it will reopen the trace + file and start reading from position 0 and thus be in sync with the firmware + when it is resumed. + + Sometimes error messages about dropped logs are printed. If that is a problem, + increasing DMA_TRACE_LOCAL_SIZE in the relevant platform.h file can be helpful. + +Trace filtering +*************** + +Current log levels can be changed for any instance of a component at +runtime. To do so, use the `-F` option and the following argument format: + + ="[, ]" + +Where ** can be one of the following: + +- ``c`` / ``critical`` +- ``e`` / ``error`` +- ``w`` / ``warning`` +- ``i`` / ``info`` +- ``d`` / ``debug`` +- ``v`` / ``verbose`` + +After the **=** character, all components are listed in a comma-separated +format. Each component begins with *name* followed by an optional *instance* +description. + +The list of possible component names comes from the UUID declaration. +Refer to the :ref:`uuid-api` for more detailed information. Use the ``-d`` +flag in the logger to list component names from the ``ldc`` file content. + +Example output: + + .. code-block:: bash + + $./sof-logger -d log/sof-cnl.ldc + logger ABI Version is 5:2:0 + ldc_file ABI Version is 5:2:0 + + Components uuid dictionary size: 824 bytes + Components uuid base address: 0x1FFFA000 + Components uuid entries: + ADDRESS UUID NAME + 0x1FFFA000 <8b9d100c-6d78-418f-90a3-e0e805d0852b> host + 0x1FFFA01C pipe-task + 0x1FFFA03C <34dc0385-fc2f-4f7f-82d2-6cee444533e0> volume-task + 0x1FFFA05C volume + 0x1FFFA078 src + 0x1FFFA134 dai + -------------------------------------------------- cnt: 6 + +A special wildcard - ``*`` - can be used to apply a given trace level to +each component. + +Instance descriptions can have one of the following forms: + +- ``*`` - each component instance +- ``X.*`` - each component on selected pipeline *X* +- ``X.Y`` - component on pipeline *X* with id *Y* + +Trace level changes work in the same order as options given in a command line, and a new set overwrites old values. +It allows you to easily enable verbose logs only for selected components and keep the lowest possible log level (critical) for +others, as shown in the following example: + + sof-logger -l ldc_file -t -Fcritical=* -Fverbose="dai*, volume1.1" + +A similar example may be prepared for components on a particular pipeline: + + sof-logger -l ldc_file -t -Fc=* -Fv=*1.* + +Verbose and debug log levels +---------------------------- + +To enable verbose and debug trace messages, select the "Trace->Trace verbose" option in the firmware build +menuconfig (in addition to setting the proper log levels as described above). + +Disabling DSP power gating +-------------------------- + +After a firmware reset (such as after power gating in suspend mode) custom filter settings will be lost. +Thus consider disabling power gating during your debug session. The way this is done is slightly different on every platform, +two examples follow: + +1. Intel PCI based: + +.. code-block:: bash + + sudo su + echo on >/sys/devices/pci0000\:00/0000\:$(lspci -nn | grep "audio contoller" | awk '{print $1;}')/power/control + +2. NXP imx8mp: + +.. code-block:: bash + + sudo su + echo on>/sys/class/devlink/platform:power-domains:audiomix-pd--platform:3b6e8000.dsp/consumer/power/control + +To re-enable automatic suspend use ``echo auto``, the current status can be read from the runtime_status file in these sysfs directories. + +Trace filtering details +----------------------- + +* The filtering mechanism occurs on the firmware side so, after changing the + log level to verbose for each component, the DSP can be overwhelmed by + tracing. + +* Core functionality is provided by the DSP, so filtering does not work in + offline mode - during conversion in a previously saved input file. + +* The trace filtering affects only traces sent after the filter setup, + so traces already stored on the kernel side are not affected. If a certain log level is needed before a filter has been setup the DECLARE_TR_CTX() + macro at the beginning of the respective component's source file can be adapted. + +* Filters are set up incrementally, so when loggers are run twice with + different settings, then filters from the first run will not be restored to + the default state but will be replaced by a new one. Active trace filters are stored in the firmware runtime memory. To reset the filters to the + default state, a firmware reset is needed. + +* Communication between the firmware and logger occurs through the kernel debugfs. The logger writes new trace settings to ``sys/kernel/debug/sof/filter``. + These will be used to create *IPC* messages with new trace levels. A simple text data format is used: + + ``log1_level uuid1_id pipe1_id comp1_id; [log2_level uuid2_id pipe2_id comp2_id;]\n`` + + Any unused uuid_id should be set here to 0; other unused fields should be + set to -1. ``log_level`` must always be set to a valid value that represents ``LOG_LEVEL_*`` defined values. diff --git a/developer_guides/debugability/perf-counters/index.rst b/developer_guides/debugability/perf-counters/index.rst new file mode 100644 index 00000000..4e1ea77b --- /dev/null +++ b/developer_guides/debugability/perf-counters/index.rst @@ -0,0 +1,63 @@ +.. _dbg-perf-counters: + +Performance Counters +#################### + +Firmware can be configured to trace performance counters for each processing +component. Each performance trace entry includes: + +- component UUID + +- peak platform and cpu timer ticks consumed during component's copy processing + +.. note:: + Performance timestamp macros use both the platform timer and cpu timer in case + the latter is not always running. + +Performance Counters usage +************************** + +Currently, you can only enable performance counters statically during FW build in +one of two ways: + +- Select **Performance counter** from **Debug** menu using ``make menuconfig``. + +- Add ``CONFIG_PERFORMANCE_COUNTERS=y`` to specific FW config, for + example, tigerlake_defconfig. + + +After you enable the performance counters, they are logged periodically for each +active component with the pipeline period frequency. + +Example +******* + +Performance counter trace example: + + .. code-block:: bash + + [ 8481257.031250] ( 51.562500) c0 demux 1.2 src/audio/pipeline.c:206 perf comp_copy peak plat 782 cpu 8136 + +``demux 1.2`` - processing component + +``plat 782`` - peak platform cycles consumed + +``cpu 8136`` - peak CPU cycles consumed + +MCPS calculation +---------------- + +The equation below illustrates how to calculate component MCPS (million cycles +per second) consumption. + + .. code-block:: bash + + MCPS = cpu_ticks / (pipeline_period[s] * 10^6) + + // for common pipeline_period = 1ms it can be simplified to + MCPS = cpu_ticks / 1000 + +In the trace example above, cpu_ticks = 8136, the pipeline_period is 1ms so the +demux consumption equals 8,136 MCPS + + diff --git a/developer_guides/debugability/probes/index.rst b/developer_guides/debugability/probes/index.rst index bc33e3d2..98e6dedc 100644 --- a/developer_guides/debugability/probes/index.rst +++ b/developer_guides/debugability/probes/index.rst @@ -12,19 +12,84 @@ from each buffer. Requirements ************ +.. _install-tinycompress: + - Install `tinycompress `_ (crecord tool) Enabling Probes *************** -- Enable the Linux kernel configuration option: ``DEBUG_FS`` -- Enable the Probe module in the SOF firmware kconfig using this command: +.. _kernel-side: + +Kernel side +=========== + +- The probes support is enabled by Kconfig on supported platforms as a SOF client + driver, check the kernel config for ``SND_SOC_SOF_DEBUG_PROBES``. + The debugfs also needs to be enabled for the probes to be usable. + + .. code-block:: bash + + CONFIG_DEBUG_FS=y + +- The probes client needs to be enabled via the 'enable' module parameter (e.g. ``/etc/modprobe.d/sof.conf``): + + .. code-block:: bash + + options snd_sof_probes enable=1 + + To make sure that the sound card for the probes is consistent between boots, a + card slot can be forced for the module. + For example to use card3, this can be added to the sof.conf file: + + .. code-block:: bash + + options snd slots=,,,snd_sof_probes + + Remove and re-load the driver: + + .. code-block:: bash + + rmmod snd_sof_probes + modprobe snd_sof_probes + + Verify that the card is available (if not, try to reboot): + + .. code-block:: bash + + cat /proc/asound/cards | grep sofprobes + +.. _firmware-side: + +Firmware side +============= - .. code-block:: bash +- The Probe module can be enabled under the 'Probe' menu's 'Probes enabled' prompt (``PROBES``) + To edit the ``kconfig`` use this command: - make menuconfig + .. code-block:: bash -- :ref:`Build the firmware ` + make menuconfig + + The following options available + + Required for audio probes: + + .. code-block:: bash + + CONFIG_PROBE=y # enable probes + CONFIG_PROBE_POINTS_MAX=16 # max probepoints + + Required for logging through probes interface: + + .. code-block:: bash + + CONFIG_LOG_BACKEND_SOF_PROBE=y + CONFIG_ZEPHYR_LOG=y + + Refer to :ref:`Simple logging case` for quick guide to use probes logging interface. + +- Refer to **Step 3 Build firmware binaries** in :ref:`Build SOF from Scratch ` for reference on how to build SOF FW. Note that you do not need to modify the audio topology file. @@ -33,24 +98,168 @@ Data extraction Extraction is the most common use case. It allows for data extraction from the audio component data buffer. It requires starting the compress stream by -starting the crecord tool. One compress stream may contain data from several -extraction probe points which means data parsing is needed at the last stage -of extraction. +starting the crecord tool. Note that one compress stream may contain data +from several extraction probe points which means data parsing is needed at +the last stage of extraction. + +#. Start the crecord tool to prepare the extraction stream (read the crecord + readme file): + + .. code-block:: bash + + crecord -c3 -d0 -b8192 -f4 -FS32_LE -R48000 -C4 /tmp/extract.dat + + Usage: + + .. code-block:: none + + -c : card number; 3 in the above example if a slot is forced + -d : device ID; equals 0 in the above example (probes card only have 1 compressed capture stream). + -b : buffer size. For probes, this is part of the probe + initialization IPC and denotes the extraction stream buffer size on the host side. + -f : fragments is basically number of periods for compress stream. + + The other parameters are "don't-cares" for the driver. + + - Use ``aplay`` to start the playback stream. + - Pause the playback stream. (optional) + - Add probe points via the ``debugfs`` "probe_points" entry in ``/sys/kernel/debug/sof`` + + + For example, to add buffer 7 with a probe point (IPC3): + + .. code-block:: bash + + echo 7,1,0 > probe_points + + Refer to the host side struct sof_probe_point_desc defined in ``sound/soc/sof/probe.h`` + or struct probe_point in ``/src/include/ipc/probe.h`` from sof for the meaning of the triplets: + + .. code-block:: c + + /** + * Description of probe point + */ + struct probe_point { + uint32_t buffer_id; /**< ID of buffer to which probe is attached */ + uint32_t purpose; /**< PROBE_PURPOSE_EXTRACTION or PROBE_PURPOSE_INJECTION */ + uint32_t stream_tag; /**< Stream tag of DMA via which data will be provided for injection. + * For extraction purposes, stream tag is ignored when received, + * but returned actual extraction stream tag via INFO function. + */ + } __attribute__((packed)); + + In the above example, 7 stands for the ``buffer_id`` which is a monolithic + counter value that follows a component instantiation order. + + One way to find out the right instance of ``buffer_id`` is to enable + dev_dbg in ``sound/sound/soc/sof/topology.c`` and search for the widget id + from the following messages: + + .. code-block:: c + + dev_dbg(scomp->dev, + "tplg: widget %d (%s) is ready [type: %d, pipe: %d, pins: %d / %d, stream: %s]\n", + swidget->comp_id, w->name, swidget->id, index, + swidget->num_input_pins, swidget->num_output_pins, + strnlen(w->sname, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) > 0 ? w->sname : "none"); + + On a booted system the list can be acquired with + + .. code-block:: bash + + dmesg | grep "tplg: widget " + ... + snd_sof:sof_widget_ready: sof-audio-pci-intel-tgl 0000:00:1f.3: tplg: widget 2 (gain.1.1) is ready [type: 6, pipe: 1, pins: 1 / 1, stream: none] + snd_sof:sof_widget_ready: sof-audio-pci-intel-tgl 0000:00:1f.3: tplg: widget 3 (mixin.1.1) is ready [type: 4, pipe: 1, pins: 1 / 3, stream: none] + snd_sof:sof_widget_ready: sof-audio-pci-intel-tgl 0000:00:1f.3: tplg: widget 4 (pipeline.1) is ready [type: 32, pipe: 1, pins: 0 / 0, stream: none] + snd_sof:sof_widget_ready: sof-audio-pci-intel-tgl 0000:00:1f.3: tplg: widget 5 (codec0_in) is ready [type: 0, pipe: 1, pins: 0 / 0, stream: none] + snd_sof:sof_widget_ready: sof-audio-pci-intel-tgl 0000:00:1f.3: tplg: widget 6 (iDisp2 Tx) is ready [type: 7, pipe: 1, pins: 0 / 0, stream: none] + snd_sof:sof_widget_ready: sof-audio-pci-intel-tgl 0000:00:1f.3: tplg: widget 7 (dai-copier.HDA.Analog.playback) is ready [type: 27, pipe: 2, pins: 1 / 0, stream: Analog] + ... + + For IPC4 system, the above example looks like this (extraction from gain.1.1): + + .. code-block:: bash + + echo 2,0,0 > probe_points + + The semantics of the buffer_id are quite different on IPC4 system: + + .. code-block:: c + + typedef union probe_point_id { + uint32_t full_id; + struct { + uint32_t module_id : 16; /**< Target module ID */ + uint32_t instance_id : 8; /**< Target module instance ID */ + uint32_t type : 2; /**< Probe point type as specified by ProbeType enumeration */ + uint32_t index : 6; /**< Queue index inside target module */ + } fields; + } __attribute__((packed, aligned(4))) probe_point_id_t; + + .. code-block:: c + + /** + * Description of probe point + */ + struct probe_point { + probe_point_id_t buffer_id; /**< ID of buffer to which probe is attached */ + uint32_t purpose; /**< PROBE_PURPOSE_xxx */ + uint32_t stream_tag; /**< Stream tag of DMA via which data will be provided for injection. + * For extraction purposes, stream tag is ignored when received, + * but returned actual extraction stream tag via INFO function. + */ + } __attribute__((packed, aligned(4))); + +2. Unpause the playback stream. (optional) +#. Close the playback stream when done. +#. Close the crecord tool. + +.. _data-parsing: + +Data parsing +************ + +As previously mentioned, one compress stream can contain data from several +extraction probe points which means data parsing is needed at the final +stage of extraction. The following example demonstrates how to extract data. Use ``-p`` for parse. + +Usage and ouput: + +.. code-block:: bash + + $ ./sof-probes -p /tmp/extract.dat + sof-probes: Parsing file: /tmp/extract.dat + sof-probes: Creating wave file for buffer id: 7 + sof-probes: done + +As a result, ``buffer_7.wav`` is generated in the *tools/build_tools/probes* folder. The wave file can then be examined with your tool of choice +such as ``Audacity``. + +.. _simple-logging-case: + +Simple logging case +******************* + +With the :ref:`crecord` and :ref:`sof-probes` in path, FW built with :ref:`probes logging enabled`, and probes enabled from :ref:`Linux side`, it should be possible to extract the logs with following steps: + +#. crecord has to be started first: + +.. code-block:: bash + + crecord -c3 -d0 -b8192 -f4 -FS32_LE -R48000 -C4 | sof-probes -l + +#. then to enable logs through probes sysfw interface use following commands as root, + + IPC3 system: -- Start the crecord tool to prepare the extraction stream (read the crecord - readme file) -- Use ``aplay`` to start the playback stream -- (optionally) Pause the playback stream -- Add probe points via the ``debugfs`` "probe_points" entry in ``/sys/kernel/debug/sof`` +.. code-block:: bash - For example, to add a buffer with 7 probe points: + echo 0,1,0 > /sys/kernel/debug/sof/probe_points - .. code-block:: bash + IPC4 system: - echo 7,1,0 > probe_points +.. code-block:: bash -- (optionally) Unpause the playback stream -- Close the playback stream when done -- Close the crecord tool -- (optionally) Parse data using the probes app from sof tools; check the - probes app help (-h) for usage info. As a result, you will receive PCM wave files for each probe point. + echo 0,0,0 > /sys/kernel/debug/sof/probe_points diff --git a/developer_guides/debugability/ri-info/output_full_bytes.txt b/developer_guides/debugability/ri-info/output_full_bytes.txt index a6c418e0..66880cfe 100644 --- a/developer_guides/debugability/ri-info/output_full_bytes.txt +++ b/developer_guides/debugability/ri-info/output_full_bytes.txt @@ -1,73 +1,53 @@ -$ python ./sof_ri_info.py --no_colors --full_bytes sof-cnl-v1.4.2.ri -SOF Binary sof-cnl-v1.4.2.ri size 0x46000 +$ python ./sof_ri_info.py --no_colors --full_bytes sof-cnl.ri +SOF Binary sof-cnl.ri size 0x5b000 - CSE Manifest ver 0x101 checksum 0x42 partition name ADSP + CSE Manifest ver 0x101 checksum 0xf1 partition name ADSP - ADSP.man (CSS Manifest) type 0x4 ver 0x10000 date 2020/01/22 + ADSP.man (CSS Manifest) type 0x4 ver 0x10000 date 2020/07/16 Rsvd0 0x0 Modulus size (dwords) 64 - 85 00 e1 68 aa eb d2 07 1b 7c 5e ed d6 e7 e5 f9 c1 0e 47 d4 4c ab 8c f0 e8 ee 8b 40 36 35 58 8f f4 6f fc fd 0f dd 55 8b 45 8c f0 47 dc b4 ac 21 3b 4b 20 e6 81 b3 cc 90 d4 5e f1 a4 9b 68 52 c8 f1 2d f9 c4 77 c6 4d a9 90 c7 10 fd 43 c8 4b 6b 23 5e 92 f5 8f ac d5 7d 60 27 36 7c 21 4e 21 99 de cb c0 45 f3 04 22 b8 7d 16 68 40 f9 5c f0 b9 7e 8c 05 b6 fc 28 bb 3d d8 ff b6 a4 d4 54 27 3b 1a 42 4e f5 a6 a8 -5e 44 e2 9e ed 68 6a 27 60 13 8d 2f 27 70 cd 57 c9 18 a3 b0 30 a1 f4 e6 32 12 89 2a af 40 a5 fd 52 f1 aa 8a a4 ef 20 3d 10 a3 70 f2 39 c5 05 99 22 10 81 83 6e 45 a4 f3 5a 9d 6a b8 88 fe 69 40 d1 b1 cb 2a db 28 05 de 54 bf 3d 86 5f 39 8b c1 f4 af 00 61 86 01 fa 22 ac f6 2c a4 17 6a a7 d8 0a 8c 9f bf 1f 62 b2 2e 68 52 3f 82 8f e5 28 4d db b5 5a 96 28 27 19 af 43 b9 + 85 00 e1 68 aa eb d2 07 1b 7c 5e ed d6 e7 e5 f9 c1 0e 47 d4 4c ab 8c f0 e8 ee 8b 40 36 35 58 8f f4 6f fc fd 0f dd 55 8b 45 8c f0 47 dc b4 ac 21 3b 4b 20 e6 81 b3 cc 90 d4 5e f1 a4 9b 68 52 c8 f1 2d f9 c4 77 c6 4d a9 90 c7 10 fd 43 c8 4b 6b 23 5e 92 f5 8f ac d5 7d 60 27 36 7c 21 4e 21 99 de cb c0 45 f3 04 22 b8 7d 16 68 40 f9 5c f0 b9 7e 8c 05 b6 fc 28 bb 3d d8 ff b6 a4 d4 54 27 3b 1a 42 4e f5 a6 a8 5e 44 e2 9e ed 68 6a 27 60 13 8d 2f 27 70 cd 57 c9 18 a3 b0 30 a1 f4 e6 32 12 89 2a af 40 a5 fd 52 f1 aa 8a a4 ef 20 3d 10 a3 70 f2 39 c5 05 99 22 10 81 83 6e 45 a4 f3 5a 9d 6a b8 88 fe 69 40 d1 b1 cb 2a db 28 05 de 54 bf 3d 86 5f 39 8b c1 f4 af 00 61 86 01 fa 22 ac f6 2c a4 17 6a a7 d8 0a 8c 9f bf 1f 62 b2 2e 68 52 3f 82 8f e5 28 4d db b5 5a 96 28 27 19 af 43 b9 Exponent size (dwords) 1 01 00 01 00 Signature - 4a d3 c8 3e a1 e2 19 63 a2 ea 51 aa 60 42 98 f6 1b bf 41 a9 df de d7 4f 56 b7 90 d3 4f 70 db a9 3a 3d 30 71 27 e9 72 0b 6d 50 a4 76 6b 0b dc 07 7b ac 91 d3 d4 d0 9e da ee a0 9b c2 5b 01 e0 bc 3f 5c 36 33 f5 d2 a1 9d b1 03 44 fb 5d 5d ae 82 63 51 80 31 15 52 36 85 fd e4 40 d2 0a b1 47 c1 d9 fa 33 f2 97 76 4a 1e 64 19 78 bf b2 df be 9b 61 14 f1 64 c9 53 e4 40 a1 77 2b 59 7b bb 00 80 42 aa 87 d9 17 a9 -d2 06 d5 27 52 8a 6d 52 f3 59 2b d6 bc 60 c9 98 85 73 fe d3 28 00 5b 91 26 3a de 2f e6 a4 d3 6f 14 34 14 d8 b2 8d 06 cb 26 df 97 d7 6d f0 e1 83 bf 66 6d 98 37 fb 50 fa b4 1d b5 d6 62 bc 81 f4 09 53 31 c6 1b 7e 47 9f 4a fd d8 0b e8 9e a2 fa 57 ee 6b a9 c5 b1 d0 03 c7 3c 6a 9a 71 31 2b be 44 4c 60 28 e4 d9 79 71 ef 66 85 ec 0a 30 29 10 7d 33 78 2b 91 5c 0d 7f f5 91 + 86 67 47 b1 d5 00 7a e9 11 61 47 3a aa fe d1 df a2 9a 52 56 d6 fc 1a 4c 01 2d a0 cf 92 2e 14 3e 0b 60 29 4e 1e 42 f5 29 ba a2 57 da 73 54 f1 be 75 63 cb 41 c5 8f 8a ec 98 5b 49 61 19 c3 a9 5b e6 d6 6b 72 2e 8c 5c 30 af f6 9b 33 50 6f 3b 44 cc 82 90 a6 bc 09 38 75 99 d8 81 e0 42 e2 9d bb ad 6c 8a 0c e9 bb 06 d3 c7 d1 25 82 24 07 b8 10 3d 53 ca 3b 1d 82 f3 55 97 39 1f ad 25 7a dc 1b 8d 85 bb 54 6f 15 3f ed e0 a6 3e 18 20 d9 15 69 0e da b7 b5 f3 d7 8c 56 ad 8b be 7a dc 2f ba 33 59 a0 95 f4 b3 42 db c5 77 4a d0 f3 3c d0 39 47 54 0e 58 87 6b 50 b0 22 6d 78 bd d8 62 7b 04 24 95 e6 00 49 72 c6 bc fb 13 03 1c 3e 95 e2 51 da 83 30 6d 49 19 76 a2 7b 28 68 d9 97 32 85 c3 e7 f6 b4 f1 13 a6 c2 27 a1 a2 fc c0 9b a9 1a 62 9b dd 3f c5 81 6a 70 a5 3f f5 30 10 9c 56 16 f1 90 Plat Fw Auth Extension name ADSP vcn 0x0 bitmap 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 svn 0x0 - Other Extension type 0x50534441 length 0x46000 + Other Extension type 0x50534441 length 0x5b000 - cavs0015.met (ADSP Metadata File Extension) ver 0x0 base offset 0x2000 limit offset 0x47b80 + cavs0015.met (ADSP Metadata File Extension) ver 0x0 base offset 0x2000 limit offset 0x5cb80 IMR type 0x3 Attributes 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - cavs0015 (ADSP Manifest) name ADSPFW build ver 1.4.0.1 feature mask 0x1ff image flags 0x0 - HW buffers base address 0x0 length 0x0 - Load offset 0x30000 - - Module Entry - sig $AME - mod_name BRNGUP - uuid 2b79e4f3-4675-f649-89df-3bc194a91aeb - type 0x21 ( loadable LL ) - hash 9d e2 3d c4 53 36 b9 26 8c 33 6b 32 92 36 9a 14 6f f1 74 9a 9c 8a 09 70 f5 0b 4a 5c 04 a0 96 b1 - entry_point 0xb0038000 - cfg_offset 0 - cfg_count 0 - affinity_mask 0x3 - instance_max_count 1 - instance_stack_size 0x1 - seg_0 flags 0x1001f ( contents alloc load readonly code type=0 pages=1 ) - seg_0 v_base_addr 0xb0038000 - seg_0 file_offset 0x8000 - seg_1 flags 0x1012f ( contents alloc load readonly data type=1 pages=1 ) - seg_1 v_base_addr 0xb0039000 - seg_1 file_offset 0x9000 - seg_2 flags 0xf00 ( type=15 pages=0 ) - seg_2 v_base_addr 0x0 - seg_2 file_offset 0x0 - - Module Entry - sig $AME - mod_name BASEFW - uuid 0e398c32-5ade-ba4b-93b1-c50432280ee4 - type 0x21 ( loadable LL ) - hash 61 73 4b 78 b1 1e 49 5a ea e6 14 e8 05 e9 80 4d a9 df 8c 69 ad a5 a4 42 11 bb fb ae af ba 31 f8 - entry_point 0xbe00c400 - cfg_offset 0 - cfg_count 0 - affinity_mask 0x3 - instance_max_count 1 - instance_stack_size 0x1 - seg_0 flags 0x23001f ( contents alloc load readonly code type=0 pages=35 ) - seg_0 v_base_addr 0xbe00c000 - seg_0 file_offset 0xa000 - seg_1 flags 0x19012f ( contents alloc load readonly data type=1 pages=25 ) - seg_1 v_base_addr 0xbe02f000 - seg_1 file_offset 0x2d000 - seg_2 flags 0xb80202 ( alloc type=2 pages=184 ) - seg_2 v_base_addr 0xbe048000 - seg_2 file_offset 0x0 \ No newline at end of file + cavs0015 + + cavs0015 (ADSP Manifest) name ADSPFW build ver 1.5.0.1 feature mask 0x1ff image flags 0x0 + HW buffers base address 0x0 length 0x0 + Load offset 0x30000 + + BRNGUP 2b79e4f3-4675-f649-89df-3bc194a91aeb + entry point 0xb0038000 type 0x21 ( loadable LL ) + cfg offset 0 count 0 affinity 0x3 instance max count 1 stack size 0x1 + .text 0xb0038000 file offset 0x8000 flags 0x1001f ( contents alloc load readonly code type=0 pages=1 ) + .rodata 0xb0039000 file offset 0x9000 flags 0x1012f ( contents alloc load readonly data type=1 pages=1 ) + .bss 0x0 file offset 0x0 flags 0xf00 ( type=15 pages=0 ) + + BASEFW 0e398c32-5ade-ba4b-93b1-c50432280ee4 + entry point 0xbe00c400 type 0x21 ( loadable LL ) + cfg offset 0 count 0 affinity 0x3 instance max count 1 stack size 0x1 + .text 0xbe00c000 file offset 0xa000 flags 0x2d001f ( contents alloc load readonly code type=0 pages=45 ) + .rodata 0xbe039000 file offset 0x37000 flags 0x24012f ( contents alloc load readonly data type=1 pages=36 ) + .bss 0xbe05d000 file offset 0x0 flags 0xa30202 ( alloc type=2 pages=163 ) + +Intel Cannonlake + imr 0xb0000000 (8192 + 136579200 0.01% used) + BRNGUP.text 0xb0038000 (4096) + BRNGUP.rodata 0xb0039000 (4096) + l2 hpsram 0xbe000000 (999424 + 2146304 31.77% used) + BASEFW.text 0xbe00c000 (184320) + BASEFW.rodata 0xbe039000 (147456) + BASEFW.bss 0xbe05d000 (667648) + l2 lpsram 0xbe800000 (65536) diff --git a/developer_guides/debugability/ri-info/output_headers.txt b/developer_guides/debugability/ri-info/output_headers.txt index 0701cdac..8f99f6cb 100644 --- a/developer_guides/debugability/ri-info/output_headers.txt +++ b/developer_guides/debugability/ri-info/output_headers.txt @@ -1,27 +1,39 @@ -$ python ./sof_ri_info.py --no_colors --headers sof-cnl-v1.4.2.ri -SOF Binary sof-cnl-v1.4.2.ri size 0x46000 +$ python ./sof_ri_info.py --no_colors --headers sof-cnl.ri +SOF Binary sof-cnl.ri size 0x5b000 - CSE Manifest ver 0x101 checksum 0x42 partition name ADSP + CSE Manifest ver 0x101 checksum 0xf1 partition name ADSP - ADSP.man (CSS Manifest) type 0x4 ver 0x10000 date 2020/01/22 + ADSP.man (CSS Manifest) type 0x4 ver 0x10000 date 2020/07/16 Rsvd0 0x0 Modulus size (dwords) 64 85 00 e1 68 aa eb d2 07 ... 5a 96 28 27 19 af 43 b9 (Community key) Exponent size (dwords) 1 01 00 01 00 Signature - 4a d3 c8 3e a1 e2 19 63 ... 78 2b 91 5c 0d 7f f5 91 + 86 67 47 b1 d5 00 7a e9 ... f5 30 10 9c 56 16 f1 90 Plat Fw Auth Extension name ADSP vcn 0x0 bitmap 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 svn 0x0 - Other Extension type 0x50534441 length 0x46000 + Other Extension type 0x50534441 length 0x5b000 - cavs0015.met (ADSP Metadata File Extension) ver 0x0 base offset 0x2000 limit offset 0x47b80 + cavs0015.met (ADSP Metadata File Extension) ver 0x0 base offset 0x2000 limit offset 0x5cb80 IMR type 0x3 Attributes 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - cavs0015 (ADSP Manifest) name ADSPFW build ver 1.4.0.1 feature mask 0x1ff image flags 0x0 - HW buffers base address 0x0 length 0x0 - Load offset 0x30000 \ No newline at end of file + cavs0015 + + cavs0015 (ADSP Manifest) name ADSPFW build ver 1.5.0.1 feature mask 0x1ff image flags 0x0 + HW buffers base address 0x0 length 0x0 + Load offset 0x30000 + +Intel Cannonlake + imr 0xb0000000 (8192 + 136579200 0.01% used) + BRNGUP.text 0xb0038000 (4096) + BRNGUP.rodata 0xb0039000 (4096) + l2 hpsram 0xbe000000 (999424 + 2146304 31.77% used) + BASEFW.text 0xbe00c000 (184320) + BASEFW.rodata 0xbe039000 (147456) + BASEFW.bss 0xbe05d000 (667648) + l2 lpsram 0xbe800000 (65536) diff --git a/developer_guides/debugability/ri-info/output_verbose.txt b/developer_guides/debugability/ri-info/output_verbose.txt index 5ed20e59..5b7315c7 100644 --- a/developer_guides/debugability/ri-info/output_verbose.txt +++ b/developer_guides/debugability/ri-info/output_verbose.txt @@ -1,80 +1,76 @@ -$ python ./sof_ri_info.py --no_colors -v sof-cnl-v1.4.2.ri -Reading SOF ri image sof-cnl-v1.4.2.ri -File size 0x46000 (286720) +$ python ./sof_ri_info.py --no_colors -v sof-cnl.ri +Reading SOF ri image sof-cnl.ri +File size 0x5b000 (372736) +0x0 Looking for Extended Manifest 0x0 info: Extended Manifest not found (sig = $CPD) +0x0 Looking for CSE Manifest 0x0 CSE Manifest ($CPD) +0x8 # of entries 3 +0x10 Looking for CSE Manifest entry +0x28 CSE Entry name ADSP.man length 888 +0x58 Parsing CSS Manifest 0x58 CSS Manifest type 4 +0x58 Parsing CSS Manifest type 4 +0x2dc Parsing CSS Manifest extensions end 0x3d0 +0x2e0 Reading extension type 0xf +0x350 Reading extension type 0x50534441 +0x28 Looking for CSE Manifest entry +0x40 CSE Entry name cavs0015.met length 96 +0x40 Looking for CSE Manifest entry +0x58 CSE Entry name cavs0015 length 371584 0x2000 ADSP Manifest ($AM1) 0x2034 Module Entry signature found ($AME) 0x20a8 Module Entry signature found ($AME) Parsing finished -SOF Binary sof-cnl-v1.4.2.ri size 0x46000 +SOF Binary sof-cnl.ri size 0x5b000 - CSE Manifest ver 0x101 checksum 0x42 partition name ADSP + CSE Manifest ver 0x101 checksum 0xf1 partition name ADSP - ADSP.man (CSS Manifest) type 0x4 ver 0x10000 date 2020/01/22 + ADSP.man (CSS Manifest) type 0x4 ver 0x10000 date 2020/07/16 Rsvd0 0x0 Modulus size (dwords) 64 85 00 e1 68 aa eb d2 07 ... 5a 96 28 27 19 af 43 b9 (Community key) Exponent size (dwords) 1 01 00 01 00 Signature - 4a d3 c8 3e a1 e2 19 63 ... 78 2b 91 5c 0d 7f f5 91 + 86 67 47 b1 d5 00 7a e9 ... f5 30 10 9c 56 16 f1 90 Plat Fw Auth Extension name ADSP vcn 0x0 bitmap 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 svn 0x0 - Other Extension type 0x50534441 length 0x46000 + Other Extension type 0x50534441 length 0x5b000 - cavs0015.met (ADSP Metadata File Extension) ver 0x0 base offset 0x2000 limit offset 0x47b80 + cavs0015.met (ADSP Metadata File Extension) ver 0x0 base offset 0x2000 limit offset 0x5cb80 IMR type 0x3 Attributes 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - cavs0015 (ADSP Manifest) name ADSPFW build ver 1.4.0.1 feature mask 0x1ff image flags 0x0 - HW buffers base address 0x0 length 0x0 - Load offset 0x30000 + cavs0015 - Module Entry - sig $AME - mod_name BRNGUP - uuid 2b79e4f3-4675-f649-89df-3bc194a91aeb - type 0x21 ( loadable LL ) - hash 9d e2 3d c4 53 36 b9 26 ... f5 0b 4a 5c 04 a0 96 b1 - entry_point 0xb0038000 - cfg_offset 0 - cfg_count 0 - affinity_mask 0x3 - instance_max_count 1 - instance_stack_size 0x1 - seg_0 flags 0x1001f ( contents alloc load readonly code type=0 pages=1 ) - seg_0 v_base_addr 0xb0038000 - seg_0 file_offset 0x8000 - seg_1 flags 0x1012f ( contents alloc load readonly data type=1 pages=1 ) - seg_1 v_base_addr 0xb0039000 - seg_1 file_offset 0x9000 - seg_2 flags 0xf00 ( type=15 pages=0 ) - seg_2 v_base_addr 0x0 - seg_2 file_offset 0x0 + cavs0015 (ADSP Manifest) name ADSPFW build ver 1.5.0.1 feature mask 0x1ff image flags 0x0 + HW buffers base address 0x0 length 0x0 + Load offset 0x30000 - Module Entry - sig $AME - mod_name BASEFW - uuid 0e398c32-5ade-ba4b-93b1-c50432280ee4 - type 0x21 ( loadable LL ) - hash 61 73 4b 78 b1 1e 49 5a ... 11 bb fb ae af ba 31 f8 - entry_point 0xbe00c400 - cfg_offset 0 - cfg_count 0 - affinity_mask 0x3 - instance_max_count 1 - instance_stack_size 0x1 - seg_0 flags 0x23001f ( contents alloc load readonly code type=0 pages=35 ) - seg_0 v_base_addr 0xbe00c000 - seg_0 file_offset 0xa000 - seg_1 flags 0x19012f ( contents alloc load readonly data type=1 pages=25 ) - seg_1 v_base_addr 0xbe02f000 - seg_1 file_offset 0x2d000 - seg_2 flags 0xb80202 ( alloc type=2 pages=184 ) - seg_2 v_base_addr 0xbe048000 - seg_2 file_offset 0x0 + BRNGUP 2b79e4f3-4675-f649-89df-3bc194a91aeb + entry point 0xb0038000 type 0x21 ( loadable LL ) + cfg offset 0 count 0 affinity 0x3 instance max count 1 stack size 0x1 + .text 0xb0038000 file offset 0x8000 flags 0x1001f ( contents alloc load readonly code type=0 pages=1 ) + .rodata 0xb0039000 file offset 0x9000 flags 0x1012f ( contents alloc load readonly data type=1 pages=1 ) + .bss 0x0 file offset 0x0 flags 0xf00 ( type=15 pages=0 ) + + BASEFW 0e398c32-5ade-ba4b-93b1-c50432280ee4 + entry point 0xbe00c400 type 0x21 ( loadable LL ) + cfg offset 0 count 0 affinity 0x3 instance max count 1 stack size 0x1 + .text 0xbe00c000 file offset 0xa000 flags 0x2d001f ( contents alloc load readonly code type=0 pages=45 ) + .rodata 0xbe039000 file offset 0x37000 flags 0x24012f ( contents alloc load readonly data type=1 pages=36 ) + .bss 0xbe05d000 file offset 0x0 flags 0xa30202 ( alloc type=2 pages=163 ) + +Intel Cannonlake + imr 0xb0000000 (8192 + 136579200 0.01% used) + BRNGUP.text 0xb0038000 (4096) + BRNGUP.rodata 0xb0039000 (4096) + l2 hpsram 0xbe000000 (999424 + 2146304 31.77% used) + BASEFW.text 0xbe00c000 (184320) + BASEFW.rodata 0xbe039000 (147456) + BASEFW.bss 0xbe05d000 (667648) + l2 lpsram 0xbe800000 (65536) diff --git a/developer_guides/debugability/shell/index.rst b/developer_guides/debugability/shell/index.rst new file mode 100644 index 00000000..4f0b943c --- /dev/null +++ b/developer_guides/debugability/shell/index.rst @@ -0,0 +1,119 @@ +.. _dbg-zephyr-shell: + +Zephyr Shell +############ + +Zephyr provides a shell subystem for interactive debugging and a channel +to run custom test sequences. SOF supports use of the Zephyr shell. + +The Zephyr shell is documented at: +https://docs.zephyrproject.org/latest/services/shell/index.html + +Requirements +************ + +- SOF target platform must have Zephyr support. + +- At least one shell backend (DSP memory window, serial port, RTT, ...) compatible + with target platform. + +Build SOF with Shell Support +**************************** + +Shell is typically disabled by default and the firmware needs to be +rebuilt. For common SOF targets, a build overlay is provided in SOF +upstream to easily enable shell suppot in build. + + .. code-block:: bash + + # example build for Intel Tiger Lake platform + build-sh> sof/scripts/xtensa-build-zephyr.py tgl -o app/shell_overlay.conf + +Using Shell with Intel cavstool.py +********************************** + +This section covers use with SOF targets compatible with +CONFIG_SHELL_BACKEND_ADSP_MEMORY_WINDOW backend (for example Audio DSPs +on Intel Tiger Lake and Meteor Lake). + +Running the tool with "-p" to create a pseudo terminal for the shell: + + .. code-block:: bash + + dut-sh> sudo ./cavstool.py -l -p + INFO:cavs-fw:Existing driver "snd_sof_pci_intel_tgl" found + INFO:cavs-fw:Mapped PCI bar 0 of length 16384 bytes. + INFO:cavs-fw:Selected output stream 15 (GCAP = 0xffffffff) + INFO:cavs-fw:Mapped PCI bar 4 of length 1048576 bytes. + INFO:cavs-fw:Detected cAVS 1.8+ hardware + INFO:cavs-fw:Waiting forever for firmware handoff, ROM_STATUS = 0xffffffff + INFO:cavs-fw:FW alive, ROM_STATUS = 0x5 + INFO:cavs-fw:shell PTY at: /dev/pts/4 + +The Zephyr shell is now available at pseudo terminal /dev/pts/4 (see log above) +and can be attached with any terminal program: + + .. code-block:: bash + + dut-sh> sudo minicom -p /dev/pts/4 + Welcome to minicom 2.8 + + OPTIONS: I18n + Port /dev/modem + + Press CTRL-A Z for help on special keys + + ~$ kernel uptime + Uptime: 31600 ms + ~$ kernel stacks + 0x9e0a4e78 ll_thread0 (real size 8192): unused 6752 usage 1440 / 8192 (17 %) + 0x9e0a34b8 (real size 4096): unused 4008 usage 88 / 4096 ( 2 %) + 0x9e0a3400 (real size 4096): unused 4008 usage 88 / 4096 ( 2 %) + 0x9e0a3348 (real size 4096): unused 4008 usage 88 / 4096 ( 2 %) + 0x9e0a3290 (real size 4096): unused 4008 usage 88 / 4096 ( 2 %) + 0x9e0a37d0 edf_workq (real size 8192): unused 6304 usage 1888 / 8192 (23 %) + 0x9e0a3c48 sysworkq (real size 1024): unused 728 usage 296 / 1024 (28 %) + 0x9e0a3180 shell_adsp_memory_window (real size 2048): unused 760 usage 1288 / 2048 (62 %) + 0x9e0a3080 logging (real size 4096): unused 3488 usage 608 / 4096 (14 %) + 0x9e0a38b0 idle 00 (real size 1024): unused 824 usage 200 / 1024 (19 %) + 0xbe09df80 IRQ 00 (real size 2048): unused 1712 usage 336 / 2048 (16 %) + 0xbe09e780 IRQ 01 (real size 2048): unused 0 usage 2048 / 2048 (100 %) + 0xbe09ef80 IRQ 02 (real size 2048): unused 0 usage 2048 / 2048 (100 %) + 0xbe09f780 IRQ 03 (real size 2048): unused 0 usage 2048 / 2048 (100 %) + ~$ kernel threads + Scheduler: 1 since last call + Threads: + 0x9e0a4e78 ll_thread0 + options: 0x0, priority: -16 timeout: 0 + state: pending, entry: 0xbe02e060 + stack size 8192, unused 6752, usage 1440 / 8192 (17 %) + + 0x9e0a34b8 + options: 0x0, priority: -16 timeout: 0 + state: prestart, entry: 0xbe0154cc + stack size 4096, unused 4008, usage 88 / 4096 (2 %) + + [cut] + *0x9e0a3180 shell_adsp_memory_window + options: 0x0, priority: 14 timeout: 0 + state: , entry: 0xbe01969c + stack size 2048, unused 760, usage 1288 / 2048 (62 %) + + 0x9e0a3080 logging + options: 0x0, priority: 14 timeout: 0 + state: pending, entry: 0xbe016710 + stack size 4096, unused 3488, usage 608 / 4096 (14 %) + + 0x9e0a38b0 idle 00 + options: 0x1, priority: 15 timeout: 0 + state: , entry: 0xbe054298 + stack size 1024, unused 824, usage 200 / 1024 (19 %) + ~$ + +The memory window backend does not rely on IPC, so the shell is not +dependent on the IPC version implementation. The cavstool.py is also +implemented to handle cases where the DSP is suspended to lower power +state and the memory window is not accessible to host. When the DSP +is in such state, the shell terminal will appear inactive, but it will +resume immediately after DSP resumes to active state, without need +to rerun the cavstool.py script. diff --git a/developer_guides/firmware/async_messaging_best_practices.rst b/developer_guides/firmware/async_messaging_best_practices.rst new file mode 100644 index 00000000..efc49c1b --- /dev/null +++ b/developer_guides/firmware/async_messaging_best_practices.rst @@ -0,0 +1,651 @@ +.. _async_messaging_best_practices: + +Async Messaging Best Practices +############################## + +This section provides best practices and point out issues that developers should +be aware of when using asynchronous messages in their components. + +Message Type IDs +**************** + +The only unique value that is guaranteed to not change is UUID. The message type +IDs can change every time when a component is registering as a message +consumer/producer. In fact it depends on initialization order of components and +it may seem to be static. However it can change i.e. with addition of a new +component to pipeline and the entire mechanism will stop working. + +The components must not hardcode this value and they must use a runtime value of +component instance ID returned during producer/consumer registration. + +.. _message_handling: + +Message Handling +**************** + +The asynchronous messages consumers must be prepared to handle the messages +asynchronously. A message can be received while a component is processing data +and it can lead to undefined behavior when internal state is changed +immediately. The recommended way of handling asynchronous messages is to save a +message and apply the changes during a next processing time i.e. at the +beginning of processing. + +- When a processing of asynchronous message is done at the beginning of + component processing (Process Data), only then component processing is + deterministic. + +If an asynchronous message cannot be processed immediately by a consumer, it is +a good practice to implement a queue of incoming asynchronous messages in a +component or at least count asynchronous messages since last processing to +detect this kind of situation. + +A consumer component cannot assume that it can save a message pointer and +dereference is later. The message memory is released as soon as Asynchronous +Messaging Service will finish processing, so a consumer component must copy a +content of asynchronous message. + +Example - the pseudo code on a consumer side: + +.. code:: cpp + + queue_t request_queue; + + struct request_t { + uint8_t new_state; + }; + + struct message_context_t { + int32_t error; + queue_t *request_queue; + }; + + message_context_t message_context; + + void callback(const ams_message_payload_s * const ams_message_payload, void *ctx) { + if(ams_message_payload->message_length != sizeof(request_t)) { + ((message_context_t *)ctx)->error = 1; + return; + } + + queue_push(((message_context_t *)ctx)->request_queue, (request_t *)(ams_message_payload->message)); + } + + error_code componentA::Init(Pipeline* parent_ppl, const ModuleACfg* cfg) { + //getting Message Type ID + error = am_service_get_message_type_id(MESSAGE_A, &message_type_id); + if(error != 0) { + //error handling + } + + //registering consumer + message_context.error = 0; + message_context.request_queue = &request_queue; + error = am_service_register_consumer(message_type_id, GetcomponentID(), GetInstanceID(), callback, &message_context); + if(error != 0) { + //error handling + } + } + + error_code componentA::ProcessData(size_t max_output_data_size, uint32_t* custom_error_code) { + while(queue_size(message_context.request_queue) > 0) { + if(message_context.error != 0) { + //error handling + } + + request_t *r = queue_pop(message_context.request_queue); + //handling of async message + process_request(r); + } + + //regular processing + } + +.. _processing_in_a_consumer_callback: + +Processing in a consumer callback +********************************* + +The consumer callbacks must not do any heavy processing. Best if the callback +only saves information and a component will process it in the next processing +slot. + +- The asynchronous message callbacks are called in the context of a message + producer or in the context of IDC task. +- If a heavy processing is done in a message, then a producer MPCS or IDC task + budget needs to take into account this heavy processing. While it may be + possible to take into account additional budget for simple cases, it is + impossible to accommodate MCPS requirements in the general case (i.e. large + number of consumers with heavy processing). + +The example described in :ref:`message_handling` is also applicable for this issue. + +.. _message_filtering_mechanism: + +Message Filtering Mechanism +*************************** + +1. If a consumer is interested in async messages from one particular producer, + it needs to register for that particular producer using component ID and + instance ID. A component ID and instance ID of producer should be passed as + ``LARGE_CONFIG_SET`` parameter to the consumer. +2. If a producer wants to target a specific component instance, then it should + use send function that is parametrized with component ID and instance ID. + +Passing Pointers In Asynchronous Messages +***************************************** + +The asynchronous messages do not prevent to pass pointers between components. It +seems like a good idea at the beginning when a developer wants to return a +result. While it is simple solution, it may lead into the following issues: + +- there can be more than one consumer of a message and each of them needs to + have an allocated slot in the memory, +- the firmware framework cannot guarantee that component memory will be still + available when async message is processed i.e. a component can be subject of + firmware paging and the firmware infrastructure can decide to evict component + memory, + +The recommended method is to not pass pointers in the asynchronous messages. + +One-way Messages +**************** + +The asynchronous messages are one-way messages and there is no explicit feedback +whether a message was received or processed. The ``send`` functions return only +information whether the async messaging service return an error. + +Two-way Messages - Example #1 +***************************** + +The asynchronous messages are One-way Messages. Sometimes there is a need to +implement “function call” like functionality where a component instance wants +another component instance to take an action and return a result. This +functionality should be implemented with following issues in mind: + +- the result will not be returned immediately - :ref:`message_handling`, +- avoiding heavy processing in a consumer callback - :ref:`processing_in_a_consumer_callback`, +- 1:1 vs. M:N communication - :ref:`message_filtering_mechanism`, +- blocking vs. non-blocking execution: + + - If a producer depends on a result, it has to be handled as a blocking call + and the producer has to block its execution until result is received. To + do that, a blockade should be used when ``send`` functions return. + + - the task blockade must be removed when a result is received (in a + result callback), it will allow to continue a component execution, + - when the task blockade is set, the component execution is preempted and + the component with the highest priority is called, + +- one vs. two messages for handling action and result + + - the “function call” can be implemented as two separate messages: one for + triggering action from component A to component B and then second one for + passing result from component B to component A, it increases amount of + asynchronous messages, + - the recommended way is to implement it as one asynchronous message where + component A and B are both consumer and producer of the same asynchronous + message + + - it is important to note that filtering mechanism must be used to break + recursion - component A must discard a message from itself, + +Example - 1:1 function call: + +Consumer (component instance A) code: + +.. code:: cpp + + uint32_t message_type_id; + + queue_t request_queue; + + struct message_context_t { + int32_t error; + queue_t *request_queue; + }; + + message_context_t message_context; + + queue_t request_queue; + + struct request_t { + uint8_t message_type; //1 - request, 2 - response + uint8_t new_state; + }; + + void consumer_callback(const ams_message_payload_s * const payload, void *ctx) { + if(payload->message_length != sizeof(request_t)) { + ((message_context_t *)ctx)->error = 1; + return; + } + + //only requests are supported by a consumer + if((request_t *)(payload->message)->message_type != 1) { + ((message_context_t *)ctx)->error = 2; + return; + } + + queue_push(((message_context_t *)ctx)->request_queue, (request_t *)(payload->message)); + } + + error_code componentB::Init(Pipeline* parent_ppl, const ModuelCfg* cfg) { + //getting Message Type ID + error = am_service_get_message_type_id(MESSAGE_A, &message_type_id); + if(error != 0) { + //error handling + } + + //registering consumer + message_context.error = 0; + message_context.request_queue = &request_queue; + error = am_service_register_consumer_mi(message_type_id, GetcomponentID(), GetInstanceID(), component_A_id, component_A_instance_id, callback, &message_context); + if(error != 0) { + //error handling + } + } + + error_code componentB::ProcessData(size_t max_output_data_size, uint32_t* custom_error_code) { + while(queue_size(request_queue) > 0) { + if(message_context.error != 0) { + //error handling + } + + request_t *r = queue_pop(request_queue); + + //handling of async message + process_request(r); + + //message response + request_t response; + response.message_type = 2; + + error_code error = am_service_send_mi(message_type_id, GetcomponentID(), GetInstanceID(), component_A_id, component_A_instance_id, sizeof(request_t), &response); + if(e != 0) { + //error handling + } + } + + //regular processing + } + +Producer code: + +.. code:: cpp + + uint32_t message_type_id; + + queue_t request_queue; + + struct message_requestor_context_t { + int32_t error; + queue_t *request_queue; + uint32_t blockade; + }; + + message_requestor_context_t message_context; + + struct request_t { + uint8_t message_type; //1 - request, 2 - response + uint8_t new_state; + }; + + void consumer_callback(const ams_message_payload_s * const payload, void *ctx) { + if(payload->message_length != sizeof(request_t)) { + (message_requestor_context_t *)ctx->error = 1; + return; + } + + //only responses are supported by a producer + if((request_t *)(payload->message)->message_type != 2) { + (message_requestor_context_t *)ctx->error = 2; + return; + } + + queue_push(((message_context_t *)ctx)->request_queue, (request_t *)(payload->message)); + (message_requestor_context_t *)ctx->blockade = 0; //unblock a producer component execution + } + + error_code componentA::Init(Pipeline* parent_ppl, const ModuleACfg* cfg) { + //getting Message Type ID + error = am_service_get_message_type_id(MESSAGE_A, &message_type_id); + if(error != 0) { + //error handling + } + + //registering consumer + message_context.error = 0; + message_context.blockade = 0; + message_context.request_queue = &request_queue; + error = am_service_register_consumer_mi(message_type_id, GetcomponentID(), GetInstanceID(), component_B_id, component_B_instance_id, callback, &message_context); + if(error != 0) { + //error handling + } + + //registering as a producer + error = am_service_register_producer(message_type_id); + if(error != 0) { + //error handling + } + } + + error_code componentA::ProcessData(size_t max_output_data_size, uint32_t* custom_error_code) { + ... + SystemServiceInternal const* services = get_system_services(); + ... + //message request + request_t response; + response.message_type = 1; + + message_context.blockade = 1; //initialize blockade + error_code error = am_service_send_mi(message_type_id, GetcomponentID(), GetInstanceID(), component_B_id, component_B_instance_id, sizeof(request_t), &response); + if(e != 0) { + //error handling + } + + //block a component execution until a consumer will reply with a result + _AdspCurrentTaskBlockade blockade; + services->SetTaskRunCondition(&blockade, (uint32_t*)(&message_context.blockade), 0 /*unblocking value*/, 0xffffffff); + services->RemoveTaskRunCondition(&blockade); + + while(queue_size(request_queue) > 0) { + if(message_context.error != 0) { + //error handling + } + + request_t *r = queue_pop(request_queue); + + //handling of async message + process_request(r); + } + ... + //regular processing + } + +Two-way Messages - Example #2 +***************************** + +The below example shows a real use case where multiple control interfaces are +supported (IPC and I2C). Control application needs to produce an async message +and when async message response is received, it needs to respond to the correct +requestor IPC vs. I2C. To make it happen, the unique async message ID needs to +be introduced. The unique ID can be generated globally or locally when source +component is tracked. In the below pseudo-code, the ID is generated locally and +component B needs to respond to the correct component. + +Example - 1:1 function call: + +Consumer (component instance B) code: + +.. code:: cpp + + uint32_t message_request_id; + uint32_t message_response_id; + uint32_t current_state; + + queue_t request_queue; + queue_t requestor_info_queue; + + struct unique_message_id { + uint16_t component_id; + uint16_t instance_id; + uint32_t message_id; + }; + + struct message_context_t { + int32_t error; + queue_t *request_queue; + queue_t *requestor_info_queue; + }; + + message_context_t message_context; + + struct request_t { + unique_message_id uid; + uint32_t requested_state; + }; + + struct requestor_info_t { + unique_message_id uid; + uint16_t component_id; + uint16_t instance_id; + }; + + struct response_t { + unique_message_id uid; + uint32_t current_state; + }; + + void consumer_callback(const ams_message_payload_s * const payload, void *ctx) { + if(payload->message_length != sizeof(request_t)) { + ((message_context_t *)ctx)->error = 1; + return; + } + + queue_push(((message_context_t *)ctx)->request_queue, (request_t *)(payload->message)); + + requestor_info_t info; + info.uid = (request_t *)(payload->message)->uid; + info.component_id = payload->producer_component_id; + info.instance_id = payload->producer_instance_id; + queue_push(((message_context_t *)ctx)->requestor_info_queue, info); + } + + error_code componentB::Init(Pipeline* parent_ppl, const ModuelCfg* cfg) { + //getting Message Type ID for requests + error = am_service_get_message_type_id(MESSAGE_REQUEST, &message_request_id); + if(error != 0) { + //error handling + } + + //registering consumer + message_context.error = 0; + message_context.request_queue = &request_queue; + message_context.requestor_info_queue = &requestor_info_queue; + error = am_service_register_consumer(message_request_id, GetcomponentID(), GetInstanceID(), callback, &message_context); + if(error != 0) { + //error handling + } + + //getting Message Type ID for responses + error = am_service_get_message_type_id(MESSAGE_RESPONSE, &message_response_id); + if(error != 0) { + //error handling + } + + //registering producer + error = am_service_register_producer(message_response_id, GetcomponentID(), GetInstanceID()); + if(error != 0) { + //error handling + } + } + + void process_request(request_t *r) { + current_state = r->requested_state; + } + + uint32_t get_current_state() { + return current_state; + } + + error_code componentB::ProcessData(size_t max_output_data_size, uint32_t* custom_error_code) { + while(queue_size(request_queue) > 0) { + if(message_context.error != 0) { + //error handling + } + + request_t *r = queue_pop(request_queue); + + //handling of async message + process_request(r); + + //message response + response_t response; + response.uid = r->uid; + response.current_state = get_current_state(); + + //find requestor information + requestor_info_t *ri = find(requestor_info_queue, r->uid); + if(ri == NULL) { + //error handling + } + queue_pop(requestor_info_queue, ri); + + error_code error = am_service_send_mi(message_type_id, GetcomponentID(), GetInstanceID(), ri->component_id, ri->instance_id, sizeof(response_t), &response_t); + if(e != 0) { + //error handling + } + } + + //regular processing + ... + } + +Producer code: + +.. code:: cpp + + uint32_t message_request_id; + uint32_t message_response_id; + uint32_t message_id_counter; + + queue_t response_queue; + queue_t request_source_queue; + + struct unique_message_id { + uint16_t component_id; + uint16_t instance_id; + uint32_t message_id; + }; + + struct message_requestor_context_t { + int32_t error; + queue_t *response_queue; + uint32_t blockade; + }; + + message_requestor_context_t message_context; + + struct request_source_t { + unique_message_id uid; + uint32_t source; //0 - IPC, 1 - I2C + }; + + struct request_t { + unique_message_id uid; + uint32_t requested_state; + }; + + struct response_t { + unique_message_id uid; + uint32_t current_state; + }; + + void consumer_callback(const ams_message_payload_s * const payload, void *ctx) { + if(payload->message_length != sizeof(response_t)) { + (message_requestor_context_t *)ctx->error = 1; + return; + } + + queue_push(((message_context_t *)ctx)->response_queue, (request_t *)(payload->message)); + } + + error_code componentA::Init(Pipeline* parent_ppl, const ModuleACfg* cfg) { + message_id_counter = 0; + + //getting Message Type ID for response + error = am_service_get_message_type_id(MESSAGE_RESPONSE, &message_response_id); + if(error != 0) { + //error handling + } + + //registering consumer + message_context.error = 0; + message_context.blockade = 0; + message_context.response_queue = &response_queue; + error = am_service_register_consumer(message_response_id, GetcomponentID(), GetInstanceID(), callback, &message_context); + if(error != 0) { + //error handling + } + + //getting Message Type ID for request + error = am_service_get_message_type_id(MESSAGE_REQUEST, &message_request_id); + if(error != 0) { + //error handling + } + + //registering as a producer + error = am_service_register_producer(message_request_id, GetcomponentID(), GetInstanceID()); + if(error != 0) { + //error handling + } + } + + Message::IxcStatus componentA::LargeConfigSet(uint32_t large_param_id, + bool init_block, + bool final_block, + uint32_t data_off_size, + const ByteArray* data, + ByteArray* response) + { + ... + //message request + request_t request; + response.uid = {GetcomponentID(), GetInstanceID(), message_id_counter++}; + response.requested_state = state_from_request; + + request_source_t source; + source.uid = response.uid; + source.source = 0; //IPC + queue_push(request_source_queue, source); + + error_code error = am_service_send_mi(message_type_id, GetcomponentID(), GetInstanceID(), component_B_id, component_B_instance_id, sizeof(request_t), &response); + if(e != 0) { + //error handling + } + ... + } + + void process_request(response_t *r) { + ... + request_source_t *rs = find(request_source_queue, r->uid); + if(rs == NULL) { + //error handling + } + + if(rs->source == 0) { + //send response over IPC + } + + queue_pop(request_source_queue, rs); + ... + } + + error_code componentA::ProcessData(size_t max_output_data_size, uint32_t* custom_error_code) { + ... + while(queue_size(response_queue) > 0) { + if(message_context.error != 0) { + //error handling + } + + response_t *r = queue_pop(request_queue); + + //handling of async message + process_request(r); + } + ... + //regular processing + } + +Max message size +**************** + +The asynchronous message size is limited by size of an async message slot in the +AM queue, which is currently 4KB and should not be exceeded. + +Queue is Full +************* + +The queue of asynchronous messages is used when there are customers of messages +registered on other core than producer’s core. This queue has limited size and +it can happen that ``send`` function will fail. In such case, the best strategy +is to retry ``send`` function call in the next execution period. diff --git a/developer_guides/firmware/cmake.rst b/developer_guides/firmware/cmake.rst index f457fb80..3edbbad3 100644 --- a/developer_guides/firmware/cmake.rst +++ b/developer_guides/firmware/cmake.rst @@ -76,11 +76,39 @@ MEU_OPENSSL # Example cmake [...] -DMEU_OPENSSL=C:/path/to/openssl.exe [...] +FIRMWARE_NAME + Custom suffix for output binary. + + .. code-block:: bash + + # Example + cmake [...] -DFIRMWARE_NAME=custom [...] + +MEU_NO_SIGN + Flag that can be used to build unsigned FW binary, + that may be later used with MEU for signing. + + .. code-block:: bash + + # Example + cmake [...] -DMEU_NO_SIGN=ON [...] + +MEU_OFFSET + Default: determined by build-system, depends on MEU version. + Can be used to override MEU offset. + + .. code-block:: bash + + # Example + cmake [...] -DMEU_OFFSET=1344 [...] + Unit Tests ********** Optional arguments. Only for unit tests. +Read :ref:`unit_tests` first. + BUILD_UNIT_TESTS Default: OFF, if ON then builds unit tests. diff --git a/developer_guides/firmware/components/images/comp-dev-states.pu b/developer_guides/firmware/components/images/comp-dev-states.pu deleted file mode 100644 index 8eda7e5a..00000000 --- a/developer_guides/firmware/components/images/comp-dev-states.pu +++ /dev/null @@ -1,15 +0,0 @@ -hide empty description -[*] -right-> COMP_STATE_READY : comp_ops.new() - -COMP_STATE_READY -right-> COMP_STATE_PREPARE : COMP_TRIGGER_PREPARE - -COMP_STATE_PREPARE --> COMP_STATE_ACTIVE : COMP_TRIGGER_START - -COMP_STATE_ACTIVE --> COMP_STATE_PREPARE : COMP_TRIGGER_STOP, COMP_TRIGGER_XRUN -COMP_STATE_PAUSED --> COMP_STATE_PREPARE : COMP_TRIGGER_STOP, COMP_TRIGGER_XRUN - -COMP_STATE_ACTIVE -> COMP_STATE_PAUSED : COMP_TRIGGER_PAUSE - -COMP_STATE_PAUSED --> COMP_STATE_ACTIVE : COMP_TRIGGER_RELEASE - -COMP_STATE_PREPARE --> COMP_STATE_READY : COMP_TRIGGER_RESET diff --git a/developer_guides/firmware/images/work-queue-deps.pu b/developer_guides/firmware/images/work-queue-deps.pu deleted file mode 100644 index d9c20f44..00000000 --- a/developer_guides/firmware/images/work-queue-deps.pu +++ /dev/null @@ -1,40 +0,0 @@ -class "struct work" as s_work { - cb - cb_data - timeout - flags -} -hide s_work methods - -enum flags { - SYNC - ASYNC -} -hide flags methods - -class "struct work_queue_timesource" as s_wq_timesource -hide s_wq_timesource methods -hide s_wq_timesource attributes - -class "work_queue" as wq { - + work_schedule() - + work_reschedule() - + work_cancel() - - is_work_pending() - - work_next_timeout() - - run_work() - - work : list -} - -class client #a1a1ca -hide client methods -hide client attributes - -wq o- s_work -wq <- s_wq_timesource : provides timer INT - -s_work - flags - -client -> s_work : (1) creates -client ---> wq : (2) schedules work -wq ---> client : (3) calls cb(cb_data) upon timeout diff --git a/developer_guides/firmware/images/work-schedule.pu b/developer_guides/firmware/images/work-schedule.pu deleted file mode 100644 index 6be68be6..00000000 --- a/developer_guides/firmware/images/work-schedule.pu +++ /dev/null @@ -1,26 +0,0 @@ -actor client as c - -participant work_queue as wq -participant timer as t - --> wq : work_new_queue - wq -> t : timer_register(queue_run) -<-- wq - -c -> wq : work_schedule(&work) - activate wq - - wq -> wq : queue_reschedule() - activate wq - wq -> wq : queue_get_next_timeout() : timeout - wq -> t : work_set_timer(timeout) - deactivate wq -c <-- wq -deactivate wq -... -wq <- t : queue_run() - activate wq - loop is_work_pending() - wq -> wq : run_work() - end loop - wq -> wq : queue_reschedule() diff --git a/developer_guides/firmware/index.rst b/developer_guides/firmware/index.rst index fa7c1bcb..ee2e75cd 100644 --- a/developer_guides/firmware/index.rst +++ b/developer_guides/firmware/index.rst @@ -9,12 +9,7 @@ Developer guides and information for firmware development. :maxdepth: 1 component-tutorial/tut-intro - mem-mgmt - pm-runtime/index - work-queue - drivers/index - components/index - pipelines/index porting - kd_integration/index cmake + async_messaging_best_practices + llext_modules diff --git a/developer_guides/firmware/llext_modules.rst b/developer_guides/firmware/llext_modules.rst new file mode 100644 index 00000000..e4040d54 --- /dev/null +++ b/developer_guides/firmware/llext_modules.rst @@ -0,0 +1,108 @@ +.. _llext_modules: + +LLEXT Modules +############# + +|SOF| support for loadable modules, using Zephyr LLEXT API. + +Zephyr LLEXT API +**************** + +Please refer to https://docs.zephyrproject.org/latest/services/llext/index.html +for detailed documentation. In short, the Zephyr Linkable Loadable Extensions +(LLEXT) API implements support for run-time loading and unloading of ELF-format +executable code and data. + +SOF use of the LLEXT API +************************ + +SOF has multiple ways to implement loadable modules. LLEXT is one of them. +With it modules are built as shared or relocatable ELF objects with an addition +of a cryptographic signature, using any user-supplied key, and a manifest. When +loaded and instantiated, Zephyr LLEXT functionality is used to dynamically +resolve module internal as well as SOF and Zephyr external code and data +references. In the future support for inter-module linking will be added. + +Accessing the base firmware from LLEXT modules +********************************************** + +LLEXT modules can access all code and data from the base firmware exported, +using the ``EXPORT_SYMBOL()`` macro. Therefore writing LLEXT modules isn't very +different from built-in ones. + +Implementing LLEXT modules +************************** + +At the moment only modules, implementing the Module Adapter API +:ref:`apps-comp-world` are supported. + +.. _multiple-adapter-modules: + +It is possible to implement multiple Module Adapter modules with a common code +base, i.e. sharing a set of source files and functions. Then a single LLEXT +object would be created, implementing multiple Module Adapter interfaces. In +that case an array of ``struct sof_module_api_build_info`` objects is needed and +the TOML file should contain those multiple module entries too. +src/audio/mixin_mixout/mixin_mixout.c is an example of such a case. + +As explained above, LLEXT modules in general look very similar to native SOF +code, with the only restriction of having no access to not-exported symbols. + +LLEXT modules should also contain a ``.buildinfo`` section, containing a +``struct sof_module_api_build_info`` object and a ``.module`` section, +containing a ``struct sof_man_module_manifest`` object. The latter should also +contain a pointer to a module entry point function, returning a pointer to the +module's ``struct module_interface`` instance. All these additions can be +performed, using ``SOF_LLEXT_MOD_ENTRY()``, ``SOF_LLEXT_MODULE_MANIFEST()`` and +``SOF_LLEXT_BUILDINFO`` helper macros. See src/audio/eq_iir/eq_iir.c for an +example. + +A TOML configuration file is needed for building of LLEXT modules too. It is +generated by the C preprocessor at build time from the same components, as would +be used for a monolithic build. For this preprocessor run a small header file is +added. It mostly just includes ``platform.toml`` and ``${module}.toml``, similar +to src/samples/audio/smart_amp_test_llext/llext.toml.h. + +Finally an additional CMakeLists.txt is needed similar to +src/samples/audio/smart_amp_test_llext/CMakeLists.txt. It contains a single call +to ``sof_llext_build()``, which is an SOF helper function, using Zephyr LLEXT +cmake support by calling ``add_llext_target()`` and ``add_llext_command()``. + +With that in place, it is also possible to switch between monolithic and modular +builds by specifying the module as "tristate" in its Kconfig and selecting "m" +for modular builds. Note, that it is possible to implement third party Module +Adapter drivers, that would be built exclusively as loadable modules. Such +modules don't have to use "tristate" in their Kconfig entries. + +Installation +************ + +As specified in +:ref:`Firmware look-up paths per Intel platform ` +the |SOF| Linux kernel driver loads SOF modules by their UUIDs, +specified in the topology. For SOF in-tree modules the process of creation and +installation of modules in a deployment tree is automated by the +xtensa-build-zephyr.py script. It copies modules to the deployment tree as +files with a "llext" extension and creates symbolic links to them named as +``${UUID}.bin``. E.g. + +.. code-block:: cfg + + B36EE4DA-006F-47F9-A06D-FECBE2D8B6CE.bin -> drc.llext + +Note, that as described :ref:`above ` multiple UUIDs +can be associated with a single module, in such cases multiple symbolic links +will be created, e.g. + +.. code-block:: cfg + + 39656EB2-3B71-4049-8D3F-F92CD5C43C09.bin -> mixin_mixout.llext + 3C56505A-24D7-418F-BDDC-C1F5A3AC2AE0.bin -> mixin_mixout.llext + +See :ref:`apps-component-overview` for more information on UUID use by SOF +component and module adapter drivers. + +It is also possible to avoid using the script by running ``west build`` to build +an SOF image and any modules, then using the cross-compiler to preprocess TOML +files and finally by running rimage to sign them. This would generate the same +result but figuring out all the command-line arguments would be rather difficult. diff --git a/developer_guides/firmware/mem-mgmt.rst b/developer_guides/firmware/mem-mgmt.rst deleted file mode 100644 index e55a1a97..00000000 --- a/developer_guides/firmware/mem-mgmt.rst +++ /dev/null @@ -1,64 +0,0 @@ -.. _kernel-mem-mgmt: - -Memory Management -################# - -Heap Memory Zones -***************** - -The heap has three different zones from where memory can be allocated: - -System Zone - Fixed size heap where allocation always succeeds and is never freed. Used - by any initialization code that will never give up the memory. - -Runtime Zone - Main and larger heap zone where allocations are not guaranteed to succeed. - Memory can be freed here. - -Buffer Zone - Largest heap zone intended for audio buffers. See platform/memory.h for - heap size configuration and mappings. - -.. graphviz:: images/memory-zones.dot - :caption: Memory Zones - -System Zone -*********** - -System zone receives a series of allocations during the system initialization -phase. Since no memory is freed until the system (core) goes down, the -allocation mechanism may be simple, ensuring that a sufficient offset to the beginning of free space left is maintained. - -.. graphviz:: images/system-zone.dot - :caption: System Zone - -All system level components (schedulers, work queues, etc.) allocate their -memory blocks from the system heap. Separation between the system heap and -runtime heap(s) may be further hardened in case an access control for user mode vs. kernel mode is supported by the architecture/platform. - -Extensions for SMP Architectures -================================ - -Each CPU (core) may own a dedicated system heap. The memory assigned for system heaps is distributed asymmetrically on CAVS platforms: a large heap for the master core (#0) and smaller ones for other cores (#1+). - -When a core goes down, the entire heap can be freed by moving back the free -space offset to the beginning of the heap. - -The heap can be aligned with memory bank(s) to provide better control over -the power consumption. Once a core goes down, memory banks allocated for -its system heap can be powered off as well. - -Runtime Zone -************ - -* Provides flexible ``malloc``/``free`` operations. - -* Since the runtime zone is separated from the system zone, any adjustments - and complex usage scenarios do not interface with the system allocations. - -.. graphviz:: images/runtime-zone.dot - :caption: Runtime Zone - -Buffer Zone -*********** diff --git a/developer_guides/firmware/work-queue.rst b/developer_guides/firmware/work-queue.rst deleted file mode 100644 index f3be4a97..00000000 --- a/developer_guides/firmware/work-queue.rst +++ /dev/null @@ -1,30 +0,0 @@ -.. _work-queue: - -Work Queue -########## - -A work queue service provides a timer API for other FW parts. A timer API -client (a component, device, etc.) registers a callback and specifies when the -callback should be invoked. - -Refer to TBD for full API specification. - -A source of time for the work queue is implemented by the specific platform -and depends on the underlying architecture and the HW capabilities which -determine the resolution of the timer. - -.. uml:: images/work-queue-deps.pu - :caption: Work queue dependencies - -Basic Work Queue Flow -********************* - -.. uml:: images/work-schedule.pu - :caption: Basic work queue flow - -Extensions for SMP Architectures -******************************** - -CPUs with platforms built on the SMP architecture contain only one work queue -instance. A client registers its callback in the queue instance that is running -on the CPU that the callback is supposed to run. diff --git a/developer_guides/fuzzer/fuzzing.rst b/developer_guides/fuzzing/fuzzing_in_docker.rst similarity index 90% rename from developer_guides/fuzzer/fuzzing.rst rename to developer_guides/fuzzing/fuzzing_in_docker.rst index db50b4e3..3ebf516e 100644 --- a/developer_guides/fuzzer/fuzzing.rst +++ b/developer_guides/fuzzing/fuzzing_in_docker.rst @@ -6,8 +6,8 @@ Fuzzing in Docker Instructions ************ -#. Build a fuzzer in order to use it. Follow the instructions at `Build SOF - with docker: Step 4: Build Topology and Tools `__. +#. Build a fuzzer in order to use it. Follow the instructions at Build SOF + with docker, :ref:`docker-topology-tools`. #. Enter the Docker container: diff --git a/developer_guides/fuzzing/index.rst b/developer_guides/fuzzing/index.rst new file mode 100644 index 00000000..2967b46c --- /dev/null +++ b/developer_guides/fuzzing/index.rst @@ -0,0 +1,10 @@ +.. _fuzzing-components: + +Fuzzing +####### + +.. toctree:: + :maxdepth: 2 + + fuzzing_in_docker + testbench_afl_fuzzing \ No newline at end of file diff --git a/developer_guides/fuzzing/testbench_afl_fuzzing.rst b/developer_guides/fuzzing/testbench_afl_fuzzing.rst new file mode 100644 index 00000000..3eee6930 --- /dev/null +++ b/developer_guides/fuzzing/testbench_afl_fuzzing.rst @@ -0,0 +1,96 @@ +.. _testbench-afl-fuzzing: + +Build a Fuzzing Testbench with AFL +################################## + +American fuzzy lop (AFL) is a free software fuzzer that can be used to +detect software bugs. Use these instructions to build and run a testbench +with AFL. + +Install AFL +*********** + +Follow the steps in the `AFL Quick Start Guide `_ to install AFL on your system. + +We assume that AFL is installed at: + +:: + + $HOME/work/ + + +Build a testbench with AFL instrumentation +****************************************** + +According to AFL's `README `_, AFL is a "brute-force fuzzer coupled with an exceedingly +simple but rock-solid instrumentation-guided genetic algorithm." **You must +add instrumentation to the code before running a fuzzer in order to get +potentially useful results; otherwise, you might not get any results.** + +When you build AFL from the previous step, an ``afl-gcc`` executable is +generated; this works as a companion tool that acts as a drop-in +replacement for ``gcc`` or ``clang``. Before you build the testbench, make +sure you are compiling code with ``afl-gcc`` in order to add instrumentation +to the code. The ``host-build-all.sh`` script from the ``scripts/`` directory +**does exactly this when you run it with the -f option.** + +.. Note:: + By default, the ``host-build-all.sh`` script assumes you have installed + AFL in the ``$HOME/work/ directory``. If you install AFL in any other + directory, you must change the path in this script. + +Run AFL +******* + +From the AFL directory, run AFL by entering the following: + +:: + + ./afl-fuzz -i testcase_dir -o findings_dir /path/to/program [...params...] @@ + +AFL assumes that the inputs for the program you wish to fuzz are +in the form of files. So, you must create a directory that contains these +input files. This is the ``testcase_dir`` in the above command. + +Since you are fuzzing the testbench, the ``program`` here is testbench. + +``params`` are the different parameters of the program apart from the input +file. + +``@@``: Each file from ``testcase_dir`` is substituted in place of this. +As AFL continues to run, newly-generated testcases are placed in +``testcase_dir``, and AFL in its further iterations runs with these +newly-generated testcases. + +Example +******* + +**Use AFL to fuzz the volume component of the testbench** + +To fuzz the volume component of the testbench, use topology files as inputs +and place the topology files of volume components in an ``inputs`` directory: + +``/home/sof/work/sof/tools/testbench/inputs`` + +:: + + # Add AFL directory to $PATH + export PATH=$PATH:$HOME/AFL + + # Go to the testbench directory + cd tools/testbench + + # Run the fuzzer + afl-fuzz -i inputs/ -o output/ build_testbench/install/bin/testbench -r 48000 -R 48000 -i zeros_in.raw -o volume_out.raw -b S16_LE -t @@ + +AFL runs and places problem inputs in the provided output directory (-o +option in the above command). The inputs are well-organized into +crashes, hangs, etc. Run the testbench with the volume component in +``gdb`` to assist in figuring out the error. + +Reference +--------- + +`AFL README `_ +is a good place to learn more about the AFL tool itself as well as the +various options it provides. diff --git a/developer_guides/index.rst b/developer_guides/index.rst index f76ce642..5ac976e5 100644 --- a/developer_guides/index.rst +++ b/developer_guides/index.rst @@ -12,7 +12,9 @@ terminology before reading further. introduction firmware/index unit_tests + xtrun/index topology/topology + topology2/topology2 uuid/index.rst debugability/index tuning/sof-ctl @@ -20,7 +22,9 @@ terminology before reading further. linux_driver/index virtualization/virtualization virtualization/running - fuzzer/fuzzing.rst + fuzzing/index + testbench/index + add_new_arch Technical Notes *************** diff --git a/developer_guides/linux_driver/index.rst b/developer_guides/linux_driver/index.rst index 4082c00b..c6534d70 100644 --- a/developer_guides/linux_driver/index.rst +++ b/developer_guides/linux_driver/index.rst @@ -6,6 +6,5 @@ SOF Linux Driver .. toctree:: :maxdepth: 1 - architecture/sof_driver_arch third_party/index diff --git a/developer_guides/testbench/build_testbench.rst b/developer_guides/testbench/build_testbench.rst new file mode 100644 index 00000000..c6173507 --- /dev/null +++ b/developer_guides/testbench/build_testbench.rst @@ -0,0 +1,126 @@ +.. _build-testbench: + +Build and Run Testbench +####################### + +First, you'll need to install some dependencies to run the testbench: + +.. code-block:: bash + + sudo apt install valgrind bc # For Ubuntu/Debian + sudo dnf install valgrind bc # For Fedora + +Retrieve the required firmware from the ``thesofproject`` repository in +Github as described in :ref:`build-from-scratch`. Start a shell at the +firmware repository top level in the ``$SOF_WORKSPACE/sof`` directory as also described. + +.. code-block:: bash + + cd "$SOF_WORKSPACE"/sof + +Run the following scripts to build the test pipelines, build the testbench, +and run the testbench with the provided quick check script: + +.. code-block:: bash + + ./scripts/build-tools.sh -t + ./scripts/rebuild-testbench.sh + ./scripts/host-testbench.sh + +The current version of ``host-testbench.sh`` outputs the following text if +the previous steps are successful: + +:: + + ========================================================== + test volume with ./volume_run.sh 16 16 48000 zeros_in.raw volume_out.raw + volume test passed! + volume_out size check passed! + ========================================================== + test src with ./src_run.sh 32 32 44100 48000 zeros_in.raw src_out.raw + src test passed! + src_out size check passed! + ========================================================== + test eqiir with ./eqiir_run.sh 16 16 48000 zeros_in.raw eqiir_out.raw + eqiir test passed! + eqiir_out size check passed! + +Note that more items are slated to be tested in this check so the output +will likely change. The testbench can be used for audio quality tests and +debugging new components under development. + +host-testbench.sh +================= + +In our example, the ``host-testbench.sh`` script shows that the IIR EQ test +is run with the following commands: + +.. code-block:: bash + + cd tools/test/audio + head -c 10240 < /dev/zero > zeros_in.raw + ./eqiir_run.sh 16 16 48000 zeros_in.raw eqiir_out.raw + +The directory that contains ``eqiir_run.sh`` is entered first. Next, a file +of 10240 bytes of zeros is created. As 16-bit data, it corresponds to 2560 +frames of S16_LE format stereo frames (4 bytes per frame). At a 48 kHz rate, +it corresponds to 5.3 ms of audio stream. Audio test signals are usually +longer but this is sufficient for the quick testbench health check. + +To process a music file with an under-development SOF component, a utility +to convert from wav, mp3, etc. to raw S16_LE/S24_LE/S32_LE format is needed. +The next command installs from the Ubuntu packages repository a lot of useful +tools for audio files converting, viewing, recording, playing, and editing. +FFMPEG can be used to import/export formats that the simpler tools ``sox`` +and ``ecasound`` do not support. The last three items are light audio +waveform viewers and players with some editing and mixing capabilities. +Also, digital audio workstation (DAW) software such as Ardour, Qtractor, and +MusE can be used but there's more effort in using them for small quick tasks +such as in the following case. + +.. code-block:: bash + + sudo apt install alsa-utils pulseaudio-utils sox ecasound ffmpeg audacity snd-gtk-pulse mhwaveedit + +A sample music or voice or test signal file is needed. The above alsa-utils +package contains some wav files. The sound (and many other file types) +characteristics can be easily checked using this file command: + +.. code-block:: bash + + $ file /usr/share/sounds/alsa/Front_Center.wav + /usr/share/sounds/alsa/Front_Center.wav: RIFF (little-endian) data, WAVE audio, Microsoft PCM, 16 bit, mono 48000 Hz + +This file has the correct default 48 kHz rate and 16 bits samples but it is +in a single channel format (mono). To fix it for testing, run the following +example command. Sox automatically converts the sample format to stereo by +duplicating the channels. Also the rate would be converted if the file would +be 44100 Hz sampled. + +.. code-block:: bash + + sox /usr/share/sounds/alsa/Front_Center.wav --encoding signed-integer -L -r 48000 -c 2 -b 16 audio_in.raw + +Now the testbench can be executed for the input file and the output can be +converted back to wav format: + +.. code-block:: bash + + ./eqiir_run.sh 16 16 48000 audio_in.raw audio_out.raw + sox --encoding signed-integer -L -r 48000 -c 2 -b 16 audio_out.raw audio_out.wav + +The file can be played from the command line with the following command or +it can be launched to an audio editor tool such as mhWaveEdit: + +.. code-block:: bash + + paplay audio_out.wav + mhwaveedit audio_out.wav + +.. figure:: fig_mhwaveedit.png + + Viewing the result with mhWaveEdit + +Select the green **play** icon to play the clip in the application. Use the +mouse to zoom in on audio waveform details. Select the yellow **play** icon +to play a selected area. diff --git a/developer_guides/testbench/debug_in_testbench.rst b/developer_guides/testbench/debug_in_testbench.rst new file mode 100644 index 00000000..7aa93da0 --- /dev/null +++ b/developer_guides/testbench/debug_in_testbench.rst @@ -0,0 +1,212 @@ +.. _debug-in-testbench: + +Debug Component in Testbench +############################ + +GDB and DDD +*********** + +Code debugging with debugger is an efficient way to find issues in +components. The code may crash or operate incorrectly. The SOF data +structures can be understood better while seeing them in action. + +In a testbench environment, a severe memory access mistake typically results +in a segmentation fault where the operating system traps the application when +it performs illegal memory access to RAM it has not allocated. The debugger +shows the stack trace of calls if this happens for quick spotting of +offending code. + +A stable but incorrectly working component can be examined with breakpoints +and visualization of data structures. + +To initiate debugging, the output from our previous IIR EQ example is +used (refer to :ref:`build-testbench`). The information for debugging is shown below: + +.. code-block:: bash + + ./eqiir_run.sh 16 16 48000 audio_in.raw audio_out.raw + +:: + + Command: ../../testbench/build_testbench/install/bin/testbench + Argument: -d -r 48000 -R 48000 -i audio_in.raw -o audio_out.raw -t ../../build_tools/test/topology/test-playback-ssp5-mclk-0-I2S-eq-iir-s16le-s16le-48k-24576k-codec.tplg -b S16_LE + LD_LIBRARY_PATH: ../../testbench/build_testbench/sof_ep/install/lib:../../testbench/build_testbench/sof_parser/install/lib + +In the above output the command shows the path to the installed testbench +binary. The arguments specify the input and output sample rate, input +and output RAW data files, the topology to use for testing, and the sample +format. The command line options are described when invoking the binary with +switch ``-h``. But for the binary to work correctly, the dynamic libraries path must be instructed for the operating system. This is done by setting the +environment variable ``LD_LIBRARY_PATH`` to the above shown value. + +.. code-block:: bash + + export LD_LIBRARY_PATH=../../testbench/build_testbench/sof_ep/install/lib:../../testbench/build_testbench/sof_parser/install/lib + ../../testbench/build_testbench/install/bin/testbench -h + +If the help text appears, the testbench binary started directly from the +command line works. Next, the testbench can be started in the Data Display +Debugger (DDD) application. DDD is a graphical front-end for the GNU +Debugger (GDB). DDD and the dependencies such as GDB needs to be installed +if it is missing from the development computer. + +.. code-block:: bash + + sudo apt install ddd + +The debugging is started to the previously used shell with the +``LD_LIBRARY_PATH`` set. + +.. code-block:: bash + + ddd ../../testbench/build_testbench/install/bin/testbench + +This opens the debugger window. From there, find the code line just after +topology parsing (currently 295) by scrolling the code window with a mouse +and placing a break point there with the right-mouse button (a red stop +sign). If issues happen at topology parsing or within the component in +instantiating in ``new()``, place the breakpoint to the ``parse_topology()`` +line. + +.. figure:: fig_ddd.png + + The ddd debugger start view. + +.. figure:: fig_add_breakpoint.png + + Breakpoint added with right-mouse click. + +The breakpoint is placed after topology parsing since the component symbols +do not exist in debugger context before it is loaded by the topology. To run +the testbench until breakpoint, select **Program** -> **Run** from the menu +as shown in the image above. Then use your mouse to copy and paste the +argument line output from the previous script run and click **Run**: + +:: + + -d -r 48000 -R 48000 -i audio_in.raw -o audio_out.raw -t ../../build_tools/test/topology/test-playback-ssp5-mclk-0-I2S-eq-iir-s16le-s16le-48k-24576k-codec.tplg -b S16_L + +The execution is now stopped to breakpoint. Since the symbols exist now, the +breakpoints can be added to the component life cycle after ``new()``. Use +the lowest window part with the prompt (gdb) for convenience. + +.. code-block:: bash + + break eq_iir_cmd + break eq_iir_params + break eq_iir_prepare + break eq_iir_copy + break eq_iir_reset + break eq_iir_free + +Next, press **Cont** in the small remote control window next to the main ddd +window. The execution stops at the ``params()`` function in playback start. +To view stream parameters, mouse left-click on **params** in the function +arguments list and use the mouse to right-click "Display \*params". The same +can be done for the dev structure. The suppressed fields in brackets can be +expanded and pointers such as the field ``pipeline`` from dev can be viewed +by right-mouse clicking "Display \*()" from a viewed pointer field. The +boxes can be arranged with the mouse. + +.. figure:: fig_ddd_structs.png + + Viewing data in ddd. + +By further pressing **Cont**, the code can be run into ``prepare()``. The +next **Cont** press brings the execution to ``copy()``. A breakpoint can be +added to a known processing function: + +.. code-block:: bash + + break eq_iir_s32_default + +In the function, step with **Next** over code lines until the read frag +operation for the source buffer is completed. The input frame of two +channels to be consumed and produced can be added to view with the following +command: + +.. code-block:: bash + + graph display x[0]@2 + graph display y[0]@2 + +You can also display the entire sink buffer content to see the circular +update over two periods of data. The format can be changed to hex if desired +with a right-mouse click of the data. + +.. code-block:: bash + + graph display ((int16_t *)sinkb->stream.addr)[0]@192 + +.. note:: + + DDD has data plotting capability but the feature does not work at the + time of this writing. Such a feature can be useful in finding PCM code + data glitches. For a simpler one-time view, ``.gdbinit`` can be set up + with a macro script to plot the buffers with ``gnuplot``. Examples can be + found via web search. + +.. note:: + + Due to code optimization with the ``-O`` flag, some symbols are optimized + out and do not exist in context. Also, the code lines stepping may appear + to be non-linear. The testbench can be built as a debug version with the + cmake build type definition. + + .. code-block:: bash + + cd tools/testbench/build_testbench + cmake -DCMAKE_BUILD_TYPE=Debug .. + make install + + At the time of this writing, the flag does not propagate properly + into generated Makefiles. It may be necessary to manually edit + ``flags.make`` to remove the ``-O3`` flags. They can be found by running: + + .. code-block:: bash + + grep -r "O3" + + +Valgrind +******** + +Valgrind is a C library run-time that does extensive checks for memory +access. It finds and reports issues that normally do not segfault the +testbench. Components with violations would keep running in the firmware but +would cause random instability and failures. + +Using Valgrind is simple. The previously used command line for testbench run +is passed as an argument to the valgrind command: + +.. code-block:: bash + + valgrind ../../testbench/build_testbench/install/bin/testbench -d -r 48000 -R 48000 -i audio_in.raw -o audio_out.raw -t ../../build_tools/test/topology/test-playback-ssp5-mclk-0-I2S-eq-iir-s16le-s16le-48k-24576k-codec.tplg -b S16_L + +.. note:: + + Valgrind finds issues from the current testbench version. The issues + before component ``new()`` and after component ``free()`` are usually due + to shortcuts taken in porting part of SOF to the testbench or from + non-critical features like printing traces. Issues like these that are + found during the component life cycle should be checked and fixed. + +Gprof +***** + +The hotspots of the components can be found with a profiling tool. The +functions that are called most frequently or where the majority of CPU time +is spent are the best candidates to optimize for speed. + +The GNU C compiler (GCC) supports option ``-pg`` to enable the generation of +profiling data when running the executable. There is no cmake build option +for enabling profiling but the cmake files can be hand-edited to contain +``-pg`` instead of ``-g``. + +A run of profiling enabled code generates the data file that is viewed with +the ``gprof`` command. + +.. code-block:: bash + + ../../testbench/build_testbench/install/bin/testbench -d -r 48000 -R 48000 -i audio_in.raw -o audio_out.raw -t ../../build_tools/test/topology/test-playback-ssp5-mclk-0-I2S-eq-iir-s16le-s16le-48k-24576k-codec.tplg -b S16_L + gprof ../../testbench/build_testbench/install/bin/testbench gmon.out diff --git a/developer_guides/testbench/fig_add_breakpoint.png b/developer_guides/testbench/fig_add_breakpoint.png new file mode 100644 index 00000000..479c421b Binary files /dev/null and b/developer_guides/testbench/fig_add_breakpoint.png differ diff --git a/developer_guides/testbench/fig_ddd.png b/developer_guides/testbench/fig_ddd.png new file mode 100644 index 00000000..04b0d6d7 Binary files /dev/null and b/developer_guides/testbench/fig_ddd.png differ diff --git a/developer_guides/testbench/fig_ddd_structs.png b/developer_guides/testbench/fig_ddd_structs.png new file mode 100644 index 00000000..55c46958 Binary files /dev/null and b/developer_guides/testbench/fig_ddd_structs.png differ diff --git a/developer_guides/testbench/fig_mhwaveedit.png b/developer_guides/testbench/fig_mhwaveedit.png new file mode 100644 index 00000000..f1766fdf Binary files /dev/null and b/developer_guides/testbench/fig_mhwaveedit.png differ diff --git a/developer_guides/testbench/fig_process_test_eqiir.png b/developer_guides/testbench/fig_process_test_eqiir.png new file mode 100644 index 00000000..b7678551 Binary files /dev/null and b/developer_guides/testbench/fig_process_test_eqiir.png differ diff --git a/developer_guides/testbench/index.rst b/developer_guides/testbench/index.rst new file mode 100644 index 00000000..0e0cb9c9 --- /dev/null +++ b/developer_guides/testbench/index.rst @@ -0,0 +1,28 @@ +.. _testbench: + +Testbench +######### + +Testbench is a native code environment in computer development for +simulating one or more SOF audio processing components in an SOF +topology-defined test pipeline. The generic C versions of the +components work as such without modifications in the testbench. In an +x86 Linux computer, all normal C code tools can be used for +development. + +The pipeline audio source and sink are normal files to store the PCM +format. The pipeline simulation is scheduled to happen as fast as the +computer can process the data. Typically, the pipelines execute 10 - +100x the speed vs. real time. Therefore, the testbench allows testing +the pipeline with good coverage in a very short time. + +The next sections describe typical usage scenarios with testbench. + +.. toctree:: + :maxdepth: 1 + + build_testbench + debug_in_testbench + prepare_new_component + test_audio_quality + diff --git a/developer_guides/testbench/prepare_new_component.rst b/developer_guides/testbench/prepare_new_component.rst new file mode 100644 index 00000000..1bfc84d5 --- /dev/null +++ b/developer_guides/testbench/prepare_new_component.rst @@ -0,0 +1,78 @@ +.. _prepare-new-component: + +Prepare a New Component for Testbench +##################################### + +Since the introduction of the UUID system for SOF processing components, +we no longer need kernel topology parsing to add new components. However, at +the time of this writing, the testbench is limited in that it does not parse UUIDs from component libraries. + +The following diff shows edits that are needed in order to testbench a new +component called "newcomp". Remember to copy the UUID bytes from the actual +component code. Include it in your component pull request as a commit to keep the testbench updated. + +.. code-block:: diff + + diff --git a/tools/testbench/include/testbench/common_test.h b/tools/testbench/include/testbench/common_test.h + index 5744a84cb..093f115d9 100644 + --- a/tools/testbench/include/testbench/common_test.h + +++ b/tools/testbench/include/testbench/common_test.h + @@ -23,7 +23,7 @@ + #define MAX_OUTPUT_FILE_NUM 4 + + /* number of widgets types supported in testbench */ + -#define NUM_WIDGETS_SUPPORTED 9 + +#define NUM_WIDGETS_SUPPORTED 10 + + struct testbench_prm { + char *tplg_file; /* topology file to use */ + diff --git a/tools/testbench/testbench.c b/tools/testbench/testbench.c + index 9d3c79438..15e00e82f 100644 + --- a/tools/testbench/testbench.c + +++ b/tools/testbench/testbench.c + @@ -30,6 +30,9 @@ DECLARE_SOF_TB_UUID("crossover", crossover_uuid, 0x948c9ad1, 0x806a, 0x4131, + DECLARE_SOF_TB_UUID("tdfb", tdfb_uuid, 0xdd511749, 0xd9fa, 0x455c, + 0xb3, 0xa7, 0x13, 0x58, 0x56, 0x93, 0xf1, 0xaf); + + +DECLARE_SOF_TB_UUID("newcomp", newcomp_uuid, 0x00000000, 0x0000, 0x0000, + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + + + #define TESTBENCH_NCH 2 /* Stereo */ + + /* shared library look up table */ + @@ -43,6 +46,7 @@ struct shared_lib_table lib_table[NUM_WIDGETS_SUPPORTED] = { + {"dcblock", "libsof_dcblock.so", SOF_COMP_DCBLOCK, NULL, 0, NULL}, + {"crossover", "libsof_crossover.so", SOF_COMP_NONE, SOF_TB_UUID(crossover_uuid), 0, NULL}, + {"tdfb", "libsof_tdfb.so", SOF_COMP_NONE, SOF_TB_UUID(tdfb_uuid), 0, NULL}, + + {"newcomp", "libsof_newcomp.so", SOF_COMP_NONE, SOF_TB_UUID(newcomp_uuid), 0, NULL}, + }; + + /* main firmware context */ + +A need also exists to add pipelines generation tests for "newcomp". You +will need to add a pipeline macro ``pipe-newcomp-playback.m4`` into the +``tools/topology/sof`` directory. The file should exist for normal usage +of a playback component. + +.. code-block:: diff + + diff --git a/tools/test/topology/tplg-build.sh b/tools/test/topology/tplg-build.sh + index 5c9dcb02b..1e236b7e8 100755 + --- a/tools/test/topology/tplg-build.sh + +++ b/tools/test/topology/tplg-build.sh + @@ -227,7 +227,7 @@ done + + + # for processing algorithms + -ALG_SINGLE_MODE_TESTS=(asrc eq-fir eq-iir src dcblock tdfb) + +ALG_SINGLE_MODE_TESTS=(asrc eq-fir eq-iir src dcblock tdfb newcomp) + ALG_SINGLE_SIMPLE_TESTS=(test-capture test-playback) + ALG_MULTI_MODE_TESTS=(crossover) + ALG_MULTI_SIMPLE_TESTS=(test-playback) + +.. note:: + + Since the testbench currently only supports playback direction, a + need exists to add a playback pipeline macro even if the + component is meant only for capture direction, such as for a + microphone processing component. diff --git a/developer_guides/testbench/test_audio_quality.rst b/developer_guides/testbench/test_audio_quality.rst new file mode 100644 index 00000000..15203e3a --- /dev/null +++ b/developer_guides/testbench/test_audio_quality.rst @@ -0,0 +1,121 @@ +.. _test-audio-quality: + +Test Audio Quality +################## + +The ``tools/test/audio`` directory contains support for testing objective +audio quality parameters. The tests include gain, frequency response (FR), +dynamic range (DR), and total harmonic distortion plus noise (THD+N). +Definitions can be found in the Audio Engineering Society's AES17 standard. + +Install Octave or Matlab to execute the tests. Matlab is a commercial +product by MathWorks. GNU Octave is a free software tool that is mostly +compatible with Matlab. Install Octave and useful toolboxes for audio +development by entering the following: + +.. code-block:: bash + + sudo apt install octave octave-signal octave-control octave-io + +Start Octave from the correct directory for tests by entering the following: + +.. code-block:: bash + + cd tools/test/audio + octave --gui & + +From the Octave shell, the test script for the IIR EQ component can be +launched for all support sample formats: + +.. code-block:: octave + + help process_test + process_test('eqiir') + +The test outputs a CSV format table with test results such as the following: + +:: + + eqiir test result: Gain (dB) + in \ out, 16, 24, 32 + 16, -7.33, x, x + 24, x, -7.33, x + 32, x, x, -7.33 + + + eqiir test result: Dynamic range (dB CCIR-RMS) + in \ out, 16, 24, 32 + 16, 82.43, x, x + 24, x, 130.40, x + 32, x, x, 149.12 + + + eqiir test result: Worst-case THD+N vs. frequency + in \ out, 16, 24, 32 + 16, -54.93, x, x + 24, x, -98.01, x + 32, x, x, -99.55 + + + eqiir test result: Fails chirp/gain/DR/THD+N/FR + in \ out, 16, 24, 32 + 16, 0/0/0/0/0, x, x + 24, x, 0/0/0/0/0, x + 32, x, x, 0/0/0/0/0 + + + Number of passed tests = 15 + Number of failed tests = 0 + Number of non-applicable tests = 0 + Number of skipped tests = 0 + +The script is currently set up for batch processing in the text console. To +enable graphics plot windows, edit the script to the following: + +.. code-block:: diff + + diff --git a/tools/test/audio/process_test.m b/tools/test/audio/process_test.m + index 1a802b462..6ec6cda2b 100644 + --- a/tools/test/audio/process_test.m + +++ b/tools/test/audio/process_test.m + @@ -48,8 +48,8 @@ t.full_test = 1; % 0 is quick check only, 1 is full set + % visibility set to to 0 only console text is seen. The plots are + % exported into plots directory in png format and can be viewed from + % there. + -t.plot_close_windows = 1; % Workaround for visible windows if Octave hangs + -t.plot_visible = 'off'; % Use off for batch tests and on for interactive + +t.plot_close_windows = 0; % Workaround for visible windows if Octave hangs + +t.plot_visible = 'on'; % Use off for batch tests and on for interactive + t.files_delete = 1; % Set to 0 to inspect the audio data files + + %% Prepare + +When the example test for 24 bit to 24 bit output is executed with +process_test('eqiir', 24, 24), the following plots are generated. They +are useful to visually gain more insight into the component's +characteristics. + +.. figure:: fig_process_test_eqiir.png + + Test results for EQ IIR component: Chirp spectrogram, THD+N frequency sweep, measured FR. + +For new components development when the test set is suitable, such as the +previous example "newcomp", this script requires a small addition. A need exists to create a ``newcomp_run.sh`` script based on existing examples +found in the same directory. Additional customization can also be done such as re-defining the test pass/fail criteria for EQ components. + +.. code-block:: diff + + diff --git a/tools/test/audio/process_test.m b/tools/test/audio/process_test.m + index fd9055cae..1a802b462 100644 + --- a/tools/test/audio/process_test.m + +++ b/tools/test/audio/process_test.m + @@ -373,7 +373,7 @@ end + function test = test_run_process(test, t) + + switch lower(test.comp) + - case {'eqiir', 'eqfir', 'dcblock', 'volume', 'tdfb'} + + case {'eqiir', 'eqfir', 'dcblock', 'volume', 'tdfb', 'newcomp'} + test.ex = sprintf('./%s_run.sh', lower(test.comp)); + otherwise + error('Unknown component'); + diff --git a/developer_guides/topology/topology.rst b/developer_guides/topology/topology.rst index 452e06c2..3e2a23af 100644 --- a/developer_guides/topology/topology.rst +++ b/developer_guides/topology/topology.rst @@ -217,7 +217,7 @@ DAI_ADD macro defined as follows: | **dai_index**: index of the dai in the firmware. Please note that the DAI’s of different types can have the same dai_index. The dai_index information can be found by looking in platform-specific dai array - definitions in the firmware. For example, for apollolake these are + definitions in the firmware. For example, for Apollo Lake these are defined in src/platform/apollolake/dai.c. | **dai_be**: name of CPU DAI as defined in DAI array in the platform driver. | **buffer**: Source/sink buffer the DAI is connected to. This completes the @@ -260,7 +260,7 @@ macro: where: | **format**: is the SSP format ex: I2S or DSP_A or DSP_B etc -| **mclk**: master clock in Hz +| **mclk**: provider clock in Hz | **bclk**: bit clock in Hz | **fsync**: frame sync | **TDM**: TDM info including the slots, width, tx mask and rx mask @@ -292,8 +292,44 @@ where: can be chosen predefined configurations such as MONO_PDM0_MICA, STEREO_PDM0, FOUR_CH_PDM0_PDM1 etc. -2. How to create a new topology? -******************************** +.. _dsp-core-in-topology: + +1.7 DSP Core Index +------------------ + +The topology file can specify on which DSP core a pipeline or component will +be scheduled. + +To specify the DSP core for a pipeline, use the SOF_TKN_SCHED_CORE token +located in tools/topology/m4/pipeline.m4: + +.. code-block:: none + + W_PIPELINE(stream, period, priority, core, initiator, platform) + ... + ` SOF_TKN_SCHED_CORE' STR($4) + ... + +Then specify this 'core' in your pipeline definition, such as in +tools/topology/sof/pipe-dai-playback.m4: + +.. code-block:: none + + W_PIPELINE(N_DAI_OUT, SCHEDULE_PERIOD, SCHEDULE_PRIORITY, SCHEDULE_CORE, SCHEDULE_TIME_DOMAIN, pipe_dai_schedule_plat) + +To specify the DSP core for a component/widget, use the SOF_TKN_COMP_CORE_ID +token located in tools/topology/m4/pga.m4: + +.. code-block:: none + + dnl W_PGA(name, format, periods_sink, periods_source, core, kcontrol0. kcontrol1...etc) + ... + ` SOF_TKN_COMP_CORE_ID' STR($6) + ... + + +2. Create a new topology +************************ Following sections will show how to define single and multipipeline topologies. @@ -561,8 +597,8 @@ The graph below shows the topology defined in Section 3.1. .. image:: images/tplg2.png -3. Debugging topology -********************* +3. Debug topology +***************** SOF topology files include debug.m4 with couple of simple macros to output data. These are used for extracting information from dai_add, @@ -611,3 +647,5 @@ actually push both messages to a dot file: .. _M4: http://www.gnu.org/software/m4/m4.html .. _here: https://www.alsa-project.org/main/index.php/ALSA_topology .. _SOFT: https://github.com/thesofproject/soft + + diff --git a/developer_guides/topology2/topology2.rst b/developer_guides/topology2/topology2.rst new file mode 100644 index 00000000..86b5db62 --- /dev/null +++ b/developer_guides/topology2/topology2.rst @@ -0,0 +1,1422 @@ +.. _topology2: + +Topology 2.0 +############ + +This is a high-level keyword extension on top of the existing ALSA conf topology format designed +to: + +* Simplify the ALSA conf topology definitions by providing high level "classes". In this way, topology + designers can write less configurations for commonly defined objects. + +* Allow simple reuse of objects. Define once and reuse (like M4) with the ability to alter object + configuration attributes from defaults. + +* Allow data type and value verification. This is not done today and frequently crops up in FW bug + reports. + +.. contents:: + +Ingredients +*********** + +A typical 2.0 configuration file consists of the following components: + +* Classes +* Objects +* Arguments +* Conditional includes + +Classes +------- + +Topology today has some common definitions that are often reused with slightly altered +configurations, such as widgets (components), pipelines, dais, pcm, and controls. Topology 2.0 +introduces the concept of reusable class-like definitions that you can use to create commonly +used topology objects. Classes are defined with a new keyword ``Class``. + +A class definition always starts with the ``Class`` keyword followed by two nodes. The first node contains +the class group, and the second node contains the class name. For example: + +.. code-block:: bash + + Class.Base.data {} + +Note that '.' is the node separator in the alsaconf syntax. In the above line, ``Base`` is the class +group and ``data`` is the class name. Currently, the alsatplg compiler supports the following class groups: +widget, pipeline, DAI, control and base. Most of the commonly used topology objects can be classified into +one of these groups. If a new class group is required, the alsatplg compiler should be updated to add +support for it. + +Class Ingredients +''''''''''''''''' + +A minimalistic class definition should consist of the following: + +* One or more attributes declared with the keyword ``DefineAttribute``. Attributes are parameters that + are used to describe the object. For example: + + .. code-block:: bash + + DefineAttribute."name" { + type "string" + } + + "name" is an attribute of type string. + + +* Basic attribute qualifiers with the constructor array and unique attribute name. Attribute qualifiers + should be declared within the ``attributes {}`` node in the class definition. + + .. code-block:: bash + + # attribute qualifiers + attributes { + # + # This tells the compiler how to construct the object's name. For example, if the + # name attribute is set to "EQIIR-Coefficients", the object name will be + # constructed as "class_name.EQIIR-Coefficients" + # + !constructor [ + "name" + ] + # + # objects of the same class instantiated within the same alsaconf node have unique + # name attribute + # + unique "name" + } + +A Simple Class +'''''''''''''' + +The following example demonstrates a simple class definition with two attributes and qualifiers: + +.. code-block:: bash + + Class.Base."data" { + + # name for the data object + DefineAttribute."name" { + type "string" + } + + # bytes data + DefineAttribute."bytes" { + type "string" + } + + # attribute qualifiers + attributes { + # + # This tells the compiler how to construct the object's name. For example, if the + # name attribute is set to "EQIIR-Coefficients", the object name will be + # constructed as "data.EQIIR-Coefficients" + # + !constructor [ + "name" + ] + # + # data objects instantiated within the same alsaconf node should have unique + # name attribute + # + unique "name" + } + } + +The ``data`` class definition belonging to the ``base`` class group contains two attributes, +name and bytes, both of type ``string``. By default, all attributes have the ``integer`` type, unless +specified otherwise, like in the example above. Currently, topology 2.0 supports only ``integer`` and +``string`` types for attributes. + +The attribute qualifiers are used to describe how to instantiate an object from the class definition +and validate the attribute values. + +In the above definition, the ``constructor`` array tells the compiler how to build the object's name. +A data object instantiated with the name ``EQIIR-Coefficients`` will be given the name +``data.EQIIR-Coefficients``, that is the class name followed by '.' followed by the constructor attribute +values separated by '.'. + +The ``unique`` qualifier indicates that multiple data objects instantiated within the same alsaconf node should +have unique values for their ``name`` attribute. If two data objects are instantiated within the same alsaconf +node with the same ``name`` attribute, errors will not occur, but the two object instances will be merged. +Additionally, the attribute values in the second instance will override the attribute values in the first one. +Therefore, it is the topology writer's responsibility to ensure that multiple instances within the same parent +node have different unique attribute values. + +Let's consider another class definition example for the ``pga`` widget belonging to the class group ``Widget``: + +.. code-block:: bash + + Class.Widget."pga" { + # + # Pipeline ID for the pga widget object + # + DefineAttribute."index" {} + + # + # pga object instance + # + DefineAttribute."instance" {} + + # attribute qualifiers + attributes { + # + # The PGA widget name is constructed using the index and instance + # attributes. For ex: "pga.1.1" or "pga.10.2" etc. + # + !constructor [ + "index" + "instance" + ] + + # + # pga widget objects instantiated within the same alsaconf node should have unique + # instance attribute + # + unique "instance" + } + } + +Note that the pga object names are constructed with the class name +``pga`` followed by two attribute values, index and instance. For +example, ``pga.1.1``. Both attributes will have the ``integer`` type +by default because the definitions do not specify the type. In +practice, the unique instance attribute should also be part of the +constructor. + +Attribute default values +'''''''''''''''''''''''' + +Optionally, class definitions can be extended to give default values for their attributes. Let's add a +``uuid`` attribute of type ``string`` to the ``pga`` class and give it a default value: + +.. code-block:: bash + + Class.Widget."pga" { + # + # Pipeline ID for the pga widget object + # + DefineAttribute."index" {} + + # + # pga object instance + # + DefineAttribute."instance" {} + + DefineAttribute."uuid" { + type "string" + } + + # attribute qualifiers + attributes { + # + # The PGA widget name is constructed using the index and instance + # attributes. For ex: "pga.1.1" or "pga.10.2" etc. + # + !constructor [ + "index" + "instance" + ] + + # + # pga widget objects instantiated within the same alsaconf node should have unique + # instance attribute + # + unique "instance" + } + + # default attribute values + uuid "7e:67:7e:b7:f4:5f:88:41:af:14:fb:a8:bd:bf:86:82" + + } + +All pga objects will automatically be given the default uuid as specified above in the class definition. + +Advanced attribute qualifiers +''''''''''''''''''''''''''''' + +Apart from the mandatory basic attribute qualifiers, you can qualify attributes in the class definition +using the following advanced keywords: + +* **Mandatory:** Attributes qualified as mandatory should be provided with a value in the object + instance, failing which the alsatplg compiler will emit an error. Objects with default values in the class + definition need not be qualified as mandatory. Also, note that attributes in the constructor array are + mandatory by default as they are required for building the object's name. + +* **Immutable:** Attribute values that are set in the class definition and cannot be modified in + the object instance. + +* **Deprecated:** Attributes that have been deprecated and should not be set in the object instance. + +* **Automatic:** Attributes whose values are computed by the alsatplg compiler. + +Let's add some extra attributes and advanced qualifers into the pga class definition: + +.. code-block:: bash + + Class.Widget."pga" { + # attribute definitions + DefineAttribute.instance { + type "integer" + } + DefineAttribute.index { + type "integer" + } + DefineAttribute."type" { + type "string" + } + DefineAttribute."uuid" { + type "string" + } + DefineAttribute."preload_count" {} + + # attribute qualifiers + attributes { + # + # The PGA widget name is constructed using the index and instance attributes. + # For ex: "pga.1.1" or "pga.10.2" etc. + # + !constructor [ + "index" + "instance" + ] + + # + # immutable attributes should be given default values and cannot be modified in the object instance + # + !immutable [ + "uuid" + "type" + ] + + # + # deprecated attributes should not be added in the object instance + # + !deprecated [ + "preload_count" + ] + + # + # pga widget objects instantiated within the same alsaconf node should have + # unique instance attribute + # + unique "instance" + } + + # default attribute values + type "pga" + uuid "7e:67:7e:b7:f4:5f:88:41:af:14:fb:a8:bd:bf:86:82" + } + +Automatic attributes +'''''''''''''''''''' + +In some cases, an attribute value depends on other attribute values +and need to be computed during build time. Such attributes are +qualified with the ``automatic`` keyword in the class definition. +Refer to buffer_ for the complete class definition. + +.. code-block:: bash + + Class.Widget."buffer" { + # Other attributes skipped for simplicity. + + # + # Buffer size in bytes. Will be calculated based on the parameters of the pipeline to in which the + # buffer object belongs + # + DefineAttribute."size" { + # Token reference and type + token_ref "sof_tkn_buffer.word" + } + + attributes { + # + # size attribute value for buffer objects is computed in the compiler + # + !automatic [ + "size" + ] + } + } + +In the example above, the ``size`` attribute value of ``buffer`` is +computed based on the pipeline parameters, to which the buffer +belongs. Currently, the alsatplg compiler only has support for +computing the automatic attribute ``size`` for the buffer objects. +Support for automatic attributes in new class definitions +should be added in the alsatplg compiler if necessary. + +Attribute Constraints +''''''''''''''''''''' + +One of the key features of Topology 2.0 is validation of the values provided for objects. This is achieved +with the help of constraints added to the attribute definition. Constraints can be added to an attribute using +the ``constraints`` keyword: + +.. code-block:: bash + + DefineAttribute."foo" { + constraints {} + } + +Currently, three types of constraints are supported: + +* **min:** The minimum value for an attribute, applicable only to integer type attributes. +* **max:** The maximum value for an attribute, applicable only to integer type attributes. + + For example, the pga class definition can be expanded with an attribute for ``ramp_step_ms`` with min and + max values as follows: + + .. code-block:: bash + + DefineAttribute."ramp_step_ms" { + constraints { + min 200 + max 500 + } + } + +* **valid values:** an array of acceptable human-readable values, applicable only to string type attributes. + + For example, the pga class can have an attribue for ``ramp_step_type`` with pre-defined values as follows: + + .. code-block:: bash + + DefineAttribute."ramp_step_type" { + type "string" + constraints { + !valid_values [ + "linear" + "log" + "linear_zc" + "log_zc" + ] + } + } + +When the pga class is instantiated with a value that does not belong in +the ``valid_values`` array for ``ramp_step_type``, the alsatplg compiler emits +an error along with the list of valid values. + +Attributes with token references +'''''''''''''''''''''''''''''''' + +Typically, a lot of objects contain a private data section that is +composed of sets of tuple arrays. Some of the attributes in a class +definition may need to be packed into the tuple array. Such attributes +are identified with the ``token_ref`` node which contains the name of +the tuple array that the attribute should be built into. For example, +both the ``ramp_step_ms`` and ``ramp_step_type`` attributes in the pga +class need to be added to the tuple array. So, they contain the +token_ref node with the value ``sof_tkn_volume.word`` indicating that +the attributes should be packed with the ``sof_tkn_volume tuple`` +array of type ``word``: + +.. code-block:: bash + + # + # Volume ramp step in milliseconds + # + DefineAttribute."ramp_step_ms" { + # Token set reference name + token_ref "sof_tkn_volume.word" + constraints { + min 200 + max 500 + } + } + DefineAttribute."ramp_step_type" { + type "string" + # Token set reference name + token_ref "sof_tkn_volume.word" + constraints { + !valid_values [ + "linear" + "log" + "linear_zc" + "log_zc" + ] + } + } + +Sometimes, ``valid_values`` for attributes might need to be translated +from the human readable values to integer tuple values so that it can +be parsed correctly by the kernel driver. In the example above, valid +values for ``ramp_step_type`` are defined as human readable string +values, such as linear and log. These values are translated to tuple +values (0, 1, etc) before getting added to the tuple array. + +.. code-block:: bash + + DefineAttribute."ramp_step_type" { + type "string" + # Token set reference name + token_ref "sof_tkn_volume.word" + constraints { + !valid_values [ + "linear" + "log" + "linear_zc" + "log_zc" + ] + !tuple_values [ + 0 + 1 + 2 + 3 + ] + } + } + +.. _complete_class_definition: + +A complete class definition +''''''''''''''''''''''''''' + +Putting it all together, the following example demonstrates the complete definition for the pga widget class: + +.. code-block:: bash + + Class.Widget."pga" { + # attribute definitions + DefineAttribute.instance { + type integer + } + DefineAttribute.index { + type integer + } + DefineAttribute."type" { + type "string" + } + DefineAttribute."uuid" { + type "string" + # Token set reference name and type + token_ref "sof_tkn_comp.uuid" + } + DefineAttribute."preload_count" {} + + # + # Volume ramp step in milliseconds + # + DefineAttribute."ramp_step_ms" { + # Token set reference name + token_ref "sof_tkn_volume.word" + constraints { + min 200 + max 500 + } + } + DefineAttribute."ramp_step_type" { + type "string" + # Token set reference name + token_ref "sof_tkn_volume.word" + constraints { + !valid_values [ + "linear" + "log" + "linear_zc" + "log_zc" + ] + !tuple_values [ + 0 + 1 + 2 + 3 + ] + } + } + + # attribute qualifiers + attributes { + # + # The PGA widget name is constructed using the index and instance attributes. + # For ex: "pga.1.1" or "pga.10.2" etc. + # + !constructor [ + "index" + "instance" + ] + + # + # immutable attributes cannot be modified in the object instance + # + !immutable [ + "uuid" + "type" + ] + + # + # deprecated attributes should not be added in the object instance + # + !deprecated [ + "preload_count" + ] + + # + # pga widget objects instantiated within the same alsaconf node should have + # unique instance attribute + # + unique "instance" + } + + # default attribute values + type "pga" + uuid "7e:67:7e:b7:f4:5f:88:41:af:14:fb:a8:bd:bf:86:82" + ramp_step_ms 200 + } + +Objects +------- + +Objects are used to instantiate multiple instances of the same class to avoid duplicating +common attribute definitions. Objects are instantiated with the new keyword ``Object`` followed by +three nodes: + +.. code-block:: bash + + Object.Widget.pga."1" {} + +The nodes refer to the following elements: + +* Class group to which the object class belongs. In this case, the class belongs to ``Widget``. +* Class name. That is ``pga``. +* Unique attribute value. This is the value for the attribute that is qualified as ``unique`` in the + class definition. That is ``instance``. + +Using the pga class definition as described in +:ref:`complete_class_definition`, you can instantiate a pga widget +object in the following way: + +.. code-block:: bash + + Object.Widget.pga."1" { + index 5 + } + +where ``1`` is the value for the unique attribute ``instance`` in the pga class definition and the +``index`` attribute is given the value of 5. As the class definition contains no other mandatory +attributes, the above instance is fully valid. + +.. important:: + + You do not need to duplicate commonly used attribute values in the + object instantiation. Objects automatically inherit the default + values for attributes from their class definition. + +Modifying default attributes +'''''''''''''''''''''''''''' + +Attributes that have default values in the class definition can be overwritten by specifying the +new value in the object instance: + +.. code-block:: bash + + Object.Widget.pga."1" { + index 5 + ramp_step_ms 300 + } + +The above object overrides the ``ramp_step_ms`` default value of 200 |_| ms set in the class definition with the +new value of 300 |_| ms. + +Objects within classes +'''''''''''''''''''''' + +Class definitions can optionally also include child objects that need to be instantiated for every +instance of the class object. For example, a pga widget typically always contains a volume mixer control. +The mixer control class definition is as follows: + +.. code-block:: bash + + Class.Control."mixer" { + # + # Pipeline ID for the mixer object + # + DefineAttribute."index" {} + + # + # Instance of mixer object in the same alsaconf node + # + DefineAttribute."instance" {} + + # + # Mixer name. A mixer object is included in the built topology only if it is given a + # name + # + DefineAttribute."name" { + type "string" + } + + # + # Max volume setting + # + DefineAttribute."max" {} + + DefineAttribute."invert" { + type "string" + constraints { + !valid_values [ + "true" + "false" + ] + } + } + + # use mute LED + DefineAttribute."mute_led_use" { + token_ref "sof_tkn_mute_led.word" + } + + # LED direction + DefineAttribute."mute_led_direction" { + token_ref "sof_tkn_mute_led.word" + } + + # + # access control for mixer + # + DefineAttribute."access" { + type "compound" + constraints { + !valid_values [ + "read_write" + "tlv_read_write" + "read" + "write" + "volatile" + "tlv_read" + "tlv_write" + "tlv_command" + "inactive" + "lock" + "owner" + "tlv_callback" + ] + } + } + + attributes { + # + # The Mixer object name is constructed using the index and instance arguments. + # For ex: "mixer.1.1" or "mixer.10.2" etc. + # + !constructor [ + "index" + "instance" + ] + !mandatory [ + "max" + ] + # + # mixer control objects instantiated within the same alsaconf node should have unique + # index attribute + # + unique "instance" + } + + # Default attribute values for mixer control + invert "false" + mute_led_use 0 + mute_led_direction 0 + } + +You can add a mixer control object to the pga widget class definition: + +.. code-block:: bash + + Class.Widget."pga" { + # Attributes, qualifiers and default values are skipped for simplicity. + # Refer to the complete class definition in "Complete Class Definition" for details + + # volume control for pga widget + Object.Control.mixer."1" { + name "My Volume Control" + max 32 + } + } + } + +The mixer control ``My Volume Control`` will be programmatically added to all pga objects. + +Object attribute inheritance +'''''''''''''''''''''''''''' + +One thing to note in the above object instantiation is that the mixer object has two mandatory attributes, +index and instance. But the index attribute value is missing in the instance. This is because the mixer control +object inherits the index attribute value from its parent pga object when it gets instantiated. For example, +consider the following pga object instance: + +.. code-block:: bash + + Object.Widget.pga.1 { + index 5 + } + +The mixer control object in the pga class definition inherits the index value of ``5``. Inheritance occurs +only when a child object's class definition shares an attribute of the same name with its parent class +definition. In the case of mixer control class and pga widget class, the shared attribute is ``index``. + +.. _setting_child_object_attributes: + +Setting child object attributes +''''''''''''''''''''''''''''''' + +Let's consider the pga class definition with the mixer control object again: + +.. code-block:: bash + + Class.Widget."pga" { + # Attributes, qualifiers and default values are skipped for simplicity. + # Please refer to the complete class definition above for details + + # volume control for pga widget + Object.Control.mixer."1" { + name "My Volume Control" + max 32 + } + } + } + +Note that the mixer control object has its name set in the pga widget class definition. But, ideally, we want to +give the mixer control a new name whenever a new pga widget object is instantiated. You can do it like this: + +.. code-block:: bash + + Object.Widget.pga."1" { + index 5 + + # volume control' + Object.Control.mixer."1" { + name "My Control Volume 5" + } + } + } + +Now, the mixer control object is assigned the name ``My Control Volume 5``. + + +Nested Objects +'''''''''''''' + +Objects can also be instantiated as child objects within other object instances. For example, a +switch control can be added to pga widget objects during instantiation: + +.. code-block:: bash + + Object.Widget.pga."1" { + index 5 + + # volume control + Object.Control.mixer."1" { + name "My Control Volume 5" + } + } + + # mute control + Object.Control.mixer."2" { + name "Mute Switch Control" + max 1 + } + } + } + +Note how the ``unique`` attribute for the two mixer control objects differs to keep the mixer instances unique. + +Recursive object attribute inheritance +'''''''''''''''''''''''''''''''''''''' + +Objects can be nested within objects that are nested within other objects themselves. In this case, the attribute +values can be inherited all the way from the top-level parent object. For example, consider the following class +definition for volume-playback pipeline: + +.. code-block:: bash + + Class.Pipeline."volume-playback" { + # Other attributes and qualifiers ommitted for simplicity + DefineAttribute."index" {} + + DefineAttribute."format" { + type "string" + } + + # pipeline objects + Object.Widget { + # Other objects ommitted for simplicity + + pga."1" {} + } + } + +Note that the pga widget object above has no index attribute value. An object of volume-playback +class is instantiated as: + +.. code-block:: bash + + Object.Pipeline.volume-playback.1 { + index 1 + format s24le + } + +This ensures that all child objects within the volume-playback object will inherit the +index attribute value from it. So the pga widget object will have the same index. By the same +rule, the mixer control object within the pga widget object will also have the same index attribute +value of 1. + +Setting child object attributes deep down in the parent object tree +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +In :ref:`setting_child_object_attributes`, we saw that we can set child attribute values from its parent object instance. +For example, you can set the mixer control object's name from the pga widget object instance. This +can be extended further and it is possible to set the mixer control name from the parent object of +the pga object. Consider the volume playback object instance in the previous section. We can set the +mixer control name for the pga object as follows: + +.. code-block:: bash + + Object.Pipeline.volume-playback.1 { + index 1 + format s24le + Object.Widget.pga.1 { + Object.Control.mixer.1 { + name "My Control Volume 1" + } + } + } + + +Arguments in top-level configuration files +------------------------------------------ + +Arguments are used to pass build-time parameters that can be used for building multiple binaries +from the same configuration file. Consider the following top-level topology configuration file +with two pipelines: + +.. code-block:: bash + + # arguments + @args [ DYNAMIC_PIPELINE ] + @args.DYNAMIC_PIPELINE { + type integer + default 0 + } + + Object.Pipeline { + volume-playback.1 { + dynamic_pipeline $DYNAMIC_PIPELINE + index 1 + Object.Widget.pipeline.1 { + stream_name 'dai.HDA.0.playback' + } + Object.Widget.host.playback { + stream_name 'Passthrough Playback 0' + } + Object.Widget.pga.1 { + Object.Control.mixer.1 { + name '1 My Playback Volume' + } + } + format s24le + } + volume-playback.3 { + dynamic_pipeline $DYNAMIC_PIPELINE + index 3 + Object.Widget.pipeline.1 { + stream_name 'dai.HDA.2.playback' + } + Object.Widget.host.playback { + stream_name 'Passthrough Playback 1' + } + Object.Widget.pga.1 { + Object.Control.mixer.1 { + name '3 My Playback Volume' + } + } + format s24le + } + } + +In this example, the value for the ``dynamic_pipeline`` attribute in the volume-playback objects +is expanded from the provided value for the ``DYNAMIC_PIPELINE`` argument when building the +topology binary with the ``-DDYNAMIC_PIPELINE=1`` or ``-DDYNAMIC_PIPELINE=0`` option. + +.. note:: + + The alsatplg compiler only parses the arguments that are defined at + the top-level node in the machine topology file. + +Includes +-------- + +When building a top-level configuration file, it should include all +class definitions for the objects being instantiated, failing which +the compiler will emit errors calling out missing class definitions. +All paths are relative to the directory specified by the environment +variable ``ALSA_CONFIG_DIR``. You can specify the include paths for +dependencies as follows: + +.. code-block:: bash + + + + + +Include the class definitions as follows: + +.. code-block:: bash + + + + + + +.. _simple_machine_topology: + +Simple machine topology +*********************** + +A machine topology typically consists of the following: + +* Include paths pointing to the search directory for class definitions includes +* Conf file Includes containing class definitions +* Arguments +* Pipeline objects +* BE DAI links objects +* PCM objects +* Top-level pipeline connections + +Let's consider a simple machine topology configuration file that includes a volume-playback pipeline, +a HDA type DAI link, a playback PCM, and the top-level connection: + +.. code-block:: bash + + # Include paths + + + + + + + + # Include class definitions + + + + + + + + + + + + + + # arguments + @args.DYNAMIC_PIPELINE { + type integer + default 0 + } + + # DAI definition + Object.Dai { + HDA.0 { + name 'Analog Playback and Capture' + id 4 + default_hw_conf_id 4 + Object.Base.hw_config.HDA0 {} + Object.Widget.dai.1 { + direction playback + index 1 + type dai_in + stream_name 'Analog Playback and Capture' + period_sink_count 0 + period_source_count 2 + format s32le + } + } + } + + + # Pipeline Definition + Object.Pipeline { + volume-playback.1 { + dynamic_pipeline $DYNAMIC_PIPELINE + index 1 + Object.Widget.pipeline.1 { + stream_name 'dai.HDA.0.playback' + } + Object.Widget.host.playback { + stream_name 'Passthrough Playback 0' + } + Object.Widget.pga.1 { + Object.Control.mixer.1 { + name '1 My Playback Volume' + } + } + format s24le + } + } + + # PCM Definitions + Object.PCM { + pcm.0 { + name 'HDA Analog' + Object.Base.fe_dai.'HDA Analog' {} + Object.PCM.pcm_caps.playback { + name 'Passthrough Playback 0' + formats 'S24_LE,S16_LE' + } + direction playback + id 0 + } + } + + # Top-level pipeline connection + # Buffer.1. -> dai.HDA.1.playback + Object.Base.route.1 { + source 'buffer.1.1' + sink 'dai.HDA.1.playback' + } + +Note that the above configuration file only includes the top-level route between the buffer widget +``buffer.1.1`` in the volume-playback pipeline and the dai widget ``dai.HDA.1.playback``. The connections +between the widgets in the volume-playback pipeline are defined in the class definition. + +Let's peek into the volume-playback pipeline class definition to look at the route objects contained within +the class definition. Refer to volume-playback_ for the complete class definition. + +.. code-block:: bash + + Class.Pipeline."volume-playback" { + # pipeline attributes skipped for simplicity + + attributes { + # pipeline name is constructed as "volume-playback.1" + !constructor [ + "index" + ] + !mandatory [ + "format" + ] + !immutable [ + "direction" + ] + # + # volume-playback objects instantiated within the same alsaconf node should have + # unique instance attribute + # + unique "instance" + } + + # Widget objects that constitute the volume-playback pipeline + Object.Widget { + pipeline."1" {} + + host."playback" { + type "aif_in" + } + + buffer."1" { + periods 2 + caps "host" + } + + pga."1" { + Object.Control.mixer.1 { + Object.Base.tlv."vtlv_m64s2" { + Object.Base.scale."m64s2" {} + } + } + } + + buffer."2" { + periods 2 + caps "dai" + } + } + + # Pipeline connections. + # The index attribute values for the source/sink widgets will be populated + # when the route objects are built + Object.Base { + route."1" { + source "host..playback" + sink "buffer..1" + } + + route."2" { + source "buffer..1" + sink "pga..1" + } + + route."3" { + source "pga..1" + sink "buffer..2" + } + } + + # Default attribute values + direction "playback" + time_domain "timer" + period 1000 + channels 2 + rate 48000 + priority 0 + core 0 + frames 0 + mips 5000 + } + +The pipeline class definition is fairly easy to follow except for the route object instances. +Let's analyze it a bit further. The route class definition is defined as follows: + +.. code-block:: bash + + Class.Base."route" { + # sink widget name + DefineAttribute."sink" { + type "string" + } + + # source widget name for route + DefineAttribute."source" { + type "string" + } + + # control name for the route + DefineAttribute."control" { + type "string" + } + + # + # Pipeline ID of the pipeline the route object belongs to + # + DefineAttribute."index" {} + + # unique instance for route object in the same alsaconf node + DefineAttribute."instance" {} + + attributes { + !constructor [ + "instance" + ] + !mandatory [ + "source" + "sink" + ] + # + # route objects instantiated within the same alsaconf node should have unique + # index attribute + # + unique "instance" + } + } + +Note that a route object is expected to have instance, source, and sink attributes. + +Let's consider the route objects in the volume-playback class again: + +.. code-block:: bash + + Object.Base { + route."1" { + source "host..playback" + sink "buffer..1" + } + + route."2" { + source "buffer..1" + sink "pga..1" + } + + route."3" { + source "pga..1" + sink "buffer..2" + } + } + +Notice that the source and sink attributes are defined for all of the routes. For example, the second route object +``Object.Base.route.2`` has a sink attribute value of ``pga..1``. Referring back to the pga widget class definition +in :ref:`complete_class_definition`, we know that a pga widget object's constructor has two attributes, ``index`` and ``instance``. +We know the instance of the pga widget in the volume-playback class is 1 by looking at the list of widgets. +But the index attribute value for the pga widget in the pipeline is unknown. It will only be set from a top-level +topology config file as in :ref:`simple_machine_topology`. Therefore, the index attribute is left empty in the class definition. +The alsatplg compiler will populate the index attribute with the appropriate value when the route object is built. For the +machine topology above, the route object ``Object.base.route.2`` will be built with the right pipeline IDs as follows: + +.. code-block:: bash + + Object.base.route.2 { + source "buffer.1.1" + sink "pga.1.1" + } + +Currently, alsatplg can fill in attribute values only for the route object source +and sink attributes. If needed, this feature can be extended for other types of objects. + +Conditional includes +******************** + +Conditional includes allow building multiple topology binaries from the same input configuration file. +For example, let's consider the HDA generic machine topology. The number of DMICs determines whether +the DMIC configuration file should be included or not. This can be achieved as follows: + +.. code-block:: bash + + @args.DMIC_COUNT { + type integer + default 0 + } + + # include DMIC config if needed + IncludeByKey.DMIC_INCLUDE { + "[1-4]" "include/platform/intel/dmic-generic.conf" + } + +The regular expression ``[1-4]`` indicates that the dmic-generic.conf file should be included if +the DMIC_COUNT argument value is between 1 and 4. Assuming the top-level file is called +``sof-hda-generic.conf``, you can build two separate topology binaries with the following commands: + +* For machines with no DMICs: + + ``alsatplg -p -c sof-hda-generic.conf -o sof-hda-generic.tplg`` + +* For machines with two DMICs: + + ``alsatplg -D DMIC_COUNT=2 -p -c sof-hda-generic.conf -o sof-hda-generic-2ch.tplg`` + +Conditional includes are not limited to top-level configuration files. You can add them to any node +in the configuration file to include the configuration at the specified node. For example, we can conditionally +include the right filter coefficients for the byte controls in the EQIIR widget. + +Define the argument for the coefficients in the top-level topology file: + +.. code-block:: bash + + @args.EQIIR_BYTES { + type string + default "highpass_40hz_0db_48khz" + } + +And then include the coefficients: + +.. code-block:: bash + + Object.Widget.eqiir.1 { + Object.Control.bytes.1 { + name "my eqiir byte control" + # EQIIR filter coefficients + IncludeByKey.EQIIR_BYTES { + "[highpass.40hz.0db.48khz]" "include/components/eqiir/highpass_40hz_0db_48khz.conf" + "[highpass.40hz.20db.48khz]" "include/components/eqiir/highpass_40hz_20db_48khz.conf" + } + } + } + +Building 2.0 configuration files +******************************** + +You can use alsatplg to compile Topology 2.0 configuration files and produce the topology binary files: + +.. code-block:: bash + + alsatplg <-D args=values> -p -c input.conf -o output.tplg + +The ``-D`` switch is used to pass comma-separated argument values to the top-level configuration file. + +You can use the ``-P`` switch to convert a 2.0 configuration file to the 1.0 configuration file: + +.. code-block:: bash + + alsatplg <-D args=values> -P input.conf -o output.conf + +Split topologies +**************** + +Linux kernel can load multiple topologies, a topology for a single function. +This feature is useful to support SDCA setups with standardized components. And no need to create topologies +for every new product. To achieve this, you need to split the topology into multiple tplg files. +The split topology files should be named as follows: + +.. code-block:: bash + + sof---id.tplg + +Currently is only needed for the DMIC function and not needed for SDCA functions in general. +It should be mtl, lnl, etc. + +Where should be one of + +.. code-block:: bash + + sdca-jack: SDCA headphone and headset. + sdca-amp: SDCA amp, where n is the amp link numbers. + sdca-mic: SDCA host mic. + dmic-ch: PCH DMIC, where n is the number of supported channels. Currently, only 2ch and 4ch are supported. + hdmi-pcm: HDMI with PCM id starts from . The is 3 for the "sof-hda-dsp" card and 5 for other cards. + + +For example + +.. code-block:: bash + + sof-sdca-2amp-id2.tplg + sof-sdca-mic-id4.tplg + sof-arl-dmic-2ch-id5.tplg + sof-hdmi-pcm5-id7.tplg + +The split topologies are the subset of the monolithic topology. Usually, you just need to add a description with proper +macro settings to disable the features that you don't need and set the first BE ID that in the topology in the cmake file +to generate the split topologies. + +For example + +.. code-block:: bash + + "cavs-sdw\;sof-arl-sdca-2amp-id2\;PLATFORM=mtl,NUM_SDW_AMP_LINKS=2,SDW_JACK=false,\ + SDW_AMP_FEEDBACK=false,SDW_SPK_STREAM=Playback-SmartAmp,NUM_HDMIS=0" + + +Topology reminders +****************** + +Review the following topology considerations: + +- "index" refers to the pipeline ID in pipeline, widget, and control class groups. + +- "id" in the DAI class group objects refers to the link ID as defined in the machine driver in the kernel. + +Alsaconf reminders +****************** + +Review the following alsaconf considerations: + +- "." refers to a node separator. "foo.bar value" is quivalent to the following: + + .. code-block:: bash + + foo { + bar value + } + +- Arrays are defined with []. For example: + + .. code-block:: bash + + !constructor [ + "foo" + "bar" + ] + + We recommend to use the exclamation mark (!) in array definitions + within the class definition. Use it to ensure that the array items + are not duplicated if the class configuration file is included more + than once from different sources. + +.. _volume-playback: https://github.com/thesofproject/sof/blob/main/tools/topology/topology2/include/pipelines/volume-playback.conf +.. _buffer: https://github.com/thesofproject/sof/blob/main/tools/topology/topology2/include/components/buffer.conf + +.. |_| unicode:: 0xA0 + :trim: diff --git a/developer_guides/unit_tests.rst b/developer_guides/unit_tests.rst index ccb22201..93e0e0e7 100644 --- a/developer_guides/unit_tests.rst +++ b/developer_guides/unit_tests.rst @@ -14,21 +14,48 @@ For a successful compilation, it needs a toolchain thats supports C stdlib. Configuring for unit tests ************************** -In order to build and run unit tests, just pass additional flag to -CMake **-DBUILD_UNIT_TESTS=ON**. +Unit tests are built from the same, top-level CMakeLists.txt as the +firmware but with different CMake flags: **-DBUILD_UNIT_TESTS=ON** and a +couple others. + +Building unit tests can be more complex than building the firmware +because for the firmware the script ``./xtensa-build-all.sh`` hides most +the CMake configuration. For unit tests you must find a working +combination of environment variables and CMake flags. Fortunately +``./xtensa-build-all.sh`` logs some of its magic that you can "steal" +and re-use to build unit tests. Like this: + +- Export ``XTENSA_TOOLS_ROOT`` as you normally do when building the + firmware. +- Build the firmware using ``./xtensa-build-all.sh`` and take note of the + following variables in the build log: ``PATH``, ``XTENSA_SYSTEM`` and + the ``-DROOT_DIR`` parameter. +- ``export`` the ``PATH`` and ``XTENSA_SYSTEM`` values found above. +- Run cmake with ``-DBUILD_UNIT_TESTS=ON``, the ``-DROOT_DIR`` parameter above, + ``-DINIT_CONFIG`` and a new build directory +- Build and run the tests with ``make test`` or ``ninja test``. -Unit tests need a valid config for a used toolchain, so before building them you can use a default config such as: +.. note:: -.. code-block:: bash + Use -DTOOLCHAIN=xt option. - make _defconfig + As of December 2021, -DTOOLCHAIN=xtensa--elf is not + supported. You can use a native toolchain, see below. -Then build and run all unit tests by entering: +If you get this double ``uintptr_t`` definition error: .. code-block:: bash - make -j4 && ctest -j8 + [ 2%] Building C object test/cmocka/CMakeFiles/common_mock.dir/src/common_mocks.c.o + In file included from sof/test/cmocka/src/common_mocks.c:29: + sof/but/cmocka_git/src/cmocka_git/include/cmocka.h:132: + error: redefinition of typedef ‘uintptr_t’ + xcc/install/builds/RG-2017.8-linux/X4H3I16w2D48w3a_2017_8/xtensa-elf/include/stdint.h:252: + error: previous declaration of ‘uintptr_t’ was here +... then append this to your cmake invocation: ``-DEXTRA_CFLAGS=-D_UINTPTR_T_DEFINED=1`` + +Additional unit tests options can be found in :ref:`cmake`. Example: Running tests for APL ============================== @@ -36,13 +63,24 @@ Example: Running tests for APL .. code-block:: bash mkdir build_ut && cd build_ut - cmake -DTOOLCHAIN=xt -DROOT_DIR=$CONFIG_PATH/xtensa-elf -DBUILD_UNIT_TESTS=ON .. - make apollolake_defconfig + cmake -DBUILD_UNIT_TESTS=ON -DTOOLCHAIN=xt -DINIT_CONFIG=apollolake_defconfig \ + -DROOT_DIR=/xcc/install/builds/RG-2017.8-linux/X4H3I16w2D48w3a_2017_8/xtensa-elf .. make -j4 && ctest -j8 -.. note:: +Compiling unit tests without a cross-compilation toolchain +========================================================== + +You can also compile and run unit tests with your native compiler: + +.. code-block:: bash + + rm -rf build_ut/ + cmake -B build_ut/ -DBUILD_UNIT_TESTS_HOST=yes \ + -DBUILD_UNIT_TESTS=ON -DINIT_CONFIG=something_defconfig + make -C build_ut/ -j8 && make -C build_ut/ test - Use -DTOOLCHAIN=xt option, -DTOOLCHAIN=xtensa--elf is not supported +The ``scripts/run-cmocks.sh`` script does all that and can also run unit +tests with valgrind. Wrapping objects for unit tests ******************************* diff --git a/developer_guides/xtrun/index.rst b/developer_guides/xtrun/index.rst new file mode 100644 index 00000000..f48930ff --- /dev/null +++ b/developer_guides/xtrun/index.rst @@ -0,0 +1,145 @@ +.. _xtrun: + +Xtensa Simulator (xt-run) +######################### + +Prerequisites +************* + +The Xtensa Simulator (``xt-run``) is a proprietary Xtensa toolchain used to +run Xtensa ELFs. This guide assumes that you have correctly installed it and +the core for your platform. It describes how to use xt-run. + +Running the simulation for your platform requires that the core is set with +the **XTENSA_CORE** environment variable (just like ``xt-xcc``). If you can +successfully build the firmware with the ``xt-xcc`` compiler, then +everything is set up. + +Standalone programs +******************* + +Development with ``xt-xcc`` and ``xt-run`` is similiar to the usual +development of \*nix programs. + +Begin with a *"Hello World!"* example. Save this snippet as **test.c**: + +.. code-block:: c + + #include + + int main() { + printf("Hello World!\n"); + return 0; + } + +In order to run this program, first build Xtensa ELF with ``xt-xcc``: + +.. code-block:: bash + + xt-xcc test.c -o test + +Next, run the output binary with ``xt-run``: + +.. code-block:: bash + + xt-run test + +You can run any code independently like this, such as for testing some +algorithms. + +Progams that run in ``xt-run`` additionally support ``stdlib`` (not +available in the usual FW) so you can use ``stdio`` to print your output. All +core-specific features are also supported by ``xt-run`` so you can use +intrinsics (such as HiFi3) in your C programs. + +Unit tests +********** + +In the SOF project, ``xt-run`` is used as the executor for unit tests. + +The below example shows how you can add a simple unit test case for a sample +function: ``my_add`` in the ``math`` module. + +First, add a function that is going to be the subject of the unit test: + +.. code-block:: c + :caption: src/include/sof/math/numbers.h + + int my_add(int a, int b); + +.. code-block:: c + :caption: src/math/numbers.c + + int my_add(int a, int b) + { + return a + b; + } + +Next, add the unit test implementation: + +.. code-block:: c + :caption: test/cmocka/src/math/numbers/my_add.c + + // header with function that we test + #include + + // standard headers that have to be included in every cmocka's unit test + #include + #include + #include + #include + #include + + // one of test cases + static void my_add_2_plus_3_equals_5(void **state) + { + int result; + + (void)state; + + result = my_add(2, 3); + assert_int_equal(result, 5); + } + + int main(void) + { + // list of all test cases, here we have just 1 + const struct CMUnitTest tests[] = { + cmocka_unit_test(my_add_2_plus_3_equals_5), + }; + + cmocka_set_message_output(CM_OUTPUT_TAP); + + return cmocka_run_group_tests(tests, NULL, NULL); + } + +Use a single file for every function that is unit-tested; this is why we put +code in the ``my_add.c`` file in the ``test/cmocka/src/math/numbers`` +directory. + +Lastly, let CMake know that the unit test exists: + +.. code-block:: cmake + :caption: test/cmocka/src/math/numbers/CMakeLists.txt + + cmocka_test(my_add + my_add.c + ${PROJECT_SOURCE_DIR}/src/math/numbers.c + ) + +To run unit tests, follow the instructions at :doc:`../unit_tests`. + +If you want to run just your test case (instead of all tests), you can +replace: + +.. code-block:: bash + + make -j4 && ctest -j8 + +with: + +.. code-block:: bash + + make my_add && ctest -R my_add + +Logs from running ctest can be found in ``Testing/Temporary/LastTest.log``. diff --git a/getting_started/build-guide/build-from-scratch.rst b/getting_started/build-guide/build-from-scratch.rst index 104a8d4c..4c0519af 100644 --- a/getting_started/build-guide/build-from-scratch.rst +++ b/getting_started/build-guide/build-from-scratch.rst @@ -1,69 +1,99 @@ .. _build-from-scratch: -Build SOF from scratch -###################### +Build toolchains and SOF from sources +##################################### .. contents:: :local: :depth: 3 You may boot and test |SOF| on a target machine or VM. Current target -Intel platforms include: |BYT|, |CHT|, |HSW|, |BDW|, |APL|, |CNL|, |ICL| and |JSL|. +Intel platforms include: |BYT|, |CHT|, |HSW|, |BDW|, |APL|, |CNL|, |ICL|, |JSL|, and |TGL|. Support also exists for NXP i.MX8/i.MX8X/i.MX8M platforms. -Build SOF -********* - -The following steps describe how to install the SOF development -environment on Ubuntu 16.04, 18.04, and 18.10. They should work on -19.04, 19.10 and other Linux distributions with minor or no -modifications. +The following steps describe how to install the SOF development environment on +Ubuntu 16.04, 18.04, 18.10, and 20.04, and Fedora 36. They should work on Ubuntu +19.04, 19.10 and other Linux distributions with minor or no modifications. .. note:: - ``$SOF_WORKSPACE`` environment variable should point to the directory you - wish to store all sof work in. + Building the toolchains from source might take several hours. We + recommend that you use Docker to build SOF. For more information, + see :ref:`build-with-docker`. - The code examples assume ``$SOF_WORKSPACE`` as the top-level working - directory. Clone all git repositories at the same directory level - because some default configuration files refer to other clones using - relative locations like ``../sof/``. +Step 1. Set up the workspace directory +************************************** -Step 0 Set up the workspace directory -===================================== +Point the ``$SOF_WORKSPACE`` environment variable to the directory in +which you store all sof work. + +The code examples assume ``$SOF_WORKSPACE`` is the top-level working +directory. Clone all git repositories at the same directory level +because some default configuration files refer to other clones using +relative locations like ``../sof/``. + +Make sure that ``$SOF_WORKSPACE`` has adequate disk space when +building the toolchain. About 15GB is needed per toolchain. You can +reclaim some of the disk space after building the toolchain. .. code-block:: bash SOF_WORKSPACE=~/work/sof - mkdir "$SOF_WORKSPACE" + mkdir -p "$SOF_WORKSPACE" -Step 1 Set up build environment -=============================== +Step 2. Set up build environment +******************************** -Install packaged dependencies ------------------------------ +Install package dependencies +============================ +.. note:: -* For Ubuntu 18.10: + This guide uses Ubuntu/Fedora as an example but any modern distribution can be + used for SOF development. + +Due to continuous default package updates in distributions, SOF +documentation may not include explicit instructions for possible missing +tools and packages. When you encounter missing dependencies, refer to your +distribution's documentation on how to install them. + +* For Fedora (tested with v36, other recent versions should work fine): .. code-block:: bash - sudo apt-get install libgtk-3-dev libsdl1.2-dev libspice-protocol-dev \ - libspice-server-dev libusb-1.0-0-dev libusbredirhost-dev libtool-bin \ - acpica-tools valgrind texinfo virt-manager qemu-kvm \ - libvirt-daemon-system libvirt-clients virtinst libfdt-dev libssl-dev \ - pkg-config help2man gawk libncurses5 libncurses5-dev + sudo dnf group install "Development Tools" "C Development Tools and Libraries" + sudo dnf install ncurses-devel gtk3-devel gettext-devel texinfo help2man \ + glibc-static libstdc++-static openssl-devel tree -* For Ubuntu 16.04 and 18.04: +* For Ubuntu 20.04: + + .. code-block:: bash + + sudo apt install build-essential git autoconf flex bison texinfo help2man \ + gawk libtool-bin libncurses5 libncurses5-dev libssl-dev libgtk-3-dev \ + tree ninja-build gettext libasound2-dev + +* For Ubuntu 18.10: .. code-block:: bash - sudo apt-get install libgtk-3-dev libsdl1.2-dev libspice-protocol-dev \ - libspice-server-dev libusb-1.0-0-dev libusbredirhost-dev libtool-bin \ - iasl valgrind texinfo virt-manager qemu-kvm libvirt-bin virtinst \ + sudo apt-get install build-essential git libgtk-3-dev libsdl1.2-dev \ + libspice-protocol-dev libspice-server-dev libusb-1.0-0-dev \ + libusbredirhost-dev libtool-bin acpica-tools valgrind texinfo \ + virt-manager qemu-kvm libvirt-daemon-system libvirt-clients virtinst \ libfdt-dev libssl-dev pkg-config help2man gawk libncurses5 \ libncurses5-dev +* For Ubuntu 16.04 and 18.04: + + .. code-block:: bash + + sudo apt-get install build-essential git libgtk-3-dev libsdl1.2-dev \ + libspice-protocol-dev libspice-server-dev libusb-1.0-0-dev \ + libusbredirhost-dev libtool-bin iasl valgrind texinfo virt-manager \ + qemu-kvm libvirt-bin virtinst libfdt-dev libssl-dev pkg-config help2man \ + gawk libncurses5 libncurses5-dev + If you are using Ubuntu 16.04, the gcc version must be updated to gcc 7.3+ in order for the Advanced Linux Sound Architecture (ALSA) to build. @@ -75,19 +105,20 @@ in order for the Advanced Linux Sound Architecture (ALSA) to build. sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 70 --slave /usr/bin/g++ g++ /usr/bin/g++-7 Install CMake -------------- +============= -If you use Ubuntu 18.04+ you can install CMake with apt: +If you use Ubuntu 18.04+ or Fedora you can install CMake with apt/dnf: .. code-block:: bash - sudo apt-get install cmake + sudo apt-get install cmake # Ubuntu + sudo dnf install cmake # Fedora For Ubuntu 16.04, CMake from apt is outdated and you must install CMake from -sources. Refer to this short guide: https://cmake.org/install/ +sources. Refer to this short guide: https://cmake.org/install/. -Build alsa-lib and alsa-utils ------------------------------ +Build alsa-lib and alsa-utils from source +========================================= This project requires some new features in :git-alsa:`alsa-lib` and :git-alsa:`alsa-utils`, so build the newest ALSA from source code. @@ -104,7 +135,10 @@ This project requires some new features in :git-alsa:`alsa-lib` and cd "$SOF_WORKSPACE" git clone git://git.alsa-project.org/alsa-lib cd alsa-lib + # To install alsa-lib systemwide ./gitcompile + # To install alsa-lib locally + ./gitcompile --prefix=$HOME/local sudo make install (Optional) To enable alsabat's frequency analysis, install the FFT library @@ -112,7 +146,8 @@ before you configure alsa-utils. .. code-block:: bash - sudo apt-get install libfftw3-dev libfftw3-doc + sudo apt-get install libfftw3-dev libfftw3-doc # Ubuntu + sudo dnf install fftw3-devel # Fedora Clone, build, and install alsa-utils. @@ -121,7 +156,14 @@ Clone, build, and install alsa-utils. cd "$SOF_WORKSPACE" git clone git://git.alsa-project.org/alsa-utils cd alsa-utils + # To install alsa-utils systemwide ./gitcompile + # To install alsa-utils locally + ./gitcompile --prefix=$HOME/local \ + --with-alsa-inc-prefix=$HOME/local/include \ + --with-alsa-prefix=$HOME/local/lib \ + --with-systemdsystemunitdir=$HOME/local/lib/systemd \ + --with-udev-rules-dir=$HOME/local/lib/udev sudo make install If you run into alsa-lib linking errors, try to re-build it with the libdir @@ -147,85 +189,102 @@ Create or append to the ``LD_LIBRARY_PATH`` environment variable. export LD_LIBRARY_PATH="${SOF_WORKSPACE}"/alsa-lib/src/.libs:$LD_LIBRARY_PATH -Step 2 Build toolchains from source -=================================== +.. _build-toolchains-from-source: + +Step 3. Build toolchains from source +************************************ -Build the xtensa cross-compilation toolchains with crosstool-ng for Intel |BYT|, -|CHT|, |HSW|, |BDW|, |APL|, |CNL|, |ICL|, |JSL| platforms and NXP i.MX8/i.MX8X/i.MX8M -platforms. +Build the xtensa cross-compilation toolchains with crosstool-ng for +Intel |BYT|, |CHT|, |HSW|, |BDW|, |APL|, |CNL|, |ICL|, |JSL|, |TGL| +platforms and NXP i.MX8/i.MX8X/i.MX8M platforms. Building the toolchains +may take about an hour but only once and it removes the dependency on +the Docker image. + +For more details go to https://crosstool-ng.github.io/ crosstool-ng ------------- +============ -Clone both repos and check out the ``sof-gcc8.1`` branch. +Clone both repos and check out the ``sof-gcc10.2`` and ``sof-gcc10x`` branch. .. code-block:: bash cd "$SOF_WORKSPACE" git clone https://github.com/thesofproject/xtensa-overlay git clone https://github.com/thesofproject/crosstool-ng - cd xtensa-overlay - git checkout sof-gcc8.1 - cd ../crosstool-ng - git checkout sof-gcc8.1 + git -C xtensa-overlay/ checkout sof-gcc10.2 + git -C crosstool-ng/ checkout sof-gcc10x Build crosstool-ng and install it in its own source directory. .. code-block:: bash + cd crosstool-ng/ ./bootstrap ./configure --prefix=$(pwd) make make install Toolchains ----------- +========== The config files provided refer to ``../xtensa-overlay/`` and point at -different ``./build/xtensa-*-elf`` subdirectories. Copy the ones you +different ``./builds/xtensa-*-elf`` subdirectories. Copy the ones you want to ``.config`` and build the cross-compiler(s) for your target -platform(s). ``./ct-ng build`` requires an network connection to -download gcc components. +platform(s). Note that ``./ct-ng build`` requires an network connection +to download gcc components. While other steps take minutes at most, +building all toolchains may last about an hour depending on your network +connection and the performance of your system. .. code-block:: bash - # Baytrail/Cherrytrail - cp config-byt-gcc8.1-gdb8.1 .config - ./ct-ng build - # Haswell/Broadwell - cp config-hsw-gcc8.1-gdb8.1 .config - ./ct-ng build - # Apollo Lake - cp config-apl-gcc8.1-gdb8.1 .config - ./ct-ng build - # Cannon Lake, Ice Lake and Jasper Lake - cp config-cnl-gcc8.1-gdb8.1 .config - ./ct-ng build - # i.MX8/i.MX8X - cp config-imx-gcc8.1-gdb8.1 .config - ./ct-ng build - # i.MX8M - cp config-imx8m-gcc8.1-gdb8.1 .config - ./ct-ng build + unset LD_LIBRARY_PATH + + # byt = Bay Trail / Cherry Trail + # hsw = Haswell/Broadwell + # apl = Apollo Lake + # cnl = Cannon Lake, Ice Lake, Jasper Lake, and Tiger Lake + # imx = i.MX8/i.MX8X + # imx8m = i.MX8M + + # Omit the toolchains you don't want to save (a lot of) time + time for i in byt hsw apl cnl imx imx8m; do + cp config-$i-gcc10.2-gdb9 .config && + time ./ct-ng build || break + done + + # ... or just build all toolchains + time for i in config*gcc10.2-gdb9; do + cp "$i" .config && time ./ct-ng build || break + done + ``./ct-ng`` is a Linux kernel style Makefile; so the sample commands below -can be used to fix some out of date ``config-*-gcc8.1-gdb8.1`` file or find +can be used to fix some out of date ``config-*-gcc10.2-gdb9`` file or find default values missing from it: .. code-block:: bash ./ct-ng help - cp config-apl-gcc8.1-gdb8.1 .config + cp config-apl-gcc10.2-gdb9 .config ./ct-ng oldconfig V=1 - diff -u config-apl-gcc8.1-gdb8.1 .config + diff -u config-apl-gcc10.2-gdb9 .config -"Install" toolchains by copying them to ``$SOF_WORKSPACE``. +"Install" toolchains in the expected location by linking +from ``$SOF_WORKSPACE`` to them: .. code-block:: bash ls builds/ # xtensa-apl-elf xtensa-byt-elf xtensa-cnl-elf xtensa-hsw-elf xtensa-imx-elf xtensa-imx8m-elf - cp -r builds/* "$SOF_WORKSPACE" + cd "$SOF_WORKSPACE" + for i in crosstool-ng/builds/xtensa-*; do ln -s "$i"; done + +Remove the temporary build files (~7GB per toolchain): + +.. code-block:: bash + + rm -rf $SOF_WORKSPACE/crosstool-ng/.build .. note:: @@ -233,23 +292,13 @@ default values missing from it: |BYT| and |CHT| share the same toolchain: xtensa-byt-elf - |CNL|, |ICL| and |JSL| share the same toolchain: xtensa-cnl-elf + |CNL|, |ICL|, |JSL| and |TGL| share the same toolchain: xtensa-cnl-elf i.MX8 and i.MX8X share the same toolchain: xtensa-imx-elf -Add your toolchains to your PATH variable. - -.. code-block:: bash - - PATH="${SOF_WORKSPACE}"/xtensa-byt-elf/bin/:$PATH - PATH="${SOF_WORKSPACE}"/xtensa-hsw-elf/bin/:$PATH - PATH="${SOF_WORKSPACE}"/xtensa-apl-elf/bin/:$PATH - PATH="${SOF_WORKSPACE}"/xtensa-cnl-elf/bin/:$PATH - PATH="${SOF_WORKSPACE}"/xtensa-imx-elf/bin/:$PATH - PATH="${SOF_WORKSPACE}"/xtensa-imx8m-elf/bin/:$PATH Additional headers ------------------- +================== To get some required headers, clone the following newlib repository and switch to the `xtensa` branch. @@ -261,81 +310,114 @@ switch to the `xtensa` branch. cd newlib-xtensa git checkout -b xtensa origin/xtensa -Build and install for each platform. +Temporarily add toolchains to your PATH variable. This is *not* required +when using the high-level, "every day" build scripts described in the +next sections. It's only required for this once-off ``newlib`` headers +step or when invoking CMake manually. In other words, you don't need to +change your PATH permanently which would interfere with other, non-SOF +work. + +.. code-block:: bash + + for i in "${SOF_WORKSPACE}"/xtensa-*-elf; do PATH="$PATH:$i"/bin; done + +Build and install the newlib headers for each toolchain: .. code-block:: bash XTENSA_ROOT="${SOF_WORKSPACE}"/xtensa-root - # Baytrail/Cherrytrail - ./configure --target=xtensa-byt-elf --prefix="${XTENSA_ROOT}" - make - make install - rm -fr rm etc/config.cache - # Haswell/Broadwell - ./configure --target=xtensa-hsw-elf --prefix="${XTENSA_ROOT}" - make - make install - rm -fr rm etc/config.cache - # Apollo Lake - ./configure --target=xtensa-apl-elf --prefix="${XTENSA_ROOT}" - make - make install - rm -fr rm etc/config.cache - # Cannon Lake, Ice Lake and Jasper Lake - ./configure --target=xtensa-cnl-elf --prefix="${XTENSA_ROOT}" - make - make install - rm -fr rm etc/config.cache - # i.MX8/i.MX8X - ./configure --target=xtensa-imx-elf --prefix="${XTENSA_ROOT}" - make - make install - rm -fr rm etc/config.cache - # i.MX8M - ./configure --target=xtensa-imx8m-elf --prefix="${XTENSA_ROOT}" - make - make install + cd "${SOF_WORKSPACE}"/newlib-xtensa + time for toolchain in ../xtensa-*-elf; do + ./configure --target="${toolchain#../}" --prefix="$XTENSA_ROOT" && + make && make install || break; + rm etc/config.cache + done + ls "$XTENSA_ROOT" + => share xtensa-apl-elf xtensa-byt-elf xtensa-cnl-elf xtensa-hsw-elf ... + +This should take a few minutes. .. note:: - ``--prefix=`` expects an absolute path. Define XTENSA_ROOT according to your - environment. + ``--prefix=`` expects an absolute path. Define XTENSA_ROOT according to + your environment. -The required headers are now in ``~/work/sof/xtensa-root``, and cross-compilation -toolchains for xtensa DSPs are set up. +The required headers are now in ``"$SOF_WORKSPACE"/xtensa-root``, and +cross-compilation toolchains for xtensa DSPs are set up. -Step 3 Build firmware binaries -============================== +.. _build-and-sign-firmware-binaries-from-scratch: -After the SOF environment is set up, clone the *sof* repo. +Step 4. Build and sign firmware binaries +**************************************** + +After the SOF environment is set up, clone the *sof* repo: .. code-block:: bash - cd ~/work/sof/ - git clone https://github.com/thesofproject/sof + cd "$SOF_WORKSPACE" + git clone --recursive https://github.com/thesofproject/sof + cd sof + -One-step rebuild from scratch ------------------------------ +Copy the commented ``installer/sample-config.mk`` to +``installer/config.mk``, then select a list of platforms and provide an +optional target hostname in the latter file. Then run the installer: + +.. code-block:: bash + + make -C installer/ [ -j 4 ] + +Adjust the ``-j 4`` example to your number of CPU cores or remove it +when the build fails. + +This builds multiple platforms in parallel and deploys firmware and +topologies to ``/lib/firmware/intel/`` on the local or remote +destination that you configured. It builds with the default platform +configurations the first time and then switches to incremental builds +which preserves any ``make menuconfig`` or other configuration changes +you made. These two ways to build are described below, so read on if you +need finer control on the build system and configuration. Otherwise you +can skip the next two sections. + +The installer also builds and deploys some user-space binaries from the +``sof/tools/`` subdirectory. + +.. note:: + + The installer is much faster than the lower level ``./scripts/``, + on which it relies, because it does not delete the build + directories every time it runs. However, some "big" configuration + changes, such as switching to a different toolchain or some rare + build failures, can leave the ``installer-builds/build_*`` + directories in an inappropriate state. In such a case, just delete + these directories and run the installer again. -To rebuild |SOF| in just one step, use -:git-sof-master:`scripts/xtensa-build-all.sh` after setting up the -environment. + .. code-block:: bash + + rm -rf $SOF_WORKSPACE/sof/installer-builds + make -C installer/ + +Re-configure and rebuild from scratch +===================================== -Build the firmware for all platforms. +To rebuild |SOF| from scratch, the installer Makefile above relies on +the :git-sof-mainline:`scripts/xtensa-build-all.sh` script. If you need +finer control or to troubleshoot some build issue you can also use it +directly. To build the firmware for all platforms: .. code-block:: bash - cd ~/work/sof/sof/ + cd "$SOF_WORKSPACE"/sof/ ./scripts/xtensa-build-all.sh -a .. note:: - This script will only work if the PATH includes both the cross-compiler and - ``xtensa-root`` and if they are siblings in the same ``sof`` directory. + This script works only if the cross-compiler and ``xtensa-root`` are + siblings in the same ``sof`` directory, as instructed above. -As of April 2020, you may specify one or more of the following platform -arguments: ``byt``, ``cht``, ``hsw``, ``bdw``, ``apl``, ``cnl``, -``sue``, ``icl``, ``jsl``, ``imx8``, ``imx8x``, ``imx8m``. Example: +As of May 2021, you may specify one or more of the following platform +arguments: ``byt``, ``cht``, ``bdw``, ``hsw``, ``apl``, ``skl``, ``kbl``, ``cnl``, +``sue``, ``icl``, ``jsl``, ``tgl``, ``tgl-h``, ``imx8``, ``imx8x``, ``imx8m``. Example: .. code-block:: bash @@ -352,14 +434,28 @@ builds with -r and speed up the build with -j [n] ./scripts/xtensa-build-all.sh -d -r apl ./scripts/xtensa-build-all.sh -d -r -j 4 apl +.. note:: + The ``xtensa-build-all.sh`` script uses ``rimage`` to build the final + firmware image. ``rimage`` uses by default a public key included in the + sof repo for signing. However, if you need to use some other external key + for signing you can specify the path to your key as environment variable + before invoking the build: + + .. code-block:: bash + + export PRIVATE_KEY_OPTION=-DRIMAGE_PRIVATE_KEY=/path_to_key/private.pem + + The same export mechanism should work also when building with Docker. + Incremental builds ------------------- +================== This is a more detailed build guide for the *sof* repo. Unlike -``xtensa-build-all.sh``, this doesn't rebuild everything every time. +``xtensa-build-all.sh``, this doesn't rebuild everything every time. The +installer Makefile above relies on this for incremental builds. Snippets below assume that your current directory is the root of the -``sof`` clone (``~/work/sof/sof/``). +``sof`` clone (``"$SOF_WORKSPACE"/sof/``). CMake recommends out-of-tree builds. Among others, this lets you build different configurations/platforms in different build directories from @@ -375,104 +471,18 @@ for |BYT|: .. code-block:: bash mkdir build_byt && cd build_byt - cmake -DTOOLCHAIN=xtensa-byt-elf -DROOT_DIR="$XTENSA_ROOT"/xtensa-byt-elf .. + cmake -DTOOLCHAIN=xtensa-byt-elf -DROOT_DIR="$XTENSA_ROOT"/xtensa-byt-elf -DINIT_CONFIG=baytrail_defconfig .. make help # lists all available targets - make baytrail_defconfig make bin -j4 VERBOSE=1 -for |CHT|: - -.. code-block:: bash - - mkdir build_cht && cd build_cht - cmake -DTOOLCHAIN=xtensa-byt-elf -DROOT_DIR="$XTENSA_ROOT"/xtensa-byt-elf .. - make cherrytrail_defconfig - make bin -j4 - -for |HSW|: - -.. code-block:: bash - - mkdir build_hsw && cd build_hsw - cmake -DTOOLCHAIN=xtensa-hsw-elf -DROOT_DIR="$XTENSA_ROOT"/xtensa-hsw-elf .. - make haswell_defconfig - make bin -j4 - -for |BDW|: - -.. code-block:: bash - - mkdir build_bdw && cd build_bdw - cmake -DTOOLCHAIN=xtensa-hsw-elf -DROOT_DIR="$XTENSA_ROOT"/xtensa-hsw-elf .. - make broadwell_defconfig - make bin -j4 +You can replace ``byt`` above with any other platform listed in the help +output of the ``sof/scripts/xtensa-build-all.sh``. Find the toolchain +matching each platform in the same script or above. -for |APL|: - -.. code-block:: bash - - mkdir build_apl && cd build_apl - cmake -DTOOLCHAIN=xtensa-apl-elf -DROOT_DIR="$XTENSA_ROOT"/xtensa-apl-elf .. - make apollolake_defconfig - make bin -j4 - -for |CNL|: - -.. code-block:: bash - - mkdir build_cnl && cd build_cnl - cmake -DTOOLCHAIN=xtensa-cnl-elf -DROOT_DIR="$XTENSA_ROOT"/xtensa-cnl-elf .. - make cannonlake_defconfig - make bin -j4 - -for |ICL|: - -.. code-block:: bash - - mkdir build_icl && cd build_icl - cmake -DTOOLCHAIN=xtensa-cnl-elf -DROOT_DIR="$XTENSA_ROOT"/xtensa-cnl-elf .. - make icelake_defconfig - make bin -j4 - -for |JSL|: - -.. code-block:: bash - - mkdir build_jsl && cd build_jsl - cmake -DTOOLCHAIN=xtensa-cnl-elf -DROOT_DIR="$XTENSA_ROOT"/xtensa-cnl-elf .. - make jasperlake_defconfig - make bin -j4 - -for i.MX8: - -.. code-block:: bash - - mkdir build_imx8 && cd build_imx8 - cmake -DTOOLCHAIN=xtensa-imx-elf -DROOT_DIR="$XTENSA_ROOT"/xtensa-imx-elf .. - make imx8_defconfig - make bin -j4 - -for i.MX8X: - -.. code-block:: bash - - mkdir build_imx8x && cd build_imx8x - cmake -DTOOLCHAIN=xtensa-imx-elf -DROOT_DIR="$XTENSA_ROOT"/xtensa-imx-elf .. - make imx8x_defconfig - make bin -j4 - -for i.MX8M: - -.. code-block:: bash - - mkdir build_imx8m && cd build_imx8m - cmake -DTOOLCHAIN=xtensa-imx8m-elf -DROOT_DIR="$XTENSA_ROOT"/xtensa-imx8m-elf .. - make imx8m_defconfig - make bin -j4 .. note:: - After the 'make \*_defconfig' step, you can customize your build with + After the cmake step, you can customize your build with 'make menuconfig'. DEBUG and ROM options are available for the FW binary build. Enable them @@ -481,8 +491,7 @@ for i.MX8M: .. code-block:: bash mkdir build_cnl_custom && cd build_cnl_custom - cmake -DTOOLCHAIN=xtensa-cnl-elf -DROOT_DIR="$XTENSA_ROOT"/xtensa-cnl-elf .. - make cannonlake_defconfig + cmake -DTOOLCHAIN=xtensa-cnl-elf -DROOT_DIR="$XTENSA_ROOT"/xtensa-cnl-elf -DINIT_CONFIG=cannonlake_defconfig .. make menuconfig # select/deselect options and save make bin -j4 @@ -494,40 +503,47 @@ for i.MX8M: Firmware build results ----------------------- +====================== The firmware binary files are located in build_/src/arch/xtensa/. -Copy them to your target machine's /lib/firmware/intel/sof folder. +The installer copies them to your target machine's ``/lib/firmware/intel/sof`` +folder. .. code-block:: bash sof-apl.ri sof-bdw.ri sof-byt.ri sof-cht.ri sof-cnl.ri sof-hsw.ri +.. _build-topology-and-tools-from-scratch: -Step 4 Build topology and tools -=============================== +Step 5. Build topology and tools +******************************** + +You can probably skip this section if you use the firmware installer in +the previous section. One-step rebuild from scratch ------------------------------ +============================= -Without any argument :git-sof-master:`scripts/build-tools.sh` rebuilds -only the minimum subset of :git-sof-master:`tools/`. +Without any argument :git-sof-mainline:`scripts/build-tools.sh` builds +the default CMake target "ALL" of :git-sof-mainline:`tools/`. .. code-block:: bash - cd ~/work/sof/sof/ + cd "$SOF_WORKSPACE"/sof/ ./scripts/build-tools.sh + +To see the list of options, run :git-sof-mainline:`scripts/build-tools.sh` with the ``-h`` option. + +.. code-block:: bash + ./scripts/build-tools.sh -h - usage: ./scripts/build-tools.sh [-t|-f] - [-t] Build test topologies - [-f] Build fuzzer" Incremental build ------------------ +================= .. code-block:: bash - cd ~/work/sof/sof/tools/ + cd "$SOF_WORKSPACE"/sof/tools/ mkdir build_tools && cd build_tools cmake .. make -j4 @@ -541,37 +557,46 @@ If your ``cmake --version`` is 3.13 or higher, you may prefer the new -B option: rm -rf build_tools/ # no need to change directory ever Topology and tools build results --------------------------------- +================================ -The topology files are located in the *tools/build_tools/topology* folder. -Copy them to the target machine's /lib/firmware/intel/sof-tplg folder. +The topology files are located in the *tools/build_tools/topology* +folder. The installer Makefile copies them to the target machine's +``/lib/firmware/intel/sof-tplg/`` folder. -The *sof-logger* tool is in the *tools/build_tools/logger* folder. Copy it to -the target machine's /usr/bin directory. +The *sof-logger* tool is in the *tools/build_tools/logger* folder. The +installer Makefile copies them to the target directory of your choice. .. _Build Linux kernel: -Build Linux kernel -****************** +Step 6. Build Linux kernel +************************** -|SOF| uses the Linux kernel dev branch, and it must work with other dev branch -firmware and topology. +|SOF| uses the Linux kernel dev branch, and it must work with other dev +branch firmware and topology. This short section shows how to build +Debian kernel packages tested on Ubuntu in a small number of commands. +Note that these commands rebuild everything from scratch every time which +makes then unsuitably slow for development. If you need to make kernel +code changes, ignore this and look at +:ref:`setup-ktest-environment`, the `README `_ file of +the kconfig repo, and the :ref:`sof_driver_arch`. #. Build the kernel with this branch. .. code-block:: bash sudo apt-get install bison flex libelf-dev - cd ~/work/sof/ + cd "$SOF_WORKSPACE" git clone https://github.com/thesofproject/linux cd linux git checkout topic/sof-dev make defconfig git clone https://github.com/thesofproject/kconfig scripts/kconfig/merge_config.sh .config ./kconfig/base-defconfig ./kconfig/sof-defconfig ./kconfig/mach-driver-defconfig ./kconfig/hdaudio-codecs-defconfig - (optional) make menuconfig - Select the SOF driver support and disable SST drivers. + Optionally, you can also run ``make menuconfig``, navigate to + Device Drivers > Sound card support > Advanced Linux Sound + Architecture, and select the **Prefer SOF driver over SST on BY/CHT + platforms** option. #. Make the kernel deb package to install on the target machine. @@ -579,8 +604,8 @@ firmware and topology. make deb-pkg -j 4 -#. Copy the three resulting *.deb* files to the target machine and install - them. +#. Copy the three resulting *.deb* files from $SOF_WORKSPACE to the + target machine and install them. .. code-block:: bash diff --git a/getting_started/build-guide/build-with-docker.rst b/getting_started/build-guide/build-with-docker.rst index 597af1cf..de972829 100644 --- a/getting_started/build-guide/build-with-docker.rst +++ b/getting_started/build-guide/build-with-docker.rst @@ -10,161 +10,155 @@ Build SOF with Docker This guide will show you how to use a Docker image containing the |SOF| build environment. -.. note:: - - The example uses ``$SOF_WORKSPACE`` as the working directory. - Set up the workspace directory ****************************** - .. code-block:: bash +1. Point the ``$SOF_WORKSPACE`` environment variable to the directory + in which you store all SOF work. - SOF_WORKSPACE=~/work/sof - mkdir "$SOF_WORKSPACE" + .. code-block:: bash -Clone the *sof* repo. + SOF_WORKSPACE=~/work/sof + mkdir -p "$SOF_WORKSPACE" -.. code-block:: bash +#. Clone the SOF repository. - cd "$SOF_WORKSPACE" - git clone https://github.com/thesofproject/sof.git + .. code-block:: bash + + cd "$SOF_WORKSPACE" + git clone --recurse-submodules https://github.com/thesofproject/sof.git Set up Docker ************* -Docker is a popular container management framework. To install on Ubuntu, -visit `Get Docker CE for Ubuntu `__. - -Installation instructions for other Linux distributions: `About Docker CE `__. - -Set Proxy -========= +Docker is a popular container management framework. To install Docker and get the Docker image with the SOF build environment: -Docker must be configured if used behind a proxy. -Visit `HTTP/HTTPS proxy `__ for the guide. +1. Install Docker. -Set user group -============== + For information on how to install Docker on Ubuntu, visit `Install + Docker Engine on Ubuntu + `__. -To use Docker without ``sudo`` follow these post-install steps. -`Post-installation steps for Linux `__ + For information on how to install Docker on other Linux + distributions, visit `Install Docker Engine + `__. -Get Docker image -================ +#. Optionally, configure Docker to run under a proxy. -To easily build SOF binaries, we need a Docker image containing all -of the cross-compiler and build environment dependencies. We can either -build a Docker image from a DockerFile or pull an image binary from -Docker Hub. + For more information about configuring Docker to use a proxy, visit + `HTTP/HTTPS proxy + `__. -.. note:: +#. To use Docker without ``sudo``, add your user to the `docker` group. - Building the container from DockerFile will take more than 2 hours, - so we recommend using the pre-built image. + For more information, visit + `Post-installation steps for Linux `__. -Pull Docker image ------------------ +#. Get a Docker image with the SOF build environment. -Pull the docker image from Docker Hub. + To easily build SOF binaries, we need a Docker image containing all + of the cross-compiler and build environment dependencies. Get the + Docker image by using one of the following options: -.. code-block:: bash - - docker pull thesofproject/sof - -.. note:: + - Option 1. Pull the Docker image from Docker Hub and retag the image with `sof` for scripts: - Since there is not yet an offical |SOF| presence on Dockerhub, the - image is hosted in a personal Docker Hub repo until the - official image can go live. + .. code-block:: bash -Retag the image with `sof` for scripts. + docker pull thesofproject/sof + docker tag thesofproject/sof sof -.. code-block:: bash - - docker tag thesofproject/sof sof + .. note:: + Since there is not yet an offical |SOF| presence on + Dockerhub, the image is hosted in a personal Docker Hub repo + until the official image can go live. -Build Docker image ------------------- + - Option 2. Build a Docker image: -Run the Docker build from the `sof` repo. + .. note:: -.. code-block:: bash + Building the container from DockerFile takes more than two hours, + so we recommend using the pre-built image (Option 1). + + Run the Docker build from the SOF repository. - cd "${SOF_WORKSPACE}"/sof/scripts/docker_build/sof_qemu - ./docker-build.sh - cd "${SOF_WORKSPACE}"/sof/scripts/docker_build/sof_builder - ./docker-build.sh + .. code-block:: bash -After building the Docker image you will see: + cd "${SOF_WORKSPACE}"/sof/scripts/docker_build/sof_qemu + ./docker-build.sh + cd "${SOF_WORKSPACE}"/sof/scripts/docker_build/sof_builder + ./docker-build.sh -.. code-block:: bash + Verify that the docker image is built successfully. - docker images - #REPOSITORY TAG IMAGE ID CREATED SIZE - #sof latest c8b0e8913fcb 2 days ago 1.46 GB + .. code-block:: bash -Build with Docker -***************** + docker images + + #REPOSITORY TAG IMAGE ID CREATED SIZE + #sof latest c8b0e8913fcb 2 days ago 1.46 GB -Build firmware binaries -======================= +Build firmware binaries with Docker +*********************************** Build with scripts ------------------- +================== -Build the SOF binaries: +To build the SOF binaries for all platforms: .. code-block:: bash cd "${SOF_WORKSPACE}"/sof/ - ./scripts/docker-run.sh ./scripts/xtensa-build-all.sh - -.. note:: + ./scripts/docker-run.sh ./scripts/xtensa-build-all.sh -a - ./scripts/docker-run.sh will mount the *sof* and directories - into Docker container and build them inside the container. The build - result can be accessed outside the container after the build. +``./scripts/docker-run.sh`` mounts the *sof* and directories into the +Docker container and builds them inside the container. You can access +the build result outside the container after the build. -Build one or more platform binaries. +To build the SOF binaries for one or more platforms: .. code-block:: bash cd "${SOF_WORKSPACE}"/sof/ - # Baytrail + # Bay Trail ./scripts/docker-run.sh ./scripts/xtensa-build-all.sh byt - # Baytrail and Apollo Lake + # Bay Trail and Apollo Lake ./scripts/docker-run.sh ./scripts/xtensa-build-all.sh byt apl Build inside container ----------------------- +====================== -Enter the container bash. +1. Enter the container bash: -.. code-block:: bash + .. code-block:: bash - cd "${SOF_WORKSPACE}"/sof/ - ./scripts/docker-run.sh bash + cd "${SOF_WORKSPACE}"/sof/ + ./scripts/docker-run.sh bash -From inside the container, follow the manual configuration and build steps. +#. From inside the container, follow the manual configuration and build + steps. For more information, see + :ref:`build-and-sign-firmware-binaries-from-scratch`. Firmware build results ----------------------- +====================== -The firmware binary files are located in src/arch/xtensa/. Copy them to -your target machine's /lib/firmware/intel/sof folder. +The firmware binary files are located in the +``build_/src/arch/xtensa/`` directory. Copy them to the +``/lib/firmware/intel/sof`` directory on the target machine. .. code-block:: bash sof-apl.ri sof-bdw.ri sof-byt.ri sof-cht.ri sof-cnl.ri sof-hsw.ri -Build topology and tools -======================== +.. _docker-topology-tools: + +Build topology and tools with Docker +************************************ Build with scripts ------------------- +================== -Build the *sof* tools and topology files. +Build the SOF tools and topology files. .. code-block:: bash @@ -172,28 +166,26 @@ Build the *sof* tools and topology files. ./scripts/docker-run.sh ./scripts/build-tools.sh Build inside container ----------------------- - -Enter the container bash. +====================== -.. code-block:: bash - - cd "${SOF_WORKSPACE}"/sof/ - ./scripts/docker-run.sh bash - -From inside the container: - -.. code-block:: bash +1. Enter the container bash: - cd tools + .. code-block:: bash + + cd "${SOF_WORKSPACE}"/sof/ + ./scripts/docker-run.sh bash -and follow the manual configuration and build steps. +2. From inside the container, change to the ``tools`` directory and + follow the manual configuration and build steps. For more + information, see :ref:`build-topology-and-tools-from-scratch`. Topology and tools build results --------------------------------- +================================ -The topology files are all in the topology folder ("${SOF_WORKSPACE}"/sof/tools/build_tools/topology). Copy them to the target -machine's /lib/firmware/intel/sof-tplg folder. +The topology files are located in the +``"$SOF_WORKSPACE"/sof/tools/build_tools/topology`` folder. Copy the +files to the ``/lib/firmware/intel/sof-tplg`` directory on the target +machine. -The *sof-logger* tool is in the *tools/logger* folder. Copy it to the target machine's -/usr/bin directory. +The *sof-logger* tool is located in the ``tools/logger`` directory. Copy +it to the ``/usr/bin`` directory on the target machine. diff --git a/getting_started/build-guide/build-with-zephyr.rst b/getting_started/build-guide/build-with-zephyr.rst new file mode 100644 index 00000000..e3eb035d --- /dev/null +++ b/getting_started/build-guide/build-with-zephyr.rst @@ -0,0 +1,267 @@ +.. _build-with-zephyr: + +Build SOF with `Zephyr `_ +##################################################### + +.. contents:: + :local: + :depth: 3 + +This guide describes how to build and run |SOF| as a Zephyr application. + +.. note:: + + The following example uses ``$ZEPHYR_WORKSPACE`` as the working + directory for both SOF and Zephyr projects. + +Prepare +******* + +- The easiest way to build Zephyr is to use its recommended toolchain which is included in its SDK. Refer to `Install Zephyr SDK `_ for details. + +- Install **west**. Zephyr uses west as a source management and building system. Follow + the Zephyr `Getting Started `_ guide for dependencies and for the west installation. + +Clone and initialize SOF project +******************************** + +Initialize the west manifest ``$ZEPHYR_WORKSPACE/sof/west.yml`` using the ``west tool``: + + - Clone the SOF repository: + + .. code-block:: bash + + mkdir $ZEPHYR_WORKSPACE && cd $ZEPHYR_WORKSPACE + west init -m https://github.com/thesofproject/sof + + - Or initialize the west manifest from the existing SOF clone. Note that when using the Python convenience script, as described in the next section, this is not mandatory. + + .. code-block:: bash + + cd $ZEPHYR_WORKSPACE + west init -l ./sof + + + .. note:: + | Since the Zephyr project also uses the west manifest, your west tool might already be initialized to manifest Zephyr. In this case, west issues the following error during initialization: + | *"FATAL ERROR: already initialized in $ZEPHYR_WORKSPACE, aborting."* + | + | To verify that the manifest is currently used by the west tool, execute the following command from the ``$ZEPHYR_WORKSPACE`` directory: + | ``west config -l``. + | + | If command output shows the following, remove the ``$ZEPHYR_WORKSPACE/.west`` directory and reinitialize the west manifest using one of the two methods described above: + | *manifest.path=zephyr* + | *manifest.file=west.yml* + + .. important:: + The SOF project **must** be cloned to the ``sof`` directory because this name is hardcoded in the west manifest file. Failure to do so may result in SOF dependencies being cloned into a newly created ``$ZEPHYR_WORKSPACE/sof/rimage`` directory along with other undesirable consequences. + + **All commands described in the guide from this point should be executed from the $ZEPHYR_WORKSPACE directory.** + + +Check out and build using Python convenience script +*************************************************** + +The SOF project offers a Python convenience script, ``./sof/scripts/xtensa-build-zephyr.py``, that provides a friendly build process for the end user. It is a wrapper for a **west tool** that performs steps described in the `Check out and build using west tool directly`_ section below. + +This script can be used on both Windows and Linux operating systems. Note that it will be removed when the SOF project creates better integration with west tool commands. + +The script automates the following steps that are required to build firmware for the SOF platform: + - Initializes your west tool to SOF's west manifest. + - Clones and checks out SOF and Zephyr dependencies. + - Builds a firmware ``.elf`` file for the requested platform. + - Builds a **rimage tool**. + - Uses the **rimage tool** and a **private key** to sign the ``.elf`` file. It produces a final firmware image file with the ``.ri`` extension. + - Uses the **smex tool** to generate debugging symbols file with the ``.ldc`` extension. + +| A list of platforms that can be built with the script is shown in this help message: +| ``./sof/scripts/xtensa-build-zephyr.py --help`` + +Usage example 1: + You cloned the SOF project and you want to build firmware for the *Tigerlake* platform. + + .. code-block:: bash + + ./sof/scripts/xtensa-build-zephyr.py -u tgl + + Running this command will: + + - Initialize west to the ``./sof/west.yml`` manifest if it is not already initialized. + - Clone and check out projects to the revision defined in the ``./sof/west.yml`` file: + + - SOFs submodules (Rimage and Tomlc99) + - Zephyr project + - Zephyr project dependencies needed by SOF in ``$ZEPHYR_WORKSPACE/modules`` directory + + - Build a signed firmware image ``./build-tgl/zephyr/zephyr.ri`` and debug symbols file ``./build-sof-staging/sof/sof-tgl.ldc``. + + .. note:: + You may wish to rebuild all files from scratch. To do this, add a ``-p`` flag to the script invocation. To provide better build verbosity, use the ``-v`` flag. Make sure to check ``--help`` to see all build options. + +Usage example 2: + Your environment is set up as a cloned SOF project and you are working on a fork/branch of the Zephyr and Rimage submodules. You want to build a *Tigerlake* platform with your changes. + + .. code-block:: bash + + ./sof/scripts/xtensa-build-zephyr.py tgl + + Running this command will: + + - Initialize west to the ``./sof/west.yml`` manifest if it is not already initialized. + - Build a signed firmware image ``./build-tgl/zephyr/zephyr.ri`` and debug symbols file ``./build-sof-staging/sof/sof-tgl.ldc``. + - Skip cloning dependencies and check them out to revisions from the ``./sof/west.yml`` manifest. + +Usage example 3: + Your environment is set up as a cloned SOF project and you are working on a fork/branch of the Zephyr and Rimage submodules. You want to restore default revisions for SOF dependencies from the ``./sof/west.yml`` manifest. + + .. code-block:: bash + + ./sof/scripts/xtensa-build-zephyr.py -u + + Running this command will: + + - Initialize west to the ``./sof/west.yml`` manifest if it is not already initialized. + - Clone and checkout projects to revisions defined in the ``./sof/west.yml`` file. + - Skip building the firmware image. + +Output directory + For convenience, the ``xtensa-build-zephyr.py`` script copies all + firmware files into a single, staging directory: + + .. code-block:: bash + + $ tree build-sof-staging/ + + build-sof-staging/ + ├── sof + │   ├── community + │   │   ├── sof-apl.ri + │   │   ├── sof-imx8.ri + │   │   └── sof-tgl-h.ri + + +Check out and build using west tool directly +******************************************** + +#. Clone and check out SOF dependencies such as submodules, the Zephyr project, and some of its modules needed by SOF: + + .. code-block:: bash + + west update + + .. important:: + This command will check out revisions specified in the ``$ZEPHYR_WORKSPACE/sof/west.yml`` file for the following projects: + - Rimage (SOF submodule) + - Tomlc99 (Rimage submodule) + - Zephyr + - projects in ``$ZEPHYR_WORKSPACE/modules`` directory. + + **Make sure you back up your work before changing revisions!** + This will not affect your SOF project revision. + +#. Build a board. Make sure to use the appropriate Zephyr SDK or other toolchain of your choice. Boards to build are listed in the ``$ZEPHYR_WORKSPACE/sof/app/boards`` directory. + + .. code-block:: bash + + west build --build-dir build-tgl --board intel_adsp/cavs25 ./sof/app + + + Note that the SOF project defines platform names that have Zephyr board counterparts. In the above example, the *Tigerlake* platform matches the ``intel_adsp/cavs25`` Zephyr board target (see `Zephyr HWMv2 board terminology `_). This is why the output directory is named ``build-tgl``; however, you may use any name you wish. + + .. note:: + To add verbosity to the build output use the -v -v flags. Example: + ``west -v -v build --build-dir build-tgl --board intel_adsp/cavs25 ./sof/app`` + + To perform a complete clean rebuild, use the --pristine flag. Example: + ``west -v -v build --build-dir build-tgl --pristine always --board intel_adsp/cavs25 ./sof/app`` + + The ``.elf`` file produced by the ``west build`` is missing a + manifest and signature. A a result, you must sign the file using the **rimage tool** + and a **private key** to generate the final firmware image (``.ri`` file). + +#. Build the rimage tool by running the following: + + .. code-block:: bash + + cmake -B ./build-rimage -S ./sof/rimage + cmake --build ./build-rimage + +#. Sign the firmware using the rimage tool and a private key by running the following: + + .. code-block:: bash + + west sign --build-dir ./build-tgl -t rimage --tool-path ./build-rimage/rimage --tool-data ./sof/rimage/config -- -k ./sof/keys/otc_private_key_3k.pem + + **The signed output firmware image file is** ``./build-tgl/zephyr/zephyr.ri`` **.** + + .. note:: + The SOF project provides some pre-generated key pairs of different lengths: + - ``./sof/keys/otc_private_key_3k.pem`` + ``./sof/keys/otc_public_key_3k.pem`` + - ``./sof/keys/otc_private_key.pem`` + ``./sof/keys/otc_public_key.pem`` + + You may wish to generate your own set of keys for firmware signing. + +#. (Optional) Generate debug symbols. + + .. code-block::bash + + ./build-tgl/zephyr/smex_ep/build/smex -l ./build-tgl/zephyr/zephyr.ldc ./build-tgl/zephyr/zephyr.elf + + The output file ``./build-tgl/zephyr/zephyr.ldc`` may be used for reading firmware logs. + +Run +*** + +#. Copy the firmware image(s) to the usual location on all your target + systems. Example: + + .. code-block:: bash + + sudo rsync -a build-sof-staging/sof/ testsystemN.local:/lib/firmware/intel/sof/ + + Note that ``rsync`` also works locally and, unlike ``cp -R``, it is always + idempotent. You may want to use the ``rsync -a --delete`` option to + make absolutely sure you're not running some older version, **but do so + only after first backing up your original sof/ directory**. The + ``--delete`` option is dangerous; use it only in very well-tested + scripts. + + Also make sure nothing in ``/lib/firmware/updates`` takes precedence. Refer to `Firmware search paths `_. + +#. Reboot the system. Note that the location and name of your SOF + firmware image may vary by system. Search your kernel logs with + ``journalctl -k -g sof``, looking for a line + such as the following to identify which file under ``/lib/firmware/`` your hardware is using: + + ``sof-audio-pci 0000:00:0e.0: request_firmware intel/sof/community/sof-apl.ri successful`` + +#. Verify that the new firmware is being used by running the following: + + .. code-block:: bash + + dmesg | grep zephyr + + You should see a line such as the following: + + ``sof-audio-pci 0000:00:0e.0: Firmware info: used compiler GCC 9:2:0 zephyr used optimization flags -Os`` + +For firmware log extraction, use +``zephyr/boards/xtensa/intel_adsp_cavs15/tools/README.md``. + +You might also need to build and update your system audio topology file. For +details see :ref:`build-from-scratch`. + + +Troubleshoot +************ + +#. The west tool version is older than the minimal version requirement defined in the ``./sof/west.yml`` manifest. + + | The manifest file defines the minimal yaml schema version that sets compatibility with west tool according to `Zephyr documentation `_. If your west tools version is not sufficient to process the manifest file, west raises an exception (reference to west 0.12.0 for Windows): + + .. code-block:: bash + + west.manifest.ManifestVersionError: ('0.13', WindowsPath('$ZEPHYR_WORKSPACE/.west/manifest-tmp/west.yml')) + + | In this example, ``./sof/west.yml`` defines minimal version as ``0.13`` while the west tool used has version ``0.12.0``. Update your west tool to a newer version. + diff --git a/getting_started/index.rst b/getting_started/index.rst index b7a170d4..3d585a7c 100644 --- a/getting_started/index.rst +++ b/getting_started/index.rst @@ -3,15 +3,15 @@ Getting Started Guides ###################### -New to SOF or doing something for the first time ? Read on.... +Refer to the following getting started guides if you are new to SOF or if you are performing a task for the first time. -Building SOF -************ +Build SOF +********* -SOF can be built natively on a host PC or within a container. Use the container -method if the version of your distro is more than 6 months old. The SOF SDK -uses recent version of some external dependencies so the current distro release -is always preffered. +SOF can be built natively on a host PC or within a container. Use the +container method if the version of your distro is more than six months old. +The SOF SDK uses a recent version of some external dependencies so the +current distro release is always preferred. .. toctree:: :maxdepth: 1 @@ -19,16 +19,68 @@ is always preffered. build-guide/build-from-scratch build-guide/build-with-docker build-guide/build-3rd-party-toolchain + build-guide/build-with-zephyr -Setting up SOF on hardware -************************** +Set up SOF on a Linux machine +***************************** -SOF runs on a variety of different devices with varying audio capabilities so -instructions may differ between devices. +You can build the Linux kernel with the latest SOF code and install it locally or remotely with ktest. + +Do this first: + +.. toctree:: + :maxdepth: 1 + + setup_linux/prepare_build_environment + +Then proceed based on if you are installing locally or through ktest: + +.. toctree:: + :maxdepth: 1 + + setup_linux/install_locally + setup_linux/setup_ktest_environment + +Set up SOF on a special device +****************************** + +SOF also runs on the MinnowBoard Turbot and the Up Squared board with Hifiberry Dac+. + +.. toctree:: + :maxdepth: 1 + + setup_special_device/setup_minnowboard_turbot + setup_special_device/setup_up_2_board + +Debug Audio issues on Intel platforms +************************************* + +Intel platforms rely on different versions of DSP and audio hardware +interfaces. The following sections provide hints for integrators and +users when audio components are not working properly or are broken. + +.. toctree:: + :maxdepth: 1 + + intel_debug/introduction + intel_debug/suggestions + +SOF on NXP platforms +******************** + +This section provides guides for integrators and for users working with i.MX platforms. + +.. toctree:: + :maxdepth: 1 + + nxp/sof_imx_user_guide + +Building loadable modules using LMDK +************************************ + +This section descibes process of building loadable modules using LMDK. .. toctree:: :maxdepth: 1 - setup/setup_minnowboard_turbot - setup/setup_up_2_board - setup/setup_ktest_environment + loadable_modules/lmdk_user_guide diff --git a/getting_started/intel_debug/introduction.rst b/getting_started/intel_debug/introduction.rst new file mode 100644 index 00000000..11e8ca55 --- /dev/null +++ b/getting_started/intel_debug/introduction.rst @@ -0,0 +1,319 @@ +.. _intel_debug_introduction: + +Overview of Intel hardware platforms +#################################### + +ACPI platforms (introduced before and up to 2015) +************************************************* + +On Bay Trail, Cherry Trail, Braswell, and Broadwell devices (also referred to +as `legacy` devices), the DSP enumeration is handled by the ACPI +subsystem. + +1. Local audio accessories (mics, speakers, headset) +---------------------------------------------------- + +On Bay Trail, Cherry Trail, Braswell, and Broadwell, the BIOS can either +enable or disable the DSP: + +* Enable the DSP. In this case, a DSP driver is required. This mode is + selected on platforms where the audio interface for 3rd-party codecs is based on the I2C/I2S/TDM interfaces. + +* Disable the DSP. In this case, an HDaudio controller is exposed and the + ``snd-intel-hda`` driver will take care of all audio usages. SOF cannot be used in this case. + + +2. HDMI/DP interfaces +--------------------- + +On Broadwell, HDMI/DP is handled by an HDaudio controller. + +On Bay Trail/Cherry Trail and Braswell, the BIOS can enable two modes: + +* HDAudio-based solution (similar to Broadwell). + +* LPE HDMI Audio. This mode is used by the majority of tablets and low-cost + devices. It provides functionality similar to HDaudio, but with a different interface. This mode is enabled in Linux via the ``CONFIG_HDMI_LPE_AUDIO`` option. + +The DSP cannot control any of these interfaces because SOF does not support +HDMI/DP on those devices. + +On all of these legacy platforms, HDMI support is exposed in Linux as a +separate card. + +PCI devices (introduced after 2016) +*********************************** + +In newer devices, the same HDAudio controller can handle both local +accessories and HDMI/DP interfaces. However, SOF is not always +supported on those platforms. + +When the Intel DSP is not enabled in the BIOS (OEM choice), audio +interfaces are handled by the ``snd-hda-intel`` driver. The platform only +exposes PCM devices and no audio processing capabilities. + +When OEM platforms integrate digital microphones attached directly +to the Intel chipset (aka DMIC), or they use I2C/I2S or SoundWire +interfaces, the DSP must be enabled by the BIOS. There is, however, one +more option. On Skylake and Kaby Lake platforms, the Intel DSP is handled by +the ``snd-soc-skl`` module which relies on closed-source firmware. + +SOF is available on Intel PCI devices starting with Gemini Lake, and +has since been the only solution provided by Intel for the following +platforms: Comet Lake, Ice Lake, and Tiger Lake. + +Since multiple drivers can register for the same PCI ID, it was (until +recently) common for users and distributions to use the wrong +driver, which could only be resolved by changing the Linux ``.config`` file +or deselecting drivers in the ``/etc/modprobe.d`` configuration files. + +The ``snd-intel-dspcfg`` module introduced in early 2020 exposes an API +used by all drivers, and the user can now override default choices by +setting the ``dsp_driver`` parameter. For example, setting + +.. code-block:: cfg + + options snd-intel-dspcfg dsp_driver=1 + +will allow for the HDaudio legacy driver to be used. This will typically +work for speakers and headphones/headsets, but will not allow DMIC +capture. + +Conversely, when a platform does not require a DSP-based platform, but +the DSP is still enabled by the OEM, the user or integration can +force the SOF Linux driver to be used. + +.. code-block:: cfg + + options snd-intel-dspcfg dsp_driver=3 + + +User space and filesystem requirements +************************************** + +Selecting the SOF driver is not enough. Audio is properly configured only if +the following elements are present on the file system. + +1. Firmware +----------- + +1.1. Base firmware +------------------ + +The firmware file, ``/lib/firmware/intel/sof/sof-tgl.ri`` (example +location for Intel Tiger Lake), contains all DSP code and tables. On +PCI devices, the firmware can only be signed by an Intel production +key which prevents community users from installing their own firmware. +Notable exceptions include Google Chromebooks and Up2/Up-Extreme +boards, where the *community key* is used. + +The Intel ME (Management Engine) is responsible for authentication of +the firmware, whether it is signed by an Intel production key (consumer +products), a community key (open development systems and Chromebooks +since Gemini Lake) or an OEM key. If the Intel ME is disabled by an +OEM, or disabled by user-accessible BIOS options, the firmware +authentication will fail and the firmware boot will not complete. If +the ME is disabled by the OEM, the only solution is to fall-back +to the legacy HDAudio driver. If the ME is disabled by the user, the user +must re-enable it. Unfortunately, no documented mechanism exists for the +Linux kernel to query whether or not the firmware authentication is enabled, +which means `dmesg` logs cannot be provided to alert the user to an ME +configuration issue. + +.. _loadable-libraries: + +1.2. Loadable libraries +----------------------- + +An IPC4 library is a container of a single or multiple modules (bundle) which +can be loaded to the firmware after it is booted up. +Library loading is supported on Meteor Lake (ACE1) or newer platforms. + +Background information: the base firmware always resides in DSP SRAM while the +loaded library is stored in DRAM memory and only the needed code is copied to +SRAM for execution. By moving modules out from the base firmware to a library +can reduce the overall SRAM use depending on the device configuration and +topology. + +See :ref:`llext_modules` for technical details. + +1.3. Non-modular and modular firmware releases +---------------------------------------------- + +SOF project releases for Intel platforms are either a single firmware or modular firmware based. + +1.3.1. Non-modular firmware releases +------------------------------------ + +The release contains single a firmware image: **sof-PLAT.ri** + +1.3.2. Modular firmware releases +-------------------------------- + +Modular SOF release is technically supported with IPC4 on Meteor Lake (MTL) or newer platforms since it depends on Loadable Library support (see :ref:`loadable-libraries` for details). + +Description of files provided by a modular release: + - **sof-PLAT.ri** : The base firmware + - **sof-PLAT-openmodules.ri** : the bundle contains modules for audio processing not included in the base firmware + - **sof-PLAT-debug.ri** : the bundle contains modules that are needed for firmware debugging and profiling. Used by developers and for bug reporting if needed + - **UUID.bin** : On demand loadable library identified by UUID. If the library contains multiple modules then a UUID symlink must be provided for each one. + +The main firmware can be shipped as a + - single binary (**sof-PLAT.ri**) + - split release when the base firmware (**sof-PLAT.ri**), processing modules (**sof-PLAT-openmodules.ri**) and debug/developer modules (**sof-PLAT-debug.ri**) are provided as separate binaries. + + - After the base firmware boot, the kernel will load the **sof-PLAT-openmodules.ri** and **sof-PLAT-debug.ri** bundles to the firmware to provide equivalent functionality as the single binary release. + +Notes: + - additional libraries referenced by topology files or drivers will be loaded based on the UUID of the module from the library path (**UUID.bin**). + +1.4 Firmware lookup paths +------------------------- + +Linux SOF will look up firmware files at the following paths. + +Look-up paths per Intel platform for **non-modular firmware releases** + +.. _intel_non_modular_firmware_paths: + ++-----------------------------------------------------------+--------+------------------------------------------------+-----------+-----------------------------------+ +|Platform |IPC type|Load path |File name |Notes | ++===========================================================+========+================================================+===========+===================================+ +|Raptor Lake and older |IPC3 |/lib/firmware/intel/sof/ |sof-PLAT.ri|PLAT = glk, cml, ..., rpl | ++-----------------------------------------------------------+ +------------------------------------------------+ | | +|Raptor Lake and older (community signed) | |/lib/firmware/intel/sof/community/ | | | ++-----------------------------------------------------------+--------+------------------------------------------------+ +-----------------------------------+ +|Tiger Lake and newer |IPC4 |/lib/firmware/intel/sof-ipc4/PLAT/ | |PLAT = tgl, adl, rpl, mtl, lnl, ...| ++-----------------------------------------------------------+ +------------------------------------------------+ | | +|Tiger Lake and newer (community signed) | |/lib/firmware/intel/sof-ipc4/PLAT/community/ | | | ++-----------------------------------------------------------+--------+------------------------------------------------+-----------+-----------------------------------+ + +Look-up paths per Intel platform for **modular firmware releases (IPC4 only)** + +.. _intel_modular_firmware_paths: + ++-----------------------------------------------------------+------------------------------------------------+-----------------------------+----------------------+ +|Platform |Load path |File name |Notes | ++===========================================================+================================================+=============================+======================+ +|Meteor Lake and newer |/lib/firmware/intel/sof-ipc4/PLAT/ || || PLAT = mtl, lnl, ...| +| | || sof-PLAT.ri || [*] PLAT = ptl, ... | +| | || sof-PLAT-openmodules.ri [*]| | +| | || sof-PLAT-debug.ri [*]| | ++-----------------------------------------------------------+------------------------------------------------+ | | +|Meteor Lake and newer (community signed) |/lib/firmware/intel/sof-ipc4/PLAT/community/ | | | ++-----------------------------------------------------------+------------------------------------------------+-----------------------------+ | +|Meteor Lake and newer Loadable libraries |/lib/firmware/intel/sof-ipc4-lib/PLAT/ |UUID.bin | | ++-----------------------------------------------------------+------------------------------------------------+ | | +|Meteor Lake and newer Loadable libraries (community signed)|/lib/firmware/intel/sof-ipc4-lib/PLAT/community/| | | ++-----------------------------------------------------------+------------------------------------------------+-----------------------------+----------------------+ + +Important notices: + - The standard Linux firmware search path and order is followed. The above table covers the base "/lib/firmware" case. See https://docs.kernel.org/driver-api/firmware/fw_search_path.html for more information. + - The firmware folder and filename can be overridden with "fw_path" and "fw_filename" SOF kernel parameters. + - The loadable module library path can be overridden with "lib_path" SOF kernel parameter. + +2. Topology file +---------------- + +The topology file, such as ``/lib/firmware/intel/sof-tplg/sof-hda-generic-2ch.tplg``, describes the processing graph and controls to +be instantiated by the SOF driver. The topology can be regenerated and +reconfigured with tools but requires expert knowledge of the ALSA/ASoC/topology frameworks. + +.. list-table:: Firmware topology file look-up paths per Intel platform + :widths: 50 5 50 25 + :header-rows: 1 + + * - Platform + - IPC type + - Topology load path + - Notes + * - Raptor Lake and older + - IPC3 + - /lib/firmware/intel/sof-tplg/sof-CONFIG.tplg + - CONFIG = topology variant needed for detected hardware configuration + * - Tiger Lake and newer + - IPC4 + - /lib/firmware/intel/sof-ipc4-tplg/sof-CONFIG.tplg + - CONFIG = topology variant needed for detected hardware configuration + +Important notices: + - For compatibility reasons with respect to **Meteor Lake** ``/lib/firmware/intel/sof-ace-tplg`` must be symlinked to ``/lib/firmware/intel/sof-ipc4-tplg`` + - The standard Linux firmware search path and order is followed. The above table covers the base "/lib/firmware" case. See https://docs.kernel.org/driver-api/firmware/fw_search_path.html for more information. + - The topology folder and filename can be overridden with "tplg_path" and "tplg_filename" `snd_sof_pci` kernel parameters. + +3. UCM file +----------- + +The UCM file, such as ``/usr/share/alsa/ucm2/sof-hda-dsp/``, configures +the controls exposed by the topology file and the external audio +chips. UCM can be used in a terminal via the ``alsaucm`` command but +will typically be used by audio servers such as PulseAudio or +PipeWire. UCM files released by Intel are compatible with different +drivers and should work when changing the ``dsp_driver`` parameter. + +The selection of firmware, topology, and UCM files is based on platform +capabilities, codec names, and DMI options. While the SOF team and the +community try to cover all possible cases, errors will happen when the +wrong file is selected at any of the three layers. + +4. Chromebooks and SOF +---------------------- + +As stated above, starting from 2019/2020, Intel Chromeboooks have been +configured with the *community* key. It means that Chromebooks can run +audio firmware signed by anyone. The entire filesystem is locked by +default instead, but there are several options to disable security for +development purposes. In all cases the first step is to switch the +Chromebook to (non-secure) `Developer Mode +`_. +Developer Mode is the only required step if you only +want to install and run your own SOF firmware and are not interested in +changing anything else in Chrome OS. + +If you need the flexibility to make more changes, Chromebooks can run +Linux in several non-mutually exclusive ways. All the options listed +below let you run any SOF firmware. One of the biggest +differences between them is how to install and run your own Linux +kernel. + +- **Chrome OS** has direct hardware access, but Chrome OS development + cannot happen on Chrome OS itself. It requires a separate workstation + similar to how most embedded development typically does. For + information about setting up the ``cros_sdk``, see the `Chromium OS + Developer Guide + `_. + The ``cros_sdk`` is a complete environment that lets you modify + anything in Chrome OS and even build an entire system image. The + ``cros_sdk`` requires significant disk space and some learning + effort if you are not already familiar with Portage, a build system + in Gentoo, and especially with building the Linux kernel in Portage. + +- `Crostini + `_ + is a secure Linux Virtual Machine that does not have direct access + to the hardware and cannot be used for SOF. It does not require + Developer Mode. Crostini is listed here for completeness. You might + use Crostini as your pseudo-separate ``cros_sdk`` workstation, but a + different, more powerful system that you never have to reboot is a + much better ``cros_sdk`` option. + +- **Crouton** is a non-secure chroot that does allow direct hardware + access and can be used for SOF. It lets you install a choice of + popular Linux distributions, which you can use for development on the device + itself. Make regular backups! The Zephyr project has `very detailed + specific instructions + `_ + on how to use Crouton for SOF. Most of these instructions are not + Zephyr-specific. With Crouton, you can configure and compile a Linux + kernel as usual. However, the kernel *installation* process is similar + to the ``cros_sdk`` process with a couple of small twists. + +- Finally, it is possible to **dual-boot** or completely replace + Chrome OS with a regular Linux distribution on *some* Chromebooks and + forget it is a Chromebook entirely. However, this comes at a price: it + is the least secure option and the more likely to make your device + permanently unusable ("brick"). That level of risk is highly dependent + on your particular Chromebook model. If that does not scare you, then + https://chrx.org/ is a good starting point. Pay special attention to + the note on security. This is the only option that lets you manage + kernel installations as a typical Linux distribution does. diff --git a/getting_started/intel_debug/suggestions.rst b/getting_started/intel_debug/suggestions.rst new file mode 100644 index 00000000..5a01e805 --- /dev/null +++ b/getting_started/intel_debug/suggestions.rst @@ -0,0 +1,340 @@ +.. _debug_suggestions: + +Suggestions before filing an SOF bug +#################################### + +Run alsa-info +************* + +The ``alsa-info`` script extracts a lot of information from the platform +(PCI ids, ACPI ids, DMI, controls, dmesg) that will help the SOF team +understand which hardware and OEM configuration is used. ``alsa-info`` +can upload the results to a server; providing the link is very useful +when filing a bug. + +Disable SOF on PCI/HDaudio devices to test audio playback +********************************************************* + +When audio issues occur, first see if the HDaudio legacy can generate sound +on speakers and headsets. Accomplish this by adding "options +snd-intel-dspcfg dsp_driver=1" to ``/etc/modprobe.d/alsa-base.conf``. + +If no sound can be heard and jack detection is not functional, an +HDaudio external codec configuration is likely. In some cases, the +Linux drivers are missing configuration information and may only +enable two of the four speakers present. + +All of these cases are orthogonal to SOF issues in that the SOF driver +cannot compensate for codec driver problems on its own. The HDaudio +codec configuration is handled by the legacy HDAudio driver +(snd-hda-intel), which is not maintained directly by SOF developers. + +Try booting into Windows first, then reboot into Linux +****************************************************** + +On some platforms, such as with an HDaudio codec connected to +amplifiers over an I2C/I2S link, the codec driver needs to perform a +set of amplifier configurations. This is often handled in Windows but +not in Linux codec drivers. A classic example of such issues is when +headphone playback works, but speaker playback does not (or not on all +speakers). Booting first in Windows then rebooting in Linux may help +setup the right configuration, but additional work is needed to patch +the Linux kernel. + +Reverse-engineer the Windows audio driver +***************************************** + +The HDaudio driver configures the codec with 'verb' commands to +e.g. setup the 'pins' or a coefficient. The exact values used are +device-specific, and in the absence of any documentation from the +codec vendor need to be reverse-engineered by snooping HDAudio +commands in a Windows environment. + +The following links provide additional information on snooping the +commands and determining what needs to be added to the Linux +kernel. These links are not maintained by SOF developers. + +* `ASUS Linux blog `_ + +* `How to sniff verbs from a Windows sound driver `_ + +Make sure the ME is enabled +*************************** + +If the ME is disabled by the OEM or the user, firmware authentication +will fail without any explicit feedback provided to the user. In case +of any authentication failure, verify that the ME is not disabled. More +information about the ME is available in the "Firmware binary" section of :ref:`intel_debug_introduction`. + +Test at the ALSA 'hw' device level +********************************** + +When the legacy HDaudio driver produces audible sound without +distortion and an SOF-based solution does not, user space configuration +issues are possible. + +Use the following commands to check if the SOF driver is functional at the hardware device level: + +.. code-block:: console + + speaker-test -Dhw:0,0 -c2 -r48000 -f S16_LE + arecord -Dhw:0,0 -c2 -r48000 -f S16_LE -d 10 test.wav + +The card and device indices may need to be adjusted on different +platforms: use ``aplay -l`` and ``arecord -l`` to see supported values on +your platform. + +If the playback or capture seems ok at the hardware device level, then the +following packages may need to be updated: + +- alsa-lib +- alsa-ucm-conf +- pulseaudio + +Verify mixer settings +********************* + +A classic issue with Linux audio is that a mixer control value remains +muted or with a volume set to zero. The ``alsamixer`` command can be +used to check if any paths are disabled (represented as "m") or if the +volume settings are not correct. + +Note that randomly playing with ALSA mixer settings can damage audio +accessories, speakers, or your hearing. Never change mixer +settings while listening to loud music on a headset! + +Enable dynamic debug +******************** + +To avoid spamming all Linux users with audio-specific information, +only critical errors are reported in the ``dmesg`` log. That information +may not be enough to debug a specific issue, and the recommendation is +to add the following options to the ``/etc/modprobe.d/sof-dyndbg.conf`` +file: + +.. code-block:: cfg + + # ACPI + options snd_sof_acpi dyndbg=+pmf + options snd_sof_acpi_intel_byt dyndbg=+pmf + options snd_sof_acpi_intel_bdw dyndbg=+pmf + options snd_sof_intel_byt dyndbg=+pmf + options snd_sof_intel_bdw dyndbg=+pmf + + # PCI + options snd_sof_pci dyndbg=+pmf + options snd_sof_pci_intel_apl dyndbg=+pmf + options snd_sof_pci_intel_cnl dyndbg=+pmf + options snd_sof_pci_intel_icl dyndbg=+pmf + options snd_sof_pci_intel_tgl dyndbg=+pmf + options snd_sof_pci_intel_mtl dyndbg=+pmf + options snd_sof_pci_intel_lnl dyndbg=+pmf + + # DSP selection + options snd_intel_dspcfg dyndbg=+pmf + options snd_intel_sdw_acpi dyndbg=+pmf + + # SOF internals + options snd_sof_intel_hda_common dyndbg=+pmf + options snd_sof_intel_hda_generic dyndbg=+pmf + options snd_sof_intel_hda_mlink dyndbg=+pmf + options snd_sof_intel_hda dyndbg=+pmf + options snd_sof dyndbg=+pmf + options snd_sof_nocodec dyndbg=+pmf + + # HDA + options snd_hda_intel dyndbg=+pmf + options snd-hda-codec-realtek dyndbg=+pmf + options snd-hda-codec-generic dyndbg=+pmf + options snd-hda-codec-hdmi dyndbg=+pmf + options snd-hda-codec dyndbg=+pmf + + # SoundWire core + options soundwire_bus dyndbg=+pmf + options soundwire_generic_allocation dyndbg=+pmf + options soundwire_cadence dyndbg=+pmf + options soundwire_intel_init dyndbg=+pmf + options soundwire_intel dyndbg=+pmf + +Note that this list is only an example. + +Dynamic debug is a Linux kernel feature. For detailed information, see the +official `kernel documentation `__. + +Install sof-logger +****************** + +If an issue with the SOF firmware is reported, such as IPC errors, SOF +developers will need DSP traces. This is typically done by installing +``/usr/local/bin/sof-logger`` as well as the ``.ldc`` file, and using the +following command to extract DSP traces: + + +.. code-block:: bash + + sof-logger -t -l sof-tgl.ldc + +Trace support might need to be enabled on distribution kernels in case the +``/sys/kernel/debug/sof/trace`` file is not present by adding sof_debug=1 option +to snd_sof module: + +.. code-block:: cfg + + options snd_sof sof_debug=1 + + +Digital mic issues +****************** + +The SOF driver and firmware have limited information related to the +number of digital microphones and their physical location. + +On devices designed for Windows, the presence of the microphone is +reported as an NHLT endpoint (ACPI table in the BIOS). The SOF Linux +driver will report this information with a 'dmesg' log such as + +.. code-block:: none + + [ 4.301490] sof-audio-pci-intel-tgl 0000:00:1f.3: DMICs detected in NHLT tables: 2 + +Recent versions of the ACPICA tools (acpica-tools package) can also be +used to visualize the ACPI tables. + +In some instances the number of DMICs reported by the NHLT does not +match the hardware layout. The SOF driver provides a means to alter +the value with a kernel parameter which can be added in +/etc/modprobe.d/alsa-base.conf (or any other configuration file with +this .conf extension). A reboot is necessary after changing the value + +.. code-block:: cfg + + options snd_sof_intel_hda_common dmic_num=4 + +The following command can then be used to check if the microphones are active at the lowest level + +.. code-block:: bash + + arecord -Dhw:0,6 -c4 -r48000 -fS32_LE -d 10 test.wav + +In 99% of the cases, hardware designers connect the two microphones on +the PDM0 controller. Some platforms use PDM1, which cannot really be +detected by the OS. By capturing in 4ch mode, it's possible that +channel3 and 4 capture data while channel0 and channel1 only show +signs of transitions and DC-removal. Simply talking or recording music +in this 10s test, then visualizing the recorded file with Audacity is +often enough to diagnose the presence of 2 microphones on the 'wrong' +PDM controller. + +In that case, a different topology file needs to be used, typically +sof-hda-generic-2ch-pdm1.tplg. On older distributions, it will be +necessary to override the file installed in +/lib/firmware/intel/sof-tplg/sof-hda-generic-2ch.tplg. On kernels +5.20+ a kernel parameter will be enough with no need to change and +override installed topology files, e.g. + +.. code-block:: cfg + + options snd-sof-pci tplg_filename=sof-hda-generic-2ch-pdm1.tplg + +These PDM1 issues are tracked in GitHub with the label 'DMIC-PDM1' in the +`firmware issues `_ +and in the `Linux issues `_. + +Users running Linux distributions on Chromebooks routinely experience +issues with digital microphones. In the Chrome environment, the +topology always exposes 4 channels, and UCM files for specific +platforms specify which of the 4 channels are valid. A plugin will +then drop the useless/non-populated channels. This capability does not +exist yet in upstream UCM/Linux. Capturing with the 'arecord; command +above will help understand which channels are valid and configure UCM +files. + +ES8336 support +************** + +Since 2021, a number of OEMs relied on the ES8336 codec from Everest +Audio on platforms as varied as AppoloLake, GeminiLake, JasperLake, +CometLake, AlderLake. + +End-users can verify if the hardware uses this configuration by +running the 'alsa-info' command and checking for the presence an ACPI +_HID, e.g. + +.. code-block:: none + + /sys/bus/acpi/devices/ESSX8336:00/status 15 + +.. code-block:: none + + /sys/bus/acpi/devices/ESSX8326:00/status 15 + +Support for this platform only stated upstream with the kernel +5.19-rc1. Any attempts with earlier kernels will require backports and +experimental patches to be added. In the case of the 8326, the codec +vendor submitted a driver to the ALSA/ASoC maintainers, which was not +merged as of July 2022. In this specific case end-users will be forced +to compile their own kernel. + +The SOF driver implemented an automatic detection of the SSP/I2S port +used by hardware and the presence of digital microphones based on +platform firmware/NHLT. + +There are however a number of hardware configurations that cannot be +detected from platform firmware. To work-around this limitation, the +'sof-es8336' machine driver exposes a 'quirk' kernel parameter which +can be used for modify GPIO and jack detection settings. Existing +quirks are listed in the sound/soc/intel/boards/sof_es8336.c machine +driver: + +.. code-block:: c + + #define SOF_ES8336_SPEAKERS_EN_GPIO1_QUIRK BIT(4) + #define SOF_ES8336_JD_INVERTED BIT(6) + #define SOF_ES8336_HEADPHONE_GPIO BIT(7) + #define SOC_ES8336_HEADSET_MIC1 BIT(8) + + +The default and actual quirk values for the platform can be obtained +from the kernel logs in the line as follows: + +.. code-block:: none + + ... + sof-essx8336 sof-essx8336: quirk mask 0x1a0 + ... + +for the unchanged mask, or: + +.. code-block:: none + + ... + sof-essx8336 sof-essx8336: Overriding quirk 0x1a0 => 0x120 + ... + +for the quirk overriding. + +The overridden quirk value can also be obtained from the +/sys/module/snd_soc_sof_es8336/parameters/quirk (the value is reported +as a plain integer, not hexadecimal). If the quirk is not overridden, +the `-1` value is returned. + +Changes to the default can be added with the following option in +e.g. /etc/modprobe.d/alsa-base.conf. Only the bits listed above can be +modified; others need to remain as is. + +.. code-block:: cfg + + options snd_soc_sof_es8336 quirk= + +Changing quirk values is an extremely experimental endeavor that +should only attempted by users with working knowledge of the Linux +audio subsystem and an understanding that playing with hardware +settings MAY DAMAGE HARDWARE or generate extremely loud sounds that +MAY DAMAGE YOUR HEARING. + +In rare cases, some platforms use the MCLK1 signal instead of +MCLK0. As of July 2022, there is no turn-key solution for those +platforms. + +These ES8336 issues are tracked in GitHub with the label 'codec +ES8336' in the `Linux ES8336 issues `_. diff --git a/getting_started/loadable_modules/lmdk_user_guide.rst b/getting_started/loadable_modules/lmdk_user_guide.rst new file mode 100644 index 00000000..b6085e01 --- /dev/null +++ b/getting_started/loadable_modules/lmdk_user_guide.rst @@ -0,0 +1,25 @@ +.. _lmdk_user_guide: + +Loadable modules build guide using LMDK +####################################### + +What is LMDK +************ + +LMDK(Loadable Module Development Kit) is a standalone package required to build loadable module. It is independent from SOF FW but contains necessary data structures to interact with it. + +How to build +************ + +To build example loadable library execute: +.. code-block:: bash + + $ cd libraries/example + $ mkdir build + $ cd build + + $ cmake -DRIMAGE_COMMAND="/path/to/rimage" -DSIGNING_KEY="/path/to/signing/key.pem" .. + $ cmake --build . + +Here RIMAGE_COMMAND is path to rimage executable binary, SIGNING_KEY is path to +signing key for rimage. `LMDK ` diff --git a/getting_started/nxp/sof_imx_user_guide.rst b/getting_started/nxp/sof_imx_user_guide.rst new file mode 100644 index 00000000..c7e2f6c7 --- /dev/null +++ b/getting_started/nxp/sof_imx_user_guide.rst @@ -0,0 +1,349 @@ +.. _sof_imx_user_guide: + +SOF User Guide on NXP i.MX8 platforms +##################################### + +.. contents:: + :local: + :depth: 3 + +This guide describes how to run SOF on NXP i.MX8 platforms. + +Supported NXP platforms +*********************** + ++-----------+------------+----------------+------------------+------------------+ +| Platform | Short Name | DSP | Audio Interfaces | Supported Codecs | ++===========+============+================+==================+==================+ +| i.mx8qm | i.mx8 | hifi4\@666mhz | esai, sai | wm8960, cs42888 | ++-----------+------------+----------------+------------------+------------------+ +| i.mx8qxp | i.mx8x | hifi4\@640mhz | esai, sai | wm8960, cs428888 | ++-----------+------------+----------------+------------------+------------------+ +| i.mx8mp | i.mx8m | hifi4\@800mhz | sai | wm8960 | ++-----------+------------+----------------+------------------+------------------+ + +See :ref:`platforms` for more details. + + +Toolchain +********* + +NXP i.MX8 currently supports two toolchain families: GCC and Cadence XCC. + +1. **GCC** is an open source, publicly available, toolchain built using crosstool-NG: + + * Available as prebuilt binaries from `crosstool-NG release `_. + * Can be built from sources as documented in :ref:`build-toolchains-from-source` under **Getting Started Guides**. + +2. **Cadence XCC** is a proprietary toolchain, available under certain terms and conditions: + + * Contact NXP tech support. + + + +Quick run with SOF from i.MX8 Board Support Package +*************************************************** + +Binaries needed to run SOF on i.MX8 NXP platforms are provided in the Board Support Package (BSP) software. Use the latest +`i.MX8 BSP Release `_ binaries. + +Kernel image and modules +------------------------ + +``Image-imx8_all.bin`` is the name of the Linux kernel image. arm64 uses the same image for all platforms. + +SOF Linux driver functionality is implemented across several kernel modules: + + * **snd-sof.ko**: SOF core functionality + * **snd-sof-of.ko**: SOF OF-related functionality (SOF device probing, device tree parsing) + * **snd-sof-imx8.ko**: i.MX8QXP, i.MX8QM-specific functionality (I/O mapping, power domains, clocks, etc) + * **snd-sof-imx8m.ko**: i.MX8MP-specific functionality + * **imx-common.ko**: i.MX common helpers + * **snd-sof-xtensa-dsp.ko**: Xtensa-specific functionality (register dumps, DSP stack traces) + +Linux kernel SOF modules are installed in the ``rootfs`` image at: ``/lib/modules//kernel/sound/soc/sof/``. + +.. _nxp_device_tree_files: + +Device tree files +----------------- + +DSP is seen by the Linux kernel as an I/O mapped device. Audio interfaces are controlled by the DSP via SOF firmware. Codecs are controlled by the ARM core via the Linux kernel. + ++-----------+-----------------------------+----------------------------+ +| Platform | DTB | Comments | ++===========+=============================+============================+ +| i.mx8qm | imx8qm-mek-sof-cs42888.dtb | ESAI + cs42888 (baseboard) | ++-----------+-----------------------------+----------------------------+ +| i.mx8qm | imx8qm-mek-sof-wm8960.dtb | SAI + wm6890 (cpuboard) | ++-----------+-----------------------------+----------------------------+ +| i.mx8qxp | imx8qxp-mek-sof-cs42888.dtb | ESAI + cs42888 (baseboard) | ++-----------+-----------------------------+----------------------------+ +| i.mx8qxp | imx8qxp-mek-sof-wm8960.dtb | SAI + wm8960 (cpuboard) | ++-----------+-----------------------------+----------------------------+ +| i.mx8mp | imx8mp-evk-sof-wm8960.dtb | SAI + wm8960 | ++-----------+-----------------------------+----------------------------+ + +.. _nxp_firmware_images: + +Firmware images +--------------- + +Firmware images are installed in the ``rootfs`` image at: ``/lib/firmware/imx/sof/``. + ++-----------+-------------------------------------------+ +| Platform | Firmware Path | ++===========+===========================================+ +| i.mx8qm | /lib/firmware/imx/sof/sof-imx8.ri | ++-----------+-------------------------------------------+ +| i.mx8qxp | /lib/firmware/imx/sof/sof-imx8x.ri | ++-----------+-------------------------------------------+ +| i.mx8mp | /lib/firmware/imx/sof/sof-imx8m.ri | ++-----------+-------------------------------------------+ + +.. _nxp_topology_files: + +Topology files +-------------- + +Topology files describe one or more audio pipelines and are installed in the +``rootfs`` image at: ``/lib/firmware/imx/sof-tplg/``. + ++----------------------------------+-----------------+--------------------------------------+ +| Topology Name | Platform | Usecase | ++===============+==================+=================+======================================+ +| sof-imx8-cs42888.tplg | imx8qm/imx8qxp | PCM playback/record w/ cs42888 codec | ++----------------------------------+-----------------+--------------------------------------+ +| sof-imx8-wm8960.tplg | imx8qm/imx8qxp | PCM playback/record w/ wm8960 codec | ++----------------------------------+-----------------+--------------------------------------+ +| sof-imx8mp-wm8960.tplg | imx8mp | PCM playback/record w/ wm8960 codec | ++----------------------------------+-----------------+--------------------------------------+ +| sof-imx8-cs42888.tplg | imx8qm/imx8qxp | PCM playback/record w/ SRC (wm8960) | ++----------------------------------+-----------------+--------------------------------------+ +| sof-imx8-wm8960.tplg | imx8qm/imx8qxp | PCM playback/record w/ SRC (cs42888) | ++----------------------------------+-----------------+--------------------------------------+ +| sof-imx8mp-wm8960.tplg | imx8mp | PCM playback/record w/ SRC (wm8960) | ++----------------------------------+-----------------+--------------------------------------+ +| sof-imx8-wm8960-mixer.tplg | imx8qm/imx8qxp | PCM playback/record w/ mixer | ++----------------------------------+-----------------+--------------------------------------+ +| sof-imx8-cs42888-mixer.tplg | imx8qm/imx8qxp | PCM playback/record w/ mixer | ++----------------------------------+-----------------+--------------------------------------+ +| sof-imx8mp-wm8960-mixer.tplg | imx8mp | PCM playback/record w/ mixer | ++----------------------------------+-----------------+--------------------------------------+ +| sof-imx8-compr-mp3-wm8960.tplg | imx8qxp/imx8qmp | Compress playback (mp3) | ++----------------------------------+-----------------+--------------------------------------+ +| sof-imx8mp-compr-mp3-wm8960.tplg | imx8mp | Compress playback (mp3) | ++----------------------------------+-----------------+--------------------------------------+ +| sof-imx8-compr-aac-wm8960.tplg | imx8qxp/imx8qmp | Compress playback (aac) | ++----------------------------------+-----------------+--------------------------------------+ +| sof-imx8mp-compr-aac-wm8960.tplg | imx8mp | Compress playback (aac) | ++----------------------------------+-----------------+--------------------------------------+ + +Build SOF binaries from sources +******************************* + +Use :ref:`build-with-docker` to build SOF binaries with Docker. Otherwise, +build it on your Debian-like machine as follows. + +Kernel image and modules +------------------------ + +Use the NXP internal Linux kernel tree to get full support for i.MX8 boards: + +.. code-block:: bash + + $ git clone https://source.codeaurora.org/external/imx/linux-imx + # checkout latest stable branch + $ git checkout lf-5.10.y + +.. code-block:: bash + + # install arm64 toolchain + $ sudo apt-get install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu + + # set defconfig + $ ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- make defconfig + + # compile the kernel and modules + $ ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- make -j8 + + # install the modules + $ INSTALL_MOD_PATH=/path/to/rootfs/ make modules_install + +SOF firmware +------------ + +See Step 3 :ref:`build-from-scratch`. + +Tools +----- + +See Step 4 in :ref:`build-from-scratch`. + +The sof-logger must be cross-compiled in order to run on arm64: + +.. code-block:: bash + + $ cd "$SOF_WORKSPACE"/sof/tools/ + $ mkdir build_tools && cd build_tools + $ cmake .. -DCMAKE_TOOLCHAIN_FILE=../scripts/cross-arch64.cmake + $ make sof-logger + +Audio scenarios +*************** + +This section demonstrates all audio scenarios on i.MX8QM. Consult the list of :ref:`nxp_device_tree_files`, :ref:`nxp_firmware_images`, and +:ref:`nxp_topology_files` in order to select the proper binaries for your board and audio scenario. + +Audio playback and record +------------------------- + +Booting i.MX8QM with ``imx8qm-mek-sof-wm8960.dtb`` enables PCM audio playback/record with the wm8960 codec. This uses +the default topology found at ``/lib/firmware/imx/sof-tplg/sof-imx8-wm8960.tplg``. + +.. code-block:: bash + + root@imx8qxpc0mek:~# aplay -l + **** List of PLAYBACK Hardware Devices **** + card 1: sofwm8960audio [sof-wm8960-audio], device 0: Port0 (*) [] + Subdevices: 1/1 + Subdevice #0: subdevice #0 + + # start playback on SOF device + root@imx8qxpc0mek:~# aplay -Dhw:1,0 sample.wav + Playing WAVE 'sample.wav' : Signed 32 bit Little Endian, Rate 48000 Hz, Stereo + + # start capture on SOF device + root@imx8qxpc0mek:~# arecord -Dhw:1,0 -f S32_LE -c 2 -r 48000 capture.wav + Recording WAVE 'capture.wav' : Signed 32 bit Little Endian, Rate 48000 Hz, Stereo + +Audio mixing +------------ + +The following demonstates how to use SOF in order to mix two PCM streams on +i.MX8QM and render the output to the wm8960 codec. + +Boot the i.MX8QM board using ``imx8qm-mek-sof-wm8960.dtb``. Use the ``sof-imx8-wm8960-mixer.tplg`` topology file: + +.. code-block:: bash + + $ cp /lib/firmware/imx/sof-tplg/sof-imx8-wm8960-mixer.tplg /lib/firmware/imx/sof-tplg/sof-imx8-wm8960.tplg + +After booting, the SOF sound card contains two subdevices: + +.. code-block:: bash + + root@imx8qxpc0mek:~# aplay -l + **** List of PLAYBACK Hardware Devices **** + card 1: sofwm8960audio [sof-wm8960-audio], device 0: PCM (*) [] + Subdevices: 1/1 + Subdevice #0: subdevice #0 + card 1: sofwm8960audio [sof-wm8960-audio], device 1: PCM Deep Buffer (*) [] + Subdevices: 1/1 + Subdevice #0: subdevice #0 + + # PCM files sent to SOF card1/device0, card1/device1 will be mixed together by SOF firmware and then rendered on wm8960 codec + root@imx8qxpc0mek:~# aplay -Dhw:1,0 sample0.wav & aplay -Dhw:1,1 sample1.wav + Playing WAVE 'sample0.wav' : Signed 32 bit Little Endian, Rate 48000 Hz, Stereo + Playing WAVE 'sample1.wav' : Signed 32 bit Little Endian, Rate 48000 Hz, Stereo + +Sample rate converter +--------------------- + +Sample rate converter is supported via the **SRC** open-coded component in ``src/audio/src``. + +Based on the specific toolchain used, SOF on i.MX supports converting the following: + ++---------------+--------------------+----------------------------------------------------+--------------------+ +| Toolchain | Direction | Input Rate (kHz) | Output Rate (kHz) | ++===============+====================+====================================================+====================+ +| GCC | playback/capture | 8 16 32 44.1 48 96 | 48 | ++---------------+--------------------+----------------------------------------------------+--------------------+ +| XCC | playback | 8 11.025 16 22.05 32 44.1 48 64 88.2 96 176.4 192 | 48 | ++---------------+--------------------+----------------------------------------------------+--------------------+ +| XCC | capture | 8 11.025 16 22.050 32 44.1 48 | 48 | ++---------------+--------------------+----------------------------------------------------+--------------------+ + +Boot the i.MX8QM board using ``imx8qm-mek-sof-wm8960.dtb``. Use the +``sof-imx8-src-wm8960.tplg`` topology file: + +.. code-block:: bash + + $ cp /lib/firmware/imx/sof-tplg/sof-imx8-src-wm8960-mixer.tplg /lib/firmware/imx/sof-tplg/sof-imx8-wm8960.tplg + +Below are several runs with aplay on various rates and formats: + +.. code-block:: bash + + root@imx8qmmek:~# aplay -Dhw:1,0 -f S16_LE -c 2 -r 8000 -t raw /mnt/test/samples_16b/audio8k16b2c.wav + Playing raw data '/mnt/test/samples_16b/audio8k16b2c.wav' : Signed 16 bit Little Endian, Rate 8000 Hz, Stereo + + root@imx8qmmek:~# aplay -Dhw:1,0 -f S16_LE -c 2 -r 16000 -t raw /mnt/test/samples_16b/audio16k16b2c.wav + Playing raw data '/mnt/test/samples_16b/audio16k16b2c.wav' : Signed 16 bit Little Endian, Rate 16000 Hz, Stereo + + root@imx8qmmek:~# aplay -Dhw:1,0 -f S24_LE -c 2 -r 32000 -t raw /mnt/test/samples/audio32k24b2c.wav + Playing raw data '/mnt/test/samples/audio32k24b2c.wav' : Signed 24 bit Little Endian, Rate 32000 Hz, Stereo + + root@imx8qmmek:~# aplay -Dhw:1,0 -f S24_LE -c 2 -r 44100 -t raw /mnt/test/samples/audio44k24b2c.wav + Playing raw data '/mnt/test/samples/audio44k24b2c.wav' : Signed 24 bit Little Endian, Rate 44100 Hz, Stereo + + root@imx8qmmek:~# aplay -Dhw:1,0 -f S32_LE -c 2 -r 48000 -t raw /mnt/test/samples_32b/audio48k32b2c.wav + Playing raw data '/mnt/test/samples_32b/audio48k32b2c.wav' : Signed 32 bit Little Endian, Rate 48000 Hz, Stereo + + root@imx8qmmek:~# aplay -Dhw:1,0 -f S32_LE -c 2 -r 96000 -t raw /mnt/test/samples_32b/audio96k32b2c.wav + Playing raw data '/mnt/test/samples_32b/audio96k32b2c.wav' : Signed 32 bit Little Endian, Rate 96000 Hz, Stereo + +Compress audio +-------------- + +In order to use DSP to decode/encode compress audio, NXP uses `ALSA Compress Offload APIs `_. + +Supported codecs on i.MX8QM: + ++---------------+--------------------+----------------+-------------------------------------------------+ +| codec | topology | Test command | ++===============+=====================================+=================================================+ +| PCM | sof-imx8-processing-pcm-wm8960.m4 | cplay -c 1 -d 0 -f 2 -b 7680 -I PCM sample.wav | ++---------------+-------------------------------------+-------------------------------------------------+ +| MP3 | sof-imx8-processing-mp3-wm8960.m4 | cplay -c 1 -d 0 -f 2 -b 7680 -I MP3 sample.mp3 | ++---------------+-------------------------------------+-------------------------------------------------+ +| AAC | sof-imx8-processing-aac-wm8960.m4 | cplay -c 1 -d 0 -f 2 -b 7680 -I MP3 sample.aac | ++---------------+-------------------------------------+-------------------------------------------------+ + +See :ref:`nxp_topology_files` for the list of topology files to use on other NXP i.MX boards. + +To enable compress audio in SOF firmware, you must enable the Codec Adapter +component and select the appropriate decoding library algorithms. For i.MX8, +we use the Cadence proprietary libraries: + +.. code-block:: bash + + CONFIG_COMP_CODEC_ADAPTER=y + CONFIG_CADENCE_CODEC=y + + # Enable AAC Cadence decoder + CONFIG_CADENCE_CODEC_AAC_DEC=y + CONFIG_CADENCE_CODEC_AAC_DEC_LIB="/path/to/aac/library" + + # Enable MP3 Cadence decoder + CONFIG_CADENCE_CODEC_MP3_DEC=y + CONFIG_CADENCE_CODEC_MP3_DEC_LIB="/path/to/mp3/library" + +Contact NXP Tech support for information on how to obtain Cadence proprietary algorithms. + +Boot the i.MX8QM board using ``imx8qm-mek-sof-wm8960.dtb``. The following +example tests the MP3 audio decoder by using the ``sof-imx8-processing-mp3-wm8960.m4`` topology file: + +.. code-block:: bash + + $ cp /lib/firmware/imx/sof-tplg/sof-imx8-processing-mp3-wm8960.m4 /lib/firmware/imx/sof-tplg/sof-imx8-wm8960.tplg + +.. code-block:: bash + + $ cplay -c -d -f -b -I sample.file + # identify card and device number + $ ls /dev/snd* + comprC1D0 ==> this means => [card 1, device 0] + # fragments is always 2, buffer size is always a multiple of 768, recommended value is 7680 + $ cplay -c 1 -d 0 -f 2 -b 7680 -I MP3 samples.mp3 + diff --git a/getting_started/setup/setup_ktest_environment.rst b/getting_started/setup/setup_ktest_environment.rst deleted file mode 100644 index 8b4c244c..00000000 --- a/getting_started/setup/setup_ktest_environment.rst +++ /dev/null @@ -1,269 +0,0 @@ -.. _setup-ktest-environment: - -Set up a Ktest-based environment -################################ - -.. contents:: - :local: - :depth: 3 - -Introduction -************ -These instructions explain how a target device can be configured to -update the kernel over SSH. The use of ktest.pl and git worktrees -allow for simultaneous configs to be tested on multiple platforms, -though only one branch can be checked out at a time. Wired Ethernet -access is assumed as wireless is unreliable. If there is no Ethernet -port, use a USB-Ethernet dongle supported in the kernel. - -Prerequisites on the target device -********************************** - -The target device can be any of the SOF-supported platforms, -e.g. MinnowBoard, Up^2, Asus T100, Chromebooks) - -1. Install OS on target ------------------------ - -Install ubuntu or debian (fedora is possible with a minor change -in the *initrd* generation) - -2. Enable root password ------------------------ - -.. code-block:: bash - - sudo su (enter your password) - passwd (enter new root password) - exit - -3. Create test kernel ---------------------- - -Copy your existing known-to-work kernels/initrd - -.. code-block:: bash - - cp /boot/vmlinuz-4.13.0-16-generic /boot/vmlinuz-test - cp /boot/initrd.img-4.13.0-16-generic /boot/initrd.img-test - -Change the extensions as needed to create an initial grub entry -for a test kernel. You will never override the default -Ubuntu/Debian stuff, so you will always have the ability to boot a -working kernel if your changes fail to boot. - -4. Edit grub default --------------------- - -.. code-block:: bash - - # Use your text editor of choice. - sudo emacs /etc/default/grub - -Add ``GRUB_DISABLE_SUBMENU=y`` to the end and save. - -5. Create new grub entry ------------------------- - -.. code-block:: bash - - sudo update-grub - -6. Install openssh-server -------------------------- - -.. code-block:: bash - - sudo apt-get install openssh-server - # Use your editor of choice. - sudo emacs /etc/ssh/sshd_config - -Replace ``PermitRootLogin without-password`` with ``PermitRootLogin yes`` -and save. - -7. reboot target ----------------- - -Configure SSH without password -****************************** - -1. Check SSH connection ------------------------ - -.. code-block:: bash - - ssh root@ - -2. Generate an SSH key for the target -------------------------------------- - -.. code-block:: bash - - cd ~/.ssh - ssh-keygen -f sshktest - # Enter a 5+ character passphrase. - ssh-copy-id -i ~/.ssh/sshktest root@ - # This will prompt you for the root password. - -3. Test the key ---------------- - -.. code-block:: bash - - ssh -i ~/.ssh/sshktest root@ - # Ubuntu unlocks the key so the -i option is not necessary. - -4. Disable root access ----------------------- - -Disable the root password on the target device if you -are concerned about access control. - -.. code-block:: bash - - # Use your editor of choice. - sudo emacs /etc/ssh/sshd_config - -Replace ``PermitRootLogin yes`` by ``PermitRootLogin without-password``, save, and exit. - -Create a linux development environment -************************************** - -1. Create a main working GIT tree ---------------------------------- - -.. code-block:: bash - - git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git linux-ref.git - cd linux-ref.git - -2. Add a set of useful remotes ------------------------------- - -.. code-block:: bash - - git remote add sof https://github.com/thesofproject/linux.git - git remote add takashi git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git - git remote add broonie git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git - git remote add liam git://git.kernel.org/pub/scm/linux/kernel/git/lrg/asoc.git - git remote add keyon git://github.com/keyonjie/linux.git - git remote add vinod git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/sound.git - git remote add plb git://github.com/plbossart/sound.git - git fetch sof - git fetch takashi - git fetch broonie - git fetch liam - git fetch keyon - git fetch vinod - git fetch plb - -All of these branches will be accessible and can be updated from any -worktree. Clone once and use fetch to update the main working tree. - -3. Create a worktree for SOF in ~/ktest ---------------------------------------- - -.. note:: - Change the location of your ktest directory and which branch you use - as needed. - -.. code-block:: bash - - git worktree add ~/ktest/sof-dev sof/topic/sof-dev - -4. Set-up worktree ------------------- - -.. code-block:: bash - - cd ~/ktest/sof-dev - mkdir sof-dev-build - mkfifo sof-dev-cat - cp sof-dev/tools/testing/ktest/ktest.pl . - -5. Save your kernel config as ~/ktest/sof-dev-defconfig -------------------------------------------------------- - -If you don't know what options are needed, you can start using configurations maintained by SOF developers. - -.. code-block:: bash - - git clone https://github.com/thesofproject/kconfig.git - cd linux - make defconfig - scripts/kconfig/merge_config.sh .config ../kconfig/base-defconfig ../kconfig/sof-defconfig ../kconfig/sof-mach-driver-defconfig ../kconfig/hdaudio-codecs-defconfig - cp .config ../sof-dev-defconfig - make mrproper - cd .. - -.. note:: - - Use make proper since ktest.pl requires the source directory - to be clean. All compilation happens in the -build directory. - -6. Edit configuration as needed -------------------------------- - -Save the following in sof-dev.conf. - -.. code-block:: perl - - MACHINE = 192.168.1.205 - CLEAR_LOG = 1 - SSH_USER = root - THIS_DIR := ${PWD} - BUILD_DIR = ${THIS_DIR}/sof-dev - OUTPUT_DIR = ${THIS_DIR}/sof-dev-build - BUILD_TARGET = arch/x86/boot/bzImage - TARGET_IMAGE = /boot/vmlinuz-test - LOCALVERSION = -test - BUILD_OPTIONS = -j8 - LOG_FILE = ${OUTPUT_DIR}/sof-dev.log - CONSOLE = cat ${THIS_DIR}/sof-dev-cat - POWER_CYCLE = echo Power cycle the machine now and press ENTER; read a - #set below to help ssh connection to close after sending reboot command - REBOOT = ssh -o 'ProxyCommand none' $SSH_USER@$MACHINE 'sudo reboot > /dev/null &' - GRUB_FILE = /boot/grub/grub.cfg - GRUB_MENU = Ubuntu, with Linux test - #GRUB_MENU = ubilinux GNU/Linux, with Linux test - #GRUB_MENU = GalliumOS GNU/Linux, with Linux test - GRUB_REBOOT = grub-reboot - REBOOT_TYPE = grub2 - POST_INSTALL = ssh -o 'ProxyCommand none' $SSH_USER@$MACHINE 'sudo /usr/sbin/mkinitramfs -o /boot/initrd.img-test $KERNEL_VERSION' - #REBOOT_TYPE = script - #REBOOT_SCRIPT = ssh $SSH_USER@$MACHINE "sed -i 's|^default.*$|default test|' /boot/loader/loader.conf" - - TEST_START - TEST_TYPE = boot - BUILD_TYPE = useconfig:${THIS_DIR}/sof-dev-defconfig - BUILD_NOCLEAN = 1 - -7. Build and test ------------------ - -.. code-block:: bash - - ./ktest.pl sof-dev.conf - -If this does not work, make sure you have all the following files in the -local directory: - -* ktest.pl -* sof-dev-cat -* sof-dev -* sof-dev-build -* sof-dev.conf -* sof-dev-defconfig - -Ktest will compile, install the new kernel, and reboot. Prompt -detection only works with a UART, not over SSH, so you will have to -``Control-C`` manually when the console is not enabled. - -8. Enjoy! ---------- - -9. Enjoy even more! -------------------- - -By having multiple worktrees and configs, you can run tests in parallel -on different machines on the same kernel or different branches. diff --git a/getting_started/setup_linux/install_locally.rst b/getting_started/setup_linux/install_locally.rst new file mode 100644 index 00000000..ce50ec8f --- /dev/null +++ b/getting_started/setup_linux/install_locally.rst @@ -0,0 +1,134 @@ +.. _install-locally: + +Install the Kernel Locally +########################## + +.. contents:: + :local: + :depth: 3 + +Introduction +************ + +Make sure you have `set up your development environment `_ before following these steps. This page will guide you through the process of installing the kernel locally on your machine. It will be installed in addition to your distro's default kernel so that you can always change back to that in case something goes wrong. If you are interested in learning more about this process, there are lots of online guides available, for example `Fedora* Quick Docs `_, `Arch Linux documentation `_ or `this wiki page `_. + + +Build and install the kernel +**************************** + +1. Change directory to ``~/work/sof/linux`` that you created on the setup page. + +#. Load the base kernel configuration. + + The following command copies the configuration of the booted kernel so that it will be used as a base: + + .. code-block:: bash + + cp /boot/config-$(uname -r)* .config + + +#. Apply the SOF-specific configuration. + + + The following scripts update your base configuration so that it uses the latest SOF modules. Run only one of them depending on your needs. If it prompts you with any questions, just press **Enter** to accept the default value. Note that, by default, these scripts will set the configuration to only compile modules that are currently loaded in order to lower compile times. This means that when you've booted from the custom kernel, some external devices may not work if they were not connected while running this script. If you want to compile all modules, delete the line ``make localmodconfig`` from the script you will run in this step. + + - For most users: + + .. code-block:: bash + + ../kconfig/kconfig-distro-sof-update.sh + + + - For additional logging and experimental device support: + + .. code-block:: bash + + ../kconfig/kconfig-distro-sof-dev-update.sh + + .. _compile-kernel-step: + +#. Compile the kernel. + + The first time you run this command, it can take a while (over 30 minutes on some machines), so grab a coffee or take an exercise break while it runs. + + .. code-block:: bash + + make -j$(nproc --all) + + .. _install-kernel-step: + +#. Install the kernel. + + .. code-block:: bash + + sudo make modules_install + sudo make install + +If all went well, your freshly-built kernel will be installed and available at next boot. Restart your computer, and you should have the option to pick a kernel when it turns on. Select the kernel which name has ``-sof`` at the end of it, and your computer should boot as normal using the kernel you just built. On Ubuntu*, the kernel option may be hidden behind the **Advanced options for Ubuntu** submenu. + +Update and rebuild +****************** + +If you need to try some new changes, download the updated code and rebuild the kernel. + +Update the kernel cloned with git +--------------------------------- + +If you originally cloned the repo using git, perform the following steps to update and rebuild the kernel: + +1. Pull the changes. + + .. code-block:: bash + + git pull + +#. Clean the directory. + + .. note:: You should clean up after switching branches or configuration or any other major code change. If you just pulled some minor updates, it's likely unnecessary and will increase your build time. + + .. code:: bash + + make clean + +#. Repeat :ref:`steps 4` :ref:`and 5` to rebuild and reinstall the kernel. + +#. Reboot your computer, and select the kernel with ``-sof`` at the end of its name to test it. + +Update the kernel downloaded via zip +------------------------------------ + +Unfortunately, if you downloaded via zip, the entire process has to be restarted from the :ref:`Get the kernel source` step. There is no good way to incrementally update. However, the kernel build should be faster now as part of it will be cached. + +Make sure you delete the old folder before starting over: + +.. code-block:: bash + + cd .. + rm -rf linux + + +Remove the kernel +***************** + +If you run into issues or no longer need the custom kernel, you can remove it. + +- Ubuntu: + + .. code-block:: bash + + cd ~/work/sof/linux + sudo rm /boot/*-$(make kernelversion) + sudo rm -rf /lib/modules/$(make kernelversion) + sudo update-grub + +- Fedora: + + .. code-block:: bash + + cd ~/work/sof/linux + sudo rm /boot/*-$(make kernelversion)* + sudo rm -rf /lib/modules/$(make kernelversion) + sudo grubby --remove-kernel=/boot/vmlinuz-$(make kernelversion) + + +After rebooting, you should be back to your old kernel with all traces of the custom kernel installation gone. If you'd like, you can also delete the ``~sof`` directory to save disk space. diff --git a/getting_started/setup_linux/prepare_build_environment.rst b/getting_started/setup_linux/prepare_build_environment.rst new file mode 100644 index 00000000..d86cb6aa --- /dev/null +++ b/getting_started/setup_linux/prepare_build_environment.rst @@ -0,0 +1,79 @@ +.. _prepare-build-environment: + +Set up a Development Environment to Build the Kernel +#################################################### + +These instructions will help you set up a development environment for the SOF branch of the Linux kernel. If you have dedicated test hardware, you can use ktest to install it over ssh. Otherwise, you can install it locally on your device in addition to your default kernel. + +Review the following prerequisites: + +- **Development device:** PC running Fedora* 35+ or Ubuntu* 20.04+. + +- **Target device:** PC running Fedora 35+ or Ubuntu 20.04+, with secure boot disabled. If the target device is different than the development device, you must be able to ssh into the target, which is typically on the same local network/VPN. + +1. Create a working directory. + + This directory can be located anywhere. Simply change the ``SOF_WORKSPACE`` variable if you would like to store your sources somewhere else. + + .. code-block:: bash + + export SOF_WORKSPACE=~/work/sof + mkdir -p $SOF_WORKSPACE + cd $SOF_WORKSPACE + +#. Install kernel build dependencies. + + - Fedora (see `their guide `_ for details): + + .. code-block:: bash + + sudo dnf install fedpkg + fedpkg clone -a kernel + cd kernel + sudo dnf builddep kernel.spec + sudo dnf install ccache + cd .. + + - Ubuntu (see `their page `_ for details): + + .. code-block:: bash + + sudo apt update + sudo apt install git libncurses-dev gawk flex bison openssl libssl-dev dkms libelf-dev libudev-dev libpci-dev libiberty-dev autoconf dwarves zstd + +#. Download the configuration scripts. + + .. code-block:: bash + + git clone https://github.com/thesofproject/kconfig.git + + .. _get-kernel-source: + +#. Get the kernel source. + + There are two ways to get the kernel source. We strongly recommend using git as it makes updates **much** easier, but the zip download may be more successful if you have an unstable connection. + + - Option 1: Clone with git. + + .. code-block:: bash + + git clone https://github.com/thesofproject/linux.git --depth=1 + cd linux + + .. note:: + + If a maintainer requests that you check out a different branch to test a bug fix, add ``-b [branch]`` to the end of this command, where `[branch]` is the branch name. + + - Option 2: Download via zip. + + Visit the SOF Linux fork at https://github.com/thesofproject/linux. If a maintainer asks you to test a specific branch, click the dropdown with the text "topic/sof-dev" and select the branch they asked you to test. Then, click the green **Code** dropdown and select **Download ZIP**. Once it is downloaded, extract it to the directory you created in the previous step: + + .. code-block:: bash + + cd ~/Downloads + unzip linux-*.zip -d $SOF_WORKSPACE + cd $SOF_WORKSPACE + mv linux-* linux + cd linux + +Your device should now be ready to configure and build the kernel. How to proceed depends on if you are installing :ref:`locally` or on :ref:`dedicated test hardware`. diff --git a/getting_started/setup_linux/setup_ktest_environment.rst b/getting_started/setup_linux/setup_ktest_environment.rst new file mode 100644 index 00000000..2836890c --- /dev/null +++ b/getting_started/setup_linux/setup_ktest_environment.rst @@ -0,0 +1,405 @@ +.. _setup-ktest-environment: + +Set up a Ktest-based Environment +################################ + +.. contents:: + :local: + :depth: 3 + +Introduction +************ + +These instructions explain how a target device can be configured to +update the kernel over SSH. The use of ktest.pl and git worktrees +allow for simultaneous configs to be tested on multiple platforms. +Wired Ethernet access is assumed as wireless is unreliable. If there +is no Ethernet port, use a USB-Ethernet dongle supported in the kernel. + +The target device can be any of the SOF-supported platforms, +such as MinnowBoard, Up^2, Asus T100, Chromebooks. + +Set up a target +*************** + +1. Install Ubuntu*, Debian*, or Fedora* on the target. + +#. Enable root password. + + .. code-block:: bash + + sudo su (enter your password) + passwd (enter new root password) + exit + +#. Create a test kernel. + + Copy your existing known-to-work kernels/initrd. + + .. code-block:: bash + + sudo cp /boot/vmlinuz-$(uname -r) /boot/vmlinuz-test + + # On Ubuntu: + sudo cp /boot/initrd.img-$(uname -r) /boot/initrd.img-test + + # On Fedora: + sudo cp /boot/initramfs-$(uname -r).img /boot/initramfs-test.img + sudo grubby --add-kernel /boot/vmlinuz-test --title=test + +#. Edit grub settings. + + Perform these steps only on Ubuntu and Debian. Fedora has the proper settings by default. + + a) Open the grub configuration file in your editor of choice as a super user: + + .. code-block:: bash + + sudo emacs /etc/default/grub + + b) Change ``GRUB_DEFAULT=[n]`` to ``GRUB_DEFAULT=saved``. + + c) You must disable GRUB submenus because they confuse ktest: + + .. code-block:: console + + echo 'GRUB_DISABLE_SUBMENU=y' | sudo tee -a /etc/default/grub.d/disable-submenu.cfg + + d) Update the grub configuration. + + .. code-block:: console + + # Better safe than _very_ sorry + sudo cp /boot/grub/grub.cfg /boot/grub/saved_grub.cfg + + sudo update-grub + + # Make sure submenus are actually disabled + grep submenu /boot/grub/grub.cfg + +#. Set the default kernel. + + You will never override the default + distro kernel, so you will always have the ability to boot a + working kernel if your changes cause issues. + By setting the default kernel, you can return your system to a stable + state with just a power cycle, no grub menus involved. + + - On Ubuntu: + + .. code-block:: bash + + # Print your currently booted (and known-safe) option + cat /proc/cmdline + # List the grub entries + awk '/^menuentry|submenu/ { print i++, '\t', $0 }' /boot/grub/grub.cfg + # Find the entry that matches the output of the + # first command you ran, and take note of its number + sudo grub-set-default [n] # Where [n] is that number + # This should print saved_entry=[n] + grub-editenv list + + - On Fedora: + + .. code-block:: bash + + sudo grubby --set-default /boot/vmlinuz-$(uname -r) + +6. Get familiar with grub-reboot. + + ktest relies on grub-reboot. grub-reboot lets you try a freshly built + kernel *only once* and then boot immediately a "safe" kernel again + without interacting with the boot menu: a simple power cycle is + enough. It's a must have for testing development kernels that may not + fully boot. + + In case something goes wrong with ktest, being familiar with grub-reboot + may save you interacting with the boot menu or even better: it may save + you making your system unbootable by accident. Understanding how + grub-reboot works is required to fully understand ktest + configuration. It's much easier to discover grub-reboot alone than when + entangled with ktest. + + Here is a quick cheat sheet for grub-reboot on Ubuntu/Debian. For + more details, search the documentation of your Linux distribution. The + commands below have been tested on Ubuntu 20.04. They should be nearly + identical for most Debian-derived Linux distributions. + + .. warning:: + + ``update-grub`` does not care about menuentry order and will mess up what the numbers below point to! After running update-grub, make sure the default kernel index is correct and points towards a known-safe kernel. + + .. code-block:: bash + + # Add/remove entries in grub.cfg after making changes in /boot/ + # grub.cfg is generated, don't edit it! + update-grub + + # See which GRUB entry was booted + cat /proc/cmdline + + # Show the default menuentry + grub-editenv list + #=> saved_entry=6 + + # Show all, numbered kernel choices without (re)booting + awk '/^menuentry|submenu/ { print i++, '\t', $0 }' /boot/grub/grub.cfg + #=> 5 menuentry ... + #=> 6 menuentry 'Ubuntu, with Linux 5.4.0-53-generic' --class ubuntu ... + #=> 7 menuentry ... + + # Attempt to boot menuentry 4 only once + grub-reboot 4 + # Run this to see the updated settings + grub-editenv list + #=> saved_entry=6 + #=> next_entry=4 + reboot + + # Switch to menuentry number 4 as the new "safe" kernel + grub-set-default 4 + + + Fedora and derived distributions have a more elaborate system to manage + "installed" kernels. Instead of extracting ``menuentry`` lines from + ``/boot/grub/grub.cfg`` with the ``awk`` command above, to list all + installed kernels use ``grubby --info=ALL``. + Check ``grubby`` documentation for more details. + To boot a different kernel just once, use ``grub2-reboot [n]``, where ``[n]`` is the index of the menu entry you'd like to boot. + +#. Install and configure openssh-server. + + a) Install or enable openssh-server: + + - On Ubuntu, install the server: + + .. code-block:: bash + + sudo apt-get install openssh-server + + - On Fedora, enable the server: + + .. code-block:: bash + + sudo systemctl enable sshd + + b) Update the openssh-server configuration using your editor of choice. + + .. code-block:: bash + + sudo emacs /etc/ssh/sshd_config + + Replace ``#PermitRootLogin prohibit-password`` with ``PermitRootLogin yes`` and save the file. Make sure to remove the hash character (#). + + This is just temporary, you will change this back once you have copied over your ssh key. + +#. Reboot the target. + + Make sure it boots automatically to your safe kernel. We also recommend to test using grub-reboot to boot the test kernel, then rebooting again to make sure it goes back to the safe kernel. + +Configure SSH without password +****************************** + +1. Check the SSH connection. + + You must be able to ssh into the target device, which is typically on the same local network/VPN. Run ``ip addr`` on the target to get its IP address. All other commands should be run on your dev machine, unless specified otherwise. + + .. code-block:: bash + + # Make sure that you can connect and login to the target + ssh root@ + +#. Generate an SSH key for the target. + + If you already have an ssh key you'd prefer to use, you can skip this step. + + .. code-block:: bash + + ssh-keygen -f ~/.ssh/sshktest + # This will prompt you for the target's root password. + ssh-copy-id -i ~/.ssh/sshktest root@ + +#. Test the key. + + .. code-block:: bash + + ssh root@ + + .. note:: + + In most cases `ssh-agent` should automatically manage your password(s) and key(s). If you are still prompted for a password, it's likely your distro hasn't configured `ssh-agent`. You can either figure out how to enable it, or you can manually update your config. + To do this, put the following in ``~/.ssh/config`` (make sure to update ````) and then use ``ktest-target`` instead of the actual target's IP for ssh connections (for example, ``ssh root@ktest-target``). + + .. code-block:: text + + Host ktest-target + HostName + IdentityFile ~/.ssh/sshktest + +#. Disable root access. + + Run this on the target device to disable root password, + you do not need it now that you have copied the key. + + .. code-block:: bash + + # Use your editor of choice. + sudo emacs /etc/ssh/sshd_config + + Replace ``PermitRootLogin yes`` by ``PermitRootLogin without-password``, save, and exit. + +Build and install the kernel with ktest +*************************************** + +Follow the `prepare build environment `_ instructions before proceeding. + +1. Prepare the ktest environment. + + If you run this in a different terminal than you used for the `prepare build environment `_ instructions, you need to re-set the SOF_WORKSPACE variable by running ``export SOF_WORKSPACE = ~/work/sof``. + + .. code-block:: bash + + cd $SOF_WORKSPACE + mkdir sof-dev-build + mkfifo sof-dev-cat + cp linux/tools/testing/ktest/ktest.pl . + +#. Save your kernel configuration as ``sof-dev-defconfig``. + + If you do not know what options are needed, you can start using configurations maintained by SOF developers. + + .. code-block:: bash + + cd linux + make O=../sof-dev-build olddefconfig + echo test > ../sof-dev-build/localversion + bash ../kconfig/kconfig-sof-default.sh + cp .config ../sof-dev-defconfig + make mrproper + cd .. + + .. note:: + + Use make proper since ktest.pl requires the source directory + to be clean. All compilation happens in the -build directory. + + .. note:: + + The options provided in kconfig/sof-dev-defconfig should not be used for a distro's production kernel. + +#. Edit ktest configuration as needed. + + Save the following in ``sof-dev.conf``. Make sure to update the ``MACHINE=`` line with your target device's IP (or ``ktest-target`` if you had to do the additional ssh config). + + .. code-block:: perl + + # The difference between config variables (:=) and ktest options (=) and a + # few other things are explained in tools/testing/ktest/examples/sample.conf + + MACHINE = 192.168.1.205 + CLEAR_LOG = 1 + SSH_USER = root + THIS_DIR := ${PWD} + # BUILD_DIR is the source directory + BUILD_DIR = ${THIS_DIR}/linux + # OUTPUT_DIR is the actual build directory + OUTPUT_DIR = ${THIS_DIR}/sof-dev-build + BUILD_TARGET = arch/x86/boot/bzImage + + # ktest requires LOCALVERSION. This is normally a '-something' suffix like + # in 'vmlinuz-5.10-rc5-something'. Let's (ab)use it as the full version so + # we have a constant 'vmlinuz-something' filename and we don't have to + # make changes in /boot/ all the time. + # update-grub will complain but work anyway. + LOCALVERSION = test + TARGET_IMAGE = /boot/vmlinuz-${LOCALVERSION} + + BUILD_OPTIONS = -j8 + LOG_FILE = ${OUTPUT_DIR}/sof-dev.log + CONSOLE = cat ${THIS_DIR}/sof-dev-cat + POWER_CYCLE = echo Power cycle the machine now and press ENTER; read a + #set below to help ssh connection to close after sending reboot command + REBOOT = ssh $SSH_USER@$MACHINE 'sudo reboot > /dev/null &' + + # This how ktest finds which menuentry number to pass to grub-reboot + GRUB_FILE = /boot/grub/grub.cfg + GRUB_MENU = Ubuntu, with Linux ${LOCALVERSION} + #GRUB_MENU = ubilinux GNU/Linux, with Linux ${LOCALVERSION} + #GRUB_MENU = GalliumOS GNU/Linux, with Linux ${LOCALVERSION} + GRUB_REBOOT = grub-reboot + REBOOT_TYPE = grub2 + + # update-initramfs does not support any "version-less" 'vmlinuz-test' because it + # does not tell where to find modules like '/lib/modules/5.10.0-rc5test+' + # So we have to use a lower level, more explicit command like: + # mkinitramfs -o initrdfile 5.10.0-rc5test+ + # ktest finds the real KERNEL_VERSION thanks to "make O=${OUTPUT_DIR} + # kernelrelease" + POST_INSTALL = ssh $SSH_USER@$MACHINE sudo /usr/sbin/mkinitramfs -o /boot/initrd.img-${LOCALVERSION} $KERNEL_VERSION + + #REBOOT_TYPE = script + #REBOOT_SCRIPT = ssh $SSH_USER@$MACHINE "sed -i 's|^default.*$|default test|' /boot/loader/loader.conf" + + TEST_START + # TEST_TYPE can be: build, install, boot, ... + TEST_TYPE = boot + BUILD_TYPE = useconfig:${THIS_DIR}/sof-dev-defconfig + BUILD_NOCLEAN = 1 + + + For targets running Fedora and derived distributions, make the following changes: + + .. code-block:: perl + + # GRUB_MENU should be the title of the custom kernel entry you added, + # which will match LOCALVERSION ("test") if you followed the previous steps + # You can view all your kernel entries with `grubby --info=ALL` + GRUB_MENU = ${LOCALVERSION} + GRUB_REBOOT = grub2-reboot + REBOOT_TYPE = grub2bls + POST_INSTALL = ssh $SSH_USER@$MACHINE sudo dracut --hostonly --force /boot/initramfs-${LOCALVERSION}.img $KERNEL_VERSION + +#. Build and test. + + .. code-block:: bash + + # This can take a while, so don't kill it if it appears to freeze + ./ktest.pl sof-dev.conf + + If this does not work, make sure you have all the following files in the + local directory: + + * ktest.pl + * sof-dev-cat + * linux + * sof-dev-build + * sof-dev.conf + * sof-dev-defconfig + + Ktest will compile and install the new kernel, then reboot the target device. Check which kernel is booted by running ``uname -r`` on the target. + + .. note:: + + KTest expects a UART connection to verify that the boot was successful. If you do not have a UART connection you will get some errors at the end of the ``ktest.pl`` script's execution, but you can ignore them as long as the custom kernel was installed and booted on the target device. + +#. Enjoy! + +#. Enjoy even more! + + By having multiple `Git worktrees `_ and configs, you can run tests in parallel + on different machines on the same kernel or different branches. + +#. Clean up `/lib/modules`. + + Ktest creates a separate module directory per kernel version. + User needs to clean up old module directory periodically on the target device. + + .. code-block:: bash + + $ ls -al /lib/modules + drwxrwxr-x 3 ubuntu ubuntu 4096 Sep 28 15:07 5.9.0-rc4-test+ + drwxrwxr-x 3 ubuntu ubuntu 4096 Sep 24 11:06 5.9.0-rc5-test+ + drwxrwxr-x 3 ubuntu ubuntu 4096 Oct 5 16:39 5.9.0-rc6-test+ + drwxrwxr-x 3 ubuntu ubuntu 4096 Oct 14 21:42 5.9.0-rc7-test+ + drwxrwxr-x 3 ubuntu ubuntu 4096 Nov 2 12:16 5.9.0-rc8-test+ + diff --git a/getting_started/setup/images/minnow_turbot.png b/getting_started/setup_special_device/images/minnow_turbot.png similarity index 100% rename from getting_started/setup/images/minnow_turbot.png rename to getting_started/setup_special_device/images/minnow_turbot.png diff --git a/getting_started/setup/setup_minnowboard_turbot.rst b/getting_started/setup_special_device/setup_minnowboard_turbot.rst similarity index 100% rename from getting_started/setup/setup_minnowboard_turbot.rst rename to getting_started/setup_special_device/setup_minnowboard_turbot.rst diff --git a/getting_started/setup/setup_up_2_board.rst b/getting_started/setup_special_device/setup_up_2_board.rst similarity index 100% rename from getting_started/setup/setup_up_2_board.rst rename to getting_started/setup_special_device/setup_up_2_board.rst diff --git a/introduction/index.rst b/introduction/index.rst index 40824401..6b007c57 100644 --- a/introduction/index.rst +++ b/introduction/index.rst @@ -4,24 +4,24 @@ Introduction to the SOF Project ############################### |SOF| (SOF) is an open source audio Digital Signal Processing (DSP) firmware -infrastructure and SDK. SOF provides infrastructure, real-time control pieces, -and audio drivers as a community project. The project is governed by the |SOF| -|TSC| (TSC) who are prominent and active developers from the community. -SOF is developed in public and hosted on the github platform. +infrastructure and SDK. SOF provides infrastructure, real-time control +pieces, and audio drivers as a community project. The project is governed by +the |SOF| |TSC| (TSC) that includes prominent and active developers from the +community. SOF is developed in public and hosted on the github platform. The firmware and SDK are intended for developers who are interested in -audio or signal processing on modern DSPs. SOF provides a framework where audio -developers can create, test and tune. +audio or signal processing on modern DSPs. SOF provides a framework where +audio developers can create, test, and tune the following: -#. Audio processing pipelines and topologies. +- Audio processing pipelines and topologies. -#. Audio processing components. +- Audio processing components. -#. DSP infrastructure and drivers. +- DSP infrastructure and drivers. -#. Host OS infrastructure and drivers. +- Host OS infrastructure and drivers. -.. figure:: images/pipeline-overview.png +.. figure:: images/pipeline-overview.png :align: center :alt: SDK Overview :width: 1000px @@ -31,74 +31,73 @@ developers can create, test and tune. |SOF| has a modular and generic codebase and can be ported to different DSP -architectures or host platforms. See list of currently supported DSP +architectures or host platforms. See the list of currently supported DSP architecures and supported platforms. SDK Introduction and Overview ============================= -The |SOF| SDK is comprised of many ingredients that can be customised for use in -the firmware/software development lifecycle. Customisation allows for a -"best fit" development approach where the SDK can be optimised for a -particular process or environment. Some SDK ingredients are optional whilst -there can more than once choice for other ingredients as shown in the diagram below. +The |SOF| SDK is comprised of many ingredients that can be customized for +use in the firmware/software development lifecycle. Customization allows for +a "best fit" development approach where the SDK can be optimized for a +particular process or environment. Some SDK ingredients are optional while +there can be more than once choice for other ingredients as shown in the diagram below. .. figure:: images/sdk-overview.png :align: center :alt: SDK Overview :width: 1000px - `SDK example configuration showing development flow for SOF on the Intel Apollolake platform running Linux OS.` - `Note the choice of compiler toolchains and choice of optional DSP emulators.` + `SDK example configuration showing development flow for SOF on the Intel Apollo Lake platform running Linux OS. Note the choice of compiler toolchains and choice of optional DSP emulators.` -SOF source code, tools and topologies -------------------------------------- +SOF source code, tools, and topologies +-------------------------------------- -The firmware, tools and topologies all exist in the main SOF git repository -and at a high level it contains. +All firmware, tools, and topologies exists in the main SOF git repository. +On a high level, the repo contains: -#. Firmware - written in C with some architecture-specific assembler; it does not link to external dependencies. +- Firmware - written in C with some architecture-specific assembler; it does not link to external dependencies. -#. Test Bench - allows firmware components and pipelines to run on developers host PC. +- Test Bench - allows firmware components and pipelines to run on developers' host PCs. -#. Image Tools - C tools for converting ELF files to binary firmware images that can run on HW. +- Image Tools - C tools for converting ELF files to binary firmware images that can run on HW. -#. Debug Tools - Scripts and tools that can be used to debug firmware. +- Debug Tools - scripts and tools that can be used to debug firmware. -#. Trace Tools - Text based tools that can display tracing data from firmware. +- Trace Tools - text-based tools that can display tracing data from firmware. -#. Tuning Tools - Matlab/Octave scripts that can be used to create tuning coefficients for audio components. +- Tuning Tools - MATLAB/Octave scripts that can be used to create tuning coefficients for audio components. -#. Runtime Tools - Command line applications that can be used to exchange data with running firmware. +- Runtime Tools - command line applications that can be used to exchange data with running firmware. -#. Topologies - Real and example topologies showing construction of simple and complex audio processing pipelines. +- Topologies - real and example topologies that show construction of simple and complex audio processing pipelines. Host OS Drivers --------------- -SOF can be configured and controlled by a host OS driver or can optionally run -as a stand alone firmware. SOF host drivers currently support the Linux OS -today. +SOF can be configured and controlled by a host OS driver or it can +optionally run as standalone firmware. SOF host drivers currently support +Linux OS. -The SOF driver has a modular stack based architecture that is dual licensed -BSD & GPL code allowing it to be ported to other OSes and RTOSes. +The SOF driver has a modular stack-based architecture that is dual-licensed +BSD & GPL code, allowing it to be ported to other OSes and RTOSes. -The host driver is responsible for :- +The host driver is responsible for: -#. Loading firmware from host file system into DSP memories and booting. +- Loading firmware from the host file system into DSP memories and booting. -#. Loading topologies from host file system into DSP. +- Loading topologies from the host file system into DSP. -#. Exposing audio control devices to applications. +- Exposing audio control devices to applications. -#. Exposing audio data endpoints to applications. +- Exposing audio data endpoints to applications. -#. Managing IPC communication between host and DSP. +- Managing IPC communication between the host and DSP. -#. Abstraction of host side DSP hardware to common API operations. +- Abstraction of the host-side DSP hardware to common API operations. The Linux SOF ALSA/ASoC driver is upstream in Linux v5.2 onwards. @@ -107,7 +106,7 @@ Firmware Toolchain ------------------ GNU GCC can be used as a free SOF compiler alongside proprietary DSP vendor -compilers. The choice of compiler is up to the user depending on features +compilers. The choice of compiler is up to the user, depending on features and budget. GCC complier is open source. @@ -130,69 +129,120 @@ What license does the firmware use? Do I need to open source my firmware code changes? No. The firmware BSD and MIT licensed code means you can keep code - changes private. Patches are always welcomed if you do decide to open source - work. + changes private. Patches are always welcomed if you do decide to open + source work. What license does the host driver use? - Most of the host driver code is dual licensed BSD or GLPLv2 only + Most of the host driver code is dual-licensed BSD or GLPLv2 only (user's choice). The part of the driver that is GPLv2 only is the Linux - integration layer at the top of the driver stack + integration layer at the top of the driver stack. Do I need to open source my driver code changes? - No, for the bottom two layers of the driver stack. i.e. if you are porting the - driver to another OS, these changes can be kept private. Please note that the - driver GPL source files are all Linux specific and should not be ported to - another OS anyway. + No, for the bottom two layers of the driver stack. For example, if you are + porting the driver to another OS, these changes can be kept private. Note + that all driver GPL source files are Linux-specific and should not be + ported to another OS. How can I get involved? - The best way to get involved is via github, there is also a low volume - mailing list here http://alsa-project.org/mailman/listinfo/sound-open-firmware + The best way to get involved is via github. You can also join our + low-volume `mailing list `_. What is the development model? - |SOF| is entirely developed on github. Patches via a Pull Request are - reviewed, discussed and tested by CI before being merged. The intended - release cadence will likely be every 6 - 8 weeks. There will be a stable - release tagged after passing QA then development will continue for the - next release. + |SOF| is entirely developed on github. Patches via Pull Requests are + reviewed, discussed, and tested by CI before being merged. The intended + release cadence is every 6 - 8 weeks. A stable release is tagged after + passing QA; development continues for the next release. -Who is working on |SOF|? - Professional developers from a number of companies (please check the git - logs if you want to know) with some hobbyist developers too. +Who works on |SOF|? + Professional developers from a number of companies (check the git + logs if you want to know) with some hobbyist developers, too. How do I add support for host architecture X? - Please see the SOF architecture pages. + See the SOF architecture pages. How do I add support for host platform X? Adding a new host platform is a lot simpler than adding a new DSP architecture. A new host platform consists of adding a new src/platform/ - directory, together with mappings for memory, IRQs, GPIOs and peripheral + directory, together with mappings for memory, IRQs, GPIOs, and peripheral devices in the DSP memory space. New drivers may also have to be added (e.g. for DMA, I2S) to the drivers directory. How do I port to other OSes? - Please see the SOF host architecture page. + See the SOF host architecture page. What audio components are supported? - |SOF| now supports a small library of free and open source components that are - distrubuted alongside the source code. SOF can also support proprietary - audio processing components providing they are wrapped to use the SOF - component API. Please see the audio components page for a list of the open - source components and thier capablilities. + |SOF| now supports a small library of free and open source components that + are distrubuted alongside the source code. SOF can also support proprietary + audio processing components provided they are wrapped to use the SOF + component API. See the audio components page for a list of open + source components and their capabilites. How do I create my own pipelines? - Pipelines are currently defined using the M4 macro processing language. The M4 - topology is then preprocessed to the alsaconf format before being compiled - into a binary. An Eclipse based GUI for pipeline construction is currently - in development. + Pipelines are currently defined using the M4 macro processing language. + The M4 topology is then preprocessed to the alsaconf format before being + compiled into a binary. An Eclipse-based GUI for pipeline construction is + currently under development. - Today both static (built in) and dynamic (loaded at runtime) pipelines are + Today, both static (built in) and dynamic (loaded at runtime) pipelines are supported in upstream. Can I add my own media encoder/decoders? Yes. Can I add non-audio functions? - Yes, the instruction sets used by DSPs are also good at non audio - processing tasks too. e.g. low power sensor signal processing. Providing - your DSP has physical IO ports to connect other non audio devices then - it's possible to process data from these devices too. + Yes. The instruction sets used by DSPs are also good at non-audio + processing tasks such as low-power sensor signal processing. If + your DSP has physical IO ports to which other non-audio devices can be connected, then data can also be processed from these devices. + +Toolchain FAQ +============= + +Which Xtensa toolchains does SOF currently support? + Two toolchain families are currently supported by SOF: The GCC and the Cadence XCC. + + These families are subdivided into toolchains per Xtensa ISA because the Tensilica architecture contains a variable instruction set so you must use the toolchain variant that matches your platform. + + 1. Custom, open-source GCC toolchains built with crosstool-NG as + documented in the getting started guide. These must be built from + source. For instructions, refer to the following: + + - :ref:`build-toolchains-from-source` in the Getting Started Guide + for building SOF from scratch + + - `Toolchains and embedded distributions `_ + + 2. Cadence's partially closed source toolchains. The Cadence XCC compiler + is proprietary but uses the open source GNU binutils. XCC must be + bought from Cadence. For more information, see: + + - :ref:`build-3rd-party-toolchain` + + - `Cadence IP portfolio `_ + + The Cadence binutils patches or overlays are located in the SOF git + repo. + + Note that Cadence is not the only Tensilica user; some Xtensa + toolchains come from `elsewhere `_. However, as of June 2020, all platforms + supported by SOF come from Cadence. + +What are the primary differences between Cadence and gcc toolchains? + gcc toolchains are completely open source. Cadence's toolchains use either + a gcc-based or a clang-based open source frontend and a closed-source + backend that matches the platform. + + XCC supports full Xtensa HiFi SIMD intrinsics whereas GCC has no HiFi SIMD + support. This can lead to large performance differences, especially in + code that deals with audio processing. + +Cadence xt-xcc or Cadence xt-clang? + It depends on the platform. As of June 2020, most platforms supported by + SOF rely on xt-xcc. Going forward, all newer platforms require xt-clang. + The gcc frontend doesn't support unusually large registers, hence the move + to xt-clang. + + Note that xt-xcc does not fully support C99. xt-clang does. + +Is support for other toolchains forthcoming? + Going forward, we would like to support the LLVM C compiler. Patches are + welcome. diff --git a/maintainers/admin.rst b/maintainers/admin.rst index 867b8248..450bc783 100644 --- a/maintainers/admin.rst +++ b/maintainers/admin.rst @@ -7,18 +7,18 @@ Given the size of the project, maintainer rights are granted to multiple contributors: +---------------+-------------------+---------------+ -| Intel | Lech Betlej | @lbetlej | +| Intel | Lech Betlej | @lbetlej | +---------------+-------------------+---------------+ -| Intel | Liam Girdwood | @lgirdwood | +| Intel | Liam Girdwood | @lgirdwood | +---------------+-------------------+---------------+ -| Intel | Marcin Maka | @mmaka1 | -+---------------+-------------------+---------------+ -| Intel | Pierre Bossart | @plbossart | +| Intel | Marcin Maka | @mmaka1 | +---------------+-------------------+---------------+ | Intel | Ranjani Sridharan | @ranj063 | +---------------+-------------------+---------------+ | NXP | Daniel Baluta | @dbaluta | +---------------+-------------------+---------------+ +| Google | Johny Lin | @johnylin76 | ++---------------+-------------------+---------------+ Administrators may override specific merge rules, for example merge a PR even if it does not meet all criteria defined by a repository, but diff --git a/platforms/index.rst b/platforms/index.rst index c5b05ea7..a582511d 100644 --- a/platforms/index.rst +++ b/platforms/index.rst @@ -9,27 +9,53 @@ Supported Platforms Platform and board specific support is continually added to the SOF project as documented below. .. csv-table:: Supported Platforms - :header: "Platform", "Architecture", "Cores/Clocks", "Memory", "Audio Interfaces" - :widths: 20, 20, 10, 10, 20 - - "Host Testbench", "PC command line", "N/A", "N/A", "N/A Files are used to simulate audio interfaces" - "Qemu", "All supported SOF HW platforms", "N/A", "N/A", "WiP Files will be used to simulate audio interfaces" - "Intel Baytrail / Merrifield", "Xtensa HiFi2 EP", "1 @ 50 - 400MHz", "96KB IRAM / 192KB DRAM", "3 x SSP (I2S, PCM)" - "Intel Cherrytrail / Braswell", "Xtensa HiFi2 EP", "1 @ 50 - 400MHz", "96KB IRAM / 192KB DRAM", "6 x SSP (I2S, PCM)" - "Intel Broadwell", "Xtensa HiFi2 EP", "1 @ 50 - 400MHz", "320KB IRAM / 640KB DRAM", "2 x SSP (I2S, PCM)" - "Intel Apollolake", "Xtensa HiFi3", "2 @ 100 - 400MHz", "128KB LP SRAM / 512KB HP SRAM", "6 x SSP (I2S, PCM), HDA, DMIC" - "Intel Cannonlake / Whiskeylake / Cometlake", "Xtensa HiFi3", "4 @ 120 - 400MHz", "64KB LP / 3008KB HP SRAM", "3 x SSP (I2S, PCM), HDA, DMIC, Soundwire" - "Intel Suecreek", "Xtensa HiFi3", "2 @ 120 - 400MHz", "64KB LP SRAM / 4096KB HP SRAM", "6 x SSP (I2S, PCM), DMIC" - "Intel Icelake", "Xtensa HiFi3", "4 @ 120 - 400MHz", "64KB LP SRAM / 3008KB HP SRAM", "6 x SSP (I2S, PCM), HDA, DMIC, Soundwire" - "Intel Jasperlake", "Xtensa HiFi3", "2 @ 120 - 400MHz", "64KB LP SRAM / 1024KB HP SRAM", "3 x SSP (I2S, PCM), HDA, DMIC, Soundwire" - "Intel Tigerlake", "Xtensa HiFi3", "4 @ 120 - 400MHz", "64KB LP SRAM / 2944KB HP SRAM", "6 x SSP (I2S, PCM), HDA, DMIC, Soundwire" - "NXP i.MX8/i.MX8X", "Xtensa HiFi4", "1 @ 666MHz", "64 KB TCM / 448 KB OCRAM / 8MB SDRAM", "1 x ESAI, 1 x SAI" - "NXP i.MX8M", "Xtensa HiFi4", "1 @ 800MHz", "64 KB TCM / 256 KB OCRAM / 8MB SDRAM", "1 x SAI, MICFIL" + :header: "Platform", "Architecture", "Cores/Clocks", "Platform Clock", "Memory", "Audio Interfaces" + :widths: 20, 20, 10, 10, 10, 20 + + "Host Testbench", "PC command line", "N/A", "N/A", "N/A", "N/A Files are used to simulate audio interfaces" + "Qemu", "All supported SOF HW platforms", "N/A", "N/A", "N/A", "WiP Files will be used to simulate audio interfaces" + "Intel Tiger Lake with IPC4", "Xtensa HiFi3", "4 @ 120 - 400MHz", "38.4MHz", "64KB LP SRAM / 2944KB HP SRAM", "6 x SSP (I2S, PCM), HDA, DMIC, Soundwire" + "Intel Alder Lake with IPC4", "Xtensa HiFi3", "4 @ 120 - 400MHz", "38.4MHz", "64KB LP SRAM / 2944KB HP SRAM", "6 x SSP (I2S, PCM), HDA, DMIC, Soundwire" + "NXP i.MX8", "Xtensa HiFi4", "1 @ 666MHz", "TBD", "64 KB TCM / 448 KB OCRAM / 8MB SDRAM", "1 x ESAI, 1 x SAI" + "NXP i.MX8X", "Xtensa HiFi4", "1 @ 640MHz", "TBD", "64 KB TCM / 448 KB OCRAM / 8MB SDRAM", "1 x ESAI, 1 x SAI" + "NXP i.MX8M", "Xtensa HiFi4", "1 @ 800MHz", "TBD", "64 KB TCM / 256 KB OCRAM / 8MB SDRAM", "1 x SAI, MICFIL" + "NXP i.MX8ULP", "Xtensa HiFi4", "1 @ 520MHz", "TBD", "64 KB TCM / 256 KB OCRAM / 8MB SDRAM", "1 x SAI" + "AMD Renoir", "Xtensa HiFi3", "1 @ 200-600MHz", "TBD", "20 KB LP SRAM / 1152 KB IRAM/DRAM", "1 x SP (I2S, PCM), 1 x BT (I2S, PCM), DMIC" + "AMD Rembrandt", "Xtensa HiFi5", "1 @ 200-800MHz", "TBD", "1.75 MB HP SRAM / 512 KB IRAM/DRAM", "1 x SP (I2S, PCM), 1 x BT (I2S, PCM), 1 x HS(I2S, PCM), DMIC" + "Mediatek mt8195", "Xtensa HiFi4", "1 @ 220 - 720MHz", "TBD", "256 KB SRAM / 16 MB DRAM", "2 x TDM Out, 1 x TDM In, DMIC" + "Mediatek mt8186", "Xtensa HiFi5", "1 @ 300 - 800MHz", "TBD", "512 KB SRAM / DRAM", "2 x I2S Out, 1 x I2S In, DMIC" + "Mediatek mt8188", "Xtensa HiFi5", "1 @ 26 - 800MHz", "TBD", "512 KB SRAM / 17 MB DRAM", "2 x TDM Out, 1 x TDM In, DMIC" When support for a new platform is being added, certain interfaces required by SOF infrastructure must be implemented. Refer to Platform API documentation for details. +Some platforms have been supported by SOF in the past, but are no longer +supported in SOF mainline ("main" branch). Below table lists such platforms, +the last SOF major release that had support for the platform and the stable +branch to use. For every SOF release, a stable branch is created and critical +bugfixes can be submitted and released via these stable branches. + +.. csv-table:: Platforms No Longer Supported in Mainline + :header: "Platform", "Last Release", "Branch", "Architecture", "Cores/Clocks", "Platform Clock", "Memory", "Audio Interfaces" + :widths: 20, 10, 10, 20, 10, 10, 10, 20 + + "Intel Bay Trail / Merrifield", "2.2", "stable-v2.2", "Xtensa HiFi2 EP", "1 @ 50 - 400MHz", "25MHz", "96KB IRAM / 192KB DRAM", "3 x SSP (I2S, PCM)" + "Intel Cherry Trail / Braswell", "2.2", "stable-v2.2", "Xtensa HiFi2 EP", "1 @ 50 - 400MHz", "19.2MHz", "96KB IRAM / 192KB DRAM", "6 x SSP (I2S, PCM)" + "Intel Broadwell", "2.2", "stable-v2.2", "Xtensa HiFi2 EP", "1 @ 50 - 400MHz", "24MHz", "320KB IRAM / 640KB DRAM", "2 x SSP (I2S, PCM)" + "Intel Apollo Lake / Gemini Lake", "2.2", "stable-v2.2", "Xtensa HiFi3", "2 @ 100 - 400MHz", "19.2MHz", "128KB LP SRAM / 512KB HP SRAM", "6 x SSP (I2S, PCM), HDA, DMIC" + "Intel Cannon Lake / Whiskey Lake / Comet Lake", "2.2", "stable-v2.2", "Xtensa HiFi3", "4 @ 120 - 400MHz", "24MHz", "64KB LP / 3008KB HP SRAM", "3 x SSP (I2S, PCM), HDA, DMIC, Soundwire" + "Intel Sue Creek", "2.2", "stable-v2.2", "Xtensa HiFi3", "2 @ 120 - 400MHz","24MHz", "64KB LP SRAM / 4096KB HP SRAM", "6 x SSP (I2S, PCM), DMIC" + "Intel Ice Lake", "2.2", "stable-v2.2", "Xtensa HiFi3", "4 @ 120 - 400MHz", "38.4MHz", "64KB LP SRAM / 3008KB HP SRAM", "6 x SSP (I2S, PCM), HDA, DMIC, Soundwire" + "Intel Jasper Lake", "2.2", "stable-v2.2", "Xtensa HiFi3", "2 @ 120 - 400MHz", "38.4MHz", "64KB LP SRAM / 1024KB HP SRAM", "3 x SSP (I2S, PCM), HDA, DMIC, Soundwire" + "Intel Tiger Lake with IPC3", "2.2", "stable-v2.2", "Xtensa HiFi3", "4 @ 120 - 400MHz", "38.4MHz", "64KB LP SRAM / 2944KB HP SRAM", "6 x SSP (I2S, PCM), HDA, DMIC, Soundwire" + "Intel Alder Lake with IPC3", "2.2", "stable-v2.2", "Xtensa HiFi3", "4 @ 120 - 400MHz", "38.4MHz", "64KB LP SRAM / 2944KB HP SRAM", "6 x SSP (I2S, PCM), HDA, DMIC, Soundwire" + +The periodic sof-bin releases + +contain latest binaries for all platforms, both from SOF main and +latest binaries from "stable-vX.YY" branches. + Minimum Platform Requirements ***************************** @@ -37,14 +63,14 @@ Footprint ========= DSP platforms can vary from vendor to vendor but in general SOF can run on -small platforms like Intel Baytrail DSP with 96kb of instruction RAM and 168kb +small platforms like Intel Bay Trail DSP with 96kb of instruction RAM and 168kb of data RAM. The SOF footprint can be shrunk to approximately 50kb of TEXT and DATA by fine-tuning runtime features via Kconfig. DSP Clock Speed =============== -Required DSP clock speed depends on the DSP processing load, so it can vary greatly depending on pipeline topology and the algorithm design that is running. SOF can run several volume passthrough pipelines on the Intel Baytrail DSP at 50MHz using unoptimized C code (SIMD disabled and compiled with GCC). +Required DSP clock speed depends on the DSP processing load, so it can vary greatly depending on pipeline topology and the algorithm design that is running. SOF can run several volume passthrough pipelines on the Intel Bay Trail DSP at 50MHz using unoptimized C code (SIMD disabled and compiled with GCC). Toolchain ========= diff --git a/platforms/intel-cavs/commons/multicore-processing.rst b/platforms/intel-cavs/commons/multicore-processing.rst index c23e8263..eaa0280f 100644 --- a/platforms/intel-cavs/commons/multicore-processing.rst +++ b/platforms/intel-cavs/commons/multicore-processing.rst @@ -1,15 +1,16 @@ .. _platforms-intel-cavs-multicore: -Multicore Processing +Multicore Processing #################### Description *********** -|SOF| implements multicore processing in the way, that the whole pipelines are -executed on the selected core. Core selection is done by ``core`` field in -``struct sof_ipc_pipe_new`` during pipeline creation. Core value cannot exceed -number of cores on current platform defined by ``PLATFORM_MAX_CORE_COUNT``. +|SOF| implements multicore processing so that the whole pipelines or single +components are executed on the selected core. The core selection is done by +the ``core`` field in ``struct sof_ipc_pipe_new`` or ``struct sof_ipc_comp`` +during creation. The core value cannot exceed the number of cores on the +current platform defined by ``CONFIG_MAX_CORE_COUNT``. .. code-block:: c @@ -27,12 +28,24 @@ number of cores on current platform defined by ``PLATFORM_MAX_CORE_COUNT``. uint32_t timer; } __attribute__((packed)); + struct sof_ipc_comp { + struct sof_ipc_cmd_hdr hdr; + uint32_t id; + enum sof_comp_type type; + uint32_t pipeline_id; + uint32_t core; + + /** extended data length, 0 if no extended data (ABI3.17) */ + uint32_t ext_data_length; + } __attribute__((packed)); + Core enablement *************** -Cores are enabled and disabled by sending ``SOF_IPC_PM_CORE_ENABLE`` IPC with -the right ``enable_mask``. Core needs to be enabled **before pipeline trigger -start happens** and disabled **after pipeline trigger stop**. +Cores are enabled and disabled by sending the ``SOF_IPC_PM_CORE_ENABLE`` IPC +with the correct ``enable_mask``. The core must be enabled **before the +pipeline trigger start happens** and disabled **after the pipeline trigger +stop**. .. code-block:: c @@ -43,6 +56,7 @@ start happens** and disabled **after pipeline trigger stop**. .. uml:: images/core-enable.pu -.. note:: Kernel also needs to enable cores on host side, before even sending - ``SOF_IPC_PM_CORE_ENABLE`` IPC to FW. +.. note:: The kernel also needs to enable cores on the host side, before + even sending the ``SOF_IPC_PM_CORE_ENABLE`` IPC to the FW. + For details on how to specify the DSP core in the topology file, refer to the topology documentation, :ref:`DSP Core Index `. diff --git a/platforms/intel-cavs/commons/work-queue.rst b/platforms/intel-cavs/commons/work-queue.rst index 7b47f463..78953b9b 100644 --- a/platforms/intel-cavs/commons/work-queue.rst +++ b/platforms/intel-cavs/commons/work-queue.rst @@ -7,12 +7,12 @@ 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 +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 -*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 +*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 *master mode*. +queues can be configured in *primary mode*. Synchronous SysTick on All Cores ******************************** @@ -34,7 +34,7 @@ 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 +.. 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. diff --git a/platforms/intel-cavs/icelake/icl-memory.rst b/platforms/intel-cavs/icelake/icl-memory.rst index 689a316f..19e3e313 100644 --- a/platforms/intel-cavs/icelake/icl-memory.rst +++ b/platforms/intel-cavs/icelake/icl-memory.rst @@ -3,4 +3,4 @@ ICL Memory ########## -Memory map is the same as on Cannonlake. See :ref:`cnl-memory`. +Memory map is the same as on Cannon Lake. See :ref:`cnl-memory`. diff --git a/platforms/intel-cavs/index.rst b/platforms/intel-cavs/index.rst index 62950e1f..cc2b7bd1 100644 --- a/platforms/intel-cavs/index.rst +++ b/platforms/intel-cavs/index.rst @@ -15,7 +15,7 @@ Intel CAVS platforms supported by the |SOF|. | |ver. 2.5 | Tiger Lake | +---------+----------+-----------------------------------------+ -.. note:: While the Sky Lake and Kaby Lake platforms are also based on the +.. note:: While the Skylake and Kaby Lake platforms are also based on the cAVS 1.5 architecture, they are not supported at this time due to differences in boot flow and memory architecture. diff --git a/platforms/intel-legacy/baytrail/index.rst b/platforms/intel-legacy/baytrail/index.rst index 92bfa102..8b5040ca 100644 --- a/platforms/intel-legacy/baytrail/index.rst +++ b/platforms/intel-legacy/baytrail/index.rst @@ -1,20 +1,20 @@ .. _platform-baytrail: -Intel Baytrail/CherryTrail -########################## +Intel Bay Trail / Cherry Trail +############################## -Intel Baytrail platform is based on an Atom CPU and a Tensilica HiFi2 +Intel Bay Trail platform is based on an Atom CPU and a Tensilica HiFi2 EP DSP. It relies on both internal memory and caches to access DDR -memory. The Baytrail processor relies on a 25 MHz oscillator. +memory. The Bay Trail processor relies on a 25 MHz oscillator. Supported commercially-available platforms include the MinnowBoard Turbot, tablets and laptops built for Windows (SOF requires a Linux installation). -The Cherrytrail platform is similar to Baytrail but it relies on a +The Cherry Trail platform is similar to Bay Trail but it relies on a 19.2 MHz osciilator. Supported commercially-available platforms include the Up board and devices built for Windows (SOF requires a Linux installation). -Baytrail and Cherrytrail Chromebooks can support SOF but the official +Bay Trail and Cherry Trail Chromebooks can support SOF but the official images are still based on the closed-source firmware solution. diff --git a/platforms/intel-legacy/index.rst b/platforms/intel-legacy/index.rst index 0e257ef3..ccc590e8 100644 --- a/platforms/intel-legacy/index.rst +++ b/platforms/intel-legacy/index.rst @@ -8,7 +8,7 @@ Intel platforms based on the Tensilica Hifi2-EP DSP supported by the |SOF|. +------------+--------------------------------------------------+ | Atom/PCI | Merrifield (Edison) | +------------+--------------------------------------------------+ -| Atom/ACPI | Baytrail, Cherrytrail, Braswell | +| Atom/ACPI | Bay Trail, Cherry Trail, Braswell | +------------+--------------------------------------------------+ | Core/ACPI | Broadwell (Chromebook Pixel 2015, Dell XPS) | +------------+--------------------------------------------------+ diff --git a/platforms/intel-legacy/merrifield/index.rst b/platforms/intel-legacy/merrifield/index.rst index 0575d848..b6a7311d 100644 --- a/platforms/intel-legacy/merrifield/index.rst +++ b/platforms/intel-legacy/merrifield/index.rst @@ -4,8 +4,8 @@ Intel Merrifield ################ Intel Merrifield platform is based on an Atom CPU and a Tensilica HiFi2 -EP DSP. It is very similar to Baytrail but uses a PCI-based -enumeration and has a clocking structure similar to Cherrytrail. +EP DSP. It is very similar to Bay Trail but uses a PCI-based +enumeration and has a clocking structure similar to Cherry Trail. This platform is no longer commercially available, but the Edison platform is supported by the open-source community. diff --git a/release.rst b/release.rst index eb79d7ff..ee119d71 100644 --- a/release.rst +++ b/release.rst @@ -26,13 +26,11 @@ kernel, and documentation. Download the source code as a zip or tar.gz file: Source and Binary Releases -------------------------- -The latest SOF release is v1.5.1 (June 2020). +The latest SOF release is v2.11.0 (Sept 2024). -https://github.com/thesofproject/sof/releases/tag/v1.5.1 - -Firmware and SDK tool source code and binary releases are located -on GitHub. The GitHub release page also lists details such as new -features and new platforms: +View new feature information and release downloads for the latest and +previous releases on GitHub. Firmware and SDK tool source code and binary +releases are located here as well: https://github.com/thesofproject/sof/releases diff --git a/rules-woke.yaml b/rules-woke.yaml new file mode 100644 index 00000000..c8d582e0 --- /dev/null +++ b/rules-woke.yaml @@ -0,0 +1,39 @@ +# Use this customized file to ensure that non-inclusive language is identified +# and corrected. Keep it in the top level of the sof-docs directory. +# Customization and improvement of this file is ongoing. +# The following command calls this file: +# woke -c ./rules-woke.yaml + +rules: + - name: whitelist + terms: + - whitelist + - white-list + alternatives: + - allowlist + + - name: blacklist + terms: + - blacklist + - black-list + alternatives: + - blocklist + + - name: slave + terms: + - slave + alternatives: + - secondary for firmware-related topics and consumer for hardware-related topics + + - name: master + terms: + - master + alternatives: + - main for firmware-related topics and provider for hardware-related topics + + - name: rule of thumb + terms: + - rule of thumb + - rules of thumb + alternative: + - rule, rules \ No newline at end of file diff --git a/scripts/docker_build/Dockerfile b/scripts/docker_build/Dockerfile new file mode 100644 index 00000000..070f7209 --- /dev/null +++ b/scripts/docker_build/Dockerfile @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2023 Intel Corporation. All rights reserved. +# +# Defines a docker image that can build Sound Open Firmware documentation +# +# Usage: +# create parent directory for sof and sof-docs repository (e.g. thesofproject) +# clone sof repository to thesofproject\sof folder +# clone sof-docs repository to thesofproject\sof-docs folder +# build docker image from parent directory .\thesofproject: +# > docker build -t ubuntu-sofdocs -f ./sof-docs/scripts/docker_build/Dockerfile ./ +# run the image container: +# > docker run -d --name sofdocs_container ubuntu-sofdocs sleep infinity +# copy build output from container to host: +# > docker cp sofdocs_container:/home/thesofproject/sof/doc ./sof/ +# > docker cp sofdocs_container:/home/thesofproject/sof-docs/_build ./sof-docs/ +# stop the container: +# docker stop sofdocs_container +# +# Note: The first build can take time to setup ubuntu and install tools, +# but each next one will repeat only copy and build steps. +# + +FROM dokken/ubuntu-22.04 + +# Set image working directory +WORKDIR /home/thesofproject + +RUN apt-get update + +# Install sof-docs build tools +RUN apt-get install -y python3.6 +RUN apt-get install -y doxygen python3-pip python3-wheel make \ + default-jre graphviz cmake ninja-build + +# Copy sof-docs file with dependency tools list +COPY ./sof-docs/scripts/requirements.txt /home/thesofproject/sof-docs/scripts/requirements.txt + +# Install sof-docs requirements tools +RUN pip3 install --user -r /home/thesofproject/sof-docs/scripts/requirements.txt + +# Directly install sphinx to add 'sphinx-build' to the system +RUN apt-get install -y python3-sphinx + +# Copy sof source code from host to image +COPY ./sof/ /home/thesofproject/sof/ + +# Build API documentation from SOF source (Doxygen) +RUN cmake -S sof/doc -B sof/doc -GNinja +RUN ninja -C sof/doc -v doc + +# Copy sof-docs source code from host to image +COPY ./sof-docs/ /home/thesofproject/sof-docs/ + +# Build sof-docs, ignore eventual errors to complete image creation +RUN make -C sof-docs VERBOSE=1 html; exit 0 diff --git a/scripts/docker_build/docker-build.sh b/scripts/docker_build/docker-build.sh new file mode 100644 index 00000000..e16e7507 --- /dev/null +++ b/scripts/docker_build/docker-build.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2023 Intel Corporation. All rights reserved. + +build_docs_only="FALSE" +if [ $# -eq 1 ] && [[ $1 = "docs" ]]; then + build_docs_only="TRUE" + echo "Re-build sof-docs only." +fi + +# Build documentation using docker image +docker build -t ubuntu-sofdocs -f ./sof-docs/scripts/docker_build/Dockerfile ./ + +# Run image container to copy output. +# Add sleep infinity to keep container running. +docker run -d --rm --name sofdocs_container ubuntu-sofdocs sleep infinity + +if [ $build_docs_only = "FALSE" ]; then + echo "Copy SOF Doxygen generated documentation from container to host ./sof/doc/" + docker cp sofdocs_container:/home/thesofproject/sof/doc ./sof/ +fi + +echo "Copy SOF-DOCS generated documentation from container to host ./sof-docs/_build .." +docker cp sofdocs_container:/home/thesofproject/sof-docs/_build ./sof-docs/ + +echo "Stop the sofdocs_container" +docker stop sofdocs_container + +# It is required to prevent stacking of images for each build run +echo "Remove dangling docker images" +docker image prune --force + +echo "Press key to exit..." +read -n 1 k <&1 diff --git a/scripts/plantuml.cfg b/scripts/plantuml.cfg index ec0fc4db..2d1574e5 100644 --- a/scripts/plantuml.cfg +++ b/scripts/plantuml.cfg @@ -56,3 +56,17 @@ skinparam note { backgroundColor #f6ed80 borderColor #d6d6de } + +skinparam activity { + borderColor #33335b + backgroundColor #ffffff +} + +skinparam activityDiamond { + borderColor #33335b + backgroundColor #ffffff +} + +skinparam arrowColor #f05772 +skinparam maxMessageSize 400 +skinparam BoxPadding 4 diff --git a/scripts/requirements-lax.txt b/scripts/requirements-lax.txt index 64c9b91c..42077b41 100644 --- a/scripts/requirements-lax.txt +++ b/scripts/requirements-lax.txt @@ -9,12 +9,24 @@ # PIP_IGNORE_INSTALLED=0 pip3 install --user -r scripts/requirements-lax.txt # See https://github.com/pypa/pip/issues/4222 -breathe>=4.7.3 -sphinx>=1.6.7 -docutils>=0.14 +breathe>=4.29.2 +sphinx>=4.5.0 +docutils>=0.17.1 sphinx_rtd_theme>=0.2.4 -sphinxcontrib-plantuml - +sphinxcontrib-blockdiag>=3.0.0 +sphinxcontrib-jquery + +# - Version 0.11 is the first version that supports +# `plantuml_output_format=none` which is required for instant builds. +# https://pypi.org/project/sphinxcontrib-plantuml/0.11/ +# +# - Note 0.9 is the minimum to fix this fatal import failure: +# +# sphinx.util.compat.Directive class is now deprecated. Please +# use instead docutils.parsers.rst.Directive +# +# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=896485#12 +sphinxcontrib.plantuml>=0.11 # Differences between these "lax" version requirements and the official # ones in requirements.txt: @@ -44,3 +56,8 @@ sphinxcontrib-plantuml # - Downgrade if successfully tested. # - Test with plantum set to "none" in conf.py. We don't want small # typo fixes to depend on UML diagrams. + +# Workaround for warning "'ImageDraw' object has no attribute 'textsize'" +# with pillow 10.0.0, see +# https://github.com/thesofproject/sof-docs/issues/472 +pillow<10 diff --git a/scripts/requirements.txt b/scripts/requirements.txt index 516d4e06..17aff9e6 100644 --- a/scripts/requirements.txt +++ b/scripts/requirements.txt @@ -1,7 +1,15 @@ # This file hardcodes validated versions with '==', # see requirements-lax.txt for an alternative. -breathe==4.14.1 -sphinx==2.4.4 -docutils==0.16 + +sphinx>=7 +breathe +docutils sphinx_rtd_theme sphinxcontrib-plantuml +sphinxcontrib-applehelp + + +# blockdiag is orphaned and not compatible with pillow>=10, +# see https://github.com/thesofproject/sof-docs/issues/472 +sphinxcontrib-blockdiag +pillow<10 diff --git a/static/sof-custom.css b/static/sof-custom.css index f13450f0..c822a6b9 100644 --- a/static/sof-custom.css +++ b/static/sof-custom.css @@ -77,7 +77,7 @@ th,td { } /* tweak for doxygen-generated API headings (for RTD theme) */ -.rst-content dl.group>dt, .rst-content dl.group>dd>p { +.rst-content dl.group>dt { display:none !important; } .rst-content dl.group { diff --git a/substitutions.txt b/substitutions.txt index f382c610..8912770a 100644 --- a/substitutions.txt +++ b/substitutions.txt @@ -11,6 +11,7 @@ .. |CNL| replace:: Cannon Lake .. |ICL| replace:: Ice Lake .. |JSL| replace:: Jasper Lake +.. |TGL| replace:: Tiger Lake .. |TSC| replace:: Technical Steering Committee .. These are replacement strings for non-ASCII characters used within the project diff --git a/tsc/representatives.rst b/tsc/representatives.rst index c5c42143..66b7ff4d 100644 --- a/tsc/representatives.rst +++ b/tsc/representatives.rst @@ -6,20 +6,28 @@ TSC Representatives The TSC is currently made of the following contributors -+---------------+-------------------+---------------+ -| Company | Name | Username | -+===============+===================+===============+ -| Intel | Lech Betlej | @lbetlej | -+---------------+-------------------+---------------+ -| Intel | Liam Girdwood | @lgirdwood | -+---------------+-------------------+---------------+ -| Intel | Marcin Maka | @mmaka1 | -+---------------+-------------------+---------------+ -| Intel | Pierre Bossart | @plbossart | -+---------------+-------------------+---------------+ -| NXP | Daniel Baluta | @dbaluta | -+---------------+-------------------+---------------+ -| Google | Curtis Malainey | @cujomalainey | -+---------------+-------------------+---------------+ -| Google | Dylan Reid | @dgreid | -+---------------+-------------------+---------------+ ++---------------+----------------------+------------------+ +| Company | Name | Username | ++===============+======================+==================+ +| Intel | Michal Wasko | @mwasko | ++---------------+----------------------+------------------+ +| Intel | Liam Girdwood | @lgirdwood | ++---------------+----------------------+------------------+ +| Intel | Pierre Bossart | @plbossart | ++---------------+----------------------+------------------+ +| Intel | Beata Baranowska | @beatabaranowska | ++---------------+----------------------+------------------+ +| NXP | Daniel Baluta | @dbaluta | ++---------------+----------------------+------------------+ +| Google | Johny Lin | @johnylin76 | ++---------------+----------------------+------------------+ +| Google | Unseated | | ++---------------+----------------------+------------------+ +| Google | Unseated | | ++---------------+----------------------+------------------+ +| AMD | Carl Wakeland | @cwakeland | ++---------------+----------------------+------------------+ +| AMD | Virendra Pratap Arya | @vp-arya | ++---------------+----------------------+------------------+ +| AMD | Basavaraj Hiregoudar | @bhiregou | ++---------------+----------------------+------------------+