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/.gitignore b/.gitignore index eaadf6c0..35ffcd58 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ _build *.sav *.log *.warnings +.tox +MANIFEST 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 new file mode 100644 index 00000000..6339b6f0 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +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), # Deb Taylor (grammatical/style review), +# Marcin Maka (technical review) and Michal Wasko (technical review). + +# So if a pull request only touches javascript files, only these owners + +* @lgirdwood @deb-intel @intelkevinputnam @greg-intel @mmaka1 @mwasko diff --git a/LICENSE b/LICENSE index 5c35d344..92891804 100644 --- a/LICENSE +++ b/LICENSE @@ -1,3 +1,3 @@ -ARCN Project Documentation is under a Creative Commons Attribution 4.0 +The Sound Open Firmware Project Documentation is under a Creative Commons Attribution 4.0 International License (CC BY 4.0). For details, see https://creativecommons.org/licenses/by/4.0/. diff --git a/Makefile b/Makefile index 4c1d7247..64785f0d 100644 --- a/Makefile +++ b/Makefile @@ -1,18 +1,22 @@ # Minimal makefile for Sphinx documentation # +# You can override these defaults from the command line. ifeq ($(VERBOSE),1) Q = + SPHINXOPTS ?= -v else Q = @ endif -# You can set these variables from the command line. -SPHINXOPTS ?= -q +SOF_DOC_BUILD = ../sof/build_doxygen/ SPHINXBUILD = sphinx-build SPHINXPROJ = "SOF Project" SOURCEDIR = . BUILDDIR = _build +ifneq ($(LAX),1) +ERROROPTS = -W --keep-going +endif DOC_TAG ?= development RELEASE ?= latest @@ -27,18 +31,41 @@ help: @echo " specify RELEASE=name to publish as a tagged release version" @echo " and placed in a version subfolder. Requires repo merge permission." -.PHONY: help Makefile +.PHONY: help apidocs html clean + # Generate the doxygen xml (for Sphinx) and copy the doxygen html to the # api folder for publishing along with the Sphinx-generated API docs. +# Keep doxygen optional not to burden "drive-by" .rst contributors with +# extra dependencies. + +APIS_CMAKE := ${SOF_DOC_BUILD}/build.ninja +apidocs: +ifeq (${APIS_CMAKE},$(wildcard ${APIS_CMAKE})) + ninja -C ${SOF_DOC_BUILD} $${VERBOSE:+-v} doc +else + # 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. -html: - $(Q)$(SPHINXBUILD) -t $(DOC_TAG) -b html -d $(BUILDDIR)/doctrees $(SOURCEDIR) $(BUILDDIR)/html $(SPHINXOPTS) $(O) # Remove generated content (Sphinx and doxygen) clean: rm -fr $(BUILDDIR) +ifeq (${APIS_CMAKE},$(wildcard ${APIS_CMAKE})) + ninja -C ${SOF_DOC_BUILD} $${VERBOSE:+-v} doc-clean clean +endif # Copy material over to the GitHub pages staging repo # along with a README diff --git a/README.md b/README.md index bb94f35b..c6cb3164 100644 --- a/README.md +++ b/README.md @@ -5,4 +5,4 @@ SOF Project documentation web site published to https://thesofproject.github.io Learn how to setup and generate documentation by reading -https://thesofproject.github.io/latest/howtos/process/docbuild.html +https://thesofproject.github.io/latest/contribute/process/docbuild.html diff --git a/algos/demux/demux.rst b/algos/demux/demux.rst new file mode 100644 index 00000000..e36da7af --- /dev/null +++ b/algos/demux/demux.rst @@ -0,0 +1,139 @@ +.. _demux: + +Multiplexer/Demultiplexer +######################### + +Introduction +************ + +The multiplexer/demultiplexer component copies its input audio channels +into output audio channels according to a specific routing +matrix. Multiplexer has multiple input audio streams and a single +audio output stream. Demultiplexer has a single input stream and +multiple output streams. In the SOF codebase, multiplexer and demultiplexer +are implemented in a single component as the operations and +configurations overlap heavily. + +.. figure:: images/muxdemux.png + + Multiplexer has exactly 1 output stream and demultiplexer has exactly + 1 input stream. + +Configuration +============= + +The component configuration defines how audio channels are copied from +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 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, 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, 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. + +.. 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 +======== + +Previous figures show that the routing matrix is difficult to +parametrize in order to be easily understandable. As it is sent to firmware +with 64 bits, it is quite tedious to easily see the binary routings from +hexadecimal or integer values. SOF topology m4 macros have helpers to +"visualize" the matrix for easier configuration. + +The following example from pipe-volume-demux-playback.m4 shows how to define +2 routing matrices and a demux component: + +.. code-block:: text + + # pipeline_id, channels, matrix_rows + define(matrix1, `ROUTE_MATRIX(PIPELINE_ID, 2, + `BITS_TO_BYTE(1, 0, 0 ,0 ,0 ,0 ,0 ,0)', + `BITS_TO_BYTE(0, 1, 0 ,0 ,0 ,0 ,0 ,0)', + `BITS_TO_BYTE(0, 0, 1 ,0 ,0 ,0 ,0 ,0)', + `BITS_TO_BYTE(0, 0, 0 ,1 ,0 ,0 ,0 ,0)', + `BITS_TO_BYTE(0, 0, 0 ,0 ,1 ,0 ,0 ,0)', + `BITS_TO_BYTE(0, 0, 0 ,0 ,0 ,1 ,0 ,0)', + `BITS_TO_BYTE(0, 0, 0 ,0 ,0 ,0 ,1 ,0)', + `BITS_TO_BYTE(0, 0, 0 ,0 ,0 ,0 ,0 ,1)')') + + # pipeline_id, channels, matrix_rows + define(matrix2, `ROUTE_MATRIX(5, 2, + `BITS_TO_BYTE(1, 0, 0 ,0 ,0 ,0 ,0 ,0)', + `BITS_TO_BYTE(0, 1, 0 ,0 ,0 ,0 ,0 ,0)', + `BITS_TO_BYTE(0, 0, 1 ,0 ,0 ,0 ,0 ,0)', + `BITS_TO_BYTE(0, 0, 0 ,1 ,0 ,0 ,0 ,0)', + `BITS_TO_BYTE(0, 0, 0 ,0 ,1 ,0 ,0 ,0)', + `BITS_TO_BYTE(0, 0, 0 ,0 ,0 ,1 ,0 ,0)', + `BITS_TO_BYTE(0, 0, 0 ,0 ,0 ,0 ,1 ,0)', + `BITS_TO_BYTE(0, 0, 0 ,0 ,0 ,0 ,0 ,1)')') + + # frame_format, num_channels, num_streams, route_matrix + MUXDEMUX_CONFIG(demux_priv, 2, 2, 2, LIST(` ', `matrix1,', `matrix2')) + + # demux Bytes control with max value of 255 + C_CONTROLBYTES(DEMUX, PIPELINE_ID, + CONTROLBYTES_OPS(bytes, 258 binds the mixer control to bytes get/put handlers, 258, 258), + CONTROLBYTES_EXTOPS(258 binds the mixer control to bytes get/put handlers, 258, 258), + , , , + CONTROLBYTES_MAX(, 304), + , + demux_priv) + + # Mux 0 has 2 sink and source periods. + W_MUXDEMUX(0, 1, PIPELINE_FORMAT, 2, 2, LIST(` ', "DEMUX")) + +In the above example you can see that the routing matrices have only +"diagonal" 1's, which means that input stream's channels are copied to +corresponding output streams channels. + +ALSA control +============ + +Multiplexer configuration is loaded in the kernel/firmware boot as part of +the ALSA binary control in topology, but can be also controlled through ALSA +controls. + +The complex binary control blob can be created with a generic +python tool: + +.. code-block:: python + + python sof_gen_blob.py -a 3 14 0 -t 18 -m 3H I 1B 8B 3B I 1B 8B 3B -v "2 2 2" "1" "2" "1 2 4 8 16 32 64 128" "0 0 0" "5" "1" "1 1 4 8 16 32 64 128" "0 0 0" + +It produces the following output: + +.. code-block:: text + + sof m4 and ALSA conf format: + ` bytes "0x53,0x4f,0x46,0x00,0x12,0x00,0x00,0x00,0x3c,' + ` 0x00,0x00,0x00,0x00,0xe0,0x00,0x03,0x00,' + ` 0x00,0x00,0x00,0x02,0x00,0x02,0x00,0x02,' + ` 0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,' + ` 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,' + ` 0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x01,' + ` 0x01,0x01,0x04,0x08,0x10,0x20,0x40,0x80,' + ` 0x00,0x00,0x00,' + + sof ctl tool format: + (4607827, 18, 60, 50388992, 0, 2, 2, 2, 1, 2, 1, 2, 4, 8, 16, 32, 64, 128, 0, 0, 0, 5, 1, 1, 1, 4, 8, 16, 32, 64, 128, 0, 0, 0) + +The sof-ctl tool can be then used to set the parameters through ALSA control: + +.. code-block:: bash + + sof-ctl -Dhw:0 -n 22 -s demux_coeffs.txt diff --git a/algos/demux/images/demux.png b/algos/demux/images/demux.png new file mode 100644 index 00000000..ee3cf873 Binary files /dev/null and b/algos/demux/images/demux.png differ diff --git a/algos/demux/images/mux.png b/algos/demux/images/mux.png new file mode 100644 index 00000000..bb867516 Binary files /dev/null and b/algos/demux/images/mux.png differ diff --git a/algos/demux/images/muxdemux.png b/algos/demux/images/muxdemux.png new file mode 100644 index 00000000..ea4b96a3 Binary files /dev/null and b/algos/demux/images/muxdemux.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 new file mode 100644 index 00000000..6cd9fb9c --- /dev/null +++ b/algos/index.rst @@ -0,0 +1,43 @@ +.. _algos: + +Algorithms +########## + +Supplied Processing Algorithms +****************************** + +SOF contains several permissively-licensed and royalty-free audio processing +algorithms that can be used alongside proprietary processing components to +build pipelines. + +.. csv-table:: Supplied Audio Processing Algorithms + :header: "Processing", "Description", "Generic C", "SIMD Support", "Status", "Milestone" + :widths: 10, 30, 10, 10, 10, 10 + + "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", "(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", "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", "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" + +Algorithm Specific Information +****************************** + +Further information on specific algorithms is forthcoming. + +.. toctree:: + :maxdepth: 1 + + demux/demux.rst + eq/equalizers_tuning + src/sample_rate_conversion + tdfb/time_domain_fixed_beamformer diff --git a/algos/src/images/equiripple.png b/algos/src/images/equiripple.png new file mode 100644 index 00000000..1f8de3b3 Binary files /dev/null and b/algos/src/images/equiripple.png differ diff --git a/algos/src/images/kaiser.png b/algos/src/images/kaiser.png new file mode 100644 index 00000000..4f09d15e Binary files /dev/null and b/algos/src/images/kaiser.png differ diff --git a/algos/src/images/poly32.png b/algos/src/images/poly32.png new file mode 100644 index 00000000..ae36d59f Binary files /dev/null and b/algos/src/images/poly32.png differ diff --git a/algos/src/images/poly34.png b/algos/src/images/poly34.png new file mode 100644 index 00000000..1fee7af7 Binary files /dev/null and b/algos/src/images/poly34.png differ diff --git a/algos/src/sample_rate_conversion.rst b/algos/src/sample_rate_conversion.rst new file mode 100644 index 00000000..22c3655b --- /dev/null +++ b/algos/src/sample_rate_conversion.rst @@ -0,0 +1,435 @@ +.. _sample_rate_conversion: + +Sample Rate Conversion +###################### + +Introduction +************ + +The sample rate converter (SRC) component utilizes FIR polyphase +decomposition that is described in [1]_. In a linear system, the order +of operations can be altered while preserving the transfer function +from system input to output. The purpose of polyphase optimization is +to move the processing operations to the lowest sample rate possible and +omit computing of intermediate results that would be discarded. The +benefit of polyphase conversion is its capability to scale to very high +quality like true 24-bit studio quality, since the filtering is a +linear operation and the performance depends on the time-invariant +filter characteristics. The algorithm does not limit the audio +conversion quality. + +The SRC component is a synchronous type that converts the rates with +exact rational M/N fraction and cannot adjust for any small drift of +the sample rate. Per every call to SRC, the algorithm consumes exactly N +input samples and M output samples. + +As an example, if input to SRC is 11025 Hz and output is 48000 Hz, the +fraction for conversion is 640/147. For every 147 input samples, there +are 640 output samples at 48 kHz. Such a processing block would require +13.3 ms of buffering. To shorten the latency and ease the conversion +fractions, some of the conversions are executed in two stages. The +fraction 640/174 can be factored as 32/21*20/7. With the two fractions +approach, the SRC will input with 21 frames of granularity and output with +20 frames of granularity. The internal buffer between the stages places +an internal constraint for processing block sizes. Still, the approach +provides much shorter latency than using a single fraction. + +In some cases, it might be possible to design and use a converter that +is intentionally non-exact, such as a 48000/11000 conversion that has an +easier faction of 48/11 and provides much lower SRC latency. But use of +such approximation with as low as 0.2% error would result in a +systematic slow drift of audio presentation so it is not recommended. +Fortunately, conversions in the 48 kHz family rates such as 32 kHz to 48 kHz +is a much lower latency with the 3/2 fraction with the need for only 63 us of +additional buffer. + +Note that another asynchronous SRC (ASRC) type is needed when the ratio +drifts during time, or if a M/N fraction does not exist within the required +conversion precision, or if the fraction requires filters that are too +complex to handle with a very large M or N. + +Use of SRC generator tool +************************* + +Prerequisites +============= + +The GNU Octave tool or Matlab® is needed to run the support scripts. From the +Ubuntu desktop, Octave and the required signal package can be installed +from the stock apt repository with the following command: + +.. code-block:: bash + + sudo apt-get install octave octave-signal + +Octave users need to create a file in the home directory called +.octaverc. The file should contain the following lines to load the signal +package and disable the pager (press the space key while the scripts print +intermediate information about progress): + +.. code-block:: octave + + more off + pkg load signal + +Basic usage +=========== + +First, an Octave shell is launched from the tool directory: + +.. code-block:: bash + + cd tools/tune/src + octave --gui + +The SRC component is set up in the src_generate.m script. A help for +script usage can be printed by using the Octave shell command: + +.. code-block:: octave + + >> help src_generate + +The command to generate SRC coefficients for input rates of 32 and 48 kHz and output rates of 44.1 and 48 kHz would be: + +.. code-block:: octave + + >> src_generate([32e3 48e3],[44.1e3 48e3]) + +If the script is called without arguments, it computes a larger set of +default conversions. The text output at the end of the script reports the +fractions M/N used for conversions, and estimated millions of +operations per second (MOPS) for filter arithmetic. Some more complex +fractions are handled with the M1/N1 x M2/N2 two-stage conversion to ease +internal filters computation. In the end, estimate of coefficient +storage RAM and component data RAM are shown. + +.. literalinclude:: src_2stage.txt + :language: none + +This same output is stored in reports/src_2stage.txt to keep a record of +generated conversions. + +To apply the generated coefficients to SOF firmware, the execution of +this script outputs C header files to the ``include`` directory. They +can be then copied as such to the SOF source directory +src/include/sof/audio/coefficients/src/. In these header files, +src__define.h contains #define statements for some SRC filter +maximum characteristics. The header file src__table.h includes +all needed individual filter header files and constructs a table of +SRC stages to use when a mode with certain input and output rate is +initialized. The missing conversions refer to a minimal passthrough +filter setup. An example of generated include file “src_std_int32_table.h” +is shown below: + +.. literalinclude:: src_std_int32_table.h + :language: c + +The header file first includes the coefficient vectors. The last four +values in the file names are fraction, passband end relative to +sample rate x1000, and stop band start relative to sample rate x1000. Many +of the conversions are reused for other rates combinations with the same +fractions. + +The vectors src_in_fs and src_out_fs list supported input and output +rates. The arrays of structs src_table1 and src_table2 refer to the +FIR filters coefficients used for the rates matrix. A special single +tap FIR with a coefficient of 1.0 (Q2.30) is used when filtering is not +needed such as when the input and output rates are equal or if a SRC stage is +not used. + +Coefficient precision +===================== + +The coefficients can be generated as int16, int24, int32, or float +type. The type is the 3rd argument for the src_2stage (in_rates, out_rates, +ctype) function call. It defaults to ‘int16’, which is the least memory-consuming type that provides the minimum quality. The 16-bit +coefficients may achieve near up to a 80-90 dB stopband that will give a +“near CD quality” conversion. The int32 and float type are capable of +providing “CD quality” and better with a higher filter spec that is +explained later. + +The capabilities and qualities of the SRC component to use depends on +whether you are building a "tiny" int16 or "std" int32 coefficient set. The +testbench and FW build with the xtensa compiler defaults to 32 bit +coefficients. The gcc build for firmware uses 16 bit coefficients. The +scripts used to generate them are src_tiny_int16.m and src_std_int32.m. These scripts are the easist to use as a starting point for creating a custom SRC configuration. + +Exclusion of non-needed conversions +=================================== + +If in the previous example there would be no need to convert from 32 to 44.1 +kHz, add a matrix with zero in the place of the non-wanted conversion. This +will help save memory that is needed to store the conversion coefficients. + +.. code-block:: octave + + >> src_generate([32e3 44.1e3 48e3],[44.1e3 48e3],[0 1; 1 1; 1 1]) + +In the script output, the removed conversion is marked with an ‘x’ and +corresponding filters are not calculated. + +Adjustment of SRC filter specification +====================================== + +The default conversions are tuned with the stopband specification to +provide min -80 dBFs THD+N performance. The requested stop-band +attenuation has been chosen such that the THD+N criteria is met in the +worst-case modes. + +The bandwidth is about 20 kHz for 44.1 kHz and 48 kHz sample +rates. The bandwidth is scaled to correspond to the minimum sample rate of +the conversion. However, for rates higher than 88.1 kHz, the bandwidth is +kept as about 30 kHz to provide a measurable band extension but not stretch +it near Nyquist Fs/2 as for lower sample rates. + +The transition band starts at the filter pass-band bandwidth and ends at +the stop-band start. It is, as an example, from 20 kHz to Nyquist rate Fs/2. +The transition band is a don’t care region for filter-design but, with the +used filter design method, it connects the end of the pass-band to the start +of the stop-band with a near constant dB/log frequency line them. + +These are defined in the Octave function src_param.m in the fields of +returned struct cnv. The ratio of pass-band bandwidth to min. sample +rate is defined in c_pb. The ratio of stop-band frequency to +min. sample rate is defined in c_sb. Stopband attenuation is +rs. Passband ripple is rp. The ripple is doubled for conversions that +use both stages, so this should be the desired value divided by two. + +The end of the script defines exceptions for a high sample rate to reduce +complexity. Note that the use of exceptions for pass-band width may create +unnecessary duplicates of conversions. If the c_pb and c_sb are +unmodified then the conversions like 1/2x or 2x get maximal reuse. + +Note that parameters other than c_pb and c_sb can’t be used in +exceptions without hazard (e.g. stopband). The other parameters need +to be kept the same for all conversions. As seen from the coefficient +include file names, the individual filters are differentiated only by +their conversion fraction and these bandwidths: + +.. code-block:: octave + + %% Default SRC quality + cnv.c_pb = q * 20/44.1; % Gives 20 kHz BW @ 44.1 kHz + cnv.c_sb = 0.5; % Start stopband at Fs/2 + cnv.rs = 70; % Stopband attenuation in dB + cnv.rp = 0.1; % Passband ripple in dB + cnv.rp_tot = 0.1; % Max +/- passband ripple allowed, used in test script only + cnv.gain = -1; % Gain in decibels at 0 Hz + +The next plots show the difference between the firpm and the kaiser SRC +filter characteristic. In equiripple, the passband and stopband are just at +the allowed limit across the pass and stopband. Equiripple design is +selected with the option cnv.design set to ‘firpm’. However, in Octave it +fails in many conversions due to an apparent bug in the remez() function. In +Matlab, the function firpm() is used and it can be used for up to about 2000 +order filters. + +The cnv.design set to ‘kaiser’ is a robust choice for all conversions +but results to somewhat longer filters due to stopband and passband +shape. The stopband attenuation increases towards higher frequencies +so the specified "rs" can be lower for this filter type for +firmpm. Utilizing full allowed passband ripple may be possible but it +could not be achieved in this version. As seen below, the ripple is +much less than specified maximum: + +.. figure:: images/equiripple.png + + Equiripple SRC filter characteristic + +.. figure:: images/kaiser.png + + Kaiser SRC filter characteristic + +Test the SRC component +********************** + +Build the testbench executable +============================== + +The FW component for SRC can be compiled to a desktop Linux executable +with test bench C sources in the tools/testbench directory. It is built +from the top level SOF tree with the command: + +.. code-block:: bash + + scripts/host-build-all.sh + +The executable can be run with commands to see the command line +parameters help: + +.. code-block:: bash + + cd tools/testbench/build_testbench + ./testbench -h + +The executable can be debugged with any C debugger/IDE tool and any +code analysis tool such as valgrind and gprof. Some tips for +debugging are: + +- In interactive debugging, it can be useful to remove the default -O2 + optimization in order to get linear stepping of code lines and accurate + breakpoints. + +- When debugging audio processing in gdb-based debuggers, it can be useful + to plot with gnuplot vectors of numerical values as graphs. Instructions for setting it up is available in + https://sourceware.org/gdb/wiki/PlottingFromGDB. + +Tests for quality +================= + +A set of tests has been implemented that follows the AES17 recommended test +metric [2]_. However, the scripts provide only an indication of expected +AES17 performance since they have not been calibrated or verified. + +It is useful to run the exported coefficient set to see the impact +of tuned quality or to see the performance of newly added conversion modes. +Available modes are gain, frequency response, dynamic range, attenuation +of alias products, and attenuation of image products. + +Additionally, for a quick visual indication of the conversion +characteristic, a spectrogram of a chirp is plotted. A pass/fail count is +reported for a simple criteria for the used performance indicators. The test +is executed from an Octave shell with this command: + +.. code-block:: bash + + cd tools/test/audio/ + ./src_test.sh + +A subset of the test can be started from the Octave command line: + +.. code-block:: bash + + octave + >> src_test(32, 32, 32000, 48000); + +The test script can be more friendly for detailed study of a conversion with +a small edit in src_test.m: + +.. code-block:: diff + + diff --git a/tools/test/audio/src_test.m b/tools/test/audio/src_test.m + index 5d9b95e44da4..c89b2e4c555c 100644 + --- a/tools/test/audio/src_test.m + +++ b/tools/test/audio/src_test.m + @@ -66,9 +66,9 @@ 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.files_delete = 1; % Set to 0 to inspect the audio data files + +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 = 0; % Set to 0 to inspect the audio data files + + %% Init for test loop + n_test = 7; % We have next seven test cases for SRC + + +Tips for debugging +================== + +Additional debugging information can be obtained from the output of +src_test.m scripts. It includes command line arguments that src_test.m uses +for the shell script src_run.sh as well as for the testbench executable. + +Running the script "src_test(32, 32, 32000, 48000);" returns the following output: + +.. code-block:: none + + Running './src_run.sh 32 32 32000 48000 chirp_test_in.raw chirp_test_out.raw'... + Command: ../../testbench/build_testbench/install/bin/testbench + Arg: -d -r 32000 -R 48000 -i chirp_test_in.raw -o chirp_test_out.raw -t ../../test/topology/test-playback-ssp2-mclk-0-I2S-src-s32le-s32le-48k-24576k-nocodec.tplg -a src=libsof_src.so -b S32_LE + Ld lib path: ../../testbench/build_testbench/sof_ep/install/lib:../../testbench/build_testbench/sof_parser/install/lib + +When debugging the testbench, the library path needs to be appended to +the environment variable LD_LIBRARY_PATH, and the shown arguments need to +be set for the debugger such as text mode gdb or graphical ddd. If the +option to not delete audio data files the test input files can be used +for debugging as well. + +Currently, the testbench can be debugged only as a host (x86) gcc build. +However, the possibility of debugging with the xt-gdb will be restored to +also debug an xtensa-optimized version of the component in the testbench. + +Polyphase decomposition +*********************** + +The SRC component is utilizing an algorithm-level optimization +called polyphase decomposition. The next figure shows derivation of the +polyphase fractional resampler for a 3/4 ratio that is used in, for +example, a 32 to 24 kHz conversion. + +.. figure:: images/poly34.png + + Polyphase decomposition for fractional 32 to 24 kHz conversion (3/4) + +1. The basic conversion is shown. + +2. The interpolation is changed to polyphase filter where low-pass + filter H(z) is split into three sub-filters R\ :sub:`0`\(z), + R\ :sub:`1`\(z), and R\ :sub:`2`\(z). + +3. The “3 to 1 commutator” structure that the zp\ :sup:p unit delays are + multiplicated to match the decimation rate of 4. The subfilter outputs + need to be compensated with an additional negative delay (z\ :sup:`p`, p > 0) + to preserve the sub-filter out to the whole filter chain output Y(z). + +4. The added negative delays are moved to filter the input side by + dividing the negative delay by the interpolation factor. Also, the + decimation at filter output is moved to the commutator input side. + +5. The order of decimation and interpolation are swapped to have + decimation first. Also, a delay is added to the input to compensate for + a negative delay used to make the filter causal. + +6. The input side delays are merged. + +Note that the sub-filters R(z) in practical implementation share the +same delay line. The delay length is defined as the length of the longest +delay chain needed. + +Also, in a practical implementation, this delay length includes the +length of processing block length and store multiple channels of +audio. + +In this example the output commutator, after reformatting, remained +unit delays-based. In case of non-unit delays, a more complex +interleaving output buffer structure is needed. + +In the next example of polyphase decomposition, the input is up-sampled by a +ratio of 3/2 e.g. 32 kHz to 48 kHz conversion. The structure is the +same for down-sampling conversion: + +.. figure:: images/poly32.png + + Polyphase decomposition for fractional 32 to 48 kHz conversion (3/2) + +1. Steps 1-2 are similar to the previous case. + +2. The only difference is decimation by 2. + +3. The multiplication of unit delays in output commutator is done with + higher than decimation factor of 3 since the negative delay + elements added need to be divide with the interpolation + factor. Hence the unit delays are made z\ :sup:`-4`. This is needed + because the order of interpolation and decimation could otherwise not be + reversed. + +4. Similar to the previous example. + +5. Similar to the previous example. + +6. In the remaining structure, the output commutator delays are doubled + z\ :sup:`-2`. Therefore, the output needs a circular interleaving + buffer. There is no need to sum/mix samples; write them with a + stride and read linearly with a sufficient delay that ensures all + delay slots have been written. + +References +********** + +.. [1] P. P. Vaidyanathan: “Multirate Systems and Filter Banks,” Prentice Hall Signal Processing Series, 1993 + +.. [2] AES17-2015 Standard, http://www.aes.org/publications/standards/search.cfm?docID=21 diff --git a/algos/src/src_2stage.txt b/algos/src/src_2stage.txt new file mode 100644 index 00000000..5b23be05 --- /dev/null +++ b/algos/src/src_2stage.txt @@ -0,0 +1,19 @@ + +Dual stage fractional SRC: Ratios +in \ out, 44.1, 48.0, + 32.0, 21/20*21/16, 3/2, + 48.0, 21/20*7/8, 1, + +Dual stage fractional SRC: MOPS +in \ out, 44.1, 48.0, + 32.0, 5.47, 4.42, + 48.0, 6.68, 0.00, + +Dual stage fractional SRC: MOPS per stage + in \ out, 44.1, 48.0, + 32.0, 2.82+2.65, 4.42+0.00, + 48.0, 2.62+4.06, 0.00+0.00, + +Coefficient RAM 19.7 kB +Max. data RAM 4.8 kB + diff --git a/algos/src/src_std_int32_table.h b/algos/src/src_std_int32_table.h new file mode 100644 index 00000000..aa170fd1 --- /dev/null +++ b/algos/src/src_std_int32_table.h @@ -0,0 +1,25 @@ +/* SRC conversions */ +#include +#include +#include +#include +#include + +/* SRC table */ +int32_t fir_one = 1073741824; +struct src_stage src_int32_1_1_0_0 = { 0, 0, 1, 1, 1, 1, 1, 0, -1, &fir_one }; +struct src_stage src_int32_0_0_0_0 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, &fir_one }; +int src_in_fs[2] = { 32000, 48000}; +int src_out_fs[2] = { 44100, 48000}; +struct src_stage *src_table1[2][2] = { + { &src_int32_21_20_4535_5000, &src_int32_21_20_4167_5000 + }, + { &src_int32_3_2_4535_5000, &src_int32_1_1_0_0 + } +}; +struct src_stage *src_table2[2][2] = { + { &src_int32_21_16_4319_5000, &src_int32_7_8_4535_5000 + }, + { &src_int32_1_1_0_0, &src_int32_1_1_0_0 + } +}; 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/api/audio-stream-api.rst b/api/audio-stream-api.rst new file mode 100644 index 00000000..b95f86af --- /dev/null +++ b/api/audio-stream-api.rst @@ -0,0 +1,9 @@ +.. _audio-stream-api: + +Audio Stream API +################ + +Location: *include/sof/audio/audio_stream.h* + +.. doxygengroup:: audio_stream_api + :project: SOF Project diff --git a/api/component-api.rst b/api/component-api.rst new file mode 100644 index 00000000..81802014 --- /dev/null +++ b/api/component-api.rst @@ -0,0 +1,22 @@ +.. _component-api: + +Component API +############# + +This section is intended for all component developers. It documents the basic +component driver and component device API that must be implemented by every +component. It also documents functions that are commonly used by effects +components, or blocks inserted in the middle of the pipeline to process +enhance the audio signal. + +Another :ref:`component-ext-api` section documents macros and functions that +are used by the infrastructure and specialized components like host, dai, +and kpb. + +Location: *include/sof/audio/component.h* + +.. doxygengroup:: component_api + :project: SOF Project + +.. doxygengroup:: component_common_int + :project: SOF Project diff --git a/api/component-ext-api.rst b/api/component-ext-api.rst new file mode 100644 index 00000000..4b16b170 --- /dev/null +++ b/api/component-ext-api.rst @@ -0,0 +1,9 @@ +.. _component-ext-api: + +Component API Ext +################# + +Location: *include/sof/audio/component_ext.h* + +.. doxygengroup:: component_api_helpers + :project: SOF Project diff --git a/api/dma-drivers-api.rst b/api/dma-drivers-api.rst index 1b2e22c4..af3ca063 100644 --- a/api/dma-drivers-api.rst +++ b/api/dma-drivers-api.rst @@ -5,3 +5,15 @@ DMA Drivers API .. doxygengroup:: sof_dma_drivers :project: SOF Project + +.. + Keep 'sof_dma_copy_func' after 'sof_dma_drivers' so most dma_copy + links (correctly) point to the struct and not to the func. This also + avoids the 'WARNING: Duplicate declaration, dma_copy' for some + unclear reason. + +.. doxygengroup:: sof_dma_copy_func + :project: SOF Project + +This function is listed separately to solve a name clash with the struct +dma_copy {} above. diff --git a/api/index.rst b/api/index.rst index 1c059a3a..ec11d4d7 100644 --- a/api/index.rst +++ b/api/index.rst @@ -6,7 +6,13 @@ API Documentation .. toctree:: :maxdepth: 1 + uuid-api dma-drivers-api dai-drivers-api + pm-runtime-api platform-api + memory-alloc-api + audio-stream-api + component-api + component-ext-api uapi diff --git a/api/memory-alloc-api.rst b/api/memory-alloc-api.rst new file mode 100644 index 00000000..05da3a6c --- /dev/null +++ b/api/memory-alloc-api.rst @@ -0,0 +1,9 @@ +.. _memory-alloc-api: + +Memory Allocation API +##################### + +Location: *include/sof/lib/alloc.h* + +.. doxygengroup:: alloc_api + :project: SOF Project diff --git a/api/pm-runtime-api.rst b/api/pm-runtime-api.rst new file mode 100644 index 00000000..7a4d7772 --- /dev/null +++ b/api/pm-runtime-api.rst @@ -0,0 +1,7 @@ +.. _pm-runtime-api: + +PM Runtime API +############## + +.. doxygengroup:: pm_runtime + :project: SOF Project diff --git a/api/uuid-api.rst b/api/uuid-api.rst new file mode 100644 index 00000000..eee5911c --- /dev/null +++ b/api/uuid-api.rst @@ -0,0 +1,9 @@ +.. _uuid-api: + +UUID API +######## + +Location: *include/sof/lib/uuid.h* + +.. doxygengroup:: uuid_api + :project: SOF Project 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/getting_started/cavs-boot/apollolake/apl-boot-ldr.rst b/architectures/firmware/intel/cavs/cavs-boot/apollolake/apl-boot-ldr.rst similarity index 94% rename from getting_started/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/getting_started/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/getting_started/cavs-boot/apollolake/apl-boot-rom.rst b/architectures/firmware/intel/cavs/cavs-boot/apollolake/apl-boot-rom.rst similarity index 99% rename from getting_started/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/getting_started/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/getting_started/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 getting_started/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/getting_started/cavs-boot/apollolake/index.rst b/architectures/firmware/intel/cavs/cavs-boot/apollolake/index.rst similarity index 62% rename from getting_started/cavs-boot/apollolake/index.rst rename to architectures/firmware/intel/cavs/cavs-boot/apollolake/index.rst index 043d6503..21ee69cb 100644 --- a/getting_started/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/getting_started/cavs-boot/cavs-dsp-boot-overview.rst b/architectures/firmware/intel/cavs/cavs-boot/cavs-dsp-boot-overview.rst similarity index 98% rename from getting_started/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/getting_started/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/getting_started/cavs-boot/images/boot-dsp.pu b/architectures/firmware/intel/cavs/cavs-boot/images/boot-dsp.pu similarity index 100% rename from getting_started/cavs-boot/images/boot-dsp.pu rename to architectures/firmware/intel/cavs/cavs-boot/images/boot-dsp.pu diff --git a/getting_started/cavs-boot/images/boot-ldr-flow.pu b/architectures/firmware/intel/cavs/cavs-boot/images/boot-ldr-flow.pu similarity index 100% rename from getting_started/cavs-boot/images/boot-ldr-flow.pu rename to architectures/firmware/intel/cavs/cavs-boot/images/boot-ldr-flow.pu diff --git a/getting_started/cavs-boot/images/loading-bins.pu b/architectures/firmware/intel/cavs/cavs-boot/images/loading-bins.pu similarity index 100% rename from getting_started/cavs-boot/images/loading-bins.pu rename to architectures/firmware/intel/cavs/cavs-boot/images/loading-bins.pu diff --git a/getting_started/cavs-boot/images/write-bin.pu b/architectures/firmware/intel/cavs/cavs-boot/images/write-bin.pu similarity index 100% rename from getting_started/cavs-boot/images/write-bin.pu rename to architectures/firmware/intel/cavs/cavs-boot/images/write-bin.pu diff --git a/architectures/firmware/intel/cavs/cavs-boot/index.rst b/architectures/firmware/intel/cavs/cavs-boot/index.rst new file mode 100644 index 00000000..54ba2ef6 --- /dev/null +++ b/architectures/firmware/intel/cavs/cavs-boot/index.rst @@ -0,0 +1,17 @@ +.. _architecture-intel-cavs-boot: + +Booting up CAVS ADSP +#################### + +Intel has several generations of audio DSP. "CAVS" versions relate to the audio +DSP in Skylake Core and Apollo Lake Atom platforms onwards. + +Bay Trail, Cherry Trail, Braswell, Haswell, and Broadwell audio DSPs have a simpler +boot flow using memory copy and not authentication. + + +.. toctree:: + :maxdepth: 2 + + cavs-dsp-boot-overview + apollolake/index diff --git a/architectures/xtensa-dsp/smp/images/idc-send-message.pu b/architectures/firmware/intel/cavs/images/idc-send-message.pu similarity index 100% rename from architectures/xtensa-dsp/smp/images/idc-send-message.pu rename to architectures/firmware/intel/cavs/images/idc-send-message.pu diff --git a/architectures/firmware/intel/cavs/index.rst b/architectures/firmware/intel/cavs/index.rst new file mode 100644 index 00000000..4f3ac9a7 --- /dev/null +++ b/architectures/firmware/intel/cavs/index.rst @@ -0,0 +1,13 @@ +.. _cavs-architecture-intel: + +Intel cAVS Architecture +####################### + +The details below are specific to Intel products with an audio DSP cAVS +architecture using SOF. + +.. toctree:: + :maxdepth: 1 + + cavs-boot/index + smp/index \ No newline at end of file diff --git a/architectures/xtensa-dsp/smp/index.rst b/architectures/firmware/intel/cavs/smp/index.rst similarity index 77% rename from architectures/xtensa-dsp/smp/index.rst rename to architectures/firmware/intel/cavs/smp/index.rst index 082af802..5aff7fc0 100644 --- a/architectures/xtensa-dsp/smp/index.rst +++ b/architectures/firmware/intel/cavs/smp/index.rst @@ -1,7 +1,7 @@ -.. _architecture-xtensa-smp: +.. _architecture-intel-smp: -Symmetric Multiprocessing 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 +.. 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/apps/components/component-mgmt-api.rst b/architectures/firmware/sof-common/components/component-mgmt-api.rst similarity index 100% rename from developer_guides/apps/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/architectures/firmware/sof-common/components/component-overview.rst b/architectures/firmware/sof-common/components/component-overview.rst new file mode 100644 index 00000000..edfd4a50 --- /dev/null +++ b/architectures/firmware/sof-common/components/component-overview.rst @@ -0,0 +1,105 @@ +.. _apps-component-overview: + +Overview +######## + +A component adds audio signal processing to a pipeline that's running on the +DSP. An instance of the component, called a component device (components are +implemented in the driver-device model), is chained with other component +devices and builds an audio processing path organized as a pipeline. + +Component driver +**************** + +Every component must implement a driver (see the ``comp_driver``) which +creates instances by handling *new* component requests coming from the +command handlers. + +The driver must be registered on the system component driver list by calling +``comp_register(comp_driver *)`` and providing a unique component id in +order to receive the requests. + +Each component driver declares its unique ``type`` that is later used by the +uAPI to create a component of that ``type``. It also provides an entry point +to the component ops implementation. + +UUIDs (Universally Unique Identifiers) provide a more scalable and +collision-free way of component identification. UUIDs are currently used as +the standard interface by all users of the SOF firmware, including the +tracing subsystem, the topology .m4 files, and the Linux topology driver. +Using the ``type`` to define topology and create components is still +supported today; however, the ``type`` will be moved out of the IPC struct +in the future. Therefore, **be sure to allocate UUIDs for all newly-added component drivers.** + +The UUID entry declared in the FW code contains the identifier value as well +as the object which is the component name in this case. Both are +provided as the arguments to the ``DECLARE_SOF_RT_UUID()`` macro. For +example, the **volume** component provides the following declaration: + +.. code-block:: c + + /* b77e677e-5ff4-4188-af14-fba8bdbf8682 */ + DECLARE_SOF_RT_UUID("volume", volume_uuid, 0xb77e677e, 0x5ff4, 0x4188, + 0xaf, 0x14, 0xfb, 0xa8, 0xbd, 0xbf, 0x86, 0x82); + +Note how the ``af14`` 16bit segment is split into two bytes at the beginning +of the second line. + +``volume`` is the component name used by the sof-logger while printing the +trace source name. ``volume_uuid`` is the symbol used later to associate the +declared UUID with the volume of the component driver: + +.. code-block:: c + :emphasize-lines: 3 + + static const struct comp_driver comp_volume = { + .type = SOF_COMP_VOLUME, + .uid = SOF_RT_UUID(volume_uuid), + ... + }; + +See the :ref:`uuid-api` for more details on UUID generation and declaration. + +.. uml:: images/comp-driver.pu + :caption: Component Driver + +Create a component device +************************* + +When a new component device is requested, the system ``comp_new()`` function +finds the driver that's registered with the requested unique component type +and calls the ``new()`` function pointed by the registered driver's data in +order to instantiate the device. + +Following is the entry called to create a new component device:: + + struct comp_dev* comp_new(sof_ipc_comp *comp); + +.. uml:: images/comp-new-flow.pu + +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) +************************************** + +Every component driver implements the ``comp_ops`` API. + +.. note:: + + Some API functions are mandatory for specific component types since + the infrastructure code calls them selectively based on the target + component type. For instance, ``dai_config()`` is only called for + ``SOF_COMP_DAI`` and ``SOF_COMP_SG_DAI`` and cannot be called for other + component types. + +See ``struct comp_ops`` documentation in :ref:`component-api` for details. 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/apps/components/images/comp-driver.pu b/architectures/firmware/sof-common/components/images/comp-driver.pu similarity index 97% rename from developer_guides/apps/components/images/comp-driver.pu rename to architectures/firmware/sof-common/components/images/comp-driver.pu index 417d47bf..a96e9e1a 100644 --- a/developer_guides/apps/components/images/comp-driver.pu +++ b/architectures/firmware/sof-common/components/images/comp-driver.pu @@ -2,6 +2,7 @@ package component { class comp_driver { type : uint32_t + uid : uint32_t module_id : uint32_t ops : comp_ops } 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/apps/components/images/comp-new-flow.pu b/architectures/firmware/sof-common/components/images/comp-new-flow.pu similarity index 100% rename from developer_guides/apps/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/apps/components/images/component-mgmt-api.pu b/architectures/firmware/sof-common/components/images/component-mgmt-api.pu similarity index 100% rename from developer_guides/apps/components/images/component-mgmt-api.pu rename to architectures/firmware/sof-common/components/images/component-mgmt-api.pu diff --git a/developer_guides/apps/components/index.rst b/architectures/firmware/sof-common/components/index.rst similarity index 67% rename from developer_guides/apps/components/index.rst rename to architectures/firmware/sof-common/components/index.rst index 7f9288af..76db7fdd 100644 --- a/developer_guides/apps/components/index.rst +++ b/architectures/firmware/sof-common/components/index.rst @@ -6,6 +6,6 @@ Components .. toctree:: :maxdepth: 2 - new-comp-guide - component-api + 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/drivers/dai/images/dai-ops.pu b/architectures/firmware/sof-xtos/drivers/dai/images/dai-ops.pu similarity index 100% rename from developer_guides/drivers/dai/images/dai-ops.pu rename to architectures/firmware/sof-xtos/drivers/dai/images/dai-ops.pu diff --git a/developer_guides/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/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/drivers/dai/index.rst b/architectures/firmware/sof-xtos/drivers/dai/index.rst similarity index 100% rename from developer_guides/drivers/dai/index.rst rename to architectures/firmware/sof-xtos/drivers/dai/index.rst diff --git a/developer_guides/drivers/dma/images/dma-ops.pu b/architectures/firmware/sof-xtos/drivers/dma/images/dma-ops.pu similarity index 100% rename from developer_guides/drivers/dma/images/dma-ops.pu rename to architectures/firmware/sof-xtos/drivers/dma/images/dma-ops.pu diff --git a/developer_guides/drivers/dma/images/dma-transfer.pu b/architectures/firmware/sof-xtos/drivers/dma/images/dma-transfer.pu similarity index 100% rename from developer_guides/drivers/dma/images/dma-transfer.pu rename to architectures/firmware/sof-xtos/drivers/dma/images/dma-transfer.pu diff --git a/developer_guides/drivers/dma/index.rst b/architectures/firmware/sof-xtos/drivers/dma/index.rst similarity index 100% rename from developer_guides/drivers/dma/index.rst rename to architectures/firmware/sof-xtos/drivers/dma/index.rst diff --git a/developer_guides/drivers/dma/intel/hda-dma.rst b/architectures/firmware/sof-xtos/drivers/dma/intel/hda-dma.rst similarity index 100% rename from developer_guides/drivers/dma/intel/hda-dma.rst rename to architectures/firmware/sof-xtos/drivers/dma/intel/hda-dma.rst diff --git a/developer_guides/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/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/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/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/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/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/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/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/drivers/dma/intel/index.rst b/architectures/firmware/sof-xtos/drivers/dma/intel/index.rst similarity index 100% rename from developer_guides/drivers/dma/intel/index.rst rename to architectures/firmware/sof-xtos/drivers/dma/intel/index.rst diff --git a/developer_guides/drivers/images/device-disco.pu b/architectures/firmware/sof-xtos/drivers/images/device-disco.pu similarity index 100% rename from developer_guides/drivers/images/device-disco.pu rename to architectures/firmware/sof-xtos/drivers/images/device-disco.pu diff --git a/developer_guides/drivers/images/device-probe.pu b/architectures/firmware/sof-xtos/drivers/images/device-probe.pu similarity index 100% rename from developer_guides/drivers/images/device-probe.pu rename to architectures/firmware/sof-xtos/drivers/images/device-probe.pu diff --git a/developer_guides/drivers/images/device-remove.pu b/architectures/firmware/sof-xtos/drivers/images/device-remove.pu similarity index 100% rename from developer_guides/drivers/images/device-remove.pu rename to architectures/firmware/sof-xtos/drivers/images/device-remove.pu diff --git a/developer_guides/drivers/index.rst b/architectures/firmware/sof-xtos/drivers/index.rst similarity index 100% rename from developer_guides/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/introduction/images/fw-arch-diag.png b/architectures/firmware/sof-xtos/images/fw-arch-diag.png similarity index 100% rename from introduction/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/kernel/images/memory-zones.dot b/architectures/firmware/sof-xtos/images/memory-zones.dot similarity index 97% rename from developer_guides/kernel/images/memory-zones.dot rename to architectures/firmware/sof-xtos/images/memory-zones.dot index 868c1f8e..e4541abf 100644 --- a/developer_guides/kernel/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/kernel/images/runtime-zone.dot b/architectures/firmware/sof-xtos/images/runtime-zone.dot similarity index 97% rename from developer_guides/kernel/images/runtime-zone.dot rename to architectures/firmware/sof-xtos/images/runtime-zone.dot index ffa0e506..e2824216 100644 --- a/developer_guides/kernel/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/kernel/images/system-zone.dot b/architectures/firmware/sof-xtos/images/system-zone.dot similarity index 98% rename from developer_guides/kernel/images/system-zone.dot rename to architectures/firmware/sof-xtos/images/system-zone.dot index d18a7c8b..06768cb9 100644 --- a/developer_guides/kernel/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/architectures/firmware/sof-xtos/kd_integration/images/kd-component-diagram.pu b/architectures/firmware/sof-xtos/kd_integration/images/kd-component-diagram.pu new file mode 100644 index 00000000..252d347f --- /dev/null +++ b/architectures/firmware/sof-xtos/kd_integration/images/kd-component-diagram.pu @@ -0,0 +1,48 @@ +@startuml + +scale max 1024 width + +skinparam rectangle { + backgroundColor<> #6fccdd + backgroundColor<> #f6ed80 + backgroundColor<> #d6d6de + borderColor<> #d6d6de + borderColor<> #a1a1ca + + backgroundColor<> #f05772 + stereotypeFontColor<> #ffffff + fontColor<> #ffffff + + backgroundColor<> #f0f0f0 +} + + +together { +rectangle "MIC HW" as dmic #DDDDDD + +rectangle "Speech Capture Pipeline" as ppl_1 <>{ + rectangle "MIC DAI" as dai_1 <> + rectangle "Keyphrase Buffer Manager" as kpb + dai_1 -> kpb : 2ch/16kHz/16bit + rectangle "Host" as host + } + +} + +rectangle "Keyphrase Detector Pipeline" as ppl_2 <>{ + rectangle "Channel selector" as sel + rectangle "Keyphrase detection algorithm" as wov + sel -> wov : 1ch/16kHz/16bit +} + +rectangle "Host System" as hsys { + rectangle "Host Memory" as hmem #DDDDDD +} + +dmic -> dai_1 +kpb -> host +kpb -> sel : 2ch/16kHz/16bit +host -> hmem : 2ch/16kHz/16bit +wov ..> kpb : FW events +wov ..> hsys : FW notifications +@enduml \ No newline at end of file diff --git a/architectures/firmware/sof-xtos/kd_integration/images/kd-e2e-sequence-diagram.pu b/architectures/firmware/sof-xtos/kd_integration/images/kd-e2e-sequence-diagram.pu new file mode 100644 index 00000000..344e56bb --- /dev/null +++ b/architectures/firmware/sof-xtos/kd_integration/images/kd-e2e-sequence-diagram.pu @@ -0,0 +1,123 @@ +@startuml + +scale max 1024 width + +participant "Userspace component" as usr +participant "Audio driver" as drv +participant "FW infrastructure" as fw +participant "Data transfer to Host" as dma +participant "Keyword detection algorithm" as kda +participant "Data transfer to DSP" as gpdma + +box "Linux User/Kernel space" #LavenderBlush + participant usr + participant drv +end box + +box "DSP" #LightBlue + participant fw + participant dma + participant kda + participant gpdma +end box + +activate fw + +drv -> fw : Setup audio topology \n (Speech Capture & Keyword Detection pipes) +usr -> drv : Prepare & Open PCM capture \n(snd_pcm_open/snd_pcm_hw_params) +drv -> fw : Stream Open & Preparation +drv -> fw : HW Params +group optional (depends on keyword detection algorithm implementation) + usr -> drv : Send keyword detection algorithm parameters \n (snd_ctl_elem_tlv_write) + drv -> fw : Send keyword detection algorithm parameters + fw -> kda : Send keyword detection algorithm parameters +end + +drv ->drv : DAPM power up event +drv -> fw : HW Params for Keyphrase Detection Pipeline +usr -> drv : Trigger start (alsamixer) +drv -> fw : Keyword detection algorithm & buffer manager triggered + +fw -> fw : Keyphrase Buffer Manager \nin acquisition mode +fw -> gpdma + +activate gpdma + +fw -> kda : keypharse detection enabled + +activate kda + +usr -> drv : Trigger start (snd_pcm_read) + +note over usr +Speech application indefinitely +waits for data. +end note + +ref over usr, drv, fw , gpdma, kda, dma +Speech Capture pipeline is not transmitting data to Host system +Host system may enter the low power state +end ref + +loop keyword detection algorithm \nexecuted on DSP + kda <- gpdma +end + +hnote over kda : keyword is detected + +fw <-- kda : FW event on keyword detection +fw -> kda : keyword detection disabled + +deactivate kda + +fw -> fw : Keyphrase Buffer Manager \nin drain mode +drv <-- fw : notification on keyword detection +'drv -> fw : enable data transission to Host \n(Capture[Speech] pipeline to Host is running) +usr <-- drv : notification on keyword detection (optional) +gpdma -> dma + +activate dma + +ref over dma +Sending a burst of historic data (approx.2s) +with detected keyword for +second stage verification on host. +end ref + +gpdma <-- dma + +deactivate dma + +usr <-- drv : snd_pcm_read completed + +fw -> fw : Keyphrase Buffer Manager \nin passthrough mode + +loop Realtime capture + usr -> drv : snd_pcm_read + gpdma -> dma + activate dma + gpdma <-- dma + + deactivate dma + usr <-- drv : snd_pcm_read completed +end + +ref over usr +User space optionally performs second stage keyword verification. +end ref + +usr -> drv : Trigger stop (alsamixer) +drv ->drv : DAPM power down event +drv -> fw : Stop Keyphrase Detection algorithm pipeline +usr -> drv : Trigger stop (snd_pcm_drop / snd_pcm_free) +drv -> fw : Close Speech capture stream +fw -> gpdma + +deactivate gpdma + +ref over usr, drv, fw , gpdma, kda, dma +The flow can be repeated for next user command starting from snd_pcm_open() +end ref + +deactivate fw +@enduml \ No newline at end of file diff --git a/architectures/firmware/sof-xtos/kd_integration/images/kd-state-diagram.pu b/architectures/firmware/sof-xtos/kd_integration/images/kd-state-diagram.pu new file mode 100644 index 00000000..ab0a9c74 --- /dev/null +++ b/architectures/firmware/sof-xtos/kd_integration/images/kd-state-diagram.pu @@ -0,0 +1,44 @@ +@startuml +[*] --> KPB_DISABLED: Start \nor\n [IPC] free \nmessage \nfrom either state +KPB_DISABLED: Starting state of KPB - \nNo action has been taken yet +KPB_DISABLED--> KPB_CREATED: [IPC] \nnew component +KPB_DISABLED-[#0000FF]-> KPB_DISABLED: [IPC] \nreset + +KPB_CREATED : New KPB component has been created +KPB_CREATED --> KPB_PREPARING: [IPC] \npcm params +KPB_CREATED -[#0000FF]-> KPB_CREATED : [IPC] \nreset + +KPB_PREPARING: Prepare Key Phrase Buffer component. +KPB_PREPARING-> KPB_STATE_RUN: Success +KPB_PREPARING-> KPB_PREPARING: Failure +KPB_PREPARING-[#0000FF]-> KPB_PREPARING: [IPC] \nreset + +KPB_STATE_RUN: KPB is prepared and ready. +KPB_STATE_RUN-[#0000FF]-> KPB_PREPARING: [IPC] \nreset +KPB_STATE_RUN---> KPB_STATE_INIT_DRAINING: [EVENT] \nkey phrase detected +KPB_STATE_RUN-> KPB_STATE_BUFFERING: Start \nbuffering + +KPB_STATE_BUFFERING: Buffer incoming samples in the \ninternal history buffer +KPB_STATE_BUFFERING-> KPB_STATE_RUN: Done +KPB_STATE_BUFFERING-> KPB_STATE_INIT_DRAINING: Done +KPB_STATE_BUFFERING-> KPB_STATE_DRAINING: Done +KPB_STATE_BUFFERING-[#0000FF]-> KPB_STATE_RESETTING: [IPC] \nreset + +KPB_STATE_INIT_DRAINING: KPB received detection event +KPB_STATE_INIT_DRAINING-[#0000FF]-> KPB_PREPARING: [IPC] \nreset +KPB_STATE_INIT_DRAINING--> KPB_STATE_DRAINING: Draining task starts +KPB_STATE_INIT_DRAINING--> KPB_STATE_BUFFERING: Start \nbuffering + +KPB_STATE_DRAINING: KPB is draining internal history buffer \nto the client's buffer +KPB_STATE_DRAINING-->KPB_STATE_HOST_COPY: Draining done +KPB_STATE_DRAINING-[#0000FF]-> KPB_STATE_RESETTING: [IPC] \nreset +KPB_STATE_DRAINING--> KPB_STATE_BUFFERING: Start \nbuffering + +KPB_STATE_RESETTING: KPB is preparing itself for the reset +KPB_STATE_RESETTING-->KPB_STATE_RESET_FINISHING + +KPB_STATE_RESET_FINISHING: KPB is finishing reset sequence +KPB_STATE_RESET_FINISHING->KPB_PREPARING: Reset done + +KPB_STATE_HOST_COPY: KPB is copying real time \nstream into client's buffer +@enduml diff --git a/architectures/firmware/sof-xtos/kd_integration/images/kd-timing-diagram.pu b/architectures/firmware/sof-xtos/kd_integration/images/kd-timing-diagram.pu new file mode 100644 index 00000000..f6f4b3bb --- /dev/null +++ b/architectures/firmware/sof-xtos/kd_integration/images/kd-timing-diagram.pu @@ -0,0 +1,25 @@ +@startuml + +scale max 1024 width + +footer: timeline not to scale +robust "Speech application" as App +concise "Audio Stream" as Audio + +App is idle +Audio is "Preceeding" + +@App +0 is idle ++180 is Processing + +@Audio +0 is Keyphrase +@0 <-> @100 : keyphrase length - L1 +@100 <-> @+80 : detection\ntime - L2 +@180 <-> @+80 : burst \ntransmission time - L3 +Audio@180 -> App@180 : notification +@260 <-> @+60 : safety \nmargin - L4 +100 is Command ++200 is Following +@enduml diff --git a/architectures/firmware/sof-xtos/kd_integration/index.rst b/architectures/firmware/sof-xtos/kd_integration/index.rst new file mode 100644 index 00000000..7aaa8c00 --- /dev/null +++ b/architectures/firmware/sof-xtos/kd_integration/index.rst @@ -0,0 +1,10 @@ +.. _apps-kd-integration: + +Keyword detection +################# + +.. toctree:: + :maxdepth: 2 + + kd-integration + diff --git a/architectures/firmware/sof-xtos/kd_integration/kd-integration.rst b/architectures/firmware/sof-xtos/kd_integration/kd-integration.rst new file mode 100644 index 00000000..021639ef --- /dev/null +++ b/architectures/firmware/sof-xtos/kd_integration/kd-integration.rst @@ -0,0 +1,176 @@ +.. _KD-integration: + +Keyword Detection integration +############################# + +Keyword Detection (KD), also known as Voice Activation or Sound Trigger, is +a feature that triggers a speech recognition engine when a predefined +keyphrase (keyword) is successfully detected. Offloading the keyphrase +detection algorithm to the embedded processing environment (i.e. dedicated +DSP) reduces system power consumption while listening for an utterance. + +The terms Voice Activation and Keyphrase Detection are often used +interchangeably to describe end-to-end system-level use cases that include: + +* Keyphrase detection algorithm +* Keyphrase enrollment (parametrization of keyphrase detection algorithm) +* Management of an audio stream that is used to transport utterances +* Steps made to reduce system-level power consumption +* System wakeup on keyphrase detection + +The Keyphrase Detector component typically is used to identify a +firmware processing component that implements an algorithm for keyphrase +detection in an audio stream. + +The speech audio stream is used to indicate that the stream is primarily used +to deliver data to the automatic speech recognition (ASR) algorithm. The +voice audio stream typically indicates that the recipient of audio data is a +human. + +Depending on system-level requirements for the keyphrase detection algorithm +and the speech recognition engine, different policies for keyphrase buffering +and voice data streaming may be applied. This document covers the reference +implementation available in SOF. The following sections cover the functional +scope. + +.. note:: + Currently, SOF implements the Keyphrase Detector component with a + reference trigger function that allows testing of E2E flow by detecting + a rapid volume change. + + +Timing sequence +*************** + +.. _timing-sequence: + +.. uml:: images/kd-timing-diagram.pu + :caption: Basic diagram for a timing sequence + +A keyphrase is preceded by a period of silence and is followed by a user +command. In order to balance power savings and user experience, the host +system (CPU) is activated only if a keyphrase is detected. To reduce the +number of false triggers for user commands, the keyphrase can be sent to the +host for additional (2nd stage) verification. This requires the FW to buffer +the keyphrase in a memory. Keyphrase transmission to the host is as fast as +possible (faster than real-time) to reduce latency for a system response. + +End-2-End flows +*************** + +.. uml:: images/kd-e2e-sequence-diagram.pu + :caption: E2E flow for SW/FW components + +The fundamental assumption for the flow is that the keyphrase detection +sequence is controlled by the user space component (application) that opens +and closes the speech audio stream. The audio topology must be set up +before the speech stream is opened. There is an optional sequence to +customize the keyword detection algorithm by behavior by sending run-time +parameters. + +During the Stream Open and Preparation phase, HW parameters are sent to the +DAI and configuration parameters are passed from the topology to the FW +components. The DAPM events handlers are used to control a Keyphrase +Detector node of the FW topology graph by the audio driver. Once the +keyphrase is detected, a notification is sent to the driver. At the same +time, an internal event in the FW triggers, draining buffered audio data in +burst mode to the host. Once the buffer is drained, the speech capture +pipeline starts to work as a passthrough capture until it is closed by the +user space application. + +FW topology +*********** + +.. uml:: images/kd-component-diagram.pu + :caption: Basic diagram for FW components topology + +The diagram above provides an overview of FW and HW components that play a +role in keyphrase detection flows. The components are organized in pipelines: + +1. Speech capture pipeline + + a) DMIC DAI configures the HW interface to capture data from microphones. + + b) The Keyphrase Buffer Manager is responsible for managing the data + captured by microphones. This includes control of an internal buffer + for incoming data and routing of incoming audio samples. The + audio buffer with historic audio data is implemented as a cyclic + buffer. While listening to a keyphrase, the component stores incoming + data in an internal buffer and copies it to a sink that leads toward + the keyword detector component. On successful detection of a + keyphrase, the buffer is drained during a burst transmission to a + host. Once the buffer is drained, it starts to work as a passthrough + component on a capture pipeline. + + c) The host component configures transport (over DMA) to the host system. + The component is responsible for transmitting from local memory + (FW accessible) to remote (host CPU accessible) memory. + + +2. Keyphrase detector pipeline + + a) The channel selector is responsible for providing a single channel on + input to the keyphrase detection algorithm. The decision of which + channel to select is made by the platform integrator. The component + can accept parameters from a topology file. + + b) The keyphrase detection algorithm accepts audio frames and returns + information if a keyphrase is detected. Note that the FW infrastructure + can allow a FW event to be sent to the Keyphrase Buffer Manager + component if a keyphrase is detected. The component also sends a + notification to the audio driver and implements large parameters + support. + +KPBM state diagram +****************** + +The state diagram below presents all possible keyphrase buffer manager states +as well as the valid relationships between them. + +.. uml:: images/kd-state-diagram.pu + :caption: Keyphrase buffer manager state diagram + +Latency & buffering +******************* + +This section covers calculations needed to be done to properly configure +the keyphrase buffer size. The symbols used in a formula below are depicted +above; see :ref:`timing-sequence`. + +.. note:: + + The formula for size of a keyphrase buffer: + ( L1 + L2 + L3 + L4 ) * number of channels * bitdepth = Size [Kb] + + +Specifically: + +1. L1 is defined as length of a keyphrase with preceding or trailing silence. + The value depends highly on the keyphrase itself and detection algorithm + requirements. + +2. L2 is a sum of the algorithmic (processing) latency of a detection + algorithm and the additional time needed to execute additional components + in pipelines as well as prepare and send notifications. + +3. L3 is the time required to send already-buffered data to the host. + Typically, a Write Pointer (WP) is used to indicate where data that's + coming from microphones is written to a keyphrase buffer. The keyphrase + buffer is organized as a cyclic buffer and the WP moves if data is coming + from mics at a regular rate. The Read Pointer (RP) indicates from which + offset in the buffer data is fetched to host. To start burst + transmission, the RP is set to the WP - "history depth" position. The + history depth is defined at FW or is passed from topology. The RP moves + faster than the WP due to draining that is executed as a background task. + The draining phase lasts until the RP again reaches the WP, which + moves at a regular (slower) rate. This signals the end of the L3 period + and the RP follows the WP at a rate that the data is available in the DAI + DMA buffer. Implementation note: "history depth" may be updated + on-the-fly during the draining phase if new data is captured in the + meantime. + +4. L4 is a safety margin that can be accommodated in any period of time + defined above. It is explicitly defined to make sure it is included in + the calculation. L4 length depends on: an audio frame size that is + processed by a detector; the amount of detector compute time; the output + audio format; the keyphrase buffer size; etc. 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/apps/pipelines/images/ppl-new.pu b/architectures/firmware/sof-xtos/pipelines/images/ppl-new.pu similarity index 100% rename from developer_guides/apps/pipelines/images/ppl-new.pu rename to architectures/firmware/sof-xtos/pipelines/images/ppl-new.pu diff --git a/developer_guides/apps/pipelines/images/ppl-op-downstream.pu b/architectures/firmware/sof-xtos/pipelines/images/ppl-op-downstream.pu similarity index 100% rename from developer_guides/apps/pipelines/images/ppl-op-downstream.pu rename to architectures/firmware/sof-xtos/pipelines/images/ppl-op-downstream.pu diff --git a/developer_guides/apps/pipelines/images/ppl-operations.pu b/architectures/firmware/sof-xtos/pipelines/images/ppl-operations.pu similarity index 100% rename from developer_guides/apps/pipelines/images/ppl-operations.pu rename to architectures/firmware/sof-xtos/pipelines/images/ppl-operations.pu diff --git a/developer_guides/apps/pipelines/images/ppl-params.pu b/architectures/firmware/sof-xtos/pipelines/images/ppl-params.pu similarity index 100% rename from developer_guides/apps/pipelines/images/ppl-params.pu rename to architectures/firmware/sof-xtos/pipelines/images/ppl-params.pu diff --git a/developer_guides/apps/pipelines/images/ppl-reset.pu b/architectures/firmware/sof-xtos/pipelines/images/ppl-reset.pu similarity index 100% rename from developer_guides/apps/pipelines/images/ppl-reset.pu rename to architectures/firmware/sof-xtos/pipelines/images/ppl-reset.pu diff --git a/developer_guides/apps/pipelines/images/ppl-struct.pu b/architectures/firmware/sof-xtos/pipelines/images/ppl-struct.pu similarity index 100% rename from developer_guides/apps/pipelines/images/ppl-struct.pu rename to architectures/firmware/sof-xtos/pipelines/images/ppl-struct.pu diff --git a/developer_guides/apps/pipelines/images/ppl-task.pu b/architectures/firmware/sof-xtos/pipelines/images/ppl-task.pu similarity index 100% rename from developer_guides/apps/pipelines/images/ppl-task.pu rename to architectures/firmware/sof-xtos/pipelines/images/ppl-task.pu diff --git a/developer_guides/apps/pipelines/index.rst b/architectures/firmware/sof-xtos/pipelines/index.rst similarity index 100% rename from developer_guides/apps/pipelines/index.rst rename to architectures/firmware/sof-xtos/pipelines/index.rst diff --git a/architectures/firmware/sof-xtos/pm-runtime/images/pm-dsp-core-idle.pu b/architectures/firmware/sof-xtos/pm-runtime/images/pm-dsp-core-idle.pu new file mode 100644 index 00000000..96eb713f --- /dev/null +++ b/architectures/firmware/sof-xtos/pm-runtime/images/pm-dsp-core-idle.pu @@ -0,0 +1,28 @@ +scale max 800 width + +participant drv as "Driver" +participant ipc +participant mct as "master-core-task" +participant platform +participant pm_rt as "pm-runtime" + +drv -> ipc : PM_GATE (PPG = 0) +note left: Entering idle... + activate ipc + ipc -> pm_rt : pm_runtime_enable(PM_RUNTIME_DSP, 0) + activate pm_rt + pm_rt -> pm_rt : ref_cnt-- + note right: 1 -> 0 (if there was no other disable calls) + ipc <-- pm_rt + deactivate pm_rt +drv <-- ipc +deactivate ipc + +mct -> platform : platform_wait_for_interrupt() + activate platform + platform -> pm_rt : pm_runtime_is_active(PM_RUNTIME_DSP, 0) + activate pm_rt + platform <-- pm_rt : false + deactivate pm_rt + platform -> platform : d?-idle state if available + deactivate platform diff --git a/architectures/firmware/sof-xtos/pm-runtime/images/pm-dsp-core-init.pu b/architectures/firmware/sof-xtos/pm-runtime/images/pm-dsp-core-init.pu new file mode 100644 index 00000000..ddab1e0a --- /dev/null +++ b/architectures/firmware/sof-xtos/pm-runtime/images/pm-dsp-core-init.pu @@ -0,0 +1,34 @@ +scale max 800 width + +participant drv as "Driver" +participant ipc +participant mct as "master-core-task" +participant platform +participant pm_rt as "pm-runtime" + +mct -> platform : platform_init() + activate platform + + platform -> pm_rt : pm_runtime_disable(PM_RUNTIME_DSP, 0) + activate pm_rt + note right: Make latency of dsp core 0 int handler minimal + pm_rt -> pm_rt : ref_cnt++ + note right: 0 -> 1 + platform <-- pm_rt + deactivate pm_rt + +mct <-- platform +deactivate platform + +drv -> ipc : building topology + ipc -> mct + mct -> platform : platform_wait_for_interrupt() + activate platform + platform -> pm_rt : pm_runtime_is_active(PM_RUNTIME_DSP, 0) + activate pm_rt + platform <-- pm_rt : true + deactivate pm_rt + platform -> platform : d0-idle state + deactivate platform + ipc <-- mct +drv <-- ipc diff --git a/architectures/firmware/sof-xtos/pm-runtime/index.rst b/architectures/firmware/sof-xtos/pm-runtime/index.rst new file mode 100644 index 00000000..3b59c40d --- /dev/null +++ b/architectures/firmware/sof-xtos/pm-runtime/index.rst @@ -0,0 +1,15 @@ +.. _kernel-pm-runtime: + +Power Management +################ + +.. toctree:: + :maxdepth: 1 + + pm-dsp-core + intel/pm-dsp-core-cavs + +API +*** + +:ref:`pm-runtime-api` diff --git a/architectures/firmware/sof-xtos/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 new file mode 100644 index 00000000..4f4c2369 --- /dev/null +++ b/architectures/firmware/sof-xtos/pm-runtime/intel/images/dsp-core-lps-cavs-d0-d0i3-d0.pu @@ -0,0 +1,37 @@ +scale max 800 width + +participant platform +participant lps as "cavs/lps_wait" +participant lps_pg as "cavs/lps\npg thread" +participant pm_rt as "pm-runtime" + +participant lpsram_boot as "Lpsram\nBoot" +participant dsp_rom as "Dsp\nRom" + +-> platform : platform_wait_for_interrupt() + activate platform + platform -> lps : lps_wait_for_interrupt() + activate lps + lps -> lps : platform_pg\nint_handler(D0i3/D0) [SW INT] + activate lps + lps -> pm_rt : pm_runtime_put(DSP, 0) + note right: DSP core PG\nenabled + lps -> lps_pg : platform_pg_task + deactivate lps + activate lps_pg + lps_pg -> lps_pg : configure lpsram boot + lps_pg -> lps_pg : waiti + note right: DSP core is PG-ed + deactivate lps_pg +... + +dsp_rom <- : INT +lpsram_boot <- dsp_rom +lps <- lpsram_boot : platform_pg\nint_handler(D0i3/D0) [SW INT] + activate lps + lps -> pm_rt : pm_runtime_get(DSP, 0) + note right: DSP core PG\ndisabled + lps <-- lps + deactivate lps +platform <-- lps +<-- platform diff --git a/architectures/firmware/sof-xtos/pm-runtime/intel/pm-dsp-core-cavs.rst b/architectures/firmware/sof-xtos/pm-runtime/intel/pm-dsp-core-cavs.rst new file mode 100644 index 00000000..50c61a11 --- /dev/null +++ b/architectures/firmware/sof-xtos/pm-runtime/intel/pm-dsp-core-cavs.rst @@ -0,0 +1,30 @@ +.. _pm-dsp-core-cavs: + +cAVS pm-runtime for DSP core 0 +############################## + +cAVS provides two levels of power savings for DSP cores: + +- clock gating, + +- power gating in idle, important part of *D0i3* state. + +The clock gating is enabled by default. When a DSP core enters idle (calls +``waiti``), the clock signal is gated (note that ``CCOUNT`` is not incremented +in this state, so the only reliable always running clock is the Wall Clock). + +The power gating mechanism is enabled if the *CAVS_LPS* config option is set. +The ``waiti`` entry/exit transitions are driven by the Low Power Sequencer +(LPS). The platform is able to shut the DSP core down on ``waiti`` and power +it up on interrupt. + +The LPS mechanism is used only if ``pm_runtime_is_active()`` returns *false* +meaning that DPS core 0 does not have to be locked in D0 state. + +Implementation note: cAVS simply uses ``pm_runtime_get()`` +/``pm_runtime_put()`` operations to program the power gating control registers +in D0i3 to indicate that DSP core should be powered down/up while +entering/exiting ``waiti``. + +.. uml:: images/dsp-core-lps-cavs-d0-d0i3-d0.pu + :caption: DSP Core 0 idle in D0i3 on cAVS with LPS diff --git a/architectures/firmware/sof-xtos/pm-runtime/pm-dsp-core.rst b/architectures/firmware/sof-xtos/pm-runtime/pm-dsp-core.rst new file mode 100644 index 00000000..0f4597d7 --- /dev/null +++ b/architectures/firmware/sof-xtos/pm-runtime/pm-dsp-core.rst @@ -0,0 +1,41 @@ +.. _pm-dsp-core: + +Power Management for DSP Cores +############################## + +DSP cores are managed by using ``PM_RUNTIME_DSP`` context id and core index to +the pm-runtime functions. + +Some platforms may provide advanced power management of the DSP cores, +including clock gating in idle, full power gating in idle, etc. depending on +how the cores are integrated on that platform. + +An implementation of platform API may provide customized idle entry function +``platform_wait_for_interrupt()`` which might simply call +``arch_wait_for_interrupt()`` or perform more sophisticated power transition to +lower the power consumed when a DSP core is in idle. + +An advanced power transitions may take more time to complete since the DSP core +/ platform has to return from a deeper power state. This additional latency is +not always acceptable, for instance it might be better to complete the initial +platform setup faster, with a quicker IPC request handling. Therefore the +platform initialization code (``platform_init()``) may disable the advanced +pm-runtime of the DSP core by calling ``pm_runtime_disable(PM_RUNTIME_DSP, 0)`` +and wait for the driver to enable it once the initialization is complete by +sending ``PM_GATE (PreventPowerGating=0)`` IPC. The ``PM_GATE`` handler calls +either ``pm_runtime_enable(PM_RUNTIME_DSP, 0)`` or +``pm_runtime_disable(PM_RUNTIME_DSP, 0)`` depending on the value of the ``PPG`` +flag. Note that enable/disable calls are ref-counted as there might be other +internal clients interested in locking the DSP core in the highest state in +order to keep the ``waiti`` entry/exit latency minimal and get better +performance. + +``pm_runtime_is_active(PM_RUNTIME_DSP, 0)`` may be used to query the state of +ref-counter and decide whether transitions to deeper power states are allowed +inside the ``platform_wait_for_interrupt()``. + +.. uml:: images/pm-dsp-core-init.pu + :caption: Pm-runtime: DSP Core 0 initial setup + +.. uml:: images/pm-dsp-core-idle.pu + :caption: Pm-runtime: DSP Core 0 enters idle 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 4ca2bf3f..a7a3cd13 100644 --- a/architectures/host/index.rst +++ b/architectures/host/index.rst @@ -3,4 +3,7 @@ Host Architecture ################# -Host architecture. +.. toctree:: + :maxdepth: 1 + + linux_driver/architecture/sof_driver_arch.rst diff --git a/architectures/host/linux_driver/architecture/images/sof-driver-arch-1.png b/architectures/host/linux_driver/architecture/images/sof-driver-arch-1.png new file mode 100644 index 00000000..4c5ebe9c Binary files /dev/null and b/architectures/host/linux_driver/architecture/images/sof-driver-arch-1.png differ diff --git a/architectures/host/linux_driver/architecture/images/sof-driver-arch-2.png b/architectures/host/linux_driver/architecture/images/sof-driver-arch-2.png new file mode 100644 index 00000000..bf2246ca Binary files /dev/null and b/architectures/host/linux_driver/architecture/images/sof-driver-arch-2.png differ diff --git a/architectures/host/linux_driver/architecture/sof_driver_arch.rst b/architectures/host/linux_driver/architecture/sof_driver_arch.rst new file mode 100644 index 00000000..61f774f9 --- /dev/null +++ b/architectures/host/linux_driver/architecture/sof_driver_arch.rst @@ -0,0 +1,364 @@ +.. _sof_driver_arch: + +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: + :depth: 1 + +Overview +******** + +Audio Driver Architecture +========================= + +The Sound Open Firmware (SOF)-based audio driver stack consists of an architecture-independent SOF driver core, an SOF DSP driver for Intel High Definition Audio (HD-Audio) platforms, an ALSA System-on-Chip (ASoC)-compliant audio codec driver, and a hardware-specific machine driver. + +.. image:: images/sof-driver-arch-1.png + +Sound Open Firmware +=================== + +Sound Open Firmware (SOF) is an open source audio Digital Signal Processing (DSP) firmware infrastructure and SDK. SOF provides infrastructure, real-time control pieces, and audio drivers. A generic SOF subsystem is implemented in Linux as a subsystem of ALSA ASoC. + +.. image:: images/sof-driver-arch-2.png + +ALSA and ASoC +============= + +The Advanced Linux Sound Architecture `(ALSA) `_ provides audio and MIDI functionality to the Linux operating system. The ALSA System-on-Chip `(ASoC) `_ is a subsystem of ALSA. ASoC provides a modular architecture to share audio codec drivers across different SoC implementations, unify the controls provided to applications, and provide a common infrastructure to manage SoC audio component power and clocks. + +Related ALSA Drivers +==================== + +The upstream Linux kernel has a few drivers that are related to the SOF-based audio driver stack described in this document. These drivers include the Intel AZX HD Audio driver (``linux/sound/pci/hda``) and the Intel SST Audio driver (``linux/sound/soc/intel/``). + +The AZX driver is intended to be used with Intel HD Audio PCI hardware when the Audio DSP is disabled (e.g. BIOS configuration). The AZX driver should be used instead of SOF when the DSP is not used. + +The SST Audio implements an ASoC-compliant driver for Intel HD Audio hardware, utilizing the Intel SST firmware. SST is primarily used with older generations of Intel processors for which SOF firmware support is not available. The SST driver and firmware should be used when the DSP is enabled and SOF firmware is not available for the platform. + +Driver Probe +************ + +The probe callback in the SOF PCI/APCI driver is responsible for allocating the platform data that is used to store the machine information including the PCI device ID, name, and the ACPI mach description. For Intel platforms, it uses the ACPI matching tables to determine the correct machine driver to load. The probe callback also sets up the SOF platform driver, initializes the Inter-Process Communication (IPC) to communicate with the DSP, and registers the SOF PCM component driver and the machine driver. Upon completion, it enables the runtime power management for the platform, if supported. + +SOF Platform Driver +******************* + +The SOF 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. The platform driver is responsible for setting up platform-specific ops for the device. The mandatory and optional platform ops are defined in ``struct snd_sof_dsp_ops``. It also describes the chip info the DUT by populating the ``struct sof_intel_dsp_desc`` fields necessary for DSP initialization. + +Platform Driver Probe +===================== + +The SOF platform driver detects the presence of a DSP in the platform by checking the PCI ``class/sub-class/prog-id`` information. It sets up the platform devices (ex: HDA device, dmic device), the DSP Base Address Registers, and initializes the streams and the interrupt vectors. Finally, it initializes the DSP capabilities and enables the DSP processing pipe capability interrupts. + +Firmware Loading and Booting +============================ + +On SKL+ platforms, firmware loading is performed using a dedicated DMA for code loading which is responsible for copying the FW into DSP memory. The DSP cores are powered up in a predetermined sequence and the host driver waits for the appropriate ROM init status to be written into the ROM status register to indicate initialization. This step is attempted a few times until the ROM status registers returns the successful ROM init status. Upon successful completion, the host driver triggers the code loader DMA to start copying the FW into DSP memory and boot it while waiting for the notification from the DSP. When the FW has successfully booted, the DSP sends the firmware-ready IPC message to notify the host. Further details are provided in **IPC Processing**, below. + +IPC Processing +************** + +Introduction +============ + +The SOF Audio DSP firmware uses IPC to communicate with the host. IPC is also used by the preinstalled DSP ROM, so it is used at least to load and start an SOF image. During that phase, the host communicates with the DSP ROM. Once ROM initialization is complete and the SOF FW has booted, the consequent IPC is performed with the firmware. + +IPC is bi-directional; messages can be initiated by the host and then acknowledged by the DSP. Similarly, they can be initiated by the DSP and acknowledged by the host. To indicate the direction of the communication, terms **initiator** and **target** are used. + +After SOF completes its boot process, it informs the host that it is ready for operation. Prior to receiving this message from the DSP, the mailbox offsets are not configured. Therefore, the message is read out from the DSP-to-host mailbox configured in the PCI mailbox BAR. Once read out, the message is parsed to determine the exact layout of all the IPC mailbox buffers. After that, the host sends further IPC messages to perform DSP configuration and initialization. + +At run-time, IPC is used for streaming control and buffer management, as well as for firmware traces. + +IPC Hardware Implementation +=========================== + +On most systems, the DSP and the host CPUs can access the same memory, such as where the DSP is implemented as a PCI device on the host system. On other systems, the DSP is implemented as a stand-alone device, connected to the host by a serial bus such as SPI. + +Intel IPC +========= + +At the hardware level, IPC support is implemented using a set of doorbell +registers and mailbox buffers. Details of the implementation can vary between +architectures. In general, sending an IPC message and replying to it involves +the following steps: + +#. If the IPC message is supposed to contain a payload, which is almost always the case with SOF, the initiator first copies the payload to the respective mailbox buffer. +#. The initiator sets a BUSY bit in an initiator-side IPC register, which then sets a BUSY bit on the target side. +#. If configured, this can also generate an interrupt on the target side. +#. When the target completes processing the received message, it clears the BUSY bit on its own side. This is then reflected to the initiator side, where as a result the BUSY bit is cleared and the DONE bit is set. +#. Setting the DONE bit can also generate an interrupt on the initiator side. +#. The initiator processes the reply from the target and clears the DONE bit. + +SOF on both the host and the DSP serializes the sending of their IPC messages. Therefore, a new message cannot be initiated before the target has finished processing the previous one. However, both the host and the DSP can initiate their messages simultaneously. This cannot lead to a race because both the host and the DSP have separate target and initiator IPC registers. + +.. note:: The IPCCTL register is common for target and initiator operations + and is used to mask and unmask BUSY and DONE interrupts. Therefore, in + theory, a race is possible where one context would try to mask or unmask + one of the bits (e.g. BUSY) while a different context, running on a + different core, would try to mask or unmask the other bit (DONE). This + can lead to inconsistent register contents. To avoid this, the software + has to make sure to lock the read-modify-write operations on the IPCCTL. + +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 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 +======= + +Information on this subject matter is forthcoming. + +IPC Messages +============ + +IPC messages are divided into several groups: global reply, topology, power management, component, stream, DAI, trace, and a separate "firmware ready" message. Multiple messages can also be grouped into a message that belong to a compound group. For all IPC message definitions, see ``include/sound/sof/header.h``. Most messages are sent by the host to the DSP; only the following messages are sent by the DSP to the host: + +- firmware ready: sent only once during initialization +- trace: optional, contains firmware trace data +- position update: only used if position data cannot be transferred in a memory window or if forced by the kernel configuration + +PCM Driver +********** + +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. + +Topology Loading +================ + +The SOF PCM component probe invokes ``snd_sof_load_topology()`` to load the topology binary and triggers the parsing and loading of all the defined components. The topology operations pertinent to the loading/unloading of the various topology components are defined in ``struct snd_soc_tplg_ops`` in ``topology.c``. The topology parser invokes these callbacks to perform driver-specific loading operations for each component/widget. The load callback for each type of component in topology performs two main functions: + +#. Parse the component specific tokens associated with the component and populate the IPC structure. +#. Send the IPC to the DSP to set up, configure, and link the components. + +The unload callback is responsible for freeing the memory associated with the component and remove it from the list of components. Currently, the SOF driver supports loading only one topology file during boot up. This might be extended in the future to support multiple topologies that can be dynamically loaded/unloaded at runtime. + +The topology file also defines the IO callbacks for the kcontrols supported by the SOF topology, namely mixer, enum, and byte controls. + +Kcontrol IO +----------- + +The kcontrol IO callbacks are all defined in ``control.c``. The three types of kcontrol supported by SOF are: + +#. Volume: The volume kcontrol put callback is responsible for translating the user setting for volume level to the appropriate dB value and sending the IPC to the DSP. The get callback reads the volume dB value set in the DSP and determines the appropriate user space setting. +#. Enum: The enum put callback reads the user set value of the enum kcontrol and sends the IPC to the DSP to set the corresponding value in the FW. The get callback reads the enum value from the DSP and updates the user space setting. +#. Bytes: The byte control put callback is used for passing binary data from the user to the DSP FW. Depending on the size of the binary data being sent, the driver splits the data across multiple IPC messages. The FW is responsible for consolidating the data at the other end when the last segment of the data has been received from the host. The get callback gets the binary data from the DSP and passes it to the user space. As with the put callback, this is accomplished either in a single IPC or multiple IPCs, depending on the size of the binary data being read. + +Stream Management +================= + +The SOF PCM driver handles all stream control operations initiated by ALSA such as pcm open, close, hw_params, and trigger start/stop. It includes the code for the generic PCM operations while invoking the platform-specific callbacks to access the hardware. + +PCM open/close +-------------- + +When a pcm is opened, the SOF pcm open ``ioctl`` assigns the stream for the host DMA and the stream is released when the pcm is closed. + +PCM HW Params/Free +------------------ + +During the hw_params step, the SOF PCM driver performs the following operations: + +#. Allocates audio buffer pages. +#. Invokes the platform-specific stream hw_params op. For SKL+ platforms, this involves decoupling host and link DMA engines, resetting the streams, setting up and programming the BDLs, and enabling the DMA interrupts. +#. Sends IPC to the FW to set up the stream params in the DSP. + +The PCM free ``ioctl`` undoes the operations performed during hw_params. + +PCM Trigger +----------- + +When the trigger ``ioctl`` is invoked, the SOF PCM driver invokes the platform-specific stream trigger operation and then sends the corresponding stream trigger IPC message to the DSP. The platform-specific stream trigger operation is responsible for starting/stopping the stream DMA, depending on the trigger command being invoked. + +PCM Prepare +----------- + +The SOF PCM driver does not advertise ``SNDRV_PCM_INFO_RESUME`` in the runtime configuration for pcm streams. This means that upon resuming from system suspend, the streams that were active prior to suspend will be restarted instead of being resumed. Therefore, when restarting the suspended streams, the hw_params needs to set up again before triggering them. The SOF driver utilizes the prepare ``ioctl`` that is invoked upon resuming to determine if the hw_params needs to set up again or not. + +Power Management +**************** + +Overview +======== + +The SOF framework implements the standard Linux kernel power management interface for devices. The SOF core exports the following standard methods: + +- snd_sof_runtime_suspend() +- snd_sof_runtime_resume() +- snd_sof_suspend() +- snd_sof_resume() + +On Intel HDA platforms, the PCI device registered in ``linux/sound/soc/sof/sof-pci-dev.c`` uses the above exported symbols to fill the Linux PM struct ``dev_pm_ops``. + +SOF is configured to support both system sleep and runtime power management. In a typical configuration, the SOF device is runtime-suspended if no ALSA PCM streams are active and no ALSA mixer controls (kcontrols) are used by user space applications. Currently for Intel platforms, the only two power states supported for the DSP are D0 (DSP is on) and D3 (DSP is powered off). + +Suspend Flow +============ + +- Firmware trace is released (if enabled). +- Debugfs state is cached (if enabled, affects debugfs nodes linked to DSP memory that will lose its state in suspend). +- Context-save IPC (SAVE_CTX) message is sent to firmware to notify the DSP of upcoming D3 entry. +- DSP-specific suspend flow is run. + + - On the Intel HDA; this involves logic to reset the HDA controller, disable IRQs, and power down the DSP cores. + - Runtime and system suspend flows have their own code paths. + +Resume Flow +=========== + +- DSP specific resume flow. + + - On Intel HDA, this involves logic to take the HDA controller out of reset, power up the DSP cores, and enable IRQs. + - Runtime and system resume flows have their own code paths. + +- Firmware boot +- Firmware trace is re-enabled (if configured). +- Existing PCM pipelines are restored to the firmware, using cached data maintained in the SOF driver (see sof_restore_pipelines()). +- Kcontrol values are restored from the cached data. +- Resume is completed by notifying the firmware with the Context Restored (CTX_RESTORE) IPC message. + + +Interaction with Codec Drivers +============================== + +The audio codec drivers (compliant with the ALSA ASoC framework) are created as children of the SOF platform device in the Linux device hierarchy. While the codec drivers (located typically under ``linux/sound/soc/codecs/``) manage their power flows independently, the parent-child relationship guarantees ordering between SOF platform device and the codecs. For suspend, the codecs are suspended before the SOF platform device and, similarly for resume, the platform driver is resumed first and then the codec driver. + +Intel Drivers +************* + +Intel HDA SOF DSP Platform Driver +================================= + +SOF implementation for Intel platforms is performed by the DSP Platform drivers. A platform driver implements the generic SOF ``struct snd_sof_dsp_ops`` interface, including functions such as doorbell, IPC messages send and receive, firmware load, and power up/down. The platform implements these methods for a given hardware target. The Intel platform drivers are located in the ``linux/sound/soc/sof/intel/`` folder of the Linux kernel tree. + +Intel HDA DSP Driver for CNL/CML/WHL +==================================== + +The hardware interface for the Cannon Lake, Comet Lake, and Whiskey Lake platforms are defined in the ``linux/sound/soc/sof/intel/cnl.c`` file. For simplicity, all three platforms will be addressed with the CNL acronym in this section. This file defines the DSP ops required for initializing the SOF driver. Most of the DSP ops for the CNL are shared with the other Intel HDA platforms such as APL. The key changes in the CNL DSP driver are the doorbell registers and the corresponding IPC IRQ implementation. + +Intel Machine Drivers +===================== + +The ALSA SoC Layer (ASoC) includes machine drivers. A machine driver glues together various software components (e.g. drivers for codecs, platforms, and digital audio interfaces), describes the relationships between the components, and registers the result as an ALSA sound card to the kernel. A machine driver can be generic, handling a family of similar systems, or can be very specific, targeting a single product. + +A set of machine drivers is included in the Linux kernel and provides support for a variety of systems with the Intel Audio DSP. These drivers are located in ``linux/soc/intel/boards``. The generic SOF HD-Audio machine driver (``skl_hda_dsp_generic.c``) can handle any system that meets the following criteria: + +- HDMI/DP codec in Intel Graphics +- Optional: 1 external HDA codec +- Optional: 1 to 4 digital microphones directly connected to PCH (not via codec) + +If the system has any I2S audio codecs or MIPI SoundWire codecs, the generic HD-Audio machine driver cannot be used and a dedicated machine driver is required instead. + +.. note:: Some existing machine drivers were previously developed for Intel closed source audio firmware (SST firmware, Intel® Smart Sound Technology). The SOF platform driver works with the existing machine drivers and requires no changes. The one big difference, though, is that the SOF PCM driver ignores the FE DAI links defined in the machine driver and overrides them with the ones defined in the SOF topology. + +Support for High Definition Audio (HD-Audio) +******************************************** + +Generic HD-Audio Support +======================== + +The Intel HD Audio controller is the standard audio host controller widely adopted in the PC platform; the industry standard Intel HD Audio driver software is available for Linux-based OSs. This driver is often referred to as the legacy HD-Audio driver. HDA DMA is used to transmit data between the host memory and the HD-A bus, and then to the external HDA codecs. + +On Intel’s platforms after Skylake (SKL+ platforms), the HDA controller is converged with the Audio DSP, and the HDA DMA is split into two parts, the host DMA and the link DMA. The host DMA is used to transmit data between the host memory and DSP memory so data can be processed by DSP firmware. The link DMA is used to transmit data between the DSP memory and the ``HDA/iDisp/I2S/SoundWire`` bus (and then to the ``HDA/HDMI/I2S/SoundWire`` codecs). The SOF driver plus firmware can support this HDA DSP-converged architecture. + +In the Linux ALSA framework, use of the audio DSP is optional. The common HDA library (hdac library, in ``sound/hda/``) is designed for both legacy HDA and HDA via Audio DSP support. It implements the HDA framework-level support, including the HDA bus, the HDA controller, and the HDA stream management. + +In SOF, the HDA driver (``sound/soc/sof/intel/hda*.c``) uses the hdac library to initialize the HDA bus and controller, probe codecs, and add SOF-specific stream management. Please note that HDA controller initialization and stream management are mandatory for Intel SKL+ platforms even if no HDA/HDMI-codec support is required, because the host DMA and stream control registers are part of HDA controller. + +The Legacy HD-Audio driver and SOF driver can coexist in one Linux distribution. The ``snd-intel-dspcfg`` kernel driver implements logic to select the correct driver based on ACPI table contents and platform capabilities detected at runtime. For example, if no specific configuration is defined in ACPI tables and digital microphones are directly attached to the PCH (Intel Platform Control Hub), an audio DSP is required and thus the SOF driver is chosen automatically. + +HD-Audio Codec Support +====================== + +In ASoC, the HD-Audio codec is implemented in ``hdac_hda.c`` in the ``soc/codec`` directory. It reuses the legacy HD-Audio codec driver and implements the features required by ASoC, such as registering the audio codec component driver, dapm routes, and codec dai operators. Three dai links are supported: Analog, Digital, and Alt Analog codec dai. Since power management is implemented in the legacy hda codec driver, there is no PM function in this codec driver. + +Display Audio Support +===================== + +SOF also supports the Intel i915 audio codec driver. The Intel HDMI audio codec driver supports HDMI audio, Single Stream Transport (SST) Display Port (DP) audio, and Multi Stream Transport (MST) DP audio. It fully supports 3+ PCM playback streams; it does not support capture streams. + +When an HDMI/DP display with audio support is connected, it is attached to a free ALSA PCM node from the pool of nodes reserved for HDMI. The status of HDMI/DP PCM connections is exposed via the ALSA mixer card controls **HDMI/DP,pcm=X Jack**, where X is the PCM device number. When a connection is detected, another ALSA mixer PCM control, **name='ELD',device=X**, describes the connected monitor. This data is formatted as ELD data (**EDID Like Data**, where EDID is Extended Display Identification Data), as defined in the `HDA `_ specification. + +Starting with Linux kernel version 5.5, HDMI/DP audio is implemented with an architecture that is similar to other HDA codecs. Implementation of the HDMI/DP codec is in ``snd-hda-codec-hdmi`` (``sound/pci/hda/patch_hdmi.c``). + +In older versions of Linux, a dedicated codec driver was used (``sound/soc/codec/hdac_hdmi.c``) but is now deprecated. + +Dependency on Intel Graphics Driver (i915) +------------------------------------------ + +The HDMI/DP audio codec is integrated in the graphics card. This means the SOF HDMI/DP audio codec driver directly depends on the Intel i915 graphic driver. + +The graphics driver and the HDMI/DP audio codec driver use the “component” model to handle the upper communication between the graphics driver and the audio driver. The graphics driver is bound to the audio driver as a component. This interface is used to request power, clocks, get notifications of monitor connection changes, and to get access to auxiliary information about the monitor. The main structure that is used in the graphic and audio communication is ``struct drm_audio_component``. Refer to ``drm_audio_component.h`` for more information on the structure. + +The graphics card includes an Audio Power Domain which is dedicated to the audio power setting. Any audio operation on the HDMI/DP audio codec requires the Audio Power Domain to be turned on. After an operation, the audio driver should turn off the Audio Power Domain. The HDMI/DP audio codec clock domain is located in the graphic card. Whenever the audio sample rate/bit rate is changed, the audio driver requires the graphic driver to modify the clock setting correspondingly. + +Audio for DisplayPort Multi-Stream Transport (DP-MST) +----------------------------------------------------- + +The Multi-Stream Transport (DP-MST) feature was first introduced in the DP 1.2 specification. It allows graphics to transfer multiple streams on a single connection. In a typical implementation, the multiplexed stream is terminated at a DP-MST hub which routes the individual streams into separate displays. + +The SOF HDMI/DP audio codec driver handles DP-MST audio streams transparently, and a DP-MST is treated in a similar way as any HDMI or DP-SST stream. + +.. note:: With Linux kernel versions 5.4 and older, the HDMI/DP implementation is using another codec driver and DP-MST interface to user-space is difference. With the old codec implementation, user-space software can determine the connection matrix between the monitors and the DP-MST port though **Pin#n-Port#m Mux** kcontrols in the alsamixer tool. + +Kernel Configuration/Kconfig +**************************** + +Refer to the `README `_ file of the SOF kconfig repository. + +Debug Options +************* + +SOF provides multiple options to enable developers to quickly bring up new platforms and debug errors/crashes that occur during audio test cases. The most notable ones are as below: + +Nocodec Mode +============ + +The no-codec mode is specifically meant for speeding up the process of bringing up SOF on new platforms. This mode enables developers to quickly verify basic audio functionality on the available Digital Audio Interfaces (DAI) on the platform. This is also useful to rule out issues due to potential errors in the codec drivers. + +Debugfs +======= + +SOF exposes several memory windows to the user space through the kernel debugfs filesystem. Developers can read or dump out the contents of these debugfs entries to infer the state of the DSP in case of a panic or a crash. Some of the most useful debugfs entries SOF exposes are mailbox, exception, and trace. + +Firmware Tracing +================ + +The tracing feature in the SOF firmware allows the DSP to send trace messages to the host. This tracing feature fills in for the lack of a printf feature while executing firmware code on the DSP. The host configures and sets up the DMA buffer for receiving the trace messages from the DSP. Once the trace DMA triggers, the DSP periodically initiates a DMA transfer to copy over the trace messages to the host. These messages can then be parsed using the sof-logger utility which prints out the messages in chronological order. + +More information is available in the firmware debuggability sections for :ref:`dbg-traces` and :ref:`dbg-logger`. + +IPC Flooding +============ + +The IPC flooding feature is useful to determine the throughput when sending IPCs from the host to the DSP at a very high rate. It is also useful for exposing race conditions which might cause IPC timeouts to occur. Two available options allow the user to either flood the DSP with a specified number of IPCs or flood the DSP with IPCs for the specified duration. + +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. diff --git a/introduction/images/driver-arch-diag.png b/architectures/images/driver-arch-diag.png similarity index 100% rename from introduction/images/driver-arch-diag.png rename to architectures/images/driver-arch-diag.png diff --git a/architectures/index.rst b/architectures/index.rst index 332bea77..a82a0be0 100644 --- a/architectures/index.rst +++ b/architectures/index.rst @@ -1,15 +1,20 @@ .. _architectures: -Supported Architectures +Architecture ####################### -SOF project supports multiple different architectures. +SOF is intended to run on many different hardware architectures and is therefore +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. -When support for a new architecture is being added, certain interfaces -required by SOF infrastructure must be implemented. +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 - xtensa-dsp/index + firmware/index + + diff --git a/architectures/xtensa-dsp/index.rst b/architectures/xtensa-dsp/index.rst deleted file mode 100644 index f072ce94..00000000 --- a/architectures/xtensa-dsp/index.rst +++ /dev/null @@ -1,12 +0,0 @@ -.. _architecture-xtensa: - -Xtensa DSP Architecture -####################### - -Xtensa DSP architectures supported by the |SOF|. - -.. toctree:: - :maxdepth: 1 - - up/index - smp/index diff --git a/architectures/xtensa-dsp/up/index.rst b/architectures/xtensa-dsp/up/index.rst deleted file mode 100644 index 73cfd240..00000000 --- a/architectures/xtensa-dsp/up/index.rst +++ /dev/null @@ -1,6 +0,0 @@ -.. _architecture-xtensa-up: - -Uniprocessor Architecture -######################### - -Xtensa DSP UP architecture. diff --git a/conf.py b/conf.py old mode 100644 new mode 100755 index 8a2405ac..c64f3493 --- a/conf.py +++ b/conf.py @@ -30,7 +30,15 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = ['breathe', 'sphinx.ext.graphviz', 'sphinxcontrib.plantuml','sphinx.ext.todo'] + + +# 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', 'sphinxcontrib.blockdiag', + 'sphinxcontrib.jquery' +] + graphviz_output_format='svg' graphviz_dot_args=[ @@ -41,11 +49,25 @@ plantuml = 'java -jar ' + os.path.join(os.path.abspath('.'), 'scripts/plantuml.jar') \ + ' -config ' + os.path.join(os.path.abspath('.'), 'scripts/plantuml.cfg') + +# 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: # @@ -57,14 +79,14 @@ # General information about the project. project = u'SOF Project' -copyright = u'2018, 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. @@ -77,12 +99,12 @@ # # 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. # This patterns also effect to html_static_path and html_extra_path -exclude_patterns = ['_build' ] +exclude_patterns = ['_build','.tox' ] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' @@ -110,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, @@ -155,13 +175,34 @@ #numfig_secnum_depth = (2) numfig_format = {'figure': 'Figure %s', 'table': 'Table %s', 'code-block': 'Code Block %s'} +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-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', None), +} + # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". 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. @@ -191,6 +232,9 @@ def setup(app): } breathe_default_project = "SOF Project" breathe_default_members = ('members', 'undoc-members', 'content-only') -breathe_domain_by_extension = { - "h" : "c", -} + +try: + if "tox" not in exclude_patterns: + exclude_patterns.append(".tox") +except: + exclude_patterns = [".tox"] diff --git a/contribute/contribute_guidelines.rst b/contribute/contribute_guidelines.rst index 5647c339..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 @@ -97,7 +96,7 @@ running ``git commit --amend -s``. If you have already pushed your changes to Gi Prerequisites ************* -.. _SOF project website: https://thesofproject.org +.. _SOF project website: https://sofproject.org As a contributor, familiarize yourself with the SOF project, how to configure, install, and use it as explained on the 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/index.rst b/contribute/index.rst index 0aa4cfc8..b03a15e1 100644 --- a/contribute/index.rst +++ b/contribute/index.rst @@ -12,3 +12,7 @@ patches for code, documentation, tests, and more, directly to the project. contribute_guidelines.rst doc_guidelines.rst dox-source-code.rst + process/bug-tracking + process/docbuild + process/abiprocess + linux/development_tree diff --git a/contribute/linux/development_tree.rst b/contribute/linux/development_tree.rst new file mode 100644 index 00000000..46ca8af0 --- /dev/null +++ b/contribute/linux/development_tree.rst @@ -0,0 +1,192 @@ +.. _development_tree: + +Linux SOF drivers +################# + +.. contents:: + :local: + :depth: 3 + +Background +********** + +Linux development is split by subsystems. All SOF contributions are +merged through the sound/system (maintained by Takashi Iwai) and the +sound/soc subsystem (maintained by Mark Brown). + +All SOF patches merged by the two maintainers will be used for +linux-next (as a first pass of integration to detect conflicts with +other subsystems or compilation issues) and eventually merged in the +mainline by Linus Torvalds. + +Instructions for SOF developers +******************************* + +ABI changes +=========== + +One fundamental and non-negotiable premise of Linux kernel development +is "we don't break the userspace." More specifically, users may update +their kernels at any time while keeping the SOF firmware binary and +topology files stored in the root filesystem unchanged. The +expectation is that the SOF Linux driver does not generate any errors +and that audio functionality remains unchanged. + +Conversely, when a capability is introduced in a new firmware release, the +expectation is that the kernel shall be updated as well. In other words, +a new firmware does not need to include any backwards-compatibility +code to interface with an older kernel. + +When the ABI changes, the developer or maintainer shall tag it in +GitHub, and the ABI level change will be recorded in the official ABI +change tracker: + +https://github.com/orgs/thesofproject/projects/2 + +The process for firmware ABI changes is documented in the :ref:`SOF_ABI_changes`. + +When the ABI is not backwards-compatible, Pull Requests on the +kernel side shall include code that deals with older firmware and +topology files. + +Development branch +================== + +All SOF development takes place on the topic/sof-dev branch in the SOF tree: + +``git@github.com:thesofproject/linux.git`` + +Developers are required to submit Pull Requests (PRs) against the +topic/sof-dev branch. The Continuous Integration (CI) runs a set +of static analysis, builds, and on-device testing. + +Two approvers are required for each PR. SOF admins may in some +exceptions use their privileges to merge PRs, such as to restore +functionality and broken builds. + +When a PR is submitted by an SOF admin, another admin must approve that PR. +The PRs are integrated into the SOF tree using the 'rebase-and-merge' method +which keeps the integrated patches in a linear order. + +Rebasing tree +============= + +In addition to the topic/sof-dev branch, the SOF project maintains a +parallel topic/sof-dev-rebase branch. This branch is not intended for +development, but to make upstream contributions easier to manage. +As its name indicates, commit SHA1s in topic/sof-dev-rebase are volatile +and should not be relied on. SHA1s in topic/sof-dev are immutable. + +Upstream merges +=============== + +During Linux development, patches to the ALSA/ASoC cores, dependencies such +as audio codecs, or bug fixes may be contributed by the community. SOF Linux +maintainers will, on a regular basis (typically weekly), merge all upstream +contributions into the SOF tree. + +.. _sof_drv_maintainer_list: + +Development flow +**************** + +SOF Linux maintainers +===================== + ++---------------+-------------------+---------------+ +| Intel | Pierre Bossart | @plbossart | ++---------------+-------------------+---------------+ +| Intel | Ranjani Sridharan | @ranj063 | ++---------------+-------------------+---------------+ +| Intel | Kai Vehmanen | @kv2019i | ++---------------+-------------------+---------------+ +| NXP | Daniel Baluta | @dbaluta | ++---------------+-------------------+---------------+ + +SOF maintainers process +======================= + +Mirror all SOF patches to topic/sof-dev-rebase +---------------------------------------------- + +This mirroring consists in doing a set of git "cherry-pick" operations +from topic/sof-dev to topic/sof-dev-rebase. Once all development +patches are applied, SOF maintainers will add the relevant +Signed-off-by and Reviewed-by tags. + +In specific cases, incremental patches will be squashed to simplify +upstream reviews, commit messages will be made clearer, and the order of +patches will be changed, but in all cases the intent is that both +topic/sof-dev and topic/sof-dev-rebase provide the same code (as seen +with git diff or diff -r). + +Upstream merge/rebase +--------------------- + +When the two branches are integrated, the SOF maintainer will create +an upstream baseline. This baseline is then merged locally on top of +topic/sof-dev, then pushed as a dedicated PR and run through the CI +tests. The merge may in some cases create conflicts that have to be +resolved locally by the maintainer. Once the PR is deemed suitable for +integration, the maintainer will use a 'Commit merge' operation (in +contrast to the 'rebase-and-merge' used for development). + +In parallel, the topic/sof-dev-rebase branch is rebased on top of the +same baseline, and again compared to the topic/sof-dev branch. After +the two separate operations of merge and rebase on the two branches, +these two branches should again be identical. The net effect of the +rebase is that all patches already integrated by ALSA/ASoC maintainers +'disappear.' In other words, comparing sof-dev with sof-dev-rebase +shows all patches not currently merged upstream. This includes a limited +number of infrastructure changes that will never be merged upstream +such as github's CODEOWNERS file. + +Upstream contributions +---------------------- + +The SOF maintainer generates patch sets and sends them with a cover +to the alsa-devel mailing list, with the maintainers in Cc:. In most +cases the patches are approved without issues, but the ALSA/ASoC +maintainers or members of the community may provide feedback and +request some changes. In those cases, the changes are applied on +topic/sof-dev, then mirrored and squashed on topic/sof-dev-rebase, and +submitted again. Under no circumstances should the SOF maintainer handle +changes to the topic/sof-dev-rebase directly. + +Exceptions +---------- + +In very specific cases, such as for HDMI-related patches, it might be easier +for an SOF developer to submit the patches directly to alsa-devel. By +default, though, the process is that all patches are first submitted +to the SOF GitHub, CI-tested. Only when maintainers provide a written +agreement should developers submit SOF-related patches directly to the +alsa-devel mailing list. + +To avoid disrupting the development and rewriting its history, all +upstream patches are integrated using the "Merge commit" option. + +Development summary +******************* + +:: + + +----reject-----------+ +--------merge----------------+ + | | | | + v | v | + +----+------+ +-----+-------+ +------+--------+ +--------+----------+ + | developer +------->+ SOF reviews +--ok-->+ topic/sof-dev | +-+ upstream baseline | + | PR | | CI tests | | | | | | + +-----------+ +-----+-------+ +------+--------+ | +---------+---------+ + | | | ^ + | +--rebase-+ | + | | | ALSA maintainers ok + | | v | + | +----------v--------+--+ +--------+----------+ + | | topic/sof-dev-rebase +-email-->+ alsa-devel | + | | | | mailing list | + | +----------------------+ +--------+----------+ + | ^ + | | + | | + +-----------------direct path (exceptions)------------+ diff --git a/contribute/process/abiprocess.rst b/contribute/process/abiprocess.rst new file mode 100644 index 00000000..91308bb1 --- /dev/null +++ b/contribute/process/abiprocess.rst @@ -0,0 +1,79 @@ +.. _SOF_ABI_changes: + +SOF ABI Change Process +###################### + +SOF ABI definitions +******************* + +The SOF ABI consists of public structs used in host-FW communication +defined in: + +- src/include/kernel/ +- src/include/ipc/ +- src/include/user/ + +SOF ABI versioning is defined in firmware source code documentation: +:git-sof-mainline:`src/include/kernel/abi.h` + +Change process +************** + +When a firmware change requires extending or modifying the public +SOF ABI, the developer must go through the ABI change process as defined +in this section. The developer must drive this process, contact the +stakeholders, request reviews (and re-reviews when needed) and coordinate +with the driver maintainers. + +The main steps of the process are depicted in the following +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`_. + +.. uml:: images/abiprocess.pu + :caption: ABI process state diagram + +When the ABI change is not backwards-compatible, Pull Requests on the +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 +******************** + +TSC +--- + +Approval from an SOF :ref:`tsc` member is needed for all ABI changes. + +SOF driver +---------- + +Linux driver team approval for changes can be granted by any member of the +SOF Linux driver maintainer team. The current list of members is maintained +in :ref:`sof_drv_maintainer_list`. diff --git a/contribute/process/bug-tracking.rst b/contribute/process/bug-tracking.rst new file mode 100644 index 00000000..824d9d17 --- /dev/null +++ b/contribute/process/bug-tracking.rst @@ -0,0 +1,262 @@ +.. _bug_tracking: + +Bug Tracking +############################ +Bug type of issues have a label |label-bug|. + + +.. |label-bug| image:: images/label-bug.png + :scale: 70 + +GitHub issues only have 2 states: open, closed. So *labels* are defined +to assist SOF bug tracking. + +.. contents:: + :local: + :depth: 3 + +Life Cycle of a Bug +********************* +The life cycle of a bug is also the workflow for bugs. Here is a graphic +representation of this life cycle. + +.. image:: images/bug-life-cycle.png + :scale: 80 + +Labels +******** +Please find the labels from https://github.com/thesofproject/sof/labels. + +* *Solution*, *priority* and *platform* labels are common for SOF + firmware, linux kernel driver and tool repositories. + +* *Branch* labels are repository-specific. + +Solution Labels +---------------- +Usually a developer will fix a bug by submitting pull requests. This +is the default solution and so doesn't any solution label. + +Otherwise, **developers** need to add a label |label-invalid|, +|label-duplicate| or |label-won't-fix| to indicate the solution with +justication. + +.. |label-invalid| image:: images/label-invalid.png + :scale: 70 + +.. |label-duplicate| image:: images/label-duplicate.png + :scale: 70 + +.. |label-won't-fix| image:: images/label-will-not-fix.png + :scale: 70 + +Label |label-verified| is only added by the **bug scrub owner** after +reviewing the solution and feedback from QA and bug reporter. + +.. |label-verified| image:: images/label-verified.png + :scale: 70 + + +Priority Labels +----------------- +**Bug scrub owner** should use them to set priority to a bug according +to its impact. + +.. image:: images/label-priorities.png + :scale: 50 + + +Plaform and Branch Labels +---------------------------- +Used by **QA** and **bug reporter**. + +*Platform* labels are used to specify a platform or multiple platforms on +which a bug is observed, e.g. |label-byt|, |label-apl|, |label-glk| ... + +.. |label-byt| image:: images/label-platform-byt.png + :scale: 70 + +.. |label-apl| image:: images/label-platform-apl.png + :scale: 70 + +.. |label-glk| image:: images/label-platform-glk.png + :scale: 70 + +*Branch* labels are used specify a branch or multiple branches on which +a bug is observed, e.g. |label-branch-v1.2|, |label-branch-glk|, +|label-branch-master| ... + + +.. |label-branch-v1.2| image:: images/label-branch-v1-2.png + :scale: 70 + +.. |label-branch-glk| image:: images/label-branch-glk.png + :scale: 70 + +.. |label-branch-master| image:: images/label-branch-master.png + :scale: 70 + +.. note:: + *Platform* labels should always be applied. + + *Branch* labels are usually only applied when the branch is not + the default branch for developing/release on the platform. + + **QA** should *update (add/remove)* platform and branch labels + according to texample-trace-point.pnghe latest bug status. + +Other optional Labels +----------------------- + +Two optional labels can be used to call for attention. + +* |label-blocked| - Blocked by some dependency, whichh applies to either + feature implementation or bug reproduction. + +* |label-need-info| - Further information is requested. + +.. |label-blocked| image:: images/label-blocked.png + :scale: 70 + +.. |label-need-info| image:: images/label-need-info.png + :scale: 70 + +How to Report a Bug +******************** +Please +`create a issue `_ +and apply label |label-bug|. + +And please provide the following information: + +* Title + * The title should be a clear and concise summary of the bug. + + * The title must be unique and descriptive. Bad examples are + "ipc timeout" and "topology failed to load". Ideally the title + should contain keywords from the kernel, firmware, or user space + error message. + + * The title should also contain a prefix indicating the area of + failure e.g. "ipc:", "topology:", "pipeline:" + +* Environment + * Branch name and commit hash of 3 repositories: sof (firmware), + linux (kernel driver) and soft (tools & topology). + + * Name of the topology file + + * Name of the platform(s) on which the bug is observed. + + * Reproducibility Rate. If you can only reproduce it randomly, + it's useful to report how many times the bug has been reproduced + vs. the number of attempts it’s taken to reproduce the bug. + +* Steps to reproduce + * The steps must be precise. And please help to narrow down the steps. + + * Please number the steps from beginning to end so developers can + easily follow through by repeating the same process + +* Expected Result + * Describe what the user should expect. + +* Actual Result + * In contrast to the expected behavior, describe what currently happens. + +* Proof + * Please paste the relevant *dmesg* and *firmware logger data* to the + comment box. The pasted data should contain the actual crash or + error but also the conditions prior to the bug, i.e. also copy the + 10 lines before the crash. + + For firmare boot failure, the pasted dmesg must include the + *trace point* which indicates the progress of firmware boot process: + + |trace-point| + + * Entire kernel message and firmware logger text should also be + attached for reference. + + * If you cannot hear sound for playback or capture, please attach + your amixer settings. If there is a mixer setting seems wrong, + please paste the relevant amixer item in the comment box. + + * For audio quality issues (eg. noise, glitch sound and distortion + etc), it's helpful to + + * play/capture a sine wave, attach the captured wave file with + quality issue. + + *note:* You can + use `Audacity `_ + to `generate a sine wave `_. + Here is the screenshot of a sine wave: + |sine-wav| + + * share the parameters of the sine wave: frequency, sample rate, + format and number of channels. + + * share the waveform screenshot where the glitch/distortion happens + shown by Audacity (> 10ms). + + Here is an example of a sine wave with glitch sound: + |sine-with-glitch| + + Please also zoom in to show the start of the glitch sound, + |start-of-glitch| + + and the end of the glitch. + |end-of-glitch| + +.. |trace-point| image:: images/example-trace-point.png + :scale: 75 + +.. |sine-wav| image:: images/audacity-clean-sine-wave.png + :scale: 75 + +.. |sine-with-glitch| image:: images/audacity-sine-wave-with-glitch.png + :scale: 75 + +.. |start-of-glitch| image:: images/audacity-start-of-glitch.png + :scale: 60 + +.. |end-of-glitch| image:: images/audacity-end-of-glitch.png + :scale: 60 + +.. note:: + If you have multiple issues, please file them separately so they can + be tracked more easily. + + Please use `markdown `_ + for formatting example commands, code, diffs, patches etc. + +How to Close a Bug +******************** + +* For bugs fixed by pull requests + + *Developers* can use + `keywords `_ + to close one or multiple bugs via pull requests automatically. + + *Developers* can also leave the bug open, and *QA* should close the + bug if it cannot be reproduced after verification. + +* For bugs with label |label-invalid| or |label-won't-fix|, + *develpers* should close them with justification. + +* For bugs with label |label-duplicate|, + please keep the bug open until its duplicate is resolved and closed. + +.. note:: + After the pull request(s) is merged, *developer* should always + **@** *bug reporter* and **@** *QA engineer* who tracks this bug + to verify the solution. + + Usually the right QA engineer is the bug reporter or who updates the + bug status in the comment box. If you don't know who is the QA + engineer, please **@** *bug scrub owner*. + +.. _reStructuredText: http://sphinx-doc.org/rest.html +.. _Sphinx: http://sphinx-doc.org/ diff --git a/contribute/process/docbuild.rst b/contribute/process/docbuild.rst new file mode 100644 index 00000000..06570e03 --- /dev/null +++ b/contribute/process/docbuild.rst @@ -0,0 +1,441 @@ +.. _sof_doc: + +SOF Documentation Generation +############################ + +These instructions will walk you through generating the SOF Project's +documentation and publishing it to https://thesofproject.github.io. +You can also use these instructions to generate the SOF documentation +on your local system. + +Documentation overview +********************** + +The SOF Project content is written using the reStructuredText markup +language (``.rst`` file extension) with Sphinx extensions, and processed +using Sphinx to create a formatted standalone website. As a developer, you +can view this content either in its raw form as ``.rst`` markup files, or you +can generate the HTML content and view it with a web browser directly on +your workstation. + +Read details about `reStructuredText`_, and `Sphinx`_ from +their respective websites. + +The project's documentation contains reStructuredText source files used to +generate documentation found at the http://thesofproject.github.io website. +All of the reStructuredText sources are found in the thesofproject/sof-docs +`repo`_. + +The reStructuredText files are processed by the Sphinx documentation system, +and make use of the breathe extension for including the doxygen-generated API +material. + + +Set up documentation working folders +************************************ + +You must install git to set up the working folders: + +* For an Ubuntu development system use: + + .. code-block:: bash + + sudo apt-get install git + +* For a Fedora development system use: + + .. code-block:: bash + + sudo dnf install git + +* For a Windows development system, download and install Git manually from + the https://git-scm.com/download/win website. + +We use github.io for publishing the generated documentation. The recommended +folder setup for documentation contributions and generation is as follows: + +.. code-block:: console + + thesofproject/ + sof/ + sof-docs/ + +The parent ``thesofproject`` folder is present because we use the +publishing area (``thesofproject.github.io``) later in these steps. It's +best if the ``sof-docs`` folder is an ssh clone of your personal fork of the +upstream project repos (although https clones also work): + +#. Use your browser to visit https://github.com/thesofproject and do a + fork of the ``sof-docs`` repo to your personal GitHub account.) + + .. image:: images/fork-sof-docs.png + +#. At a command prompt, create the working folder and clone the sof-docs + repository to your local computer (and if you have publishing rights, the + thesofproject.github.io repo). If you don't have publishing rights, + can still generate the docs locally but not publish them: + + .. code-block:: bash + + cd ~ + mkdir thesofproject && cd thesofproject + git clone git@github.com:/thesofproject/sof-docs + +#. The documentation of the SOF source code generated by doxygen is referenced and included by the ``sof-docs``. Clone the ``sof`` repository, too: + + .. code-block:: bash + + git clone git@github.com:thesofproject/sof + +#. For the cloned local repos, tell git about the upstream repo: + + .. code-block:: bash + + cd sof-docs + git remote add upstream git@github.com:thesofproject/sof-docs + +#. If you haven't done so already, be sure to configure git with your name + and email address for the signed-off-by line in your commit messages: + + .. code-block:: bash + + git config --global user.name "David Developer" + git config --global user.email "david.developer@company.com" + +Install documentation tools +*************************** + +Our documentation processing has been tested to run with: + +* Python 3.6.3 +* Doxygen version 1.8.13 +* Sphinx version 1.7.5 +* Breathe version 4.9.1 +* docutils version 0.14 +* sphinx_rtd_theme version 0.4.0 + +The SOF documentation makes use of additional Sphinx extensions used for +creating drawings: + +* sphinxcontrib-plantuml +* sphinx.ext.graphviz (included with Sphinx) + +.. note:: The ``scripts/plantuml.jar`` extension uses Java to render the UML + 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: + + .. code-block:: bash + + sudo apt-get install doxygen python3-pip python3-wheel make \ + default-jre graphviz cmake ninja-build + +* For Fedora use: + + .. code-block:: bash + + sudo dnf install doxygen python3-pip python3-wheel make \ + java graphviz cmake ninja-build + +For either Linux environment, install the remaining python-based +tools: + +.. code-block:: bash + + cd ~/thesofproject/sof-docs + pip3 install --user -r scripts/requirements.txt + +.. 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-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/ + +* Python package installer (pip) from https://pip.pypa.io/en/stable/installing/ + +* Doxygen from http://www.doxygen.nl/download.html + +* GraphViz from https://graphviz.gitlab.io/ + +* Ninja from https://github.com/ninja-build/ninja/releases + +* CMake (3.10+) from https://cmake.org/install/ + +* Make - if you do not already have make, install it using MSYS2 from https://www.msys2.org/. Use the following command: + + .. code-block:: bash + + pacman -S make + +.. note:: + Make sure that installed executable files are in your path. If not, + manually add the paths to the PATH variable. + +For Windows, install the remaining python-based tools as previously +described for Linux: + +.. code-block:: bash + + cd \thesofproject\sof-docs + pip3 install --user -r scripts\requirements.txt + + +You are ready to generate the documentation. + +Documentation presentation theme +******************************** + +Sphinx supports easy customization of the generated documentation +appearance through the use of themes. Replace the theme files and do +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. + +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/build_doxygen -GNinja + ninja -C sof/build_doxygen -v doc + # UML and reStructuredText + make -C sof-docs VERBOSE=1 html + +Depending on your system, the last command may need a few minutes to run - +but only the first time. Most of the time is spent generating UML +diagrams. When done, view the HTML output with your browser, starting at +``~/thesofproject/sof-docs/_build/html/index.html`` + +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-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 +*************** + +If you have merge rights to the ``thesofproject repo`` called +``thesofproject.github.io``, you can update the public project documentation +found at https://thesofproject.github.io. + +You must perform a one-time clone of the upstream repo (we publish +directly to the upstream repo rather than to a personal forked copy): + +.. code-block:: bash + + cd ~/thesofproject + git clone git@github.com:thesofproject/thesofproject.github.io + +After you have verified that the generated HTML from ``make html`` looks +good, you can push directly to the publishing site using this command: + +.. code-block:: bash + + make publish + +This will delete everything in the publishing repo's **latest** folder (in +case the new version has deleted files) and push a copy of the +newly-generated HTML content directly to the GitHub pages publishing repo. +The public site at https://thesofproject.github.io will be updated within a +few minutes so it's best to verify the locally-generated html before +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 -C sof-docs clean`` command. + +Installation troubleshooting +**************************** + +In some cases, after you run ``make html``, the documentation processors might return the following errors: + +.. code-block:: console + + Warning: sphinx_rtd_theme missing. Use pip to install it. + Extension error: + Could not import extension breathe (exception: No module named breathe) + Makefile:36: recipe for target 'html' failed + make: *** [html] Error 1 + +The issue could be related to the default policy on Debian-based Linux +distributions (i.e. Ubuntu) that links Python commands to Python 2.7.x. You +can verify this by entering the following steps: + +.. code-block:: bash + + python --version + + Python 2.7.15rc1 + + ll /usr/bin/python + + lrwxrwxrwx 1 root root 9 sie 29 07:36 /usr/bin/python -> python2.7* + +The issue can be resolved by running a dedicated environment with the Python +3.x binary and include its own set of installed Python packages. +Virtualization of the Python environment is recommended as an alternative to: + +* adding an alias setup in ~/.bashrc +* changing the symbolic link (/usr/bin/python) +* modifying the default system behavior using update-alternatives + +Start with installing virtualization support. As a next step, activate the +virtualized environment: + +.. code-block:: bash + + apt-get install python3-venv + python3 -m venv my-sof-env + . ./my-sof-env/bin/activate + python --version + + + Python 3.6.7 + +Verify the Python version and proceed with installing all required +Python packages in the virtualized environment: + +.. code-block:: bash + + pip install sphinx + git clone https://github.com/thesofproject/sof + git clone https://github.com/thesofproject/sof-docs + cd sof-docs/ + pip install -r scripts/requirements.txt + +After the installation is finished, you should be able to generate +documentation by invoking commands listed in **Running the documentation +processors**. + +To deactivate the virtual environment and original Python environment, type: + +.. code-block:: bash + + deactivate + +Further information on how to use lightweight Python virtualization +environments can be found at https://docs.python.org/3/library/venv.html. + +Windows troubleshooting +*********************** + +It is possible that the ``cmake`` command may not be accessible from the MSYS2 shell: + +.. code-block:: console + + cmake -GNinja . + bash: cmake: command not found + +The problem may be due to the MSYS2 PATH missing the cmake installation +folder. If the cmake works correctly from the Win Command Prompt then edit +the msys2_shell.cmd and check if a PATH inherit option is enabled: + +.. code-block:: bash + + set MSYS2_PATH_TYPE=inherit + + +Another issue that may occur is the ``sphinx-build`` command not found: + +.. code-block:: bash + + make html + make: sphinx-build: Command not found + make: *** [Makefile:36: html] Error 127 + +If the above error occurs both in the Win Command Prompt and in the MSYS2 +shell then the python sphinx package needs to be updated: + +.. code-block:: bash + + pip install -U sphinx + +Diagram compilation troubleshooting +*********************************** + +If you are creating a diagram that is using the lastest features of +plantuml, you may encounter the following compilation error: + +.. code-block:: console + + WARNING: error while running plantuml + b'ERROR\n2\nSyntax Error?\nSome diagram description contains errors\n' + +If you excluded syntax errors in the diagram description, one of remaining +possibilities is lack of compatibility with the installed plantuml.jar +version. You can verify it using the following command: + +.. code-block:: bash + + java -jar ./scripts/plantuml.jar -version + +If the installed version of plantuml.jar is missing necessary features, +submit a pull request to the SOF documentation repository with a new one. + + +.. _reStructuredText: http://sphinx-doc.org/rest.html +.. _Sphinx: http://sphinx-doc.org/ +.. _repo: https://github.com/thesofproject/sof-docs diff --git a/contribute/process/images/abiprocess.pu b/contribute/process/images/abiprocess.pu new file mode 100644 index 00000000..e80e41b9 --- /dev/null +++ b/contribute/process/images/abiprocess.pu @@ -0,0 +1,23 @@ +[*] --> NewABIChange: Firmware feature that requires ABI change + +NewABIChange --> RFC: Send RFC Pull Request\ncovering interface changes\n and rationale for the change + +note right of RFC : RFC stage is intended to\navoid wasted effort via early\n engagement with the ABI users + +RFC --> RFCApproved: a) Approve+1 from at least\none Driver and one FW Maintainer,\n b) Owners assigned for FW and driver impl + +NewABIChange --> FWImplementation: Fast path implementation\nonly when no driver impact + +RFCApproved --> FWImplementation: Implementation done, submit as non-RFC PR + +FWImplementation --> ABIClassification: Tag PR for ABI classifier + +ABIClassification --> ABIApproved: TSC member approval and\nABI MAJOR.MINOR classification done + +ABIApproved --> DriverPRCheck: If driver change is needed,\nwait until both sides ready for merge + +DriverPRCheck --> FWChangeMerged: No driver change:\nAfter review and validation ok, merge + +DriverPRCheck --> DrvChangeMerged: After review and\nvalidation ok, merge + +DrvChangeMerged --> FWChangeMerged: After review and\nvalidation ok, merge diff --git a/contribute/process/images/audacity-clean-sine-wave.png b/contribute/process/images/audacity-clean-sine-wave.png new file mode 100644 index 00000000..e99d300a Binary files /dev/null and b/contribute/process/images/audacity-clean-sine-wave.png differ diff --git a/contribute/process/images/audacity-end-of-glitch.png b/contribute/process/images/audacity-end-of-glitch.png new file mode 100644 index 00000000..d6e3417d Binary files /dev/null and b/contribute/process/images/audacity-end-of-glitch.png differ diff --git a/contribute/process/images/audacity-sine-wave-with-glitch.png b/contribute/process/images/audacity-sine-wave-with-glitch.png new file mode 100644 index 00000000..747ac5e0 Binary files /dev/null and b/contribute/process/images/audacity-sine-wave-with-glitch.png differ diff --git a/contribute/process/images/audacity-start-of-glitch.png b/contribute/process/images/audacity-start-of-glitch.png new file mode 100644 index 00000000..068a9105 Binary files /dev/null and b/contribute/process/images/audacity-start-of-glitch.png differ diff --git a/contribute/process/images/bug-life-cycle.png b/contribute/process/images/bug-life-cycle.png new file mode 100644 index 00000000..4eee5669 Binary files /dev/null and b/contribute/process/images/bug-life-cycle.png differ diff --git a/contribute/process/images/example-trace-point.png b/contribute/process/images/example-trace-point.png new file mode 100644 index 00000000..4a360a6b Binary files /dev/null and b/contribute/process/images/example-trace-point.png differ diff --git a/contribute/process/images/fork-sof-docs.png b/contribute/process/images/fork-sof-docs.png new file mode 100644 index 00000000..a050f2c5 Binary files /dev/null and b/contribute/process/images/fork-sof-docs.png differ diff --git a/contribute/process/images/label-blocked.png b/contribute/process/images/label-blocked.png new file mode 100644 index 00000000..2d75b159 Binary files /dev/null and b/contribute/process/images/label-blocked.png differ diff --git a/contribute/process/images/label-branch-glk.png b/contribute/process/images/label-branch-glk.png new file mode 100644 index 00000000..aeff6c9b Binary files /dev/null and b/contribute/process/images/label-branch-glk.png differ diff --git a/contribute/process/images/label-branch-master.png b/contribute/process/images/label-branch-master.png new file mode 100644 index 00000000..e507b9df Binary files /dev/null and b/contribute/process/images/label-branch-master.png differ diff --git a/contribute/process/images/label-branch-v1-2.png b/contribute/process/images/label-branch-v1-2.png new file mode 100644 index 00000000..a431403b Binary files /dev/null and b/contribute/process/images/label-branch-v1-2.png differ diff --git a/contribute/process/images/label-bug.png b/contribute/process/images/label-bug.png new file mode 100644 index 00000000..1adb7ce1 Binary files /dev/null and b/contribute/process/images/label-bug.png differ diff --git a/contribute/process/images/label-duplicate.png b/contribute/process/images/label-duplicate.png new file mode 100644 index 00000000..0f5b8ea2 Binary files /dev/null and b/contribute/process/images/label-duplicate.png differ diff --git a/contribute/process/images/label-invalid.png b/contribute/process/images/label-invalid.png new file mode 100644 index 00000000..11bf0a4a Binary files /dev/null and b/contribute/process/images/label-invalid.png differ diff --git a/contribute/process/images/label-need-info.png b/contribute/process/images/label-need-info.png new file mode 100644 index 00000000..ac21e406 Binary files /dev/null and b/contribute/process/images/label-need-info.png differ diff --git a/contribute/process/images/label-platform-apl.png b/contribute/process/images/label-platform-apl.png new file mode 100644 index 00000000..5f6ac276 Binary files /dev/null and b/contribute/process/images/label-platform-apl.png differ diff --git a/contribute/process/images/label-platform-byt.png b/contribute/process/images/label-platform-byt.png new file mode 100644 index 00000000..2bfea24b Binary files /dev/null and b/contribute/process/images/label-platform-byt.png differ diff --git a/contribute/process/images/label-platform-glk.png b/contribute/process/images/label-platform-glk.png new file mode 100644 index 00000000..8a428ee3 Binary files /dev/null and b/contribute/process/images/label-platform-glk.png differ diff --git a/contribute/process/images/label-priorities.png b/contribute/process/images/label-priorities.png new file mode 100644 index 00000000..c5ba96e9 Binary files /dev/null and b/contribute/process/images/label-priorities.png differ diff --git a/contribute/process/images/label-verified.png b/contribute/process/images/label-verified.png new file mode 100644 index 00000000..caa0a331 Binary files /dev/null and b/contribute/process/images/label-verified.png differ diff --git a/contribute/process/images/label-will-not-fix.png b/contribute/process/images/label-will-not-fix.png new file mode 100644 index 00000000..9fac372d Binary files /dev/null and b/contribute/process/images/label-will-not-fix.png differ 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/apps/components/component-api.rst b/developer_guides/apps/components/component-api.rst deleted file mode 100644 index 2d9b61c4..00000000 --- a/developer_guides/apps/components/component-api.rst +++ /dev/null @@ -1,92 +0,0 @@ -.. _apps-component-api: - -Component API -############# - -Component Device "Constructor" -****************************** - -The following call creates a new component device:: - - struct comp_dev *(*new)(struct sof_ipc_comp *comp); - -This framework calls ``comp_ops::new()`` to create a new instance of the -component, called the component device. All required data objects should be -allocated from the run-time heap (``RZONE_RUNTIME``). - -Note that any component-specific private data is allocated separately and the pointer to that one is connected to the common ``comp_dev`` structure's -`private` field by calling the ``comp_set_drvdata()`` function. In order to retrieve the private data structure in other component routines, use ``comp_get_drvdata()``. - -Parameters should be initialized to their default values. - -Component Device "Destructor" -***************************** - -This framework calls ``free(struct comp_dev *dev)`` to free a component -instance. All data structures previously allocated on the run-time heap are freed: - - void (\*free)(struct comp_dev \*dev); - -.. uml:: images/comp-ops-free.pu - -Setting Audio Stream Parameters -******************************* - -The following call configures a dai object attached to the component device:: - - int (*dai_config)(struct comp_dev *dev, - struct sof_ipc_dai_config *dai_config); - -.. uml:: images/comp-ops-dai-config.pu - -.. note:: It must be implemented by dai components only. - -Setting Parameters & Preparing for Use -************************************** - -The following call sets parameters and prepares the component device:: - - int (*params)(struct comp_dev *dev); - int (*prepare)(struct comp_dev *dev); - -It is called for all pipeline components to configure their audio -parameters. - -Commands -******** - -A handler for the commands coming from the IPC channel:: - - /* COMP_CMD_SET_VALUE - * COMP_CMD_GET_VALUE - * COMP_CMD_SET_DATA - * COMP_CMD_GET_DATA - */ - int (*cmd)(struct comp_dev *dev, int cmd, void *data); - -Triggering State Transition -*************************** - -Trigger:: - - int (*trigger)(struct comp_dev *dev, int cmd); - -Reset -***** - -Reset:: - - int (*reset)(struct comp_dev *dev); - -``pipeline_reset()`` resets the components by calling -``...upstream()``/``...downstream()`` with ``COMP_OPS_RESET`` (see -*Pipelines*). - -Processing Audio Data -********************* - -Processing audio data:: - - int (*copy)(struct comp_dev *dev); - -.. uml:: images/comp-ops-copy.pu diff --git a/developer_guides/apps/components/images/comp-dev-states.pu b/developer_guides/apps/components/images/comp-dev-states.pu deleted file mode 100644 index 69c97b3d..00000000 --- a/developer_guides/apps/components/images/comp-dev-states.pu +++ /dev/null @@ -1,16 +0,0 @@ -hide empty description -[*] -right-> READY : comp_ops.new() - -READY -right-> PREPARE : prepare - -PREPARE --> ACTIVE : start -PAUSED --> ACTIVE : start - -ACTIVE --> PREPARE : stop, xrun -PAUSED --> PREPARE : stop, xrun - -ACTIVE -> PAUSED : pause - -PAUSED --> ACTIVE : release - -PREPARE --> READY : reset diff --git a/developer_guides/apps/components/images/comp-ops-copy.pu b/developer_guides/apps/components/images/comp-ops-copy.pu deleted file mode 100644 index 835ffdaf..00000000 --- a/developer_guides/apps/components/images/comp-ops-copy.pu +++ /dev/null @@ -1,17 +0,0 @@ -participant "pipeline" as ppl -participant "component" as comp - --> ppl : pipeline_task() - activate ppl - ppl -> ppl : pipeline_copy_from_upstream() - activate ppl - ppl -> comp : comp_copy() - ppl <-- ppl - deactivate ppl - ppl -> ppl : pipeline_copy_to_downstream() - activate ppl - ppl -> comp : comp_copy() - ppl <-- ppl - deactivate ppl -<-- ppl -deactivate ppl diff --git a/developer_guides/apps/components/images/comp-ops-dai-config.pu b/developer_guides/apps/components/images/comp-ops-dai-config.pu deleted file mode 100644 index ec6c54b2..00000000 --- a/developer_guides/apps/components/images/comp-ops-dai-config.pu +++ /dev/null @@ -1,17 +0,0 @@ -actor host -participant "ipc/handler" as hdl -participant "/dai" as dai -participant "ipc/ipc" as ipc -participant "audio/" as drv - -host -> hdl : ipc_dai_config(config) - == Configure DAI Instance == - hdl -> dai : dai_get() - hdl <-- dai_instance - hdl -> dai : dai_set_config(dai_instance, config) - hdl <-- dai - == Configure DAI Components who use that DAI == - hdl -> ipc : ipc_comp_dai_config() - loop for each SOF_COMP_DAI, SOF_COMP_SG_DAI - ipc -> drv : dev.drv.ops.dai_config(config) - end loop diff --git a/developer_guides/apps/components/images/comp-ops-free.pu b/developer_guides/apps/components/images/comp-ops-free.pu deleted file mode 100644 index b593ebd6..00000000 --- a/developer_guides/apps/components/images/comp-ops-free.pu +++ /dev/null @@ -1,7 +0,0 @@ -actor host -participant "ipc/ipc" as ipc -participant "audio/" as drv -host -> ipc : ipc_comp_free(comp_id) - ipc -> ipc : ipc_get_comp(id) - ipc -> drv : dev.drv.ops.free() -host <-- ipc diff --git a/developer_guides/apps/components/images/comp-ops.pu b/developer_guides/apps/components/images/comp-ops.pu deleted file mode 100644 index c8d16815..00000000 --- a/developer_guides/apps/components/images/comp-ops.pu +++ /dev/null @@ -1,14 +0,0 @@ -class comp_ops { - new(sof_ipc_comp*) - free() - params() - dai_config() - cmd() - trigger() - prepare() - reset() - copy() - host_buffer() - position() -} -hide comp_ops attributes diff --git a/developer_guides/apps/components/new-comp-guide.rst b/developer_guides/apps/components/new-comp-guide.rst deleted file mode 100644 index 0aa99e16..00000000 --- a/developer_guides/apps/components/new-comp-guide.rst +++ /dev/null @@ -1,81 +0,0 @@ -.. _apps-new-comp-guide: - -Writing a New Component -####################### - -A component may deliver audio processing function to a pipeline running on the DSP. -An instance of the component, called a component device (components are implemented -in the driver-device model), chained with other component devices build an audio -processing path organized as a pipeline. - -Component Driver -**************** - -Every component must implement a driver (see the ``comp_driver``) which is -responsible for creation of the instances by handling *new component* requests -coming from the command handlers. - -The driver must be registered on the system component driver list, by calling -``comp_register(comp_driver *)`` and providing unique component id in order to -receive the requests. - -Each component driver declares its unique ``type`` that is later used by the -uAPI to create a component of that ``type``. It also provides an entry point to -the component ops implementation. - -.. uml:: images/comp-driver.pu - :caption: Component Driver - -Creating a Component Device -*************************** - -When a new component device is requested, system ``comp_new()`` function finds -the driver registered with the requested unique component type and calls -``new()`` function pointed by the registered driver's data in order to -instantiate the device. - -Entry called to create a new component device:: - - struct comp_dev* comp_new(sof_ipc_comp *comp); - -.. uml:: images/comp-new-flow.pu - -Handling the Component Device State -*********************************** - -Utility function ``comp_set_state()`` should be called a component code at -the beginning of its state transition to verify whether the trigger is valid -in the current state and set a new state accordingly to the state diagram. - -.. uml:: images/comp-dev-states.pu - -READY - This is an initial state of a component device once it is created. - -PREPARE - Transition to this state is usually invoked internally by the component's - implementation of the ``prepare()`` handler. - -ACTIVE, PAUSE - Transitions to these states is caused by external trigger passed to the - component's implementation of the ``trigger()`` handler. - -Implementing Component API (comp_ops) -************************************* - -Every component implements ``comp_ops`` API. All functions, except for -``new()`` and ``free()`` return 0 for success, negative values for errors and -1 to stop the pipeline walk operation. - -.. note:: - - Some API functions are mandatory for specific component types only since - the infrastructure code calls them selectively based on the target - component type. - - For instance ``dai_config()`` is called for ``SOF_COMP_DAI`` and - ``SOF_COMP_SG_DAI`` only and there is no point in implementing this handler - in case of a component of any other type. - -.. uml:: images/comp-ops.pu - :caption: Component API diff --git a/developer_guides/apps/index.rst b/developer_guides/apps/index.rst deleted file mode 100644 index 024d79de..00000000 --- a/developer_guides/apps/index.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. _apps: - -Applications -############ - -.. toctree:: - :maxdepth: 1 - - components/index - pipelines/index diff --git a/developer_guides/debugability/coredump-reader/index.rst b/developer_guides/debugability/coredump-reader/index.rst new file mode 100644 index 00000000..1ca6d5d2 --- /dev/null +++ b/developer_guides/debugability/coredump-reader/index.rst @@ -0,0 +1,86 @@ +.. _dbg-coredump-reader: + +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 +function call format either to a file or stdout. + +Coredump-reader usage +********************* + +Usage sof-coredump-reader.py [-h] [-a ARCH] [-c] [-l COLUMNCOUNT] [-v] (--stdout | -o OUTFILE) [--stdin | -i INFILE] + +-h show this help message and exit +-a ARCH determine architecture of dump file; valid archs are: LE64bit, LE32bit +-c set output to be colourful +-l COLUMNCOUNT set how many colums to group the output in +-v increase output verbosity +--stdin input is from stdin +-i INFILE path to sys dump bin +--stdout output is to stdout +-o OUTFILE output is to FILE + + +sof-coredump-to-gdb.sh shows example usage of sof-coredump-reader.py +We read from dump file into sof-coredump-reader.py, then we pipe its output to xt-gdb, which operates on given elf-file. + +.. 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 e3e12e76..ed686043 100644 --- a/developer_guides/debugability/index.rst +++ b/developer_guides/debugability/index.rst @@ -7,3 +7,9 @@ Debugability :maxdepth: 1 traces/index + logger/index + 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 new file mode 100644 index 00000000..17e41d1c --- /dev/null +++ b/developer_guides/debugability/logger/index.rst @@ -0,0 +1,274 @@ +.. _dbg-logger: + +Logger +###### + +The sof-logger is used to print logs delivered from the FW `dma_trace` +mechanism by searching log entries in the `ldc` file (logs dictionary) +that are generated by the *SMEX* (Sof Metadata Extractor) tool. Every entry +declared in the FW is placed in an elf output file (e.g. sof-apl) in the +`.static_log_entries` section in a struct form that is defined in `sof/src/include/sof/trace.h` in the `sof` fw repo. + +The `ldc` file contains `snd_sof_logs_header` and `snd_sof_uids_header` +(defined in smex/ldc.h). `snd_sof_logs_header` is followed by a +`.static_log_entries` section incorporated from the FW elf file (e.g. +sof-apl). `snd_sof_logs_header` contains basic information about the +`.static_log_entries` section such as `base_address` and `data_length`. The +sof-logger works by reading entry parameters values and entries addresses from the FW `dma_trace` mechanism and searching for suitable entries in the +`ldc` file by its address. The `snd_sof_uids_header` struct is followed by +the `.static_uuid_entries` section content from the FW elf file. A single +uuid entry is defined in `sof/src/include/sof/lib/uuid.h` in the `sof` fw +repo. + +Logger usage +************ + +Usage sof-logger + +-h help +-l ldc_file Specify the ldc file (debugging information and strings) +-i in_file Get traces from the in_file instead of from the default "/sys/kernel/debug/sof/etrace" +-o out_file Specify the output file instead of the default stdout +-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 +-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 +-L Hide log location in source code +-f precision Set timestamp precision +-g Hide timestamp +-d Dump ldc information +-F filter Update trace filtering + +Examples +-------- + +- 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 -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 + + .. code-block:: bash + + sof-logger -l ldc_file -v ver_file + +- Get traces from the "/sys/kernel/debug/sof/etrace" file and prints logs to + stdout + + .. code-block:: bash + + sof-logger -l ldc_file + +- Get traces from the "/sys/kernel/debug/sof/etrace" file and prints logs to + the out_file file + + .. code-block:: bash + + sof-logger -l ldc_file -o out_file + +- Get traces from the "/sys/kernel/debug/sof/trace" file and prints logs to + stdout + + .. code-block:: bash + + sof-logger -l ldc_file -t + +- Get traces from the "/sys/kernel/debug/sof/trace" file and prints logs to + the out_file file + + .. code-block:: bash + + sof-logger -l ldc_file -t -o out_file + +- Get traces from stdin and prints logs to stdout + + .. code-block:: bash + + sof-logger -l ldc_file -p + +- Get traces from stdin and prints logs to the out_file file + + .. code-block:: bash + + sof-logger -l ldc_file -p -o out_file + +- Get traces from the trace_dump file and prints logs to stdout + + .. code-block:: bash + + sof-logger -l ldc_file -i trace_dump + +- Get traces from the trace_dump file and prints logs to the out_file file + + .. code-block:: bash + + sof-logger -l ldc_file -i trace_dump -o out_file + +- c flag defines the clock value (in MHz) used to format log timestamps. By + default, the clock value is set to 19.2 (MHz). The below example sets the + clock value to 19.9 (MHz). + + .. code-block:: bash + + sof-logger -l ldc_file -i trace_dump -o out_file -c 19.9 + +- Dump information from the ldc file (such as the ABI version and the uuid + dictionary) to stdout + + .. 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 new file mode 100644 index 00000000..98e6dedc --- /dev/null +++ b/developer_guides/debugability/probes/index.rst @@ -0,0 +1,265 @@ +.. _dbg-probes: + +Probes +###### + +Typically, pipeline for audio data processing contains several components +separated by data buffers; the probe module is a debug feature that allows +for data extraction from (or injection into) these buffers. It aids in +finding audio issues or bugs in audio components with possible data analysis +from each buffer. + +Requirements +************ + +.. _install-tinycompress: + +- Install `tinycompress `_ (crecord tool) + +Enabling Probes +*************** + +.. _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 +============= + +- The Probe module can be enabled under the 'Probe' menu's 'Probes enabled' prompt (``PROBES``) + To edit the ``kconfig`` use this command: + + .. code-block:: bash + + 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. + +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. 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: + +.. code-block:: bash + + echo 0,1,0 > /sys/kernel/debug/sof/probe_points + + IPC4 system: + +.. code-block:: bash + + echo 0,0,0 > /sys/kernel/debug/sof/probe_points diff --git a/developer_guides/debugability/ri-info/index.rst b/developer_guides/debugability/ri-info/index.rst new file mode 100644 index 00000000..6fbf0414 --- /dev/null +++ b/developer_guides/debugability/ri-info/index.rst @@ -0,0 +1,42 @@ +.. _dbg-ri-info: + +sof-ri-info +########### + +sof-ri-info is a python3 script that parses manifests that are included in +the sof binary. It prints extracted metadata in readable form. The output is +manifest-type dependent content. The binary file layout displays when +verbose mode is selected. + +Currently, the following can be parsed: + ++-------------------+------------------+ +| name | signature | ++===================+==================+ +| CSE Manifest | `$CPD` | ++-------------------+------------------+ +| CSS Manifest | | ++-------------------+------------------+ +| ADSP Manifest | `$AM1`, `$AME` | ++-------------------+------------------+ +| Extended Manifest | `$AE1`, `XMan` | ++-------------------+------------------+ + +Examples +******** + +.. note:: + Run ``sof_ri_info.py -h`` to see how to switch to the appropriate display + mode. + +.. literalinclude:: output_headers.txt + :caption: Example of "headers only" mode. + :linenos: + +.. literalinclude:: output_verbose.txt + :caption: Example of "verbose" mode. + :linenos: + +.. literalinclude:: output_full_bytes.txt + :caption: Example of "full bytes" mode - complete content of relevant binary objects is printed out. + :linenos: diff --git a/developer_guides/debugability/ri-info/output_full_bytes.txt b/developer_guides/debugability/ri-info/output_full_bytes.txt new file mode 100644 index 00000000..66880cfe --- /dev/null +++ b/developer_guides/debugability/ri-info/output_full_bytes.txt @@ -0,0 +1,53 @@ +$ python ./sof_ri_info.py --no_colors --full_bytes sof-cnl.ri +SOF Binary sof-cnl.ri size 0x5b000 + + + CSE Manifest ver 0x101 checksum 0xf1 partition name ADSP + + 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 + Exponent size (dwords) 1 + 01 00 01 00 + Signature + 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 0x5b000 + + 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 + + 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 new file mode 100644 index 00000000..8f99f6cb --- /dev/null +++ b/developer_guides/debugability/ri-info/output_headers.txt @@ -0,0 +1,39 @@ +$ python ./sof_ri_info.py --no_colors --headers sof-cnl.ri +SOF Binary sof-cnl.ri size 0x5b000 + + + CSE Manifest ver 0x101 checksum 0xf1 partition name ADSP + + 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 + 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 0x5b000 + + 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 + + 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 new file mode 100644 index 00000000..5b7315c7 --- /dev/null +++ b/developer_guides/debugability/ri-info/output_verbose.txt @@ -0,0 +1,76 @@ +$ 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.ri size 0x5b000 + + + CSE Manifest ver 0x101 checksum 0xf1 partition name ADSP + + 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 + 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 0x5b000 + + 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 + + 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/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 new file mode 100644 index 00000000..3edbbad3 --- /dev/null +++ b/developer_guides/firmware/cmake.rst @@ -0,0 +1,145 @@ +.. _cmake: + +CMake Arguments +############### + +For firmware and unit tests only **TOOLCHAIN** and **ROOT_DIR** +arguments are mandatory. Other arguments are optional. + +For host build, only **BUILD_HOST** switch is needed. + +Firmware & Unit Tests +********************* + +Mandatory arguments for firmware and unit tests builds. + +TOOLCHAIN + Specifies toolchain to use, usually it's prefix to tools that + follow GCC naming convention. Toolchain should contain tools like: + + * -gcc + * -ar + * -objdump + * -objcopy + + There are more tools from GCC-like toolchains that may be used by build + system, but these are used in most cases. + For example toolchain *xtensa-apl-elf*, should have tools xtensa-apl-elf-gcc, + xtensa-apl-elf-ar, etc. + Toolchain has to be in PATH. + + .. code-block:: bash + + # Examples + cmake [...] -DTOOLCHAIN=xt [...] + cmake [...] -DTOOLCHAIN=xtensa-apl-elf [...] + cmake [...] -DTOOLCHAIN=xtensa-cnl-elf [...] + +ROOT_DIR + Path to directory with xtensa core's lib and include. + + .. code-block:: bash + + # Examples + cmake [...] -DROOT_DIR=$CONFIG_PATH/xtensa-elf [...] + cmake [...] -DROOT_DIR=/my-xtensa-newlib/xtensa-root/xtensa-apl-elf [...] + +Firmware +******** + +Optional arguments. Only for firmware. + +MEU_PATH + Path to directory with MEU tool. For example full path to MEU that will + be used, should be `$MEU_PATH/meu` or `$MEU_PATH/meu.exe`. + + .. code-block:: bash + + # Example + cmake [...] -DMEU_PATH=/path/to/meu/installation [...] + +MEU_PRIVATE_KEY + Path to file with key that will be used by meu. + + .. code-block:: bash + + # Example + cmake [...] -DMEU_PRIVATE_KEY=/path/to/meu/private-key.pem [...] + +MEU_OPENSSL + Default: /usr/bin/openssl + Path to OpenSSL binary used by MEU. Usually you should use it only + on Windows. + + .. code-block:: bash + + # 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. + + .. code-block:: bash + + # Example: build unit tests instead of firmware + cmake -DTOOLCHAIN=xt -DROOT_DIR=$CONFIG_PATH/xtensa-elf -DBUILD_UNIT_TESTS=ON [...] + +.. _cmocka-directory-label: + +CMOCKA_DIRECTORY + Path to directory with prebuilt Cmocka library. + Usually you shouldn't use it, because if this argument is not used, then + CMake will build Cmocka automatically for you in build directory. + Cmocka directory should contain include subdirectory with `cmocka.h` header + and lib subdirectory with `cmocka-static.a` library. + + .. code-block:: bash + + # Example + cmake [...] -DCMOCKA_DIRECTORY=/path/to/cmocka-install-apl [...] + +Host Testbench +************** + +Optional arguments. Only for host build. + +BUILD_HOST + Default: OFF, if ON then builds testbench for host. + + .. code-block:: bash + + # Example: build testbench instead of firmware + cmake -DBUILD_HOST=ON -DCMAKE_INSTALL_PREFIX=install [...] diff --git a/developer_guides/firmware/component-tutorial/tut-i-basic-fw-code.rst b/developer_guides/firmware/component-tutorial/tut-i-basic-fw-code.rst new file mode 100644 index 00000000..248814f4 --- /dev/null +++ b/developer_guides/firmware/component-tutorial/tut-i-basic-fw-code.rst @@ -0,0 +1,404 @@ +.. _developer_guides_tut-i: + +Part I - Adding a Component Code to FW +###################################### + +This lesson describes how to add a component code to the FW source tree, with +a minimal Component API implementation and a simple copying function inside. +It also demonstrates how to register the component driver in the FW +infrastructure so that the FW can respond to the *new component* request sent +by the driver and instantiate it. + +For this lesson, the new component is called "the amplifier." The amplifier +is based on a processing component class such as effect. + +Adding Basic Component Code +*************************** + +New Component Type +================== + +First, define a new component type in *src/include/ipc/topology.h*. It is a +unique identifier used while declaring instances of the component as parts of +the topology. More details on the required topology modifications will be +provided in the next part of the tutorial; for now, our focus is on the FW +source code. + +.. note:: + Simple component IDs currently used at the moment will be replaced by + UUIDs in the future to avoid conflict resolutions while integrating + independently developed components. The current implementation requires + you to assign an unoccupied number. The UUIDs assigned to the components + today are used for logging purposes only. + +.. code-block:: c + + enum sof_comp_type { + /* ... + */ + + SOF_COMP_AMP = 1000, + + /* ... + */ + }; + +Identifier for Logging +====================== + +Components use Universally Unique Identifiers (UUIDs) for logging. Refer to +the :ref:`uuid-api` documentation for basic information about UUIDs in FW and +how to generate one for your component. + +The example UUID generated for the amplifier component is +*1d501197-da27-4697-80c8-4e694d3600a0*. + +Add the following declaration at the beginning of the source file: + +.. code-block:: c + + DECLARE_SOF_RT_UUID("amp", amp_uuid, 0x1d501197, 0xda27, 0x4697, + 0x80, 0xc8, 0x4e, 0x69, 0x4d, 0x36, 0x00, 0xa0); + + DECLARE_TR_CTX(amp_tr, SOF_UUID(amp_uuid), LOG_LEVEL_INFO); + +where *"amp"* is the component name that will be printed by the logger tool. +Amplifier's UUID value and its name is stored in the ldc file deployed on the +target system and is used by the logger to resolve and print the name of the +component. The only thing required to "teach" the logger the new component's +name is to update the ldc file along with the FW binary on the target +system. No tool recompilation is required. + +Every component has to define its trace context. It groups UUID to be inserted +into the traces produced by the component for identification purposes, as well +as run-time trace settings like the tracing level, initialized to +``LOG_LEVEL_INFO`` in this example. The trace context is declared using +``DECLARE_TR_CTX()`` macro as ``amp_tr``. + +Basic Component API +=================== + +Create a folder for your component source code in *src/audio*, such as +*src/audio/amp*, and create a new *amp.c* file inside. + +Declare the basic required part of the API for your component using ``struct +comp_driver`` in *amp.c*. To learn more about component instances, or +devices, and their drivers, refer to :ref:`apps-component-overview` and +:ref:`component-api`. + +.. code-block:: c + + #include + + /* ... + */ + + struct comp_driver comp_amp = { + .type = SOF_COMP_AMP, + .uid = SOF_RT_UUID(amp_uuid), + .tctx = &_tr, + .ops = { + .create = amp_new, + .free = amp_free, + .params = NULL, + .cmd = NULL, + .trigger = amp_trigger, + .prepare = amp_prepare, + .reset = amp_reset, + .copy = amp_copy, + }, + }; + + static SHARED_DATA struct comp_driver_info comp_amp_info = { + .drv = &comp_amp, + }; + + static void sys_comp_amp_init(void) + { + comp_register(platform_shared_get(&comp_amp_info, + sizeof(comp_amp_info))); + } + + DECLARE_MODULE(sys_comp_amp_init); + +Note that the ``type`` used for the component driver is set to the +``SOF_COMP_AMP`` which is declared earlier. The ``uid`` used for logging is +initialized by the ``SOF_RT_UUID(amp_uuid)``, where ``amp_uuid`` is declared at +the beginning of the source file. The trace context ``amp_tr`` is associated +with the driver object as well. + +The API declaration is followed by a registration handler attached to the +initialization list by the ``DECLARE_MODULE()`` macro. This is all the +infrastructure needs to know in order to find and create an instance of the +``SOF_COMP_AMP`` component. + +The following operations are currently not implemented: + +* ``params`` - the amplifier will do all the preparations and setup inside + the ``prepare`` handler; this one will not be used. + +* ``cmd`` - a handler to report and receive our custom run-time parameters + will be implemented later in :ref:`amp-run-time-params`. + +Constructor ``amp_new()`` +========================= + +Add the following handler before your API declaration: + +.. code-block:: c + + static struct comp_dev *amp_new(const struct comp_driver *drv, + struct sof_ipc_comp *comp) + { + struct comp_dev *dev; + struct sof_ipc_comp_process *amp; + struct sof_ipc_comp_process *ipc_amp + = (struct sof_ipc_comp_process *)comp; + struct amp_comp_data *cd; + int ret; + + dev = comp_alloc(drv, COMP_SIZE(struct sof_ipc_comp_process)); + if (!dev) + return NULL; + + cd = rzalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM, sizeof(*cd)); + if (!cd) { + rfree(dev); + return NULL; + } + + amp = COMP_GET_IPC(dev, sof_ipc_comp_process); + ret = memcpy_s(amp, sizeof(*amp), ipc_amp, + sizeof(struct sof_ipc_comp_process))); + assert(!ret); + + comp_set_drvdata(dev, cd); + + dev->state = COMP_STATE_READY; + + comp_dbg(dev, "amplifier created"); + + return dev; + } + +The constructor: + +* Allocates the memory, usually in two steps. Both allocations are done from + the SOF_MEM_ZONE_RUNTIME heap that should be used by the application layer + which includes processing components. + + * First, a common context for the device is allocated including some + extensions specific for a component class. In this example, the component + device is based on the ``struct sof_ipc_comp_process``, which is used for + processing components. Component's parameters received from the IPC + request are copied to the allocated space. :cpp:func:`comp_alloc()` used + for the first allocation guarantees that all important parts of the ``dev`` are initialized as well. + + * The second allocation acquires memory for the private data of the + amplifier instance, ``struct amp_comp_data``. This structure contains a + placeholder at the moment. You will redefine it later to store run-time + parameters of the instance. Note how the private data is attached to the + device by calling ``comp_set_drvdata()``. You will use symmetric + ``comp_get_drvdata()`` to retrieve the private data object from the + device object later while implementing other handlers. + + .. code-block:: c + + struct amp_comp_data { + int placeholder; + }; + +* The device state is set to ``COMP_STATE_READY``. To learn more + about the component device state machine, refer to + :ref:`apps-component-overview`. + +Note the ``comp_dbg()`` macro used to log the creation event where ``dev`` is +the first argument that lets the logger resolve the name of the trace source +while processing the log entry. DEBUG level messages are not traced by +default; the trace subsystem has to be reconfigured. The trace system +outputs INFO, WARN, and ERROR messages by default. + +Destructor ``amp_free()`` +========================= + +The destructor frees the memory allocated previously in the ``amp_new()``. + +.. code-block:: c + + static void amp_free(struct comp_dev *dev) + { + struct comp_data *cd = comp_get_drvdata(dev); + + rfree(cd); + rfree(dev); + } + + +State Transition Handler ``amp_trigger()`` +========================================== + +The transition handler just invokes ``comp_set_state()``. No specific +actions are defined in this simple example. + +.. code-block:: c + + static int amp_trigger(struct comp_dev *dev, int cmd) + { + comp_dbg(dev, "amplifier got trigger cmd %d", cmd); + return comp_set_state(dev, cmd); + } + +Stream Parameters Handler ``amp_prepare()`` +=========================================== + +This is where your component can be reconfigured for the stream parameters. + +This example assumes that only one source buffer and one sink buffer are +connected; therefore, only the first item from ``dev->bsink_list`` is +verified. + +Note that in the event that another "prepare" call was previously issued, +the handler returns ``PPL_STATUS_PATH_STOP`` and exits to prevent +propagation of a likely configuration coming from another connected pipeline. + +Add the following handler code before your API declaration: + +.. code-block:: c + + static int amp_prepare(struct comp_dev *dev) + { + int ret; + struct comp_buffer *sink_buf; + struct sof_ipc_comp_config *config = dev_comp_config(dev); + uint32_t sink_per_bytes; + + ret = comp_set_state(dev, COMP_TRIGGER_PREPARE); + if (ret < 0) + return ret; + + if (ret == COMP_STATUS_STATE_ALREADY_SET) + return PPL_STATUS_PATH_STOP; + + sink_buf = list_first_item(&dev->bsink_list, + struct comp_buffer, source_list); + + sink_per_bytes = audio_stream_period_bytes(&sink_buf->stream, + dev->frames); + + if (sink_buf->stream.size < config->periods_sink * sink_per_bytes) { + comp_err(dev, "amp_prepare(): sink buffer size is insufficient"); + return -ENOMEM; + } + + comp_dbg(dev, "amplifier prepared"); + return 0; + } + +Reset Handler ``amp_reset()`` +============================= + +The *reset* handler toggles the device state. It is a good place to add any +instance reset code later. + +.. code-block:: c + + static int amp_reset(struct comp_dev *dev) + { + return comp_set_state(dev, COMP_TRIGGER_RESET); + } + + +Signal Processing Function ``amp_copy`` +======================================= + +This first version of the processing function simply copies input samples to +output and shows how to: + +* Use :cpp:class:`comp_copy_limits` and :cpp:func:`comp_get_copy_limits_with_lock()` + to retrieve information about the number of samples to be processed. + +* Refresh the local data cache with :cpp:func:`buffer_invalidate()` in case + the input data is being provided to the source buffer by a component + running on another core. + +* Iterate over the frames, channels, and samples using the + :cpp:class:`comp_copy_limits` descriptor. + +* Read/write from/to the circular buffers. This implementation assumes both + input and output are signed 16-bit samples; therefore, + :cpp:func:`audio_stream_read_frag_s16()` and + :cpp:func:`audio_stream_write_frag_s16()` are used. You may prepare more + alternatives and use the one suitable for the input/output format obtained + from the ``sink_buf->stream.frame_fmt`` in the ``amp_prepare()`` handler. + +* Update the shared memory containing produced samples with the local data + cache using :cpp:func:`buffer_writeback()` in the event that the output + data is being consumed from the sink buffer by a component running on + another core. + +* Update the buffers' pointers using :cpp:func:`comp_update_buffer_consume()` + and :cpp:func:`comp_update_buffer_produce()` to indicate the data consumed + and produced. + +The ``*dst = *src`` copy operation will be replaced later by amplification. + +Add the following handler code before your API declaration: + +.. code-block:: c + + static int amp_copy(struct comp_dev *dev) + { + struct comp_copy_limits cl; + struct comp_buffer *source; + struct comp_buffer *sink; + int frame; + int channel; + uint32_t buff_frag = 0; + int16_t *src; + int16_t *dst; + + source = list_first_item(&dev->bsource_list, struct comp_buffer, + sink_list); + sink = list_first_item(&dev->bsink_list, struct comp_buffer, + source_list); + + comp_get_copy_limits_with_lock(source, sink, &cl); + + buffer_invalidate(source, cl.source_bytes); + + for (frame = 0; frame < cl.frames; frame++) { + for (channel = 0; channel < sink->stream.channels; channel++) { + src = audio_stream_read_frag_s16(&source->stream, + buff_frag); + dst = audio_stream_write_frag_s16(&sink->stream, + buff_frag); + *dst = *src; + ++buff_frag; + } + } + + buffer_writeback(sink, cl.sink_bytes); + + comp_update_buffer_produce(sink, cl.sink_bytes); + comp_update_buffer_consume(source, cl.source_bytes); + + return 0; + } + +Build Scripts +************* + +Add the following line to *src/audio/CMakeLists.txt* inside the block where +other components' subfolders are specified: + +.. code-block:: cmake + + add_subdirectory(amp) + +Create a new file *src/audio/amp/CMakeLists.txt* and add this line inside: + +.. code-block:: cmake + + add_local_sources(sof amp.c) + +Rebuild the firmware. diff --git a/developer_guides/firmware/component-tutorial/tut-ii-topology.rst b/developer_guides/firmware/component-tutorial/tut-ii-topology.rst new file mode 100644 index 00000000..71252eff --- /dev/null +++ b/developer_guides/firmware/component-tutorial/tut-ii-topology.rst @@ -0,0 +1,247 @@ +.. _developer_guides_tut-ii: + +Part II - Modifying the Topology & Driver +######################################### + +Topology +******** + +Create *tools/topology/m4/amp.m4* and add the following Amp widget definition. +Note the highlighted line containing the definition of the type of your new +processing component. The *Driver* section refers to it later. + +.. code-block:: text + :caption: tools/topology/m4/amp.m4 + :linenos: + :emphasize-lines: 32 + + divert(-1) + + dnl Define macro for example Amp widget + + dnl AMP(name) + define(`N_AMP', `AMP'PIPELINE_ID`.'$1) + + dnl W_AMP(name, format, periods_sink, periods_source, kcontrols_list) + define(`W_AMP', + `SectionVendorTuples."'N_AMP($1)`_tuples_w" {' + ` tokens "sof_comp_tokens"' + ` tuples."word" {' + ` SOF_TKN_COMP_PERIOD_SINK_COUNT' STR($3) + ` SOF_TKN_COMP_PERIOD_SOURCE_COUNT' STR($4) + ` SOF_TKN_COMP_CORE_ID' STR($5) + ` }' + `}' + `SectionData."'N_AMP($1)`_data_w" {' + ` tuples "'N_AMP($1)`_tuples_w"' + `}' + `SectionVendorTuples."'N_AMP($1)`_tuples_str" {' + ` tokens "sof_comp_tokens"' + ` tuples."string" {' + ` SOF_TKN_COMP_FORMAT' STR($2) + ` }' + `}' + `SectionData."'N_AMP($1)`_data_str" {' + ` tuples "'N_AMP($1)`_tuples_str"' + `}' + `SectionVendorTuples."'N_AMP($1)`_tuples_str_type" {' + ` tokens "sof_process_tokens"' + ` tuples."string" {' + ` SOF_TKN_PROCESS_TYPE' "AMP" + ` }' + `}' + `SectionData."'N_AMP($1)`_data_str_type" {' + ` tuples "'N_AMP($1)`_tuples_str_type"' + `}' + `SectionWidget."'N_AMP($1)`" {' + ` index "'PIPELINE_ID`"' + ` type "effect"' + ` no_pm "true"' + ` data [' + ` "'N_AMP($1)`_data_w"' + ` "'N_AMP($1)`_data_str"' + ` "'N_AMP($1)`_data_str_type"' + ` ]' + ` bytes [' + $6 + ` ]' + `}') + + divert(0)dnl + +Add a definition of parameters and specify default values for them (handling +parameters in the FW code is discussed in the next lesson but you prepare a +complete topology upfront). Create *tools/topology/amp_bytes.m4* and add the +following code. + +Note the size of the parameters data and the data highlighted (two 32-bit +number set to 1 to unmute both channels by default, little-endian byte +ordering). The data begins with `struct sof_abi_hdr` content, note the SOF +magic number in line 3 and the ABI version in line 6. The latter must be set +to a version compatible with the SOF stack. + +.. code-block:: text + :caption: tools/topology/amp_bytes.m4 + :linenos: + :emphasize-lines: 5, 11-12 + + # AMP Example - Parameters + CONTROLBYTES_PRIV(AMP_priv, + ` bytes "0x53,0x4f,0x46,0x00,' + ` 0x00,0x00,0x00,0x00,' + ` 0x08,0x00,0x00,0x00,' + ` 0x00,0x00,0x00,0x03,' + ` 0x00,0x00,0x00,0x00,'' + ` 0x00,0x00,0x00,0x00,' + ` 0x00,0x00,0x00,0x00,' + ` 0x00,0x00,0x00,0x00,' + ` 0x01,0x00,0x00,0x00,' + ` 0x01,0x00,0x00,0x00"' + ) + +Add the Amp widget to a playback pipeline. Create a copy of +*tools/topology/sof/pipe-volume-playback.m4* and save it as +*tools/topology/sof/pipe-amp-volume-playback.m4*. Add the definitions +in your copy as highlighted below. + +.. code-block:: text + :caption: tools/topology/sof/pipe-amp-volume-playback.m4 + :linenos: + :emphasize-lines: 14, 16, 43-58, 73-75, 81-86, 96-99, 104 + + # Low Latency Passthrough with volume Pipeline and PCM + # + # Pipeline Endpoints for connection are :- + # + # host PCM_P --> B0 --> Amp -> B1 -> Volume 0 --> B2 --> sink DAI0 + + # Include topology builder + include(`utils.m4') + include(`buffer.m4') + include(`pcm.m4') + include(`pga.m4') + include(`dai.m4') + include(`mixercontrol.m4') + include(`bytecontrol.m4') + include(`pipeline.m4') + include(`amp.m4') + + # + # Controls + # + # Volume Mixer control with max value of 32 + C_CONTROLMIXER(Master Playback Volume, PIPELINE_ID, + CONTROLMIXER_OPS(volsw, 256 binds the mixer control to volume get/put handlers, 256, 256), + CONTROLMIXER_MAX(, 32), + false, + CONTROLMIXER_TLV(TLV 32 steps from -64dB to 0dB for 2dB, vtlv_m64s2), + Channel register and shift for Front Left/Right, + LIST(` ', KCONTROL_CHANNEL(FL, 1, 0), KCONTROL_CHANNEL(FR, 1, 1))) + + # + # Volume configuration + # + + define(DEF_PGA_TOKENS, concat(`pga_tokens_', PIPELINE_ID)) + define(DEF_PGA_CONF, concat(`pga_conf_', PIPELINE_ID)) + + W_VENDORTUPLES(DEF_PGA_TOKENS, sof_volume_tokens, + LIST(` ', `SOF_TKN_VOLUME_RAMP_STEP_TYPE "0"' + ` ', `SOF_TKN_VOLUME_RAMP_STEP_MS "250"')) + + W_DATA(DEF_PGA_CONF, DEF_PGA_TOKENS) + + # Amp Parameters + include(`amp_bytes.m4') + + # Amp Bytes control with max value of 140 + # The max size needs to also take into account the space required to hold the control data IPC message + # struct sof_ipc_ctrl_data requires 92 bytes + # AMP priv in amp_bytes.m4 (ABI header (32 bytes) + 2 dwords) requires 40 bytes + # Therefore at least 132 bytes are required for this kcontrol + # Any value lower than that would end up in a topology load error + C_CONTROLBYTES(AMP, PIPELINE_ID, + CONTROLBYTES_OPS(bytes, 258 binds the control to bytes get/put handlers, 258, 258), + CONTROLBYTES_EXTOPS(258 binds the control to bytes get/put handlers, 258, 258), + , , , + CONTROLBYTES_MAX(, 140), + , + AMP_priv) + + # + # Components and Buffers + # + + # Host "Passthrough Playback" PCM + # with 2 sink and 0 source periods + W_PCM_PLAYBACK(PCM_ID, Passthrough Playback, 2, 0, SCHEDULE_CORE) + + + # "Volume" has 2 source and 2 sink periods + W_PGA(0, PIPELINE_FORMAT, DAI_PERIODS, 2, DEF_PGA_CONF, SCHEDULE_CORE, + LIST(` ', "PIPELINE_ID Master Playback Volume")) + + # "Amp" has 2 sink periods and 2 source periods + W_AMP(0, PIPELINE_FORMAT, 2, 2, SCHEDULE_CORE, + LIST(` ', "AMP")) + + # Playback Buffers + W_BUFFER(0, COMP_BUFFER_SIZE(2, + COMP_SAMPLE_SIZE(PIPELINE_FORMAT), PIPELINE_CHANNELS, COMP_PERIOD_FRAMES(PCM_MAX_RATE, SCHEDULE_PERIOD)), + PLATFORM_HOST_MEM_CAP) + W_BUFFER(1, COMP_BUFFER_SIZE(2, + COMP_SAMPLE_SIZE(PIPELINE_FORMAT), PIPELINE_CHANNELS, COMP_PERIOD_FRAMES(PCM_MAX_RATE, SCHEDULE_PERIOD)), + PLATFORM_HOST_MEM_CAP) + W_BUFFER(2, COMP_BUFFER_SIZE(DAI_PERIODS, + COMP_SAMPLE_SIZE(DAI_FORMAT), PIPELINE_CHANNELS, COMP_PERIOD_FRAMES(PCM_MAX_RATE, SCHEDULE_PERIOD)), + PLATFORM_DAI_MEM_CAP) + + # + # Pipeline Graph + # + # host PCM_P --> B0 --> Amp -> B1 --> Volume 0 --> B2 --> sink DAI0 + + P_GRAPH(pipe-amp-volume-playback-PIPELINE_ID, PIPELINE_ID, + LIST(` ', + `dapm(N_BUFFER(0), N_PCMP(PCM_ID))', + `dapm(N_AMP(0), N_BUFFER(0))', + `dapm(N_BUFFER(1), N_AMP(0))', + `dapm(N_PGA(0), N_BUFFER(1))', + `dapm(N_BUFFER(2), N_PGA(0))')) + + # + # Pipeline Source and Sinks + # + indir(`define', concat(`PIPELINE_SOURCE_', PIPELINE_ID), N_BUFFER(2)) + indir(`define', concat(`PIPELINE_PCM_', PIPELINE_ID), Passthrough Playback PCM_ID) + + + # + # PCM Configuration + + # + PCM_CAPABILITIES(Passthrough Playback PCM_ID, `S32_LE,S24_LE,S16_LE', PCM_MIN_RATE, PCM_MAX_RATE, 2, PIPELINE_CHANNELS, 2, 16, 192, 16384, 65536, 65536) + +Create a copy of your topology in *tools/topology* and replace the +definition of low latency playback pipeline with the one crated in the previous +step. + +.. code-block:: text + :caption: Main topology .m4 file + :linenos: + :emphasize-lines: 3 + + # Low Latency playback pipeline 1 on PCM 0 using max 2 channels of s24le. + # Schedule 48 frames per 1000us deadline on core 0 with priority 0 + PIPELINE_PCM_ADD(sof/pipe-amp-volume-playback.m4, + 1, 0, 2, s24le, + 1000, 0, 0, + 48000, 48000, 48000) + +Driver +****** + +Add a mapping between ``SOF_TKN_PROCESS_TYPE`` set to **"AMP"** +in your m4 topology definition and the ``SOF_COMP_AMP`` defined in the FW code +in lesson 1. Refer to the driver documentation for further details about the +topology mappings location and recompilation of the driver. diff --git a/developer_guides/firmware/component-tutorial/tut-iii-runtime-params.rst b/developer_guides/firmware/component-tutorial/tut-iii-runtime-params.rst new file mode 100644 index 00000000..76248653 --- /dev/null +++ b/developer_guides/firmware/component-tutorial/tut-iii-runtime-params.rst @@ -0,0 +1,197 @@ +.. _developer_guides_tut-iii: + +.. _amp-run-time-params: + +Part III - Adding Run-time Parameter Control +############################################ + +This lesson describes how to add startup and run-time parameters to your component. +You will add a command handler to the "amp" to mute/unmute individual channels. + +Changes in the topology definition are required as well. You will add binary +bytes kcontrol connected to your widget in order to enable parameter transfer +from a user space application, through the driver to the FW running your "amp" +component. + +This simple example defines the parameter blob as two 32-bit integer numbers, +one per channel, where non-zero value causes the channel samples to pass while +zero value "mutes" the channel. + +Changing the FW Code +******************** + +First, change the private data definition to store run-time parameters. + +.. code-block:: c + :emphasize-lines: 2 + + struct amp_comp_data { + int channel_volume[2]; + }; + +Add start-up parameter handling to ``amp_new()``. + +.. code-block:: c + :emphasize-lines: 10-16, 22-23 + + static struct comp_dev *amp_new(struct sof_ipc_comp *comp) + { + /* ... */ + + amp = COMP_GET_IPC(dev, sof_ipc_comp_process); + ret = memcpy_s(amp, sizeof(*amp), ipc_amp, + sizeof(struct sof_ipc_comp_process))); + assert(!ret); + + cd->channel_volume[0] = 1; + cd->channel_volume[1] = 1; + + if (ipc_amp->size == sizeof(cd->channel_volume)) { + memcpy_s(cd->channel_volume, sizeof(cd->channel_volume), + ipc_amp->data, ipc_amp->size); + } + + comp_set_drvdata(dev, cd); + + /* ... */ + + comp_dbg(dev, "amplifier created vol[0] %d vol[1] %d", + cd->channel_volume[0], cd->channel_volume[1]); + + } + +Modify ``amp_copy()`` to pass/mute channels based on your settings. + +.. code-block:: c + :emphasize-lines: 3, 14-17 + + static int amp_copy(struct comp_dev *dev) + { + struct amp_comp_data *cd = comp_get_drvdata(dev); + struct comp_copy_limits cl; + + /* ... */ + + for (frame = 0; frame < cl.frames; frame++) { + for (channel = 0; channel < sink->stream.channels; channel++) { + src = audio_stream_read_frag_s16(&source->stream, + buff_frag); + dst = audio_stream_write_frag_s16(&sink->stream, + buff_frag); + if (cd->channel_volume[channel]) + *dst = *src; + else + *dst = 0; + ++buff_frag; + } + } + +Add the command handlers to report parameters and receive updates. + +First, add the handler to receive parameters. + +.. code-block:: c + + static int amp_cmd_set_data(struct comp_dev *dev, + struct sof_ipc_ctrl_data *cdata) + { + struct amp_comp_data *cd = comp_get_drvdata(dev); + + if (cdata->cmd != SOF_CTRL_CMD_BINARY) { + comp_err(dev, "amp_cmd_set_data(): invalid cmd %d", + cdata->cmd); + return -EINVAL; + } + + if (cdata->data->size != sizeof(cd->channel_volume)) { + comp_err(dev, "amp_cmd_set_data(): invalid data size %d", + cdata->data->size); + return -EINVAL; + } + + memcpy_s(cd->channel_volume, sizeof(cd->channel_volume), + cdata->data->data, cdata->data->size); + comp_dbg(dev, "amplifier new settings vol[0] %d vol[1] %d", + cd->channel_volume[0], cd->channel_volume[1]); + return 0; + } + +Add another one to report parameters back to the host. Note how the +``cdata->data`` (``struct sof_abi_hdr``) is updated. + +.. code-block:: c + + static int amp_cmd_get_data(struct comp_dev *dev, + struct sof_ipc_ctrl_data *cdata, int max_size) + { + struct amp_comp_data *cd = comp_get_drvdata(dev); + + if (cdata->cmd != SOF_CTRL_CMD_BINARY) { + comp_err(dev, "amp_cmd_get_data(): invalid cmd %d", + cdata->cmd); + return -EINVAL; + } + + if (sizeof(cd->channel_volume) > max_size) + return -EINVAL; + + memcpy_s(cdata->data->data, + ((struct sof_abi_hdr *)(cdata->data))->size, + cd->channel_volume, + sizeof(cd->channel_volume)); + cdata->data->abi = SOF_ABI_VERSION; + cdata->data->size = sizeof(cd->channel_volume); + + return 0; + } + +Put everything together as a command handler. + +.. code-block:: c + + static int amp_cmd(struct comp_dev *dev, int cmd, void *data, int max_data_size) + { + struct sof_ipc_ctrl_data *cdata = data; + int ret = 0; + + switch (cmd) { + case COMP_CMD_SET_DATA: + ret = amp_cmd_set_data(dev, cdata); + break; + case COMP_CMD_GET_DATA: + ret = amp_cmd_get_data(dev, cdata, max_data_size); + break; + default: + comp_err(dev, "amp_cmd(): unhandled command %d", cmd); + ret = -EINVAL; + break; + } + return ret; + } + +Attach the handler to your component driver API. + +.. code-block:: c + :emphasize-lines: 7 + + struct comp_driver comp_amp = { + .type = SOF_COMP_AMP, + .ops = { + .new = amp_new, + .free = amp_free, + .params = NULL, + .cmd = amp_cmd, + .trigger = amp_trigger, + .prepare = amp_prepare, + .reset = amp_reset, + .copy = amp_copy, + .cache = NULL + }, + }; + + +Binary Bytes KControl in Topology +********************************* + +An example of data section for component parameters is presented as +*amp_bytes.m4* content in the previous part of the tutorial. diff --git a/developer_guides/firmware/component-tutorial/tut-intro.rst b/developer_guides/firmware/component-tutorial/tut-intro.rst new file mode 100644 index 00000000..8c0417ee --- /dev/null +++ b/developer_guides/firmware/component-tutorial/tut-intro.rst @@ -0,0 +1,25 @@ +.. _developer_guides_hello_world: + +Hello World Tutorial +#################### + +This guide takes a step-by-step approach to creating a new audio component +and adding it to a simple audio pipeline. + +At the end of this tutorial, a very simple audio signal amplifier will be +running as a part of the playback pipeline implemented. You will be able to +control the amplification strength from the command line. + +The amplifier will log its basic activities to demonstrate use of the FW +logging capabilities. + +We will also inject a division by 0 instruction to the amplifier code to +demonstrate how to collect and analyze the FW exception reports. + +.. toctree:: + :maxdepth: 1 + + tut-i-basic-fw-code + tut-ii-topology + tut-iii-runtime-params + tut-iv-exceptions diff --git a/developer_guides/firmware/component-tutorial/tut-iv-exceptions.rst b/developer_guides/firmware/component-tutorial/tut-iv-exceptions.rst new file mode 100644 index 00000000..b93df9e0 --- /dev/null +++ b/developer_guides/firmware/component-tutorial/tut-iv-exceptions.rst @@ -0,0 +1,6 @@ +.. _developer_guides_tut-iv: + +Part IV - Working with Exception Reports +######################################## + +This chapter is forthcoming. diff --git a/developer_guides/firmware/index.rst b/developer_guides/firmware/index.rst new file mode 100644 index 00000000..ee2e75cd --- /dev/null +++ b/developer_guides/firmware/index.rst @@ -0,0 +1,15 @@ +.. _firmware: + +Firmware +######## + +Developer guides and information for firmware development. + +.. toctree:: + :maxdepth: 1 + + component-tutorial/tut-intro + porting + 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/porting.rst b/developer_guides/firmware/porting.rst new file mode 100644 index 00000000..b2cae769 --- /dev/null +++ b/developer_guides/firmware/porting.rst @@ -0,0 +1,46 @@ +.. _porting: + +Porting Guides +############## + +Details about porting SOF to other DSP architectures and platforms. This guide +is still work in progress. + +Architecture Porting Guide +************************** +|SOF| currently supports the Cadence xtensa DSP architecture but is designed to +support other DSP architectures via an architecture abstraction layer (AAL) in +the src/arch directory. The AAL provides an architecture agnostic API that +exports functioality for common architecure features. + +#. Boot +#. Interrupts +#. Exceptions +#. Timers +#. Spinlocks +#. Atomic Arithmetic +#. Cache +#. Wait +#. GDB debug + +The AAL API is exported via headers in 'src/arch//include/arch'. + +The AAL thinly wraps architecture specific HALs or RTOSes and is intended to +"compile out" so that there is no runtime performance penalty. i.e. on +Cadence xtensa architecture it wraps xtensa HAL and XTOS API calls. + +Platform Porting Guide +********************** + +The SOF infrastructure requires every platform to provide certain interfaces +and define certain macros with platform specific configuration. + +#. Platform capabilities +#. Memmory mapping +#. Device platform data +#. Interrupt mapping +#. Platform timers + +Refer to :ref:`platform-api` for documentation. + +.. TODO: reference to flows that illustrate calls to platform api. diff --git a/developer_guides/fuzzing/fuzzing_in_docker.rst b/developer_guides/fuzzing/fuzzing_in_docker.rst new file mode 100644 index 00000000..3ebf516e --- /dev/null +++ b/developer_guides/fuzzing/fuzzing_in_docker.rst @@ -0,0 +1,62 @@ +.. _fuzzing-in-docker: + +Fuzzing in Docker +################# + +Instructions +************ + +#. Build a fuzzer in order to use it. Follow the instructions at Build SOF + with docker, :ref:`docker-topology-tools`. + +#. Enter the Docker container: + + :: + + #To be run from sof/ directory + ./scripts/docker-run.sh bash + + A container is created from the ``sof`` Docker image. We are + provided with a shell prompt. Let's call this Terminal #1. + +#. Connect to the container's shell from another terminal. + + To do this, you must first know the container ID. + + :: + + docker ps + + #Sample output + CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES + 1c383e3c08ae sof "bash" 4 minutes ago Up 4 minutes objective_kilby + + The first column of the output gives you the container ID. + + To connect to the container's shell, do the following: + + :: + + docker exec -i -t container_id bash + + This opens a shell prompt. Let's call this Terminal #2. + +#. Run the QEMU DSP VM in Terminal #1 by following instructions from `Using + the QEMU DSP emulator `__. + +#. Run the sof-fuzzer built from Step 1. Run this in Terminal #2. + + When you see **FW boot complete** in Terminal #2, the setup is complete. + +Important notes +*************** + +#. The platform should be the same for the QEMU DSP VM and the fuzzer. + + Ex: If you run your QEMU DSP VM with the 'byt' platform, use the same platform when you run your fuzzer. + +#. Make sure that you pass your kernel using the '-k' flag in the QEMU DSP + VM. + +#. You must run the fuzzer and the QEMU DSP VM in the same container; + otherwise they can't communicate with each other! 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 5eb9b34b..5ac976e5 100644 --- a/developer_guides/index.rst +++ b/developer_guides/index.rst @@ -3,13 +3,36 @@ Developer Guides ################ +New developers are best starting by reading the introduction which describes the +terminology before reading further. + .. toctree:: :maxdepth: 1 - porting - drivers/index - kernel/index - apps/index + introduction + firmware/index unit_tests + xtrun/index topology/topology + topology2/topology2 + uuid/index.rst debugability/index + tuning/sof-ctl + rimage/index.rst + linux_driver/index + virtualization/virtualization + virtualization/running + fuzzing/index + testbench/index + add_new_arch + +Technical Notes +*************** + +Some how-to technical notes that help explain how you can use SOF capabilities. + +.. toctree:: + :maxdepth: 1 + + tech/build-cmocka + tech/compile_wsl diff --git a/developer_guides/introduction.rst b/developer_guides/introduction.rst new file mode 100644 index 00000000..33acd03e --- /dev/null +++ b/developer_guides/introduction.rst @@ -0,0 +1,52 @@ +.. _developer_guides_introduction: + +Introduction +============ + +|SOF| is mainly written in C with a small assembler for DSP initialization +and some DSP intrinsic values for media processing. The intended audience is +software developers familiar with C programming and media processing. + +Developers wishing to participate with upstream should also be familiar with +git and GitHub. + +Knowledge of hardware debugging (such as JTAG) and use of emulators is also +desirable if bringing up new hardware. + + +SOF Core Concepts +----------------- + +Following are core concepts and terms used by SOF developers. + +**Component** An audio or signal processing component that processes input +data into output data. Components can have one or more input source buffers +and one or more output sink buffers. Components can also send and receive +runtime configuration data that can be used to monitor or alter the data processing. + +**Buffer** A memory region that can be used to share audio processing data +between components. Buffers can have certain attributes depending on their +usage, such as a DMA'able buffer. + +**Pipeline** A collection of audio processing components and buffers that +are scheduled for processing together such as a schedA pipeline that can +have multiple source and sink endpoints. The endpoints may be other +pipelines or components. + +**DAI** Digital Audio Interface. A hardware audio serial interface used to +send audio data between hardware devices. Examples are I2S, Soundwire, PDM, HDA, and HDMI. + +**Topology** A high-level description of the network of all pipelines and +components enumerated on the DSP. This is initially described in text format +before being compiled into a binary that firmware can process. + +**Module** Another name for component. Implies that a component is linked at +runtime rather than at build time. + +**Driver** A device driver used by firmware to control hardware devices (such as DMA or I2S) or a |SOF| host OS device driver. + +**AAL** Architecture Abstraction Layer. A firmware abstraction layer used to +abstract architecture-specific code. + +**SRC** Sample Rate Converter. An audio component used to convert the sample +rate of a synchronous input stream to a synchronous output stream. diff --git a/developer_guides/kernel/images/work-queue-deps.pu b/developer_guides/kernel/images/work-queue-deps.pu deleted file mode 100644 index d9c20f44..00000000 --- a/developer_guides/kernel/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/kernel/images/work-schedule.pu b/developer_guides/kernel/images/work-schedule.pu deleted file mode 100644 index 6be68be6..00000000 --- a/developer_guides/kernel/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/kernel/index.rst b/developer_guides/kernel/index.rst deleted file mode 100644 index bd9011f0..00000000 --- a/developer_guides/kernel/index.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. _kernel: - -Kernel -###### - -.. toctree:: - :maxdepth: 1 - - mem-mgmt - work-queue diff --git a/developer_guides/kernel/mem-mgmt.rst b/developer_guides/kernel/mem-mgmt.rst deleted file mode 100644 index e55a1a97..00000000 --- a/developer_guides/kernel/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/kernel/work-queue.rst b/developer_guides/kernel/work-queue.rst deleted file mode 100644 index f3be4a97..00000000 --- a/developer_guides/kernel/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/linux_driver/index.rst b/developer_guides/linux_driver/index.rst new file mode 100644 index 00000000..c6534d70 --- /dev/null +++ b/developer_guides/linux_driver/index.rst @@ -0,0 +1,10 @@ +.. _sof_linux_driver: + +SOF Linux Driver +################ + +.. toctree:: + :maxdepth: 1 + + third_party/index + diff --git a/developer_guides/linux_driver/third_party/index.rst b/developer_guides/linux_driver/third_party/index.rst new file mode 100644 index 00000000..77d5be07 --- /dev/null +++ b/developer_guides/linux_driver/third_party/index.rst @@ -0,0 +1,10 @@ +.. _third_party_features: + +Third Party Features User Guides +################################ + +.. toctree:: + :maxdepth: 1 + + keyword_detect/keyword_detect + diff --git a/developer_guides/linux_driver/third_party/keyword_detect/keyword_detect.rst b/developer_guides/linux_driver/third_party/keyword_detect/keyword_detect.rst new file mode 100644 index 00000000..f33bdde4 --- /dev/null +++ b/developer_guides/linux_driver/third_party/keyword_detect/keyword_detect.rst @@ -0,0 +1,174 @@ +.. _keyword_detect: + +Keyword Detection Driver Implementation and User Guide +###################################################### + +.. contents:: + :local: + :depth: 1 + +Keyword Detection +***************** + +Keyword Detection (KD), also known as Voice Activation or Sound Trigger, is +a feature that triggers a speech recognition engine when a predefined +keyphrase (keyword) is successfully detected. Offloading the keyphrase +detection algorithm to the embedded processing environment (i.e. dedicated +DSP) reduces system power consumption while listening for an utterance. + +To learn how to integrate a 3rd-party detection algorithm into the SOF firmware, refer to :ref:`KD-integration`. + +Keyword Detection pipelines +*************************** + +.. code-block:: none + + # PCM <-----(pipe 8)--------- host <---------------- KPB <------- Volume <--- DMIC (dmic16k) + # | | + # | | + # | | + # Detector Sink <---Detector(pipe 9) <--- selector <---+ + +We use DAPM events to trigger the detect pipeline (pipe 9). Here, the +**Detector Sink** is a virtual DAPM widget (visible to the driver, but not +to the FW). It is used to send pipeline control IPCs (hw_params, trigger +start/stop, hw_free) to the firmware. These control IPCs are sent to the +firmware in a sequence like this (1->2(2.1->2.2)->3->4->5->6(6.1->6.2)): + +.. csv-table:: DAPM events and stream control sequence + :header: "IPCs", "Pipe 8", "Detector Sink event", "Pipe 9" + :widths: 20, 10, 25, 10 + + "hw_params", "1", "2 (DAPM_PRE_PMU)", "2.1" + "trigger start", "3", "", "2.2" + "trigger stop", "4", "6 (DAPM_POST_PMD)", "6.1" + "hw_free", "5", "", "6.2" + +Kcontrols for Keyword Detection +******************************* + +The KWD detection topology contains several kcontrols mainly belonging to +the following types: + +PGA kcontrol +============ + +These are associated with the volume components and are used to adjust the +volume after the samples are captured by DMIC. + +.. code-block:: none + + numid=12,iface=MIXER,name='PGA8.0 8 KWD Capture Volume' + +Bytes kcontrols +=============== + +**KPB**, **Selector**, and **Detector** are all treated as processing type +components in the SOF driver. Each of these components have an associated +byte type kcontrol and are configured using the default values from topology +as follows: + +KPB kcontrol +------------ + +The kcontrol for the KPB configuration is (amixer controls | grep “KPB”): + +.. code-block:: none + + numid=13,iface=MIXER,name='KPBM8.0 KPB' + +The initial value of it is in the ``KPB_priv`` section of ``sof/tools/topology/sof/pipe-kfbm-capture.m4`` (the first 32 Bytes are the abi header); +it is aligned with the definition of struct ``sof_kpb_config`` in ``sof/include/user/kpb.h``. + +Selector kcontrol +----------------- + +The kcontrol for the Selector configuration is (amixer controls | grep “SELECTOR”): + +.. code-block:: none + + numid=16,iface=MIXER,name='SELECTOR9.0 SELECTOR' + +The initial value of it is in the ``SELECTOR_priv`` section of ``sof/tools/topology/sof/pipe-detect.m4`` (the first 32 Bytes are the abi header); it is +aligned with the definition of struct ``sof_sel_config`` in ``sof/include/user/selector.h``. + +Detector kcontrol for component configuration +--------------------------------------------- + +The kcontrol for the Detector configuration is (amixer controls | grep “Detector Config”): + +.. code-block:: none + + numid=14,iface=MIXER,name='DETECT9.0 Detector Config' + +The initial value of it is in the ``DETECTOR_priv`` section of ``sof/tools/topology/m4/detect_test_coef.m4`` (the first 32 Bytes are the abi header); +it is aligned with the definition of struct ``sof_detect_test_config`` in ``sof/include/user/detect_test.h``. + +Detector kcontrol for algorithm data +------------------------------------ + +The kcontrol for the detector algorithm configuration is (amixer controls | grep “Hotword Model”): + +.. code-block:: none + + numid=15,iface=MIXER,name='DETECT9.0 Hotword Model' + +This is vendor-specific; by default, it is initialized to 64 Bytes 0s only. + +The sof-ctl tool +**************** + +For all TLV Bytes kcontrols, after the pipeline/PCM is created, we can use +the SOF tool named **sof-ctl** to configure/update with the new blob. + +The source is located in ``sof/tools/ctl/ctl.c``. Run ``./scripts/build-tools.sh`` in the sof folder to build and generate it. + +To set: + +.. code-block:: none + + #./sof-ctl -Dhw:0 -c name='DETECT9.0 Hotword Model' -br -s en_us_data_memory.mmap -t 1 + +To read it back: + +.. code-block:: none + + #./sof-ctl -Dhw:0 -c name='DETECT9.0 Hotword Model' -br + +Run the Keyword Detection pipeline +********************************** + +After the Detector blob is configured, we run aplay/arecord to verify the +KWD on our side. Run it in mmap ``-M`` non-blocking ``-N`` mode, as shown in +the example below: + +.. code-block:: none + + #arecord -Dhw:0,8 -M -N -c 2 -f S16_LE -r 16000 --buffer-size=68000 tmp.wav -vvv + +The supported formats of the PCM are 16KHz s16_le/s24_le/s32_le 2 channels. + +.. note:: The waking up and the host system resuming may take up to 1~2 + seconds. To make sure the captured keyword data is not overwritten by the + subsequent realtime data, the host ``buffer-size`` must be at least 67200 + frames (4.2 Seconds); smaller values will be rejected by the firmware and + will fail at the ``hw_param`` stage. + +Run the Keyword Detection feature at S0ix status +************************************************ + +In one terminal, run: + +.. code-block:: none + + #arecord -Dhw:0,8 -M -N -c 2 -f S16_LE -r 16000 --buffer-size=64000 tmp.wav -vvv + +In another terminal, run: + +.. code-block:: none + + #echo freeze > /sys/power/state + +The Keyword Detection feature is activated at S0Ix. Say the keyword to +trigger the Keyword detected; the system wakes up and the keyword and +command data are captured. diff --git a/developer_guides/porting.rst b/developer_guides/porting.rst deleted file mode 100644 index 56a65cd4..00000000 --- a/developer_guides/porting.rst +++ /dev/null @@ -1,16 +0,0 @@ -.. _porting: - -Porting Guides -############## - -Architecture Porting Guide -************************** - -Platform Porting Guide -********************** - -The SOF infrastructure requires every platform to provide certain interfaces. - -Refer to :ref:`platform-api` for documentation. - -.. TODO: reference to flows that illustrate calls to platform api. diff --git a/developer_guides/rimage/extended_manifest.rst b/developer_guides/rimage/extended_manifest.rst new file mode 100644 index 00000000..3cddcd94 --- /dev/null +++ b/developer_guides/rimage/extended_manifest.rst @@ -0,0 +1,32 @@ +.. _extended_manifest: + +Extended Manifest +################# + +The extended manifest is a place to store build-time known firmware metadata +such as the firmware version or a used compiler description. Given that +information is read on the host side before firmware startup, this is +especially important for ABI compatibility checks. +This part of the output binary is located as a first structure in the binary +file and it is skipped in the DSP loading routine; so, the attached +information does not affect DSP memory. + + +Build flow +========== + +.. uml:: images/ext_man_build_flow.pu + :caption: Extended manifest generation + + +Add a new element +================= + +To add a new element to the extended manifest, do the following: + +#. Add a new element definition in the ``ext_manifest.h`` file located in + the firmware and driver repository. +#. Add a new element declaration in the ``ext_manifest.c`` file located in + the firmware repository. +#. Add a new element handling routine in the driver repository: + ``sound/soc/sof/loader.c:snd_sof_fw_ext_man_parse()`` \ No newline at end of file diff --git a/developer_guides/rimage/images/ext_man_build_flow.pu b/developer_guides/rimage/images/ext_man_build_flow.pu new file mode 100644 index 00000000..2ca7f731 --- /dev/null +++ b/developer_guides/rimage/images/ext_man_build_flow.pu @@ -0,0 +1,33 @@ +@startuml + +title Extended manifest build flow + +class firmware_elf_file << (F, orchid) >> { + suffix: + -- Content -- + +FW metadata section + +other sections +} + +rectangle rimage { + class ext_man_write { + 1. Create .ri.xman file + 2. Find elf file with .fw_metadata section + 3. Read .fw_metadata section + 4. Build ext_man_header + 5. Validate sum of elements size with section size + 6. Save output to .ri.xman file + } +} + +class ext_man_file << (F, orchid) >> { + suffix: .ri.xman + -- Content -- + + ext_man_header + + .fw_metadata section content +} + +firmware_elf_file -down-> ext_man_write +ext_man_write -down-> ext_man_file + +@enduml diff --git a/developer_guides/rimage/images/image_build_flow.pu b/developer_guides/rimage/images/image_build_flow.pu new file mode 100644 index 00000000..0550e3c9 --- /dev/null +++ b/developer_guides/rimage/images/image_build_flow.pu @@ -0,0 +1,70 @@ +@startuml + +title image build flow + +class bootloader_elf_file << (F, orchid) >> { + -- note -- + Used for CAVS 1.5 and newer +} +class firmware_elf_file << (F, orchid) >> { +} + +class rimage { + +write_firmware() + +write_firmware_meu() + +ext_man_write() +} + +class adsp_manifest_file << (F, orchid) >> { + suffix: .ri.met +} + +class image_file << (F, orchid) >> { + suffix: .ri + -- Content -- + +CSE manifest + +CSS manifest + +ADSP manifest + +runtime code +} + +class unsigned_image_file << (F, orchid) >> { + suffix: .ri.uns + -- Content -- + +ADSP manifest + +runtime code +} + +class ext_man_file << (F, orchid) >> { + suffix: .ri.xman +} + +class build_step { + + glue binary files() +} +hide build_step circle + +class final_image_file << (F, orchid) >> { + suffix: .ri + -- Content -- + +Extended manifest + +CSE manifest + +CSS manifest + +ADSP manifest + +runtime code +} + +firmware_elf_file -down-> rimage +bootloader_elf_file -down-> rimage +rimage -down-> ext_man_file : with -e flag +ext_man_file -down-> build_step +rimage -down-> adsp_manifest_file +rimage -down-> image_file : without MEU\nwithout -s argument +rimage -down-> unsigned_image_file : with MEU \nwith -s argument +unsigned_image_file -down-> MEU +MEU -down-> image_file +adsp_manifest_file -down-> image_file +image_file -down-> build_step +build_step -down-> final_image_file + +@enduml diff --git a/developer_guides/rimage/index.rst b/developer_guides/rimage/index.rst new file mode 100644 index 00000000..2ac94ec7 --- /dev/null +++ b/developer_guides/rimage/index.rst @@ -0,0 +1,29 @@ +.. _rimage: + +Rimage +###### + +Rimage is a DSP firmware image creation and signing tool used by +Sound Open Firmware (SOF) to generate binary image files. + +Rimage contains a built-in generator for: + +#. Extended manifest - describes firmware metadata for drivers +#. CSE manifest +#. CSS manifest +#. ADSP manifest + +For more details see: + +.. toctree:: + :maxdepth: 1 + + extended_manifest + + + +Build flow +========== + +.. uml:: images/image_build_flow.pu + :caption: Image build generation diff --git a/developer_guides/tech/build-cmocka.rst b/developer_guides/tech/build-cmocka.rst new file mode 100644 index 00000000..03fee6a2 --- /dev/null +++ b/developer_guides/tech/build-cmocka.rst @@ -0,0 +1,142 @@ +.. _build-cmocka: + +Build Cmocka for Xtensa +####################### + +Cmocka for SOF is built automatically by default, however you may need a prebuilt version that can be used with :ref:`CMOCKA_DIRECTORY `. + +This article exaplains how to build Cmocka manually. +Please note that it currently works only for Xtensa xt-* toolchain. +Xtensa GCC toolchain is not supported yet. + +Cmocka fork +*********** + +We use our Cmocka fork that adds some options for embedded compilers: + +WITH_POSITION_INDEPENDENT_CODE + Some compilers cannot compile CMake's test program with PIC, + so you can disable it with this option. + +WITH_TINY_CONFIG + Usually compiler checks are cheap, so configuration is quick. + However many compilers are expensive to call, + so this option can be used to minimize checks count. + +WITH_SHARED_LIB + For compilers that cannot build shared libs and still want to + use make / make install for making prebuilt libs + +All changes made to Cmocka are enabled with these options, without them it works just like vanilla Cmocka. + +Clone repo and enter its directory, because all examples will be executed there: + +.. code-block:: bash + + git clone https://github.com/thesofproject/cmocka + cd cmocka + +Simple build on Linux +********************* + +On any system that CMake identifies as `UNIX `_, you can just call following +commands and it should work: + +.. code-block:: bash + + mkdir build && cd build + cmake \ + -DCMAKE_C_COMPILER=xt-xcc \ + -DWITH_STATIC_LIB=ON \ + -DWITH_SHARED_LIB=OFF \ + -DWITH_EXAMPLES=OFF \ + -DWITH_POSITION_INDEPENDENT_CODE=OFF \ + -DCMAKE_INSTALL_PREFIX=install \ + .. + make install + +Arguments used: + +CMAKE_C_COMPILER + We specify "xt-xcc" as compiler that CMake should use + to compile C files. +WITH_STATIC_LIB + By default static lib for Cmocka is not built, + so we enable it. +WITH_SHARED_LIB + By default Cmocka builds shared lib, but we don't want that. +WITH_EXAMPLES + Examples don't work without shared lib, so we disable them. +WITH_POSITION_INDEPENDENT_CODE + PIC will make CMake's testing programs + to fail, so disable it. +CMAKE_INSTALL_PREFIX + By default, it will go to host system binary files + (for example /usr/bin), we change it to "install", so **make install**, will + place output to **build/install** directory. This is the directory that you + can use as input for :ref:`CMOCKA_DIRECTORY ` in SOF build system. + +Cross-platform build +******************** + +In order to build Cmocka for generic system, you need to use +`CMAKE_TOOLCHAIN_FILE `_. + +Create **xt-toolchain-for-cmocka.cmake** file with following contents: + +.. code-block:: cmake + + # Generic because we build for embedded system + set(CMAKE_SYSTEM_NAME Generic) + # It should be always set when CMAKE_SYSTEM_NAME is changed + set(CMAKE_SYSTEM_VERSION 1) + + # Make CMake use "xt-xcc" for compiling C files + set(CMAKE_C_COMPILER xt-xcc) + # Override ar and ranlib tools that CMake should use for linking lib + set(CMAKE_AR xt-ar CACHE STRING "") + set(CMAKE_RANLIB xt-ranlib CACHE STRING "") + + # Cmocka is written in C99, but for some reason it sets this flag, only on Posix + # We set up it here, because our system is Generic + add_definitions("-std=gnu99") + +Now you can build Cmocka using file above (use correct path to your toolchain file): + +.. code-block:: bash + + mkdir build && cd build + cmake \ + -DCMAKE_TOOLCHAIN_FILE=/path/to/xt-toolchain-for-cmocka.cmake \ + -DWITH_STATIC_LIB=ON \ + -DWITH_SHARED_LIB=OFF \ + -DWITH_EXAMPLES=OFF \ + -DWITH_POSITION_INDEPENDENT_CODE=OFF \ + -DCMAKE_INSTALL_PREFIX=install \ + .. + make install + +After these commands are successfully completed, the Cmocka's static lib and +headers should be in **build/install**. + +Please note that commands above were for CMake's Make generator. +If you are using Windows and want to use Ninja, your commands will +look more like: + +.. code-block:: bash + + mkdir build && cd build + cmake \ + -DCMAKE_TOOLCHAIN_FILE=/path/to/xt-toolchain-for-cmocka.cmake \ + -DWITH_STATIC_LIB=ON \ + -DWITH_SHARED_LIB=OFF \ + -DWITH_EXAMPLES=OFF \ + -DWITH_POSITION_INDEPENDENT_CODE=OFF \ + -DCMAKE_INSTALL_PREFIX=install \ + -GNinja \ + .. + ninja install + +.. note:: + + You can use -DWITH_TINY_CONFIG=ON, if configuration step takes too much time. diff --git a/howtos/tech/compile_wsl.rst b/developer_guides/tech/compile_wsl.rst similarity index 100% rename from howtos/tech/compile_wsl.rst rename to developer_guides/tech/compile_wsl.rst 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 db9ec3eb..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,7 +597,45 @@ The graph below shows the topology defined in Section 3.1. .. image:: images/tplg2.png -3. Acronyms +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, +pcm_add, and graph creation phases. + +Debug macros use errprint to print to stderr, so you can differentiate +between actual macro output and debug messaging. To get the graph +printing correct, you need to surround your m4 with DEBUG_START and +DEBUG_END. You can see examples in sof-apl-pcm512x.m4 and +sof-apl-da7219.m4 + +There are currently 2 debug types defined, GRAPH and INFO. GRAPH +produces dot file describing the topology graph connection. INFO +produces diagnostic messages mainly related to dai indexing. + +You can invoke the debugging like this (in the topology folder): + +.. code-block:: bash + + m4 -I m4 -I common -I platform/common --define=GRAPH sof-apl-da7219.m4 > /dev/null + m4 -I m4 -I common -I platform/common --define=INFO sof-apl-da7219.m4 > /dev/null + +To produce a graph image: + +.. code-block:: bash + + m4 -I m4 -I common -I platform/common --define=GRAPH sof-apl-da7219.m4 2> test.dot + dot test.dot -Tpng -o tplg.png + +INFO messages are surrounded by C-like comment markers, so you can +actually push both messages to a dot file: + +.. code-block:: bash + + m4 -I m4 -I common -I platform/common --define=GRAPH --define=INFO sof-apl-da7219.m4 2> test.dot + +4. Acronyms *********** | **DAI**: Digital Audio Interface @@ -573,3 +647,5 @@ The graph below shows the topology defined in Section 3.1. .. _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/getting_started/effects/how-to-use-sof-eqctl-tool.rst b/developer_guides/tuning/sof-ctl.rst similarity index 65% rename from getting_started/effects/how-to-use-sof-eqctl-tool.rst rename to developer_guides/tuning/sof-ctl.rst index 43413121..63973510 100644 --- a/getting_started/effects/how-to-use-sof-eqctl-tool.rst +++ b/developer_guides/tuning/sof-ctl.rst @@ -1,13 +1,22 @@ -.. _how-to-use-sof-eqctl-tool: +.. _runtime_tuning: -How to use sof-eqctl tool -######################### +Runtime Tuning +############## -The tool is available in SOFT repository in directory eqctl. It helps -to access the ext bytes or tlv bytes control of |SOF| equalizer -components eq_iir and eq_fir. Capability to change EQ response in -runtime is useful for transducers tuning and for scenario of -having equalizers under control of user space service. +Runtime tuning of components and pipelines can be achieved using the sof-ctl +tool. + +The tool is available in SOF repository in directory tools/ctl. It performs +runtime IO access using the ext bytes or tlv bytes control of |SOF| components +like eq_iir and eq_fir. This tool is used to upload runtime data to alter the +performance or processing characteristsics at runtime of a audio component. +e.g Capability to change EQ response in runtime is useful for transducer tuning +and for scenario of having equalizers under control of user space service. + +This document mainly focuses on examples of the sof-ctl around the EQ FIR and +IIR components since the tool was developed alongside these components. The +concepts outlined here for EQs will equally applt to other component types +that support updating runtime data. Please find other document(s) in this section how to setup persistently equalizers via topology in boot. There will be also general documentation @@ -22,9 +31,9 @@ as follows: .. code-block:: bash - $ amixer -Dhw:0 controls | grep EQ - numid=23,iface=MIXER,name='EQFIR1.0 EQFIR' - numid=22,iface=MIXER,name='EQIIR1.0 EQIIR' + amixer -Dhw:0 controls | grep EQ + #numid=23,iface=MIXER,name='EQFIR1.0 EQFIR' + #numid=22,iface=MIXER,name='EQIIR1.0 EQIIR' Therefore to control the IIR instance use numid=22 and to control the FIR EQ instance use numid=23. Note that this depends on topology and @@ -46,7 +55,7 @@ coefficients and other embedded control is described in uapi/eq.h. Creating equalizer configurations requires GNU Octave or Matlab(R) numerical computing software with signal toolbox. The equalizer tuning -tool is found from SOFT/tune/eq directory. +tool is found in tools/tune/eq directory. ===================== ================================================ File name Explanation @@ -74,24 +83,24 @@ does not yet support preset switching without re-uploading the whole configuration. The equalizer can be updated only when SOF is idle. Update during -playback is not currently supported and when attempted the playback -will continue with existing setting. The driver will re-send to +playback is not currently supported (until SOF v1.4) and when attempted the +playback will continue with existing setting. The driver will re-send to configuration when DSP is not busy. E.g. to switch the IIR equalizer to bandpass use command: .. code-block:: bash - $ sof-eqctl -Dhw:0 -n 22 -s eq_iir_bandpass.txt + sof-ctl -Dhw:0 -n 22 -s eq_iir_bandpass.txt Succesfull execution will produce next output. .. code-block:: bash - Applying configuration "eq_iir_bandpass.txt" into device hw:0 control numid=22. - 84,2,1,0,0,2,2,3316150158,2048164275,513807534,3267352229,513807534,0,16384, - 3867454526,1191025347,38870735,77741469,38870735,4294967294,16458 - Success. + #Applying configuration "eq_iir_bandpass.txt" into device hw:0 control numid=22. + #84,2,1,0,0,2,2,3316150158,2048164275,513807534,3267352229,513807534,0,16384, + #3867454526,1191025347,38870735,77741469,38870735,4294967294,16458 + #Success. After this command the playback sound will have all lowest and highest frequencies suppressed and sound very thin. You may experiment with @@ -103,31 +112,16 @@ be read back by omitting the -s switch. .. code-block:: bash - $ sof-eqctl -Dhw:0 -n 22 - Retrieving configuration for device hw:0 control numid=22. - Success. - 84,2,1,0,0,2,2,3316150158,2048164275,513807534,3267352229,513807534,0,16384, - 3867454526,1191025347,38870735,77741469,38870735,4294967294,16458 + sof-ctl -Dhw:0 -n 22 + #Retrieving configuration for device hw:0 control numid=22. + #Success. + #84,2,1,0,0,2,2,3316150158,2048164275,513807534,3267352229,513807534,0,16384, + #3867454526,1191025347,38870735,77741469,38870735,4294967294,16458 Help **** For completeness the command line options are described with -h switch. -.. code-block:: bash - - $ sof-eqctl -h - Usage ./sof-eqctl - Set example ./sof-eqctl -Dhw:0 -c "numid=22,name=\"EQIIR1.0 EQIIR\"" -s iir.txt - Set example ./sof-eqctl -Dhw:0 -n 22 -s iir.txt - Get example ./sof-eqctl -Dhw:0 -n 22 - ./sof-eqctl: Control SOF equalizers - ./sof-eqctl: -D Use device , defaults to hw:0 - ./sof-eqctl: -c Get configuration for EQ - ./sof-eqctl: -n Get configuration for given numid - ./sof-eqctl: -s Setup equalizer with data in . - The ASCII text file must contain comma - separated unsigned integers. - Mail list sound-open-firmware@alsa-project.org is recommended contact for technical discussion about equalizers and tuning. diff --git a/developer_guides/unit_tests.rst b/developer_guides/unit_tests.rst index aa2e4111..93e0e0e7 100644 --- a/developer_guides/unit_tests.rst +++ b/developer_guides/unit_tests.rst @@ -6,119 +6,114 @@ Unit Tests Prerequisites ************* -You must use `cmocka `_ to run unit tests. +This guide assumes that you have the proper setup and that you know how to build firmware. If this is not correct, follow the instructions at :doc:`../getting_started/build-guide/build-from-scratch` first. -Use the package manager such as apt to build native libraries: +`Cmocka `_ is fetched and built automatically. +For a successful compilation, it needs a toolchain thats supports C stdlib. -.. code-block:: bash +Configuring for unit tests +************************** + +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: - sudo apt install libcmocka-dev +- 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``. -If you want to use the `custom version of cmocka `_ -(for example for cross-compilation), provide the path to cmocka -built for the chosen architecture using the **--with-cmocka-prefix** option. +.. note:: -Enabling unit tests -******************* + Use -DTOOLCHAIN=xt option. -In order to build and run unit tests, provide the path to cmocka using the -**--with-cmocka-prefix** option of the configure script. + As of December 2021, -DTOOLCHAIN=xtensa--elf is not + supported. You can use a native toolchain, see below. -After the configuration is complete, build and run all unit tests by entering: +If you get this double ``uintptr_t`` definition error: .. code-block:: bash - make check + [ 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 -Reconfiguration is not necessary when the cmocka path is set in the -configuration script. Use **make** for building the normal APL Binary -and **make check** to build and run unit tests. +... 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 ============================== -In order to build tests for the APL platform, use the `custom version of -cmocka `_. Run the **./configure** script -with the same parameters as when building APL binaries. You must also -add **--with-cmocka-prefix=**. For example: - .. code-block:: bash - ./autogen.sh - ./configure --with-arch=xtensa-smp --with-platform=apollolake \ - --with-dsp-core=$XTENSA_CORE --with-root-dir=$CONFIG_PATH/xtensa-elf \ - --host=xtensa-bxt-elf --with-meu=$MEU_PATH \ - --with-key=$PRIVATE_KEY_PATH CC=xt-xcc OBJCOPY=xt-objcopy \ - OBJDUMP=xt-objdump --with-cmocka-prefix=/home/admin/cminstall_apl_2017_8/ - make check - -Preparing cmocka package -************************ - -#. Build cmocka with the static library on: - - .. code-block:: bash - - cmake -DWITH_STATIC_LIB=ON - - In order to build cmocka with xt-xcc to link with a DSP binary code, - do the following: + mkdir build_ut && cd build_ut + 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 - a. add another option -DCMAKE_C_COMPILER=xt-xcc - #. edit the cmocka build scripts to disable building of shared library +Compiling unit tests without a cross-compilation toolchain +========================================================== -#. Create a directory for the package to be referenced by the main - **sof** build script and copy the required files there: +You can also compile and run unit tests with your native compiler: - .. code-block:: bash - - mkdir /home//cminstall - mkdir /home//cminstall/include - mkdir /home//cminstall/lib - - cp cmocka.h /home//cminstall/include - cp libcmocka.a /home//cminstall/lib - -#. Use the target location for the cmocka files when invoking the - **configure** script: +.. code-block:: bash - .. 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 - ./configure --with-cmocka-prefix=/home//cminstall ... +The ``scripts/run-cmocks.sh`` script does all that and can also run unit +tests with valgrind. Wrapping objects for unit tests ******************************* -If you need to mock a symbol, define it in a unit test and include the .h file. -There are 2 cases where this isn't possible: +If you need to mock a symbol, define it in a unit test and include the .h file. There are two cases where this isn't possible: -* Static functions in headers(those most probably are inline short functions - and don't have to be mocked) +* Static functions in headers (those most probably are inline short functions + and don't have to be mocked). * Static functions that are in the same file as tested functionality and are exceedingly large so they can't be tested as one functionality. -Whatever the reason, mocking of those symbols can be done by using --wrap linker -functionality. To wrap the symbol follow these steps: +Whatever the reason, mocking of those symbols can be done by using the --wrap linker functionality. To wrap the symbol follow these steps: #. Create mocked symbol named __wrap_symbol_name #. Pass instruction for the linker -Wl, --wrap=symbol_name during compilation. -Now every symbol call to symbol_name will call __wrap_symbol_name. +Now every symbol calls to symbol_name will call __wrap_symbol_name. Instructions can be passed to the linker in the SOF UT environment using -CFLAGS, however they should be passed in separate variables in the makefile. +CFLAGS; however, they should be passed in separate variables in the makefile. Example: -.. code-block:: bash +.. code-block:: cmake # some tests before ... - check_PROGRAMS += pipeline_connect_upstream - pipeline_connect_upstream_SOURCES = ../../src/audio/pipeline.c src/audio/pipeline/ pipeline_mocks.c src/audio/pipeline/pipeline_connect_upstream.c src/audio/pipeline/pipeline_mocks_rzalloc.c - pipeline_connect_upstream_CFLAGS = -Wl, --wrap=symbol_name + cmocka_test(pipeline_connect_upstream + pipeline_connect_upstream.c + ... + ) + target_link_libraries(pipeline_connect_upstream PRIVATE "-Wl,--wrap=symbol_name") Full information about wrapping can be found here: @@ -127,16 +122,9 @@ https://lwn.net/Articles/558106/ Notes ***** -#. Use the **make check -j** option while running tests that use xt-run +#. Use the **ctest -j** option while running tests that use xt-run (to speed up tests significantly) by running multiple instances of the - xt-run simulator (it also speeds up build if you have many unit tests). - -#. When you switch platforms, such as from native to APL, use **make - clean**; otherwise, **make** will not build binaries for the new - platform and your tests will fail. - -#. To speed up development of new unit tests, run specific tests such as: - - .. code-block:: bash + xt-run simulator (it also speeds up the build if you have many unit tests). - make check check_PROGRAMS="testname1 testname2" +#. **ctest** only runs unit tests; to rebuild them, you have to explicitly + run **make**. diff --git a/developer_guides/uuid/index.rst b/developer_guides/uuid/index.rst new file mode 100644 index 00000000..0ce49310 --- /dev/null +++ b/developer_guides/uuid/index.rst @@ -0,0 +1,124 @@ +.. _uuid: + +UUID Usage in SOF +################# + +Why UUIDs +********* + +A new audio signal processing component is typically developed by +implementing the component driver with a newly introduced, unique component +type. The pain points with this method include keeping the ABI (Application +Binary Interface) aligned for the topology file, the driver, and the +firmware. If unaligned, how can we make them backwards compatible. For +example, a new component can have 8 combinations of topology/driver/firmware +versions where the new component may or may not be supported in each +topology/driver/firmware part. Additionally, if the enumerated type is used +for the component type and the sequence in the enumerated list +(the value) is changed during an update, a component type collision can +occur if the versions are not aligned. + +UUIDs (Universally Unique Identifiers) provide a more scalable and +collision-free way of component identification. UUIDs are used as the +standard interface by all users of the SOF firmware, including the +tracing subsystem, the topology .m4 files, and the Linux topology +driver. + +Allocate a new UUID for a newly added component since it will replace the +component type in the IPC structures in the future. For the entire SOF stack, follow these UUID usage rules: + +UUID allocation +*************** +The UUID of a specific component uses random generation (version 4; see +`UUID wikipedia `__ for a description) +and the value is declared in the firmware component driver with +``DECLARE_SOF_RT_UUID``. For details, refer to the :ref:`uuid-api` documentation. + +UUID in topology +**************** +Use the same UUID in the topology .m4 file for a newly added +widget (corresponding to the newly added component). Since we have +implemented a macro to help handle the conversion task, just use the +same macro ``DECLARE_SOF_RT_UUID`` as depicted in the firmware +source. For the SRC component, for example, the below shall be added to the +topology .m4 tools/topology/m4/src.m4: + +.. code-block:: none + + DECLARE_SOF_RT_UUID("host", host_uuid, 0x8b9d100c, 0x6d78, 0x418f, + 0x90, 0xa3, 0xe0, 0xe8, 0x05, 0xd0, 0x85, 0x2b); + +Linux topology driver +********************* +The topology driver parses the 16-byte UUID token, appends it to the +extended data of the IPC struct, and sends it to the firmware in the +component creation stage **for all components**. + +.. code-block:: none + + /* Component extended tokens */ + static const struct sof_topology_token comp_ext_tokens[] = { + {SOF_TKN_COMP_UUID, + SND_SOC_TPLG_TUPLE_TYPE_UUID, get_token_uuid, + offsetof(struct sof_ipc_comp_new_ext, uuid), 0}, + }; + +.. code-block:: none + + static int sof_widget_ready(struct snd_soc_component *scomp, int index, + struct snd_soc_dapm_widget *w, + struct snd_soc_tplg_dapm_widget *tw) + { + ... + ret = sof_parse_tokens(scomp, &swidget->comp_ext, comp_ext_tokens, + ARRAY_SIZE(comp_ext_tokens), tw->priv.array, + le32_to_cpu(tw->priv.size)); + ... + } + +UUID arrays stored section +************************** +Only the UUID arrays for component types used in the topology file are +stored to the .rodata section as static data, for limited memory +footprint purposees. For example, 19 component types * 16 Bytes/component type = 304 Bytes. + +UUID to component driver mapping +******************************** +The firmware uses a UUID byte array to match the component driver if +it is provided from the Linux driver side. Otherwise, fallback to the +traditional component type for backwards-compatible behavior. + +.. code-block:: none + + static const struct comp_driver *get_drv(struct sof_ipc_comp *comp) + { + ... + /* validate the extended data */ + ... + /* use UUID to match the driver if UUID is provided */ + if (comp->ext_data_offset) { + /* use component type if old tplg without UUID used */ + if (sof_is_uuid_nil(comp_ext->uuid)) + goto comp_type_match; + + /* search driver list with UUID */ + ... + /* matched, return drv */ + return drv; + + /* not found, failed */ + return NULL; + } + + comp_type_match: + /* search driver list for driver type */ + ... + /* return the component type matched driver */ + return drv; + } + +ABI alignment for UUID support +****************************** +In general, use UUIDs only for all FW/topologies/drivers whose ABI version +equals or is greater than 3.17. Otherwise, use component type. + diff --git a/developer_guides/virtualization/files/q-v6.sh b/developer_guides/virtualization/files/q-v6.sh new file mode 100644 index 00000000..396db3df --- /dev/null +++ b/developer_guides/virtualization/files/q-v6.sh @@ -0,0 +1,86 @@ +# Assuming "enp2s0" is the name of the network interface, it corresponds +# to the lower ethernet socket on my Up^2. "virbr0" is the bridge, the +# master of both the physical ethernet interface and the virtual network; +# "disk" is a partition, used to store a VM image; "path" is a mount point. +# Note: This is a sample file which you can adjust as necessary. For example, +# if you do not have access to a German keyboard, you may remove the "-k de" +# qemu launch option. Also note that a second instance of the QEMU VNC server +# starts, which then listens on port 5901. + +net=enp2s0 +vir=virbr0-nic +br=virbr0 +ip= + +disk=/dev/sda1 +path=/var/lib/libvirt/images/ + +if ! brctl show | grep -q $net; then + ip link set $net master $br +fi +if ip addr show dev $net | grep -q "$ip/24"; then + ip addr del $ip/24 dev $net + dhclient $br +fi +if ! ip addr show dev $vir | grep -q UP; then + ip link set $vir up + sleep 0.5 + ip link set $vir master $br + echo 1 > /proc/sys/net/ipv4/ip_forward +fi +if ! mount | grep -q "$path"; then + mount $disk $path +fi + +modprobe vhost-sof + +/usr/bin/qemu-system-x86_64 \ +-enable-kvm \ +-name guest=ubuntu19.04,debug-threads=on \ +-machine pc-q35-4.0,accel=kvm,usb=off,vmport=off,dump-guest-core=off \ +-cpu IvyBridge-IBRS,ss=on,vmx=on,movbe=on,hypervisor=on,arat=on,tsc_adjust=on,mpx=on,rdseed=on,smap=on,clflushopt=on,sha-ni=on,umip=on,md-clear=on,stibp=on,arch-capabilities=on,xsaveopt=on,xsavec=on,xgetbv1=on,xsaves=on,pdpe1gb=on,3dnowprefetch=on,rdctl-no=on,skip-l1dfl-vmentry=on,ssb-no=on,mds-no=on,avx=off,f16c=off \ +-m 2048 \ +-overcommit mem-lock=off \ +-smp 2,sockets=2,cores=1,threads=1 \ +-uuid 100213e7-1dfd-4a74-a424-ced175df9461 \ +-no-user-config \ +-nodefaults \ +-rtc base=utc,driftfix=slew \ +-global kvm-pit.lost_tick_policy=delay \ +-no-hpet \ +-global ICH9-LPC.disable_s3=1 \ +-global ICH9-LPC.disable_s4=1 \ +-boot strict=on \ +-device pcie-root-port,port=0x10,chassis=1,id=pci.1,bus=pcie.0,multifunction=on,addr=0x2 \ +-device pcie-root-port,port=0x11,chassis=2,id=pci.2,bus=pcie.0,addr=0x2.0x1 \ +-device pcie-root-port,port=0x12,chassis=3,id=pci.3,bus=pcie.0,addr=0x2.0x2 \ +-device pcie-root-port,port=0x13,chassis=4,id=pci.4,bus=pcie.0,addr=0x2.0x3 \ +-device pcie-root-port,port=0x14,chassis=5,id=pci.5,bus=pcie.0,addr=0x2.0x4 \ +-device pcie-root-port,port=0x15,chassis=6,id=pci.6,bus=pcie.0,addr=0x2.0x5 \ +-device ich9-usb-ehci1,id=usb,bus=pcie.0,addr=0x1d.0x7 \ +-device ich9-usb-uhci1,masterbus=usb.0,firstport=0,bus=pcie.0,multifunction=on,addr=0x1d \ +-device ich9-usb-uhci2,masterbus=usb.0,firstport=2,bus=pcie.0,addr=0x1d.0x1 \ +-device ich9-usb-uhci3,masterbus=usb.0,firstport=4,bus=pcie.0,addr=0x1d.0x2 \ +-device virtio-serial-pci,id=virtio-serial0,bus=pci.2,addr=0x0 \ +-drive file=/var/lib/libvirt/images/ubuntu18.04.qcow2,format=qcow2,if=none,id=drive-virtio-disk0 \ +-device virtio-blk-pci,scsi=off,bus=pci.3,addr=0x0,drive=drive-virtio-disk0,id=virtio-disk0,bootindex=1 \ +-chardev pty,id=charserial0 \ +-device isa-serial,chardev=charserial0,id=serial0 \ +-chardev spicevmc,id=charchannel1,name=vdagent \ +-device usb-tablet,id=input0,bus=usb.0,port=1 \ +-device qxl-vga,id=video0,ram_size=67108864,vram_size=67108864,vram64_size_mb=0,vgamem_mb=16,max_outputs=1,bus=pcie.0,addr=0x1 \ +-chardev spicevmc,id=charredir0,name=usbredir \ +-device usb-redir,chardev=charredir0,id=redir0,bus=usb.0,port=2 \ +-chardev spicevmc,id=charredir1,name=usbredir \ +-device usb-redir,chardev=charredir1,id=redir1,bus=usb.0,port=3 \ +-device virtio-balloon-pci,id=balloon0,bus=pci.4,addr=0x0 \ +-object rng-random,id=objrng0,filename=/dev/urandom \ +-device virtio-rng-pci,rng=objrng0,id=rng0,bus=pci.5,addr=0x0 \ +-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \ +-netdev tap,id=tapnet0,vhost=on,ifname=$vir,script=no,downscript=no \ +-device virtio-net-pci,netdev=tapnet0,mac=52:54:00:31:c5:a2,bus=pci.1,addr=0x0 \ +-device vhost-dsp-pci,topology=sof-apl-uos0.tplg \ +-display vnc="$ip":1 \ +-serial stdio \ +-k de \ +-msg timestamp=on diff --git a/developer_guides/virtualization/images/data-flow-v1.png b/developer_guides/virtualization/images/data-flow-v1.png new file mode 100644 index 00000000..9e3a5fe2 Binary files /dev/null and b/developer_guides/virtualization/images/data-flow-v1.png differ diff --git a/developer_guides/virtualization/images/data-flow.png b/developer_guides/virtualization/images/data-flow.png new file mode 100644 index 00000000..ed7565ed Binary files /dev/null and b/developer_guides/virtualization/images/data-flow.png differ diff --git a/developer_guides/virtualization/images/ipc-v3.png b/developer_guides/virtualization/images/ipc-v3.png new file mode 100644 index 00000000..0236759d Binary files /dev/null and b/developer_guides/virtualization/images/ipc-v3.png differ diff --git a/developer_guides/virtualization/images/ipc.png b/developer_guides/virtualization/images/ipc.png new file mode 100644 index 00000000..a0839f07 Binary files /dev/null and b/developer_guides/virtualization/images/ipc.png differ diff --git a/developer_guides/virtualization/images/topology-v2.png b/developer_guides/virtualization/images/topology-v2.png new file mode 100644 index 00000000..31a35747 Binary files /dev/null and b/developer_guides/virtualization/images/topology-v2.png differ diff --git a/developer_guides/virtualization/images/topology.png b/developer_guides/virtualization/images/topology.png new file mode 100644 index 00000000..baac84f5 Binary files /dev/null and b/developer_guides/virtualization/images/topology.png differ diff --git a/developer_guides/virtualization/running.rst b/developer_guides/virtualization/running.rst new file mode 100644 index 00000000..57a0f403 --- /dev/null +++ b/developer_guides/virtualization/running.rst @@ -0,0 +1,63 @@ +.. _running: + +Run SOF VirtIO +############## + +.. contents:: + :local: + :depth: 1 + +General information +********************** + +To run SOF in a virtualized environment, we are using an Up^2 board that is +natively running Ubuntu Linux. We are using QEMU to run a KVM virtualized +Ubuntu instance. This document describes essential steps that are required to +run this configuration. + +1. Create a VM image +******************** + +We recommend using the "Virtual Machine Manager." Run it on a separate Linux +machine to create a Linux VM image that can be used by QEMU. In this way, the +Manager doesn't have to be installed on the target system, thus leaving +that system clean and free from numerous additional packages that the +Manager installs with it. + +2. Build the Linux kernel +************************* + +You must build kernel images and modules for the host and the guest. Use the +topic/sof-dev branch of the SOF project `kernel repository `_ +as usual. Use the ``sof-host-defconfig`` and ``sof-guest-defconfig`` +`kernel configuration `_ +patches to configure the respective kernels. + +3. Build QEMU +************* + +Use the sof-v4.1 branch of the `SOF QEMU fork `_ +to build QEMU. Use the following flags to configure the QEMU build: + +| --target-list=x86_64-softmmu --enable-kvm --enable-vhost-kernel \\ +| --enable-vhost-dsp --enable-vhost-net --enable-system --enable-spice \\ +| --enable-usb-redir --enable-libusb --audio-drv-list=oss,alsa,pa \\ +| --enable-libssh --enable-gnutls --enable-replication --enable-seccomp \\ +| --prefix=/usr + +4. Build the SOF firmware +************************* + +Build the `SOF firmware `_ +as usual. Two topology images are needed: one for the host and one for the +guest. Both must be installed under the standard directory on the host, where +the SOF topology is usually installed. The required files are +``sof-apl-pcm512x-sos.tplg`` and ``sof-apl-pcm512x-uos.tplg`` which must be +renamed ``sof-apl-pcm512x.tplg`` and ``sof-apl-uos0.tplg`` respectively. + +5. Run QEMU +*********** + +Use the provided :download:`script ` to run QEMU on the target +system. The guest system should present a duplex audio device, routed to +SSP5 on Up^2. diff --git a/developer_guides/virtualization/virtualization.rst b/developer_guides/virtualization/virtualization.rst new file mode 100644 index 00000000..bd520f59 --- /dev/null +++ b/developer_guides/virtualization/virtualization.rst @@ -0,0 +1,367 @@ +.. _virtualization: + +SOF VirtIO design +################# + +.. contents:: + :local: + :depth: 2 + +General information +******************* + +This document describes the Linux driver architecture to virtualize SOF DSP +firmware features across multiple guest VMs. + +The following components are described: + +1. SOF VirtIO (Virtual Frontend) driver to be run on a guest virtual machine. +2. SOF vhost (Virtual Backend) driver to be run on the host. +3. SOF audio topology for both host and guest. +4. SOF supporting code in QEMU. + +This driver architecture allows full SOF audio use cases to reside on either +the guest VM, or host, or both. It also allows non audio DSP firmware +features to be virtualized on guests. + +VirtIO_ is a platform-agnostic virtualization standard, managed by OASIS_. It +defines a design and a protocol for implementing device IO in VMs. + +Different machine-level virtualization designs exist, and each implements a +different software configurations. Typically each design has one instance of +a general purpose OS that runs either directly on the hardware, or at least +with direct unrestricted access to the hardware. This instance is usually +called a "host." Within that host OS, one or more user-space processes exist +that further emulate independent virtual machines and run their own complete +instances of general-purpose OSes. Those instances are called "guests." + +When Linux is running in a VM, multiple drivers are already available, using +virtio: networking, storage, etc. Those drivers use the virtio and virtqueue +APIs to communicate with the host or, potentially, with other guests. + +The host part depends on the used virtualization solution. Some +solutions implement the VirtIO API themselves while using their method of +choice to talk to the host on which they are running. A typical Linux +virtualization implementation uses QEMU + KVM to run guest instances. In +this case, a VirtIO host can be implemented in the user space or in the +kernel. If implemented in the user space, it is managed by QEMU. In thekernel space, VirtIO host drivers use the Linux vhost API. + +Note that VirtIO guest drivers reside in their respective driver +subdirectories in the Linux kernel, such as drivers/net, drivers/block, etc. +On the contrary, all vhost drivers are located under drivers/vhost. That +directory also contains vhost.c and vhost.h which are used by all vhost +drivers. + +Every VirtIO driver requires explicit support in QEMU, which can be very +minimal and simple in cases where all the functionality is implemented in +VirtIO and vhost drivers, or it can actually implement a user-space +counterpart to VirtIO drivers on the guest. In our case, the SOF QEMU +support code is rather minimal; it only adds support for guest reset +reporting and guest identification to a set standard and mostly empty device +node and virtual queue management operations. + +SOF VirtIO design +***************** + +The SOF_ VirtIO design implements an interface to transport IPC ( +Inter-Process Communication) commands and audio data between host and +guests. It re-uses the SOF-defined IPC API (that is also used by the native +SOF driver) to communicate with the SOF firmware that is running on the +audio DSP. This means that the virtualization mechanisms are inserted +between the SOF host driver and the DSP hardware. Thus, IPC messages that +the guest SOF driver sends to the DSP are instead sent to the host, which +then forwards them to the DSP firmware, and then returns the response. + +Typically an IPC message exchange between the driver and the DSP takes place +over a mailbox and a doorbell. A mailbox is a shared memory region between +the DSP and host. It is used for the IPC message payload. A doorbell is +asynchronous signaling hardware between DSP and host. This mailbox / +doorbell interface can be hardware-specific; thus, every hardware variant +provides its own set of mailbox / doorbell handling operations. This +virtualization solution creates its own set of IPC operations to redirect +IPC messages from the guest over virtual queues to the host. + +We use the same IPC message structures; we also add three more +VirtIO-specific IPC messages but they are never sent to the DSP, so the +firmware doesn't need to support them. For the most part, IPCs from guests +are forwarded without change by the host driver to the DSP; only some of +them are intercepted on the host. Some IPC messages trigger additional +actions on the host before being forwarded to the DSP; others aren't sent to +the DSP at all. See examples below for more details. + +Communication channels +---------------------- + +The goals of SOF virtualization are: + +1. Enable streaming to/from guest audio endpoints with the lowest possible + overhead. +2. Allow a guest to control some of the processing on the DSP while + isolating it from other parts of the graph owned by the host or other VMs. + +This includes avoiding copying of audio data and stream position updates. In +this case, only one VirtQueue would be required for IPC message exchange. +However, the initial version also transfers data and stream position updates +via VirtQueues. That means that the SOF VirtIO / vhost drivers currently use +three VirtQueues: for IPC, audio data, and stream position updates. Plans to +support both the copying and the zero-copy methods are forthcoming. The +copying method will be used either where the zero-copy method is unavailable +or for debugging. + +The low latency requirement also forces us to only support the vhost mode; +no plans exist to implement SOF VirtIO host support in the user space. + +Below is the SOF VirtIO architecture: + +.. image:: images/data-flow-v1.png + +Topology +-------- + +When running natively, the SOF driver uses the ALSA topology API to configure +audio pipelines on the DSP. The SOF driver reads a platform-specific topology +file from the filesystem, parses it, and sends all the components from it to the DSP firmware for their instantiation, configuration, and enablement. + +When configured to support virtualized audio, the host topology additionally +provides connection points for guest audio pipelines. Guest playback +pipelines might, for example, connect to mixer and capture pipelines to +demultiplexer components. In this way, both the host and possibly multiple +guests can share parts of the audio topology. It is also possible to +dedicate certain audio pipelines to specific guests only. In such cases, +guest pipeline fragments can be connected to other component types such as +volume control. The term "connection points" is used throughout this +document to refer to these locations in the host audio topology. In the +`example topology image`_ below, the sink endpoint of the "Master volume" +component is a connection point. + +The ALSA / ASoC / DAPM subsystems on the host are unaware of any guests, but +they should be aware of any streaming taking place on host parts of VM +pipelines for configuration and power management. That means that if, for +example, a guest initializes playback, the ASoC subsystem on the host should +be aware that the pipeline joining the respective connection point and the +used codec is currently performing audio playback. To achieve this, for each +such guest connection point we add a virtual PCM as a widget of type +"aif_in" or "aif_out" for playback and capture respectively, and a virtual +0-size buffer. The 0-size buffer is required by ALSA which mandates that one +side of each topology connection must always be a buffer. These virtual +components are only visible on the host and are not sent to the DSP. When +processed by the ASoC topology parser, those virtual PCM widgets serve as +DPCM front-ends and thus create front-end DAI links and ASoC PCM runtime +contexts. These are later used for guest audio stream management. + +When running in a VM, the SOF driver also uses ALSA topology; in this case, +the guest topology should have no relation to the host hardware. The guest +audio topology should be decided upon by the host system administrator. +Therefore we let the SOF instance, running on the guest, obtain topology +from the host. Such a topology should only contain "software components" +like PCMs, buffers, and software volume controls. Those components thus +represent partial pipelines that will then be linked to connection points of +the host SOF topology. + +Given this design, the topology is represented in the following way: + +.. _example topology image: + +.. image:: images/topology-v2.png + +**DSP:** Has a full topology, including host and guests components. No +distinction is made between the host and any guests. + +**host:** The SOF driver core, running on the host, and the ASoC subsystem +only see the host topology. The vhost driver manages connection points +between the host and the guests' topology fragments. + +**guest:** Only "sees" the guest components; therefore, the driver cannot +address components that do not belong to its topology. + +Refer to the SOF virtualized IPC paths below: + +.. image:: images/ipc-v3.png + +As mentioned above, SOF IPC messages from guests are forwarded exactly +1-to-1 to the DSP. For example, if a guest sends an IPC message to adjust +the audio volume on one of the components from its topology, it will be +forwarded to the DSP. That IPC contains the volume component ID as seen by +the respective guest. Therefore, it should be exactly the same ID as the one +used by the firmware. + +DPCM audio routing +------------------ + +In the most trivial case when a user-space application opens an audio +interface, there is a unique sequence of audio components, involved in this +operation, that will comprise a DPCM audio route. Such a sequence is called +an audio pipeline. However, modern audio hardware often contains more +complex audio graphs, sometimes requiring dynamic re-routing. Support for +such configurations is provided by the ASoC DPCM_ API. We also use this API +to activate and deactivate guest audio interfaces. + +Implementation +************** + +Initialization +-------------- + +The SOF driver probing on the host remains unmodified except that the vhost +driver is also initialized; this registers a newly-added /dev/vhost-dsp misc +device that is used by guests to establish VirtQueue links and control the +SOF vhost driver, using dedicated vhost ioctls. + +When QEMU starts a guest instance, it detects the above misc device and +creates a PCI device for the guest with dedicated vendor and device IDs. All +vhost PCI devices use the Qumranet / RedHat vendor ID and are allocated a +device ID from a range, specially donated by RedHat for this purpose. + +The SOF VirtIO driver on the guest registers support for that PCI device and +its .probe() method is called. + +During the SOF VirtIO driver probing, the QEMU SOF code reports to the vhost +driver which VM image is being instantiated. This is used as a reliable +guest identification and is needed for SOF guest audio topology selection. + +The SOF driver on the guest performs partial initialization; it skips any +steps involving communicating with the actual DSP such as sending the +firmware, booting it, and initializing the tracing interface. + +The VirtIO driver on the guest uses a new SOF_IPC_TPLG_VFE_GET +VirtIO-specific IPC message to request the topology from the host. The host +reads the file that corresponds to the guest ID (obtained from the QEMU +communication described above) from its filesystem, and then sends it in +multiple chunks to the guest in reply to that request. + +Guest topology files contain virtual DAI components of type "dai_out" and +"dai_in." Their data tuples contain a new token of type SOF_TKN_COMP_REF, +which contains an ID of the connection point, to which this pipeline should +be attached. + +After obtaining the topology, the guest SOF instance uses a second new +SOF_IPC_TPLG_VFE_COMP_ID VirtIO-specific IPC message to request its allocated +component ID base. It then uses this base to assign IDs to all of its +topology components. Those components are then sent to the DSP firmware. +This ensures that no two components that are sent to the DSP have the same +ID. Note that gaps in component IDs are allowed and don't have any side +effects. + +The host maintains a list of guest component ID ranges to guarantee that +component IDs stay unique regardless of the order in which guests are +brought up and down. + +Next, the guest proceeds by sending the parsed topology over IPC to the host, +which is then forwarded to the DSP firmware. The guest uses the .send_msg() +callback to forward any IPC (including topology related) to the host with no +processing at all. The only exception is blocking IPCs when the guest is +resuming while the host does not need to be resumed (see `reset count`_ in +Deactivation for details). + +The host receives the parsed guest topology over the IPC virtual queue. In +most cases, the host IPC handler just forwards IPCs to the DSP; in other +cases, it has to handle or modify IPC contents locally. One such example is +linking the guest and host topology fragments together. As mentioned above, +guest topology files contain a new token that references connection point +components in the host topology. This value is then assigned to the +.ref_comp_id field of struct sof_ipc_comp_config during topology file +parsing and is sent by the guest to the host. The host then uses that value +to modify SOF_IPC_TPLG_COMP_CONNECT connection IPC messages, involving +connection points, from the guest before sending them to the DSP. + +Streaming +--------- + +Guest audio streaming is mostly transparent for the host. The host audio +subsystem doesn't get involved with most guest streaming or kcontrol +operations because the virtualization is applied at the SOF hardware driver +level, not at the ASoC API level. Those operations are completely processed +on the guest and only resulting DSP IPCs are forwarded to the host. The only +exceptions are beginning and ending of the streaming, at which times the +ASoC subsystem on the host has to be informed that a certain pipeline is +becoming active or inactive respectively. + +The SOF vhost driver uses the STREAM_PCM_PARAMS IPC message from the guest to +allocate an ALSA PCM runtime object and configure the audio hardware. If +zero-copy is not used and audio data is transferred via VirtQueues, DMA +buffers are also allocated. + +Upon reception of the STREAM_TRIG_START IPC message, the vhost driver +activates the associated pipeline and updates the DPCM routing information +on the host. + +As mentioned above, it is our goal to implement data zero-copy. As long as +this isn't the case, the guest VirtIO driver implements .block_write() and +.block_read() SOF DSP operation methods for data streaming via the data +VirtQueue. + +The host receives requests on the data VirtQueue and copies data between PCM +runtime buffers and the queue and responds either with a status or with a +data buffer. + +Similar to the data, streaming buffer position updates are currently also +transferred via a dedicated VirtQueue. Both the host and the guest are +configured to use IPC messages for position updates. Buffers for VirtIO +VirtQueues are always provided by guests; therefore a position update buffer +should always be waiting on the host side to send a position update message +as soon as one arrives from the DSP. + +Upon reception of the STREAM_TRIG_STOP IPC message, the vhost driver updates +the DPCM routing information and deactivates the virtual PCM pipeline, +described in the `Topology`_ section above, on the host. This operation +requires particular care; in the present state, simply calling + +.. code-block:: none + + soc_dpcm_runtime_update(); + snd_soc_runtime_deactivate(); + +does not deactivate the pipeline and therefore doesn't allow the runtime PM +to suspend the interface, which then breaks following activation attempts. +The current SOF VirtIO implementation contains a fix for that, which has to +be upstreamed along with the rest of the ALSA core virtualization +modifications and extensions. + +Deactivation +------------ + +When the guest isn't actively using audio, we want to allow the host to +runtime suspend the DSP. That usually means switching off the DSP. Therefore, +resuming after a runtime suspend is similar to booting the DSP for the first +time during driver probing. During host driver probing, the driver reads in a +DSP firmware image and a topology file and sends both to the DSP. The driver +then keeps the firmware and the topology in RAM to be re-sent to the DSP for +runtime resume. The guest driver in the virtualized setup does the same: it +keeps its topology fragment in memory and only re-sends it to the host for +DSP resume. + +With automatic runtime suspending on the host and the guest, we must +guarantee that the host never suspends the hardware while any of the +guests are active. Therefore, it is natural to forward guests' power +transition events to the host. We add a SOF_IPC_PM_VFE_POWER_STATUS IPC +message for this purpose. Only when all guests have entered runtime-suspend +and any local users have released their audio resources can the host +runtime-suspend the DSP. + +.. _reset count: + +Special consideration is given to the case when a guest resumes after a +runtime suspend. If the DSP also was suspended since the last time that +guest was using it, it has lost the respective topology fragment. However, +if the DSP stayed powered on, it still has the guest's topology and it +shouldn't be received again. To achieve this, the host replies to the +SOF_IPC_PM_VFE_POWER_STATUS message with a status flag, indicating whether +the topology has to be re-sent. + +Guest reboot also requires special handling to deinitialize virtual queues. +At the moment, no standard way exists to get notified about the guest reboot +event on the host. Various vhost drivers implement this in their own +context-specific ways. For example, the vhost networking driver uses a link +status update for that purpose. However, the vsock vhost driver comes very +close to our needs. It doesn't have a suitable context notification; +therefore, it implements a dedicated QEMU / misc-device ioctl +VHOST_VSOCK_SET_RUNNING. We reuse the ioctl definition, but change its name +to VHOST_SET_RUNNING. In this way, no existing user-space software has to be +modified. Then, we use this ioctl() in the QEMU SOF code to inform the SOF +vhost driver about a guest reboot. This is then used to reset the VirtQueue +status on the host. + +.. _DPCM: https://www.kernel.org/doc/html/v4.16/sound/soc/dpcm.html +.. _OASIS: https://www.oasis-open.org/ +.. _SOF: https://sofproject.org/ +.. _VirtIO: https://www.oasis-open.org/committees/tc_home.php?wg_abbrev=virtio 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-3rd-party-toolchain.rst b/getting_started/build-guide/build-3rd-party-toolchain.rst new file mode 100644 index 00000000..2beb0ffc --- /dev/null +++ b/getting_started/build-guide/build-3rd-party-toolchain.rst @@ -0,0 +1,53 @@ +.. _build-3rd-party-toolchain: + +Build SOF with a 3rd Party Toolchain +#################################### + +A "3rd party toolchain" is a supported toolchain provided by an external +organization. + +Toolchains are provided by various vendors and are available under a +variety of commercial, academic, or open source terms; visit the providers' +websites for further information. + +.. contents:: + :local: + :depth: 3 + +Cadence® Tensilica® Xtensa® C/C++ Compiler (XCC) +************************************************ + +.. note:: + Currently |APL|, |CNL| and |ICL| targets are verified with Xtensa C/C++ + Compiler (xt-xcc). The xt-clang compiler is not supported. + +The Xtensa compiler provides support for HiFi coprocessor SIMD instructions. +An example below depicts how to enable conditional compilation of the code depending +on the toolchain installed and the coproccessor model on a target system. + +.. code-block:: c + + /* Select optimized code variant when xt-xcc compiler is used */ + #if defined __XCC__ + #include + #define FIR_GENERIC 0 + #if XCHAL_HAVE_HIFI2EP == 1 + #define FIR_HIFIEP 1 + #define FIR_HIFI3 0 + #elif XCHAL_HAVE_HIFI3 == 1 + #define FIR_HIFI3 1 + #define FIR_HIFIEP 0 + #else + #error "No HIFIEP or HIFI3 found. Cannot build FIR module." + #endif + #else + /* GCC */ + #define FIR_GENERIC 1 + #define FIR_HIFIEP 0 + #define FIR_HIFI3 0 + #endif + + +Once you have installed the toolchain according to procedures outlined in the +toolchain documentation, see the :ref:`build-from-scratch` chapter on how to build +FW binaries. diff --git a/getting_started/build-guide/build-from-scratch.rst b/getting_started/build-guide/build-from-scratch.rst index 88b6b3e6..4c0519af 100644 --- a/getting_started/build-guide/build-from-scratch.rst +++ b/getting_started/build-guide/build-from-scratch.rst @@ -1,351 +1,613 @@ .. _build-from-scratch: -Build SOF from scratch -###################### +Build toolchains and SOF from sources +##################################### .. contents:: :local: :depth: 3 -You may enable and test |SOF| on a target machine or VM. Current target -Intel platforms include: |BYT|, |CHT|, |HSW|, |BDW|, |APL| and |CNL|. +You may boot and test |SOF| on a target machine or VM. Current target +Intel platforms include: |BYT|, |CHT|, |HSW|, |BDW|, |APL|, |CNL|, |ICL|, |JSL|, and |TGL|. -Build SOF binaries -****************** -The following steps describe how to install the sof development environment -on Ubuntu 16.04 or 18.04. +Support also exists for NXP i.MX8/i.MX8X/i.MX8M platforms. + +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:: - The code examples assume ~/work/sof/ as the working directory, and - all git repos should be added to this directory. + 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`. + +Step 1. 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 -p "$SOF_WORKSPACE" + +Step 2. Set up build environment +******************************** + +Install package dependencies +============================ +.. note:: + + 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): -Set up build environment -======================== + .. code-block:: bash -Install package dependencies. + 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 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 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. .. code-block:: bash - $ sudo apt-get install libgtk-3-dev libsdl-dev libspice-protocol-dev libspice-server-dev libusb-1.0-0-dev libusbredirhost-dev \ - libtool-bin iasl valgrind texinfo virt-manager kvm libvirt-bin virtinst libfdt-dev libssl-dev pkg-config + sudo add-apt-repository ppa:ubuntu-toolchain-r/test + sudo apt-get update + sudo apt-get install gcc-7 g++-7 + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 70 --slave /usr/bin/g++ g++ /usr/bin/g++-7 -If you are using Ubuntu 16.04, the gcc must be updated to gcc 7.3+ -for the Advanced Linux Sound Architecture (ALSA) to build. +Install CMake +============= + +If you use Ubuntu 18.04+ or Fedora you can install CMake with apt/dnf: .. code-block:: bash - $ sudo add-apt-repository ppa:ubuntu-toolchain-r/test - $ sudo apt-get update - $ sudo apt-get install gcc-7 g++-7 - $ sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 70 --slave /usr/bin/g++ g++ /usr/bin/g++-7 + 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/. + +Build alsa-lib and alsa-utils from source +========================================= -Build alsa-lib and alsa-utils ------------------------------ +This project requires some new features in :git-alsa:`alsa-lib` and +:git-alsa:`alsa-utils`, so build the newest ALSA from source code. -This project requires some new features in alsa-lib and alsa-utils, so build -the newest ALSA from source code. +.. warning:: + + Installing alsa-lib systemwide may break some audio applications. + Only perform this if you know what you are doing. We recommend that you + install it locally (under $HOME) or use Docker + (see :ref:`build-with-docker`.) .. code-block:: bash - $ cd ~/work/sof/ - $ git clone git://git.alsa-project.org/alsa-lib.git - $ cd alsa-lib - $ ./gitcompile - $ sudo make install + 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 -Replace the default Ubuntu alsa-lib with the one we just built. +(Optional) To enable alsabat's frequency analysis, install the FFT library +before you configure alsa-utils. .. code-block:: bash - $ sudo cp /usr/lib/libasound.* /usr/lib/x86_64-linux-gnu/ - $ sudo cp /usr/lib/alsa_lib/* /usr/lib/x86_64-linux-gnu/alsa-lib + sudo apt-get install libfftw3-dev libfftw3-doc # Ubuntu + sudo dnf install fftw3-devel # Fedora Clone, build, and install alsa-utils. .. code-block:: bash - $ cd ~/work/sof/ - $ git clone git://git.alsa-project.org/alsa-utils.git - $ cd alsa-utils - $ ./gitcompile - $ sudo make install + 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 +parameter. + +.. code-block:: bash -Build toolchain from source -=========================== + cd ../alsa-lib + ./gitcompile --prefix=/usr --libdir=/usr/lib/x86_64-linux-gnu/ + sudo make install + cd ../alsa-utils + ./gitcompile --prefix=/usr --with-curses=ncurses --disable-xmlto --disable-bat + sudo make install -Build cross-compiler --------------------- +.. note:: -Build the xtensa cross compiler with crosstool-ng for Intel |BYT|, -|CHT|, |HSW|, |BDW|, |APL|, and |CNL| platforms. + If the gitcompile script does not work, refer to the INSTALL file for + manual build instructions. -Clone both repos and checkout the sof-gcc8.1 branch. +Create or append to the ``LD_LIBRARY_PATH`` environment variable. .. code-block:: bash - $ cd ~/work/sof/ - $ git clone https://github.com/thesofproject/xtensa-overlay.git - $ cd xtensa-overlay - $ git checkout sof-gcc8.1 - $ cd ~/work/sof/ - $ git clone https://github.com/thesofproject/crosstool-ng.git - $ cd crosstool-ng - $ git checkout sof-gcc8.1 + export LD_LIBRARY_PATH="${SOF_WORKSPACE}"/alsa-lib/src/.libs:$LD_LIBRARY_PATH + +.. _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|, |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. -Build and install the ct-ng tools in the local folder. +For more details go to https://crosstool-ng.github.io/ + +crosstool-ng +============ + +Clone both repos and check out the ``sof-gcc10.2`` and ``sof-gcc10x`` branch. .. code-block:: bash - $ ./bootstrap - $ ./configure --prefix=`pwd` - $ make - $ make install + cd "$SOF_WORKSPACE" + git clone https://github.com/thesofproject/xtensa-overlay + git clone https://github.com/thesofproject/crosstool-ng + git -C xtensa-overlay/ checkout sof-gcc10.2 + git -C crosstool-ng/ checkout sof-gcc10x -Copy the config files to the .config directory, and build the cross compiler -for your target platforms. +Build crosstool-ng and install it in its own source directory. .. code-block:: bash - - #Baytrail - $ cp config-byt-gcc8.1-gdb8.1 .config - $ ./ct-ng build - #Haswell - $ 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 - $ cp config-cnl-gcc8.1-gdb8.1 .config - $ ./ct-ng build - -Copy all four cross-compiler toolchains to ~/work/sof/. + + cd crosstool-ng/ + ./bootstrap + ./configure --prefix=$(pwd) + make + make install + +Toolchains +========== + +The config files provided refer to ``../xtensa-overlay/`` and point at +different ``./builds/xtensa-*-elf`` subdirectories. Copy the ones you +want to ``.config`` and build the cross-compiler(s) for your target +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 - $ ls builds/ - xtensa-apl-elf xtensa-byt-elf xtensa-cnl-elf xtensa-hsw-elf - $ cp -r builds/* ~/work/sof/ + unset LD_LIBRARY_PATH -.. note:: + # 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 - |HSW| and |BDW| share the same cross compiler toolchain: xtensa-hsw-elf + # 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 -Add these compilers to your PATH variable. + # ... 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-*-gcc10.2-gdb9`` file or find +default values missing from it: .. code-block:: bash - $ export PATH=~/work/sof/xtensa-byt-elf/bin/:$PATH - $ export PATH=~/work/sof/xtensa-hsw-elf/bin/:$PATH - $ export PATH=~/work/sof/xtensa-apl-elf/bin/:$PATH - $ export PATH=~/work/sof/xtensa-cnl-elf/bin/:$PATH + ./ct-ng help + cp config-apl-gcc10.2-gdb9 .config + ./ct-ng oldconfig V=1 + diff -u config-apl-gcc10.2-gdb9 .config -Clone header repository. +"Install" toolchains in the expected location by linking +from ``$SOF_WORKSPACE`` to them: .. code-block:: bash - $ cd ~/work/sof/ - $ git clone https://github.com/jcmvbkbc/newlib-xtensa.git - $ cd newlib-xtensa - $ git checkout -b xtensa origin/xtensa + ls builds/ + # xtensa-apl-elf xtensa-byt-elf xtensa-cnl-elf xtensa-hsw-elf xtensa-imx-elf xtensa-imx8m-elf + cd "$SOF_WORKSPACE" + for i in crosstool-ng/builds/xtensa-*; do ln -s "$i"; done -Build and install the headers for each platform. +Remove the temporary build files (~7GB per toolchain): .. code-block:: bash - #Baytrail - $ ./configure --target=xtensa-byt-elf --prefix=~/work/sof/xtensa-root - $ make - $ make install - #Haswell - $ ./configure --target=xtensa-hsw-elf --prefix=~/work/sof/xtensa-root - $ make - $ make install - #Apollo Lake - $ ./configure --target=xtensa-apl-elf --prefix=~/work/sof/xtensa-root - $ make - $ make install - #Cannon Lake - $ ./configure --target=xtensa-cnl-elf --prefix=~/work/sof/xtensa-root - $ make - $ make install - -The required headers are now in ~/work/sof/xtensa-root, and we have set up a -cross compiler toolchain for xtensa DSPs. - -Build firmware binaries -======================= - -After the SOF environment is set up, we can clone the *sof* and *soft* -repos. + rm -rf $SOF_WORKSPACE/crosstool-ng/.build -.. code-block:: bash +.. note:: + + |HSW| and |BDW| share the same toolchain: xtensa-hsw-elf + + |BYT| and |CHT| share the same toolchain: xtensa-byt-elf - $ cd ~/work/sof/ - $ git clone https://github.com/thesofproject/sof.git - $ git clone https://github.com/thesofproject/soft.git + |CNL|, |ICL|, |JSL| and |TGL| share the same toolchain: xtensa-cnl-elf + i.MX8 and i.MX8X share the same toolchain: xtensa-imx-elf -Build with scripts ------------------- -To build |SOF| quickly use the built-in scripts after setting up the -environment. +Additional headers +================== -Build the firmware. +To get some required headers, clone the following newlib repository and +switch to the `xtensa` branch. .. code-block:: bash - $ cd ~/work/sof/sof/ - $ ./scripts/xtensa-build-all.sh + cd "$SOF_WORKSPACE" + git clone https://github.com/jcmvbkbc/newlib-xtensa + cd newlib-xtensa + git checkout -b xtensa origin/xtensa -.. note:: +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 - This script will only work if the PATH includes both crosscompiler and - xtensa-root and they are siblings of the sof and soft repos. + for i in "${SOF_WORKSPACE}"/xtensa-*-elf; do PATH="$PATH:$i"/bin; done -You may specify one or more of the following platform arguments: -``byt``, ``cht``, ``hsw``, ``bdw``, ``apl``, and ``cnl`` +Build and install the newlib headers for each toolchain: .. code-block:: bash - $ ./scripts/xtensa-build-all.sh byt - $ ./scripts/xtensa-build-all.sh byt apl + XTENSA_ROOT="${SOF_WORKSPACE}"/xtensa-root + 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:: -Build with commands -------------------- + ``--prefix=`` expects an absolute path. Define XTENSA_ROOT according to + your environment. -This is a detailed build guide for the *sof* and *soft* repos. +The required headers are now in ``"$SOF_WORKSPACE"/xtensa-root``, and +cross-compilation toolchains for xtensa DSPs are set up. -Build *rimage* before building the *sof* firmware. +.. _build-and-sign-firmware-binaries-from-scratch: + +Step 4. Build and sign firmware binaries +**************************************** + +After the SOF environment is set up, clone the *sof* repo: .. code-block:: bash - $ ./autogen.sh - $ ./configure --enable-rimage - $ make - $ sudo make install + cd "$SOF_WORKSPACE" + git clone --recursive https://github.com/thesofproject/sof + cd sof -Then configure and make -for |BYT|: +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 - $ ./configure --with-arch=xtensa --with-platform=baytrail --with-root-dir=`pwd`/../xtensa-root/xtensa-byt-elf --host=xtensa-byt-elf - $ make - $ make bin + 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. + + .. code-block:: bash + + rm -rf $SOF_WORKSPACE/sof/installer-builds + make -C installer/ + +Re-configure and rebuild from scratch +===================================== -for |CHT|: +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 - $ ./configure --with-arch=xtensa --with-platform=cherrytrail --with-root-dir=`pwd`/../xtensa-root/xtensa-cht-elf --host=xtensa-cht-elf - $ make - $ make bin + cd "$SOF_WORKSPACE"/sof/ + ./scripts/xtensa-build-all.sh -a +.. note:: + + This script works only if the cross-compiler and ``xtensa-root`` are + siblings in the same ``sof`` directory, as instructed above. -for |HSW|: +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 - $ ./configure --with-arch=xtensa --with-platform=haswell --with-root-dir=`pwd`/../xtensa-root/xtensa-hsw-elf --host=xtensa-hsw-elf - $ make - $ make bin + ./scripts/xtensa-build-all.sh byt + ./scripts/xtensa-build-all.sh byt apl -for |BDW|: +For the latest platforms list and help message, run the script without +any argument. You can also enable debug builds with -d, enable rom +builds with -r and speed up the build with -j [n] .. code-block:: bash - $ ./configure --with-arch=xtensa --with-platform=broadwell --with-root-dir=`pwd`/../xtensa-root/xtensa-hsw-elf --host=xtensa-hsw-elf - $ make - $ make bin + ./scripts/xtensa-build-all.sh -d byt + ./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. The +installer Makefile above relies on this for incremental builds. + +Snippets below assume that your current directory is the root of the +``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 +the same source without starting from scratch. -for |APL|: +.. note:: + + The ``-j`` argument tells make how many processes to use concurrently. + Select a value that matches your build system. + +for |BYT|: .. code-block:: bash - $ ./configure --with-arch=xtensa-smp --with-platform=broxton --with-root-dir=`pwd`/../xtensa-root/xtensa-bxt-elf --host=xtensa-bxt-elf - $ make - $ make bin + mkdir build_byt && cd build_byt + cmake -DTOOLCHAIN=xtensa-byt-elf -DROOT_DIR="$XTENSA_ROOT"/xtensa-byt-elf -DINIT_CONFIG=baytrail_defconfig .. + make help # lists all available targets + make bin -j4 VERBOSE=1 -for |CNL|: +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. + + +.. note:: + + 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 + with 'make menuconfig'. .. code-block:: bash - $ ./configure --with-arch=xtensa-smp --with-platform=cannonlake --with-root-dir=`pwd`/../xtensa-root/xtensa-cnl-elf --host=xtensa-cnl-elf - $ make - $ make bin + mkdir build_cnl_custom && cd build_cnl_custom + 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 + +.. note:: + + If you have `Ninja `_ installed, you can use it + instead of Make. Just type *cmake -GNinja ...* during the configuration + step. + Firmware build results ----------------------- +====================== -The firmware binary files are located in src/arch/xtensa/. Copy them to -your target machine's /lib/firmware/intel/ folder. +The firmware binary files are located in build_/src/arch/xtensa/. +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 + sof-apl.ri sof-bdw.ri sof-byt.ri sof-cht.ri sof-cnl.ri sof-hsw.ri +.. _build-topology-and-tools-from-scratch: -Build topology and tools -======================== +Step 5. Build topology and tools +******************************** -Build with scripts ------------------- +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-mainline:`scripts/build-tools.sh` builds +the default CMake target "ALL" of :git-sof-mainline:`tools/`. + +.. code-block:: bash + + 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 - $ cd ~/work/sof/sof/ - $ ./scripts/build-soft.sh + ./scripts/build-tools.sh -h -Build with commands -------------------- +Incremental build +================= .. code-block:: bash - $ cd ~/work/sof/soft/ - $ ./autogen.sh - $ ./configure - $ make + cd "$SOF_WORKSPACE"/sof/tools/ + mkdir build_tools && cd build_tools + cmake .. + make -j4 + +If your ``cmake --version`` is 3.13 or higher, you may prefer the new -B option: + +.. code-block:: bash + + cmake -B build_tools/ + make -C build_tools/ -j4 VERBOSE=1 + 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. The installer Makefile copies them to the target machine's +``/lib/firmware/intel/sof-tplg/`` folder. -The topology files are all in the topology folder. Copy them to the target -machine's /lib/firmware/intel/ folder. +The *sof-logger* tool is in the *tools/build_tools/logger* folder. The +installer Makefile copies them to the target directory of your choice. -The *rmbox* tool is in the *rmbox* folder. Copy it to the target machine's -/usr/bin directory. +.. _Build Linux kernel: -Build Linux kernel -****************** +Step 6. Build Linux kernel +************************** -|SOF| uses the Linux kernel dev branch, and we need it to 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 - $ cd ~/work/sof/ - $ git clone https://github.com/thesofproject/linux.git - $ cd linux - $ git checkout sof-dev - $ make menuconfig + sudo apt-get install bison flex libelf-dev + 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 - Select 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. .. code-block:: bash - $ make deb-pkg -j 4 + make deb-pkg -j 4 - .. note:: +#. Copy the three resulting *.deb* files from $SOF_WORKSPACE to the + target machine and install them. - The *-j* argument indicites the number of cores to use in the build - process. Select a value that matches your build system. + .. code-block:: bash -#. Copy resulting *.deb* files to the target machine and install them. + sudo dpkg -i /absolute/path/to/deb/file + sudo apt-get install -f diff --git a/getting_started/build-guide/build-with-docker.rst b/getting_started/build-guide/build-with-docker.rst index c6f648f0..de972829 100644 --- a/getting_started/build-guide/build-with-docker.rst +++ b/getting_started/build-guide/build-with-docker.rst @@ -10,181 +10,182 @@ Build SOF with Docker This guide will show you how to use a Docker image containing the |SOF| build environment. -.. note:: +Set up the workspace directory +****************************** - The example uses ~/work/sof/ as the working directory. +1. Point the ``$SOF_WORKSPACE`` environment variable to the directory + in which you store all SOF work. -Clone the *sof* and *soft* repo. + .. code-block:: bash -.. code-block:: bash + SOF_WORKSPACE=~/work/sof + mkdir -p "$SOF_WORKSPACE" - $ cd ~/work/sof/ - $ git clone https://github.com/thesofproject/sof.git - $ git clone https://github.com/thesofproject/soft.git +#. Clone the SOF repository. -Set up Docker -************* + .. code-block:: bash -Docker is a popular container management framework. To install on Ubuntu, -visit `Get Docker CE for Ubuntu `__. + cd "$SOF_WORKSPACE" + git clone --recurse-submodules https://github.com/thesofproject/sof.git -Installation instructions for other Linux distributions: `About Docker CE `__. - -Set Proxy -========= +Set up Docker +************* -Docker must be configured if used behind a proxy. -Visit `HTTP/HTTPS proxy `__ for the guide. +Docker is a popular container management framework. To install Docker and get the Docker image with the SOF build environment: -Set user group -============== +1. Install Docker. -To use Docker without ``sudo`` follow these post-install steps. -`Post-installation steps for Linux `__ + For information on how to install Docker on Ubuntu, visit `Install + Docker Engine on Ubuntu + `__. -Get Docker image -================ + For information on how to install Docker on other Linux + distributions, visit `Install Docker Engine + `__. -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. +#. Optionally, configure Docker to run under a proxy. -.. note:: + For more information about configuring Docker to use a proxy, visit + `HTTP/HTTPS proxy + `__. - Building the container from DockerFile will take more than 2 hours, - so we recommend using the pre-built image. +#. To use Docker without ``sudo``, add your user to the `docker` group. -Pull Docker image ------------------ + For more information, visit + `Post-installation steps for Linux `__. -Pull the docker image from Docker Hub. +#. Get a Docker image with the SOF build environment. -.. code-block:: bash + 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: - $ docker pull xiulipan/sof + - Option 1. Pull the Docker image from Docker Hub and retag the image with `sof` for scripts: -.. note:: + .. code-block:: bash - 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. + docker pull thesofproject/sof + docker tag thesofproject/sof sof -Retag the image with `sof` for scripts. + .. note:: -.. code-block:: bash + 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. - $ docker tag xiulipan/sof sof + - Option 2. Build a Docker image: + .. note:: -Build Docker image ------------------- + 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. -Run the Docker build from the `sof` repo. + .. code-block:: bash -.. code-block:: bash + cd "${SOF_WORKSPACE}"/sof/scripts/docker_build/sof_qemu + ./docker-build.sh + cd "${SOF_WORKSPACE}"/sof/scripts/docker_build/sof_builder + ./docker-build.sh - $ cd ~/work/sof/sof/ - $ cd scripts/docker_build/ - $ ./docker-build.sh + Verify that the docker image is built successfully. -After building the Docker image you will see: + .. code-block:: bash -.. code-block:: bash + docker images + + #REPOSITORY TAG IMAGE ID CREATED SIZE + #sof latest c8b0e8913fcb 2 days ago 1.46 GB - $ docker images - REPOSITORY TAG IMAGE ID CREATED SIZE - sof latest c8b0e8913fcb 2 days ago 1.46 GB - -Build with Docker -***************** - -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 ~/work/sof/sof/ - $ ./scripts/docker-run.sh ./scripts/xtensa-build-all.sh -l - -.. note:: - - ./scripts/docker-run.sh will mount the *sof* and *soft* directories - into Docker container and build them inside the container. The build - result can be accessed outside the container after the build. - -.. note:: + cd "${SOF_WORKSPACE}"/sof/ + ./scripts/docker-run.sh ./scripts/xtensa-build-all.sh -a - The ``-l`` argument causes *rimage* to be installed in the - local *sof* folder and does not change the container environment. +``./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 ~/work/sof/sof/ - # Baytrail - $ ./scripts/docker-run.sh ./scripts/xtensa-build-all.sh -l byt - # Baytrail and Apollo Lake - $ ./scripts/docker-run.sh ./scripts/xtensa-build-all.sh -l byt apl + cd "${SOF_WORKSPACE}"/sof/ + # Bay Trail + ./scripts/docker-run.sh ./scripts/xtensa-build-all.sh byt + # 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 ~/work/sof/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/ 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 *soft* tools and topology files. +Build the SOF tools and topology files. .. code-block:: bash - $ cd ~/work/sof/sof/ - $ ./scripts/docker-run.sh ./scripts/build-soft.sh + cd "${SOF_WORKSPACE}"/sof/ + ./scripts/docker-run.sh ./scripts/build-tools.sh Build inside container ----------------------- +====================== -Enter the container bash. - -.. code-block:: bash +1. Enter the container bash: - $ cd ~/work/sof/sof/ - $ ./scripts/docker-run.sh bash + .. code-block:: bash + + cd "${SOF_WORKSPACE}"/sof/ + ./scripts/docker-run.sh bash -From inside the container, 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. Copy them to the target -machine's /lib/firmware/intel/ 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 *rmbox* tool is in the *rmbox* 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/cavs-boot/index.rst b/getting_started/cavs-boot/index.rst deleted file mode 100644 index 38d84943..00000000 --- a/getting_started/cavs-boot/index.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. _cavs-boot: - -Booting up CAVS ADSP -#################### - -.. toctree:: - :maxdepth: 2 - - cavs-dsp-boot-overview - apollolake/index diff --git a/getting_started/index.rst b/getting_started/index.rst index 24d234f3..3d585a7c 100644 --- a/getting_started/index.rst +++ b/getting_started/index.rst @@ -3,37 +3,84 @@ Getting Started Guides ###################### -Building SOF -************ +Refer to the following getting started guides if you are new to SOF or if you are performing a task for the first time. + +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 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 + + build-guide/build-from-scratch + build-guide/build-with-docker + build-guide/build-3rd-party-toolchain + build-guide/build-with-zephyr + +Set up SOF on a Linux machine +***************************** + +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 - :glob: - build-guide/* + setup_linux/prepare_build_environment -Setting up SOF on hardware -************************** +Then proceed based on if you are installing locally or through ktest: .. toctree:: :maxdepth: 1 - setup/setup_minnowboard_turbot - setup/setup_up_2_board - setup/setup_ktest_environment + setup_linux/install_locally + setup_linux/setup_ktest_environment -Audio processing components -*************************** +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 - effects/how-to-use-sof-eqctl-tool + 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 +************************************ -Other guides -************ +This section descibes process of building loadable modules using LMDK. .. toctree:: :maxdepth: 1 - cavs-boot/index + 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 4a4dc871..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 - $ cd /boot/initrd.img-4.13.0-16-generic cd /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 - $ 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 87% rename from getting_started/setup/setup_minnowboard_turbot.rst rename to getting_started/setup_special_device/setup_minnowboard_turbot.rst index 333ffd54..9c1f4dbf 100644 --- a/getting_started/setup/setup_minnowboard_turbot.rst +++ b/getting_started/setup_special_device/setup_minnowboard_turbot.rst @@ -62,9 +62,9 @@ Set up Xtensa config to build with xt-xcc: .. code-block:: bash - $ tar xvzf Intel\_HiFiEP\_linux.tgz - $ cd Intel\_HiFiEP - $ ./install + tar xvzf Intel\_HiFiEP\_linux.tgz + cd Intel\_HiFiEP + ./install #. When prompted, enter the Xtensa tools directory. @@ -95,7 +95,7 @@ After you have built and setup SOF: UCM --- -Pierre Brossart provides a `UCM repository `__ that supports both headset mode and line in/out mode. +Pierre Bossart provides a `UCM repository `__ that supports both headset mode and line in/out mode. You can also use alsactl to restore a recommended asound.state file for the amixer setting. @@ -128,13 +128,13 @@ Branch: heads/topic/sof-v4.14 .. code-block:: bash - $ make -j8 && make -j8 deb-pkg + make -j8 && make -j8 deb-pkg #. Package all the \*.deb files for MinnowBoard, and install all deb files. .. code-block:: bash - $ dpkg -i \*.deb + dpkg -i \*.deb #. Reboot your system. @@ -156,24 +156,24 @@ Steps: .. code-block:: bash - $ vim startup.nsh + vim startup.nsh Add the following: .. code-block:: bash - $ fs0: - $ cd EFI - $ AudioSsdtUpdate.efi "codecname".aml(like RT5651.aml or DA7212.aml) - $ cd ubuntu - $ grubx64.efi + fs0: + cd EFI + AudioSsdtUpdate.efi "codecname".aml(like RT5651.aml or DA7212.aml) + cd ubuntu + grubx64.efi #. Move .aml and .efi files to EFI directory. .. code-block:: bash - $ cp \*.aml /boot/efi/EFI - $ cp AudioSsdtUpdate.efi /boot/efi/EFI + cp \*.aml /boot/efi/EFI + cp AudioSsdtUpdate.efi /boot/efi/EFI #. Configure the BIOS. diff --git a/getting_started/setup/setup_up_2_board.rst b/getting_started/setup_special_device/setup_up_2_board.rst similarity index 63% rename from getting_started/setup/setup_up_2_board.rst rename to getting_started/setup_special_device/setup_up_2_board.rst index a6f56548..091eb418 100644 --- a/getting_started/setup/setup_up_2_board.rst +++ b/getting_started/setup_special_device/setup_up_2_board.rst @@ -16,22 +16,25 @@ version is not currently supported. Setup Instructions ****************** -1. Flash BIOS version 3.6 onto the Up squared board. +1. Flash BIOS version 4.0 onto the Up squared board. ====================================================== -BIOS v3.6 added the audio OEM key for SOF. The BIOS main menu -will show UP-APL01 R3.6. +The BIOS main menu will show UP-APL01 R4.0. -* Download the `BIOS `__. +* Download the `BIOS `_. - .. todo:: - - this link needs to be updated to something accessible externally +* If the current BIOS version is older than 1.8, please update to v1.8 + before flashing v4.0. + + .. note:: + + To check your BIOS version press + + 1) DELETE or + 2) F7 and select 'Enter Setup' -* Press F2 to enter BIOS main menu and check the BIOS version. * Press ENTER when prompted for password. -* If the current BIOS version is older than 1.8, please update to 1.8 - before flashing v3.6. + * Use board `BIOS update `__ instructions to flash the BIOS. @@ -51,47 +54,41 @@ guide, if needed. 3. Update kernel ================ -Update kernel based on https://github.com/thesofproject/linux from the -``topic/sof-dev`` branch. - -ref kernel config: -https://drive.google.com/open?id=1IYiTeCUFqZkLMPCRS0abtXYFtLqEKckq - -.. todo:: - - this link needs to be updated to something accessible externally +Follow :ref:`Build Linux kernel` section 4. Firmware =========== -Build SOF firmware and copy ``sof-apl.ri`` into /lib/firmware/intel +Build SOF firmware and copy ``sof-apl.ri`` into /lib/firmware/intel/sof 5. Topology =========== Copy test topology -``test-ssp5-I2S-volume-s16le-s24le-48k-24576k-codec.tplg`` as -``sof-apl-pcm512x.tplg`` into /lib/firmware/intel +``sof-apl-eq-pcm512x.tplg`` as +``sof-apl-pcm512x.tplg`` into /lib/firmware/intel/sof-tplg 6. Add ACPI support for Hifiberry dac+ ====================================== -Copy scripts from https://github.com/plbossart/acpi-scripts +Clone scripts from https://github.com/thesofproject/acpi-scripts .. code-block:: bash - $ sudo ./install hooks - $ sudo ./acpi-add Up2/\*.asl + sudo ./install_hooks + sudo ./acpi-add Up2/PCM512X.asl Reboot and check if the status of the device is 15 .. code-block:: bash - $ cat /sys/bus/acpi/devices/104C5122\\:00/status + cat /sys/bus/acpi/devices/104C5122\:00/status 7. Add sst drivers to blacklist-dsp.conf ======================================== +Create blacklist-dsp.conf in /etc/modprobe.d/ if not exist + :: blacklist snd\_soc\_sst\_acpi @@ -109,3 +106,7 @@ Reboot and check if the status of the device is 15 ========= Make sure the green LED lights up on the Hifiberry. + +.. note:: + + If any problem has occured use ``dmesg | grep sof`` to track it. diff --git a/howtos/index.rst b/howtos/index.rst deleted file mode 100644 index d039d2d3..00000000 --- a/howtos/index.rst +++ /dev/null @@ -1,27 +0,0 @@ -.. _howtos: - -How-Tos -####### - -Our technical documentation for Project SOF is being developed right -along with the features. Here are some how-to technical notes that help -explain how you can use SOF capabilities. - - -Technical Notes -=============== - -.. toctree:: - :maxdepth: 1 - :glob: - - tech/* - -Process Notes -============= - -.. toctree:: - :maxdepth: 1 - :glob: - - process/* diff --git a/howtos/process/docbuild.rst b/howtos/process/docbuild.rst deleted file mode 100644 index 63862b8a..00000000 --- a/howtos/process/docbuild.rst +++ /dev/null @@ -1,217 +0,0 @@ -.. _sof_doc: - -SOF documentation generation -############################ - -These instructions will walk you through generating the SOF Project's -documentation and publishing it to https://thesofproject.github.io. -You can also use these instructions to generate the ARCN documentation -on your local system. - -Documentation overview -********************** - -The SOF Project content is written using the reStructuredText markup -language (.rst file extension) with Sphinx extensions, and processed -using Sphinx to create a formatted stand-alone website. Developers can -view this content either in its raw form as .rst markup files, or you -can generate the HTML content and view it with a web browser directly on -your workstation. - -You can read details about `reStructuredText`_, and `Sphinx`_ from -their respective websites. - -The project's documentation contains the following items: - -* ReStructuredText source files used to generate documentation found at the - http://thesofproject.github.io website. All of the reStructuredText sources - are found in this thesofproject/sof-docs repo. - -The reStructuredText files are processed by the Sphinx documentation system, -and make use of the breathe extension for including the doxygen-generated API -material. - - -Set up the documentation working folders -**************************************** - -You'll need git installed to get the working folders set up: - -* For an Ubuntu development system use: - - .. code-block:: bash - - sudo apt-get install git - -* For a Fedora development system use - - .. code-block:: bash - - sudo dnf install git - -We use github.io -for publishing the generated documentation. -Here's the recommended folder setup for documentation contributions and -generation: - -.. code-block: none - - thesofproject/ - sof/ - sof-docs/ - -The parent ``thesofproject`` folder is there because we'll also be using a -publishing area (``thesofproject.github.io``) later in these steps. It's -best if the ``sof-docs`` folder is an ssh clone of your personal fork of the -upstream project repos (though https clones work too): - -#. Use your browser to visit https://github.com/thesofproject and do a - fork of the ``sof-docs`` repo to your personal GitHub account.) - - .. image:: images/fork-sof-docs.png - -#. At a command prompt, create the working folder and clone the sof-docs - repository to your local computer (and if you have publishing rights, the - thesofproject.github.io repo). If you don't have publishing rights - you'll still be able to generate the docs locally, but not publish them: - - .. code-block:: bash - - cd ~ - mkdir thesofproject && cd thesofproject - git clone git@github.com:/thesofproject/sof-docs.git - -#. The documentation of the sof source code generated by doxygen is referenced - and included by the sof-docs. Clone the sof repository too: - - .. code-block:: bash - - git clone git@github.com:thesofproject/sof.git - # use next until merged back to master - cd sof - git checkout next - cd .. - -#. For the cloned local repos, tell git about the upstream repo: - - .. code-block:: bash - - cd sof-docs - git remote add upstream git@github.com:thesofproject/sof-docs.git - -#. If you haven't done so already, be sure to configure git with your name - and email address for the signed-off-by line in your commit messages: - - .. code-block:: bash - - git config --global user.name "David Developer" - git config --global user.email "david.developer@company.com" - -Installing the documentation tools -********************************** - -Our documentation processing has been tested to run with: - -* Python 3.6.3 -* Doxygen version 1.8.13 -* Sphinx version 1.7.5 -* Breathe version 4.9.1 -* docutils version 0.14 -* sphinx_rtd_theme version 0.4.0 - -The SOF documentation makes use of additional Sphinx extensions used for -creating drawings: - -* sphinxcontrib-plantuml -* sphinx.ext.graphviz (included with Sphinx) - -.. note:: The plantuml extension uses Java to render the uml drawing - syntax into an image. You'll need to have a Java runtime environment - (JRE) installed when generating documentation. - -Depending on your Linux version, install the needed tools: - -* For Ubuntu use: - - .. code-block:: bash - - sudo apt-get install doxygen python3-pip python3-wheel make \ - default-jre graphviz - -* For Fedora use: - - .. code-block:: bash - - sudo dnf install doxygen python3-pip python3-wheel make \ - default-jre graphviz - -And for either Linux environment, install the remaining python-based -tools: - -.. code-block:: bash - - cd ~/thesofproject/sof-docs - pip3 install --user -r scripts/requirements.txt - -And with that you're ready to generate the documentation. - -Documentation presentation theme -******************************** - -Sphinx supports easy customization of the generated documentation -appearance through the use of themes. Replace the theme files and do -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. - -Running the documentation processors -************************************ - -The sof-docs directory has all the .rst source files, extra tools, and Makefile for -generating a local copy of the SOF technical documentation. - -.. code-block:: bash - - cd ~/thesofproject/sof - ./autogen.sh - ./configure --enable-doc - make doc - - cd ~/thesofproject/sof-docs - make html - -Depending on your development system, it will take about 10 seconds to -collect and generate the HTML content. When done, you can view the HTML -output with your browser started at ``~/thesofproject/sof-docs/_build/html/index.html`` - -Publishing content -****************** - -If you have merge rights to the thesofproject repo called -thesofproject.github.io, you can update the public project documentation -found at https://thesofproject.github.io. - -You'll need to do a one-time clone of the upstream repo (we publish -directly to the upstream repo rather than to a personal forked copy): - -.. code-block:: bash - - cd ~/thesofproject - git clone git@github.com:thesofproject/thesofproject.github.io.git - -Then, after you've verified the generated HTML from ``make html`` looks -good, you can push directly to the publishing site with: - -.. code-block:: bash - - make publish - -This will delete everything in the publishing repo's **latest** folder -(in case the new version has -deleted files) and push a copy of the newly-generated HTML content -directly to the GitHub pages publishing repo. The public site at -https://thesofproject.github.io will be updated within a few minutes -so it's best to verify the locally generated html before publishing. - -.. _reStructuredText: http://sphinx-doc.org/rest.html -.. _Sphinx: http://sphinx-doc.org/ diff --git a/howtos/process/images/fork-sof-docs.png b/howtos/process/images/fork-sof-docs.png deleted file mode 100644 index a4b15645..00000000 Binary files a/howtos/process/images/fork-sof-docs.png and /dev/null differ diff --git a/index.rst b/index.rst index 9fbb9ddb..500c5794 100644 --- a/index.rst +++ b/index.rst @@ -6,7 +6,7 @@ SOF Project documentation Welcome to the SOF Project (version |version|) documentation. For information about the changes and additions for releases, please -consult the published :ref:`release_notes` documentation. +consult the published :ref:`release` documentation. Source code for the SOF Project is maintained in the `SOF Project GitHub repo`_, and is provided under the BSD 3-clause @@ -24,13 +24,16 @@ Sections .. toctree:: :maxdepth: 1 + SOF project website introduction/index.rst getting_started/index.rst architectures/index.rst platforms/index.rst + algos/index.rst developer_guides/index.rst - release_notes.rst - howtos/index.rst + release.rst contribute/index.rst + tsc/index.rst + maintainers/index.rst api/index.rst presentations/index.rst diff --git a/introduction/images/pipeline-overview.png b/introduction/images/pipeline-overview.png new file mode 100644 index 00000000..92607e1a Binary files /dev/null and b/introduction/images/pipeline-overview.png differ diff --git a/introduction/images/sdk-overview.png b/introduction/images/sdk-overview.png new file mode 100644 index 00000000..a6bffe92 Binary files /dev/null and b/introduction/images/sdk-overview.png differ diff --git a/introduction/index.rst b/introduction/index.rst index ad796634..6b007c57 100644 --- a/introduction/index.rst +++ b/introduction/index.rst @@ -3,225 +3,246 @@ Introduction to the SOF Project ############################### -|SOF| (SOF) is an open source audio Data Signal Processing (DSP) firmware -infrastructure and SDK that offers a single code base for all Intel -hardware platforms. SOF provides infrastructure, real-time control pieces, and -audio drivers as a community 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) 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 or who are interested in -microkernels that run on small but powerful processors. - -SOF currently targets the Cadence xtensa architecture DSPs found on some -Intel-based devices such as the MinnowBoard MAX. |SOF| is modular and -generic so it can be ported to other DSP architectures or host -platforms. - -.. contents:: - :local: - :depth: 3 - -Components -========== - -The |SOF| SDK comprises of five source components: - -#. **SOF source code.** The firmware is written in C with some - architecture-specific assembler; it does not link to external - dependencies. - -#. **SOF tools.** These tools are required to convert firmware from the - ELF file format to formats understood by the kernel drivers and tools to - assist with debugging running firmware images. - -#. **ASoC Linux kernel drivers.** An ASoC kernel driver is required to - register the DSP and firmware as a kernel audio device and to expose - PCMs, kcontrols etc. This driver can also load any topology data. - -#. **Crosstool-NG toolchain.** Crosstool-NG is used to build a GNU cross - toolchain (gcc, gdb, binutils, etc.) that is used to build the firmware - binaries. Other compilers and toolchains can also be used to build the - firmware. - -#. **Qemu DSP and host emulator.** Qemu is used to provide a functional - emulator to simultaneously trace and debug driver and DSP firmware code. - -SOF Architecture -================ - -The diagram below shows the high-level firmware architecture with the -Baytrail platform integration. 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 +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. - `Sound Open Firmware Architecture` +- Audio processing components. -SOF Driver Architecture -======================= +- DSP infrastructure and drivers. -The ASoC driver architecture for |SOF| is shown in the diagram below. -The driver architecture is also split into four layers, like a protocol -stack, each with a different purpose. +- Host OS infrastructure and drivers. + +.. figure:: images/pipeline-overview.png + :align: center + :alt: SDK Overview + :width: 1000px + :height: 300px -#. **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. + `Example Equalizer pipeline with host OS control of EQ coefficients and pipeline volume.` -#. **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 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. +|SOF| has a modular and generic codebase and can be ported to different DSP +architectures or host platforms. See the list of currently supported DSP +architecures and supported platforms. -#. **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. +SDK Introduction and Overview +============================= - .. figure:: images/driver-arch-diag.png +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: SOF Driver Architecture - :width: 800px + :alt: SDK Overview + :width: 1000px + + `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 +-------------------------------------- + +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. + +- 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. + +- Debug Tools - scripts and tools that can be used to debug 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. + +- Runtime Tools - command line applications that can be used to exchange data with running firmware. + +- 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 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 host driver is responsible for: + +- Loading firmware from the host file system into DSP memories and booting. + +- Loading topologies from the host file system into DSP. + +- Exposing audio control devices to applications. + +- Exposing audio data endpoints to applications. + +- Managing IPC communication between the host and DSP. - `Sound Open Firmware Driver Architecture` +- Abstraction of the host-side DSP hardware to common API operations. -The right-hand side of the diagram shows the mailbox/doorbell mechanism and the DSP. +The Linux SOF ALSA/ASoC driver is upstream in Linux v5.2 onwards. -The PCM and IPC drivers can be reused without modification on every -platform. The platform differentiation will occur via the topology data -and firmware. There is also scope for differentiation via the machine -driver and platform driver. The ACPI or Device Tree could be used to -specify the HW configuration. -FAQ -=== +Firmware Toolchain +------------------ -What license does the firmware and SDK use? - The firmware is released using a standard BSD license with some parts - MIT. The SDK is GPL. +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 +and budget. GCC complier is open source. + + +DSP Emulator +------------ + +Qemu can be used to provide a functional emulator to simultaneously trace and +debug driver and DSP firmware code. Proprietary emulators are also available. + +Emulation is also used within SOF CI for feature validation prior to merging +new code. + + +General FAQ +=========== + +What license does the firmware use? + The firmware is released using a standard BSD 3-clause license with some + files released under MIT. 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 do decide to open source - work. + changes private. Patches are always welcomed if you do decide to open + source work. -What DSP architectures are supported? - |SOF| currently supports the Cadence/Tensilica Xtensa audio DSP - architecture and ISA. +What license does the host driver use? + 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. -What host platforms are supported? - |SOF| currently supports the Intel Baytrail and Cherrytrail based - platforms. This includes devices like the MinnowBoard MAX and the ASUS - T100 laptop, but should also include any Baytrail or Cherrytrail based - devices that have the audio DSP enabled in the BIOS. - - The code has also been designed to easily port to other host platform - architectures like ARM, MIPS etc. +Do I need to open source my driver code changes? + 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? - Please join the developer mailing where new development features and - patches are discussed: - 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| has a similar development model to the Linux kernel. Patches are - discussed and posted on the mailing list before being merged. The - 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. - -Who is working on |SOF|? - Currently Intel is sponsoring development work on the MinnowBoard MAX - and other Intel-based platforms. - -How do I add support for DSP architecture X? - It's straightforward enough to add support for a new DSP architecture. - New architectures usually requires support in the GNU tool chain, - although other tool chains can be used, too. It also helps to have qemu - support for the architecture in order to provide an emulator. - - The main work in adding the new architecture 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 version of common C functions (memcpy, - memset, etc) for that architecture. Adding a new architecture also - usually means adding a new host platform too. + |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 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? + 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? - There is nothing stopping the firmware working with non Linux based OSes - providing a driver exists or can be written for that OS. The main area - for potential optimization in porting to another OS is aligning the IPC - (Inter processor Communication) mechanism to the audio driver flow for - that OS. Sound Open Firmware has IPC optimized for the ALSA driver flow, - but it's easy enough to reuse this IPC on other OSes or add a completely - new IPC for the OS of your choice. The IPC ABI is defined in - src/include/uapi/ and the IPC IO logic lives in src/ipc/. - - The current upstream IPC uses a memory mapped doorbell and mailbox to - pass messages between the host and DSP. Non memory mapped IO (like I2C) - can also be supported by adding new doorbell and mailbox driver in your - platform code. + See the SOF host architecture page. What audio components are supported? - Firmware currently supports mixers, volume, DAIs and Host PCMs in the - upstream code base. More components are in progress... + |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? - The current upstream supports creating statically defined pipelines in - src/audio/static-pipeline.c. This default pipeline can be changed in - this file and in the driver to match any new pipeline topology. + 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. - Dynamic pipeline topology will be supported upstream soon. This will - allow pipelines to be defined at run time in the firmware and driver by - using the alsa topology framework. + 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 new file mode 100644 index 00000000..450bc783 --- /dev/null +++ b/maintainers/admin.rst @@ -0,0 +1,28 @@ +.. _admin: + +SOF admin +######### + +Given the size of the project, maintainer rights are granted +to multiple contributors: + ++---------------+-------------------+---------------+ +| Intel | Lech Betlej | @lbetlej | ++---------------+-------------------+---------------+ +| Intel | Liam Girdwood | @lgirdwood | ++---------------+-------------------+---------------+ +| 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 +will only do so for exceptional cases. + +Administrators can add new contributors to the project, define their +contributor levels and assign them to specific teams. diff --git a/maintainers/code_owners.rst b/maintainers/code_owners.rst new file mode 100644 index 00000000..13305699 --- /dev/null +++ b/maintainers/code_owners.rst @@ -0,0 +1,16 @@ +.. _code_owners: + + +SOF code owners +############### + +Each repository defines its own CODE_OWNER file, which identifies key +contributors and experts in each area of the SOF project. + +Code owners will be notified of each change to their respective +domains, and are encouraged to approve or provide feedback on +contributions being reviewed. + +Each repository in the SOF project may define their own rules, but the +general expectation is that Pull Requests are approved by 2 or more +code owners, maintainers or admin. diff --git a/maintainers/index.rst b/maintainers/index.rst new file mode 100644 index 00000000..0a7ed6bb --- /dev/null +++ b/maintainers/index.rst @@ -0,0 +1,15 @@ +.. _maintainers: + + +SOF admin, maintainers and code owners +###################################### + +The SOF project defines administrators, grants key contributors merge +rights and defines code owners for each subsystem and technical area. + +.. toctree:: + :maxdepth: 1 + + admin.rst + merge_rights.rst + code_owners.rst diff --git a/maintainers/merge_rights.rst b/maintainers/merge_rights.rst new file mode 100644 index 00000000..72a2c105 --- /dev/null +++ b/maintainers/merge_rights.rst @@ -0,0 +1,13 @@ +.. _merge_rights: + +Merge rights +############ + +For the firmware tree, additional key contributors have merge rights +into the master branch: + ++---------------+-------------------+---------------+ +| Intel | Tomasz Lauda | @tlauda | ++---------------+-------------------+---------------+ +| Intel | Janusz Jankowski | @jajanusz | ++---------------+-------------------+---------------+ diff --git a/make.bat b/make.bat new file mode 100644 index 00000000..14880ee5 --- /dev/null +++ b/make.bat @@ -0,0 +1,36 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build +set DOC_TAG=development + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% -t %DOC_TAG% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% + +:end +popd \ No newline at end of file diff --git a/platforms/index.rst b/platforms/index.rst index d0236372..a582511d 100644 --- a/platforms/index.rst +++ b/platforms/index.rst @@ -1,18 +1,92 @@ .. _platforms: +Platforms +######### + 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", "Platform Clock", "Memory", "Audio Interfaces" + :widths: 20, 20, 10, 10, 10, 20 -Platform and board specific support is continually added to SOF project as -documented below. + "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 +***************************** + +Footprint +========= + +DSP platforms can vary from vendor to vendor but in general SOF can run on +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 Bay Trail DSP at 50MHz using unoptimized C code (SIMD disabled and compiled with GCC). + +Toolchain +========= + +It's recommended to use the best optimizing compiler available for your DSP ISA; however, GCC can also be used provided it has your DSP architecture support. GCC will produce functional code, but it may not necessarily be the fastest code for your DSP architecture. + + .. TODO: Replace with reference to API tree once created. +Platform Specific Information +***************************** + +Further information on specific platforms can be found here. + .. toctree:: :maxdepth: 2 + intel-legacy/index intel-cavs/index diff --git a/platforms/intel-cavs/apollolake/index.rst b/platforms/intel-cavs/apollolake/index.rst index d2cfe286..72c1bcf0 100644 --- a/platforms/intel-cavs/apollolake/index.rst +++ b/platforms/intel-cavs/apollolake/index.rst @@ -6,6 +6,13 @@ Intel Apollo Lake Intel Apollo Lake (APL) platform is built on cAVS 1.5 HW and uses Xtensa DSP architecture. +The only Apollo Lake commercially-available platform is the Up Squared +board, which uses a BIOS compatible with SOF. Other platforms can be +supported but require a BIOS change. + +Chromebooks based on Gemini Lake support SOF directly in both the +official images and in developer mode. + .. toctree:: :maxdepth: 1 diff --git a/platforms/intel-cavs/cannonlake/cnl-memory.rst b/platforms/intel-cavs/cannonlake/cnl-memory.rst index 0b6dfaef..c6577aa2 100644 --- a/platforms/intel-cavs/cannonlake/cnl-memory.rst +++ b/platforms/intel-cavs/cannonlake/cnl-memory.rst @@ -11,15 +11,19 @@ A000 0000h B000 0000h L2 uncacheable memory (L1 cacheable). - IMR (4MB size), see *IMR Allocation* + IMR (4MB size), see *IMR Allocation*. BE00 0000h L2 local HPSRAM (L1 cacheable). - Seen as 8MB of virtual memory space (48 * 64KB). + Seen as 8MB of virtual memory space (47 * 64KB). + +.. note:: By default, address virtualization is disabled. The Translation + Lookup Buffer (TLB) entries for populated HPSRAM banks have the + same values for virtual addresses and physical addresses. BE80 0000h L2 local LPSRAM (L1 cacheable). - Directly accessed LPSRAM (64KB). + Accessed using physical addresses (1 * 64KB). BEFE 0000h DSP ROM Code 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-config.rst b/platforms/intel-cavs/icelake/icl-config.rst new file mode 100644 index 00000000..f8e93dcf --- /dev/null +++ b/platforms/intel-cavs/icelake/icl-config.rst @@ -0,0 +1,9 @@ +.. _icl-config: + +ICL Configuration +################# + +Supported DAIs +************** + +- SSP: 6 instances exposed via ``dai_get()``. diff --git a/platforms/intel-cavs/icelake/icl-memory.rst b/platforms/intel-cavs/icelake/icl-memory.rst new file mode 100644 index 00000000..19e3e313 --- /dev/null +++ b/platforms/intel-cavs/icelake/icl-memory.rst @@ -0,0 +1,6 @@ +.. _icl-memory: + +ICL Memory +########## + +Memory map is the same as on Cannon Lake. See :ref:`cnl-memory`. diff --git a/platforms/intel-cavs/icelake/index.rst b/platforms/intel-cavs/icelake/index.rst index 233b5031..cbcaeb3d 100644 --- a/platforms/intel-cavs/icelake/index.rst +++ b/platforms/intel-cavs/icelake/index.rst @@ -5,3 +5,10 @@ Intel Ice Lake The Intel Ice Lake (ICL) platform is built on cAVS 2.0 HW and uses Xtensa DSP architecture. + + +.. toctree:: + :maxdepth: 1 + + icl-memory + icl-config \ No newline at end of file diff --git a/platforms/intel-cavs/images/cavs-platform-deps.pu b/platforms/intel-cavs/images/cavs-platform-deps.pu index 30e8749d..e1cc19f8 100644 --- a/platforms/intel-cavs/images/cavs-platform-deps.pu +++ b/platforms/intel-cavs/images/cavs-platform-deps.pu @@ -1,4 +1,5 @@ package cavs { + component 2.5 component 2.0 component 1.8 component 1.5 @@ -13,13 +14,16 @@ package arch { 1.5 ..> xtensa_smp 1.8 ..> xtensa_smp 2.0 ..> xtensa_smp +2.5 ..> xtensa_smp package platform { component icelake component cannonlake component apollolake + component tigerlake } apollolake ..> 1.5 cannonlake ..> 1.8 icelake ..> 2.0 +tigerlake ..> 2.5 diff --git a/platforms/intel-cavs/index.rst b/platforms/intel-cavs/index.rst index c10371ee..cc2b7bd1 100644 --- a/platforms/intel-cavs/index.rst +++ b/platforms/intel-cavs/index.rst @@ -5,13 +5,19 @@ Intel CAVS Platforms Intel CAVS platforms supported by the |SOF|. -+---------+----------+--------------------+ -|cAVS |ver. 1.5 | Apollo Lake | -| +----------+--------------------+ -| |ver. 1.8 | Cannon Lake | -| +----------+--------------------+ -| |ver. 2.0 | Ice Lake | -+---------+----------+--------------------+ ++---------+----------+-----------------------------------------+ +|cAVS |ver. 1.5 | Apollo Lake, Gemini Lake | +| +----------+-----------------------------------------+ +| |ver. 1.8 | Cannon Lake, Whiskey Lake, Comet Lake | +| +----------+-----------------------------------------+ +| |ver. 2.0 | Ice Lake | +| +----------+-----------------------------------------+ +| |ver. 2.5 | Tiger Lake | ++---------+----------+-----------------------------------------+ + +.. 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. .. uml:: images/cavs-platform-deps.pu :caption: CAVS Platforms @@ -23,3 +29,4 @@ Intel CAVS platforms supported by the |SOF|. apollolake/index cannonlake/index icelake/index + tigerlake/index diff --git a/platforms/intel-cavs/tigerlake/images/tgl-memory.dot b/platforms/intel-cavs/tigerlake/images/tgl-memory.dot new file mode 100644 index 00000000..0bba71e5 --- /dev/null +++ b/platforms/intel-cavs/tigerlake/images/tgl-memory.dot @@ -0,0 +1,24 @@ +digraph G { + node [fontsize=10, shape="record"] + edge [fontsize=10] + rankdir=LR + splines=line + + dsp [label="DSP"] + mem_rom [label="ROM\n9F18 00000\n(512KB)"] + mem_alias [label="Alias\n8000 0000\n" group="memory"] + mem [label=" IMR\nB000 0000 + | HPSRAM\nBE00 0000 + | LPSRAM\nBE80 0000 + | L1DSRAM\n9F10 000 + | L1ISRAM\n9F18 0000" group="memory"] + + dsp -> mem_alias:a [label="L1 uncached"] + + dsp -> mem:imr [label="L1 cached"] + dsp -> mem:hpsram [label="L1 cached"] + dsp -> mem:lpsram [label="L1 cached"] + + dsp -> mem:l1dsram [label="L1 local"] + dsp -> mem:l1isram [label="L1 local"] +} diff --git a/platforms/intel-cavs/tigerlake/index.rst b/platforms/intel-cavs/tigerlake/index.rst new file mode 100644 index 00000000..aa76c83e --- /dev/null +++ b/platforms/intel-cavs/tigerlake/index.rst @@ -0,0 +1,13 @@ +.. _platform-tigerlake: + +Intel Tiger Lake +################# + +The Intel Tiger Lake (TGL) platform is built on cAVS 2.5 HW and uses Xtensa +DSP architecture. + +.. toctree:: + :maxdepth: 1 + + tgl-memory + tgl-config diff --git a/platforms/intel-cavs/tigerlake/tgl-config.rst b/platforms/intel-cavs/tigerlake/tgl-config.rst new file mode 100644 index 00000000..52b20041 --- /dev/null +++ b/platforms/intel-cavs/tigerlake/tgl-config.rst @@ -0,0 +1,9 @@ +.. _tgl-config: + +TGL Configuration +################# + +Supported DAIs +************** + +- SSP: 6 instances exposed via ``dai_get()``. diff --git a/platforms/intel-cavs/tigerlake/tgl-memory.rst b/platforms/intel-cavs/tigerlake/tgl-memory.rst new file mode 100644 index 00000000..58458625 --- /dev/null +++ b/platforms/intel-cavs/tigerlake/tgl-memory.rst @@ -0,0 +1,44 @@ +.. _tgl-memory: + +TGL Memory +########## + +8000 0000h + Aliasing to A000 0000h - BFFF FFFFh range (non L1 cacheable). + +A000 0000h + L2 cacheable memory (L1 cacheable). + +B000 0000h + L2 uncacheable memory (L1 cacheable). + IMR (4MB size), see *IMR Allocation*. + +BE00 0000h + L2 local HPSRAM (L1 cacheable). + Seen as 8MB of virtual memory space (46 * 64KB). + +.. note:: By default, address virtualization is disabled. The Translation + Lookup Buffer (TLB) entries for populated HPSRAM banks have the + same values for virtual addresses and physical addresses. + +BE80 0000h + L2 local LPSRAM (L1 cacheable). + Accessed using physical addresses (1 * 64KB). + +9F00 0000h + L1 local D-SRAM (512 KB) + Directly accessed from core #0 only. + +9F10 0000h + L1 local I-SRAM (512 KB) + Directly accessed from core #0 only. + +.. note:: The SOF version that will add Local L1 Data & Instruction SRAM + support is subject to future development. + +9F18 0000h + DSP ROM Code + Directly accessed from core #0 only. + + .. graphviz:: images/tgl-memory.dot + :caption: TGL Memory Map diff --git a/platforms/intel-legacy/baytrail/index.rst b/platforms/intel-legacy/baytrail/index.rst new file mode 100644 index 00000000..8b5040ca --- /dev/null +++ b/platforms/intel-legacy/baytrail/index.rst @@ -0,0 +1,20 @@ +.. _platform-baytrail: + +Intel Bay Trail / Cherry Trail +############################## + +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 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 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). + +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/broadwell/index.rst b/platforms/intel-legacy/broadwell/index.rst new file mode 100644 index 00000000..1fd903b9 --- /dev/null +++ b/platforms/intel-legacy/broadwell/index.rst @@ -0,0 +1,17 @@ +.. _platform-broadwell: + +Intel Broadwell +############### + +Intel Broadwell platform is based on an core CPU and a Tensilica HiFi2 +EP DSP integrated in the PCH. It relies only on internal memory (no +caches). + +Supported commercially-available platforms are limited, but include: + +- Dell Latitude 13 7350 2-in-1 laptop +- Dell Venue 11 Pro 7140 tablet +- Dell XPS 13 9343 laptop +- Google Chromebook Pixel (2015) +- HP Spectre x360 (2015) 2-in-1 laptop + diff --git a/platforms/intel-legacy/index.rst b/platforms/intel-legacy/index.rst new file mode 100644 index 00000000..ccc590e8 --- /dev/null +++ b/platforms/intel-legacy/index.rst @@ -0,0 +1,21 @@ +.. _platforms-intel-Hifi2-EP: + +Intel Hifi2-EP Platforms +######################## + +Intel platforms based on the Tensilica Hifi2-EP DSP supported by the |SOF|. + ++------------+--------------------------------------------------+ +| Atom/PCI | Merrifield (Edison) | ++------------+--------------------------------------------------+ +| Atom/ACPI | Bay Trail, Cherry Trail, Braswell | ++------------+--------------------------------------------------+ +| Core/ACPI | Broadwell (Chromebook Pixel 2015, Dell XPS) | ++------------+--------------------------------------------------+ + +.. toctree:: + :maxdepth: 1 + + merrifield/index + baytrail/index + broadwell/index diff --git a/platforms/intel-legacy/merrifield/index.rst b/platforms/intel-legacy/merrifield/index.rst new file mode 100644 index 00000000..b6a7311d --- /dev/null +++ b/platforms/intel-legacy/merrifield/index.rst @@ -0,0 +1,11 @@ +.. _platform-merrifield: + +Intel Merrifield +################ + +Intel Merrifield platform is based on an Atom CPU and a Tensilica HiFi2 +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/presentations/images/Liam_ELCE.JPG b/presentations/images/Liam_ELCE.JPG deleted file mode 100644 index 7a4a78ce..00000000 Binary files a/presentations/images/Liam_ELCE.JPG and /dev/null differ diff --git a/presentations/images/Liam_ELCE.png b/presentations/images/Liam_ELCE.png deleted file mode 100644 index 538edbd0..00000000 Binary files a/presentations/images/Liam_ELCE.png and /dev/null differ diff --git a/presentations/index.rst b/presentations/index.rst index f93c7c1f..256be831 100644 --- a/presentations/index.rst +++ b/presentations/index.rst @@ -22,6 +22,9 @@ The following presentations from past conferences are available. *Figure 1: Liam Girdwood presenting at ELCE 2018* +SOF Mentions +************ +`Linux Audio Miniconf 2018 Report `_, Edinburgh diff --git a/release.rst b/release.rst new file mode 100644 index 00000000..ee119d71 --- /dev/null +++ b/release.rst @@ -0,0 +1,59 @@ +.. _release: + +Release +####### + +Firmware and Tools +****************** + +The SOF firmware and tools can be downloaded either as a compressed source +release, binary release, or via Git. + +Git +--- + +All project SOF source code is maintained in the https://github.com/thesofproject +repository and includes folders for SOF, SOF tools and topologies, the Linux +kernel, and documentation. Download the source code as a zip or tar.gz file: + +.. code-block:: bash + + git clone https://github.com/thesofproject/sof.git + cd sof.git + git checkout master -b master + + +Source and Binary Releases +-------------------------- + +The latest SOF release is v2.11.0 (Sept 2024). + +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 + +Binary releases for different platforms are made available via the ``sof-bin`` repository: + + https://github.com/thesofproject/sof-bin + +Intermediate releases are also included on this page. General releases +include the "vX.Y" naming convention and are tagged on GitHub as such. + + +Linux Driver +************ + +The SOF Linux driver is upstreamed from Linux version 5.2 onwards. It is +included as part of official Linux releases from v5.2. + +The following SOF Linux driver development branch includes new features that +are integrated prior to upstreaming. + +.. code-block:: bash + + git clone https://github.com/thesofproject/linux.git + cd linux.git + git checkout origin/sof-dev -b sof-dev + diff --git a/release_notes.rst b/release_notes.rst deleted file mode 100644 index da2ac25c..00000000 --- a/release_notes.rst +++ /dev/null @@ -1,68 +0,0 @@ -.. _release_notes: - -Release Notes -############# - -Sound Open Firmware (SOF) is an open source audio Data Signal Processing (DSP) firmware infrastructure and SDK that offers a single code base for all Intel hardware platforms. |SOF| provides infrastructure, real-time control pieces, and audio drivers as a community project. Visit the :ref:`SOF Introduction ` for more information. - -Source Code Location -******************** - -All project SOF source code is maintained in the https://github.com/thesofproject repository and includes folders for SOF, SOF tools and topologies, Linux kernel, and documentation. Download the source code as a zip or tar.gz file: - -.. code-block:: bash - - $ git clone https://github.com/thesofproject/sof.git - $ cd sof - $ git checkout -b stable-1.2 origin/stable-1.2 - -Current Release: v1.2 (Sept 2018) -********************************* - -The following features are available in v1.2. - -Docker availability -=================== - -SOF and SDK can now be built inside a Docker container. This -removes the need to install git versions of ALSA dependencies locally. - -API unit tests -============== - -A unit test suite, based on the cmocka library, enables fast and reliable regression testing of the core APIs. - -Intel Gemini Lake platform support -================================== - -PCM playback/capture and PDM DMIC are now supported on the Intel Gemini Lake platform. - -Travis CI support -================= - -The continuous integration process enhanced by builds and tests run by Travis for every code change provides immediate feedback. - -DSP-to-Host DMA tracing support -=============================== - -A DMA tracing mechanism has been added to provide high-frequency trace output. Bandwidth for the code traces increases significantly by transmitting the data through the DSP-to-Host DMA. - -Userspace application support for test benching processing algorithms -===================================================================== - -|SOF| can now be built as an x86 library that can be linked to userspace applications that can parse audio processing pipelines/topologies. This enables verifying functionality of audio components that are part of the pipeline. - -Intel DMIC support on Apollo Lake and Gemini Lake -================================================= - -The DMIC driver has been added to support the directly-attached PDM (Pulse Density Modulation) type of digital microphones. A digital microphone's array can be connected directly to the Intel System on Chip (SoC) for minimized system power consumption and lowest delay. |SOF| topology parameters for PDM bus characteristics enable support for a wide range of microphone models and operating modes. - -Xtensa HiFi SIMD optimizations -============================== - -The SIMD optimizations for volume, FIR, and SCR processing components typically reduce the power consumption and execution time of algorithms processing that is similar to generic C code. Note that this requires an xt-xcc compiler. - -Numerous additional stress test hardening patches -================================================= - -Both the stability and robustness of PCM playback/capture are greatly improved via numerous stress tests. 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/plantuml.jar b/scripts/plantuml.jar index a84c5a0f..b177c0c6 100644 Binary files a/scripts/plantuml.jar and b/scripts/plantuml.jar differ diff --git a/scripts/requirements-lax.txt b/scripts/requirements-lax.txt new file mode 100644 index 00000000..42077b41 --- /dev/null +++ b/scripts/requirements-lax.txt @@ -0,0 +1,63 @@ +# Do not use this file for any intensive/"serious" documentation work. + +# The purpose of this "laxer" file is to lower the barrier for drive-by +# contributors and make it easier for anyone to verify a typo fix +# locally. + +# On Debian/Ubuntu/etc., don't forget PIP_IGNORE_INSTALLED=0 (double +# negation): +# PIP_IGNORE_INSTALLED=0 pip3 install --user -r scripts/requirements-lax.txt +# See https://github.com/pypa/pip/issues/4222 + +breathe>=4.29.2 +sphinx>=4.5.0 +docutils>=0.17.1 +sphinx_rtd_theme>=0.2.4 +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: + +## Pros + +# - These use '>=' instead of '==' hardcoding so: +# - you don't need a virtualenv per project. +# - you can spend less time downloading and more time fixing the doc. +# - The build was successful the last time someone updated this file. +# - These versions produce something usable and mostly complete. + +## Cons + +# - They're not official and not regulary tested; they are "best effort", +# community-maintained. +# - They may produce warnings (but nothing that stops the build). +# - Output may be ugly and/or incomplete. +# - Upgrading only some of them outside this file may result in sphinx +# modules that are incompatible with each other. + +## Maintenance + +# - Bump up only when really needed. Prefer (reasonable) backward +# compatibility if possible, +# - When bumping up, upgrade to the lowest possible version required. +# - 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 939919ae..17aff9e6 100644 --- a/scripts/requirements.txt +++ b/scripts/requirements.txt @@ -1,5 +1,15 @@ -breathe==4.9.1 -sphinx==1.7.5 -docutils==0.14 +# This file hardcodes validated versions with '==', +# see requirements-lax.txt for an alternative. + +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/setup.py b/setup.py new file mode 100644 index 00000000..c9d4542d --- /dev/null +++ b/setup.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +from distutils.core import setup + +setup(name='Sphinx GUI Utility', + version='0.1', + description='Build Sphinx docs from a GUI', + author='Deb Taylor', + author_email='deb.taylor@intel.com', + url='https://github.com/deb-intel/sof-docs' + ) 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 847367f9..8912770a 100644 --- a/substitutions.txt +++ b/substitutions.txt @@ -9,6 +9,10 @@ .. |BDW| replace:: Broadwell .. |APL| replace:: Apollo Lake .. |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 using the same name as the html entity names (e.g., ©) for that character diff --git a/tox.ini b/tox.ini new file mode 100644 index 00000000..46ccd38f --- /dev/null +++ b/tox.ini @@ -0,0 +1,13 @@ +[tox] +envlist = py3-{mylinux,mywindows} + +[testenv] +platform = mylinux: linux + mywindows: win32 +whitelist_externals = make.bat + /usr/bin/make +deps = -rscripts\requirements.txt +commands = + mylinux: make {posargs} + mywindows: make.bat {posargs} + diff --git a/tsc/index.rst b/tsc/index.rst new file mode 100644 index 00000000..477adfe8 --- /dev/null +++ b/tsc/index.rst @@ -0,0 +1,15 @@ +.. _tsc: + +Technical Steering Committee (TSC) +################################## + +The TSC serves as the highest technical decision body for the project, +with members chosen from involved project maintainers. This committee +sets the technical direction for the project, defines milestone +release features, and coordinates cross-community collaboration. + +.. toctree:: + :maxdepth: 1 + + representatives.rst + meetings.rst diff --git a/tsc/meetings.rst b/tsc/meetings.rst new file mode 100644 index 00000000..2516ed52 --- /dev/null +++ b/tsc/meetings.rst @@ -0,0 +1,12 @@ +.. _meetings: + +TSC Meetings +############ + +TSC meetings take place every month, typically the first Monday of +each month, with a agenda circulated ahead of time on the SOF mailing +list. + +The notes and decisions made by the TSC will be made public, stored on +GitHub and a link provided on the SOF mailing list. + diff --git a/tsc/representatives.rst b/tsc/representatives.rst new file mode 100644 index 00000000..66b7ff4d --- /dev/null +++ b/tsc/representatives.rst @@ -0,0 +1,33 @@ +.. _representatives: + + +TSC Representatives +################### + +The TSC is currently made of the following contributors + ++---------------+----------------------+------------------+ +| 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 | ++---------------+----------------------+------------------+