diff --git a/configs/board/stm32f746g-disco/board.cmake b/configs/board/stm32f746g-disco/board.cmake index d11be58..2997e61 100644 --- a/configs/board/stm32f746g-disco/board.cmake +++ b/configs/board/stm32f746g-disco/board.cmake @@ -30,3 +30,16 @@ set(DMBOOT_RENODE_SDCARD_DETECT_PIN 13) # emulation mode can write the frames it renders to a file (see # DMBOOT_RENODE_LCD_CAPTURE) set(DMBOOT_RENODE_LCD "sysbus.ltdc" CACHE STRING "Renode peripheral of the LCD controller") + +# Audio (WM8994 codec on I2C3 address 0x1A, fed by SAI2): Renode has neither +# peripheral, and its DMA2 has no circular mode or half-transfer interrupt, +# which the SAI stream needs - configs/renode/STM32F7_Audio.cs supplies all +# three. Emulation mode loads them when DMBOOT_RENODE_AUDIO_CAPTURE is set +# (see there); DMA2 is replaced by a model that moves peripheral-to-memory +# data like Renode's own, so the SD host still works. stands for the +# file the codec writes what it plays to. +set(DMBOOT_RENODE_AUDIO_MODEL "${CMAKE_SOURCE_DIR}/configs/renode/STM32F7_Audio.cs") +set(DMBOOT_RENODE_AUDIO_DMA "sysbus.dma2") +set(DMBOOT_RENODE_AUDIO_DMA_DESCRIPTION "dma2: DMA.STM32F7_AudioDMA @ sysbus 0x40026400 { [0-7] -> nvic@[56-60,68-70] }") +set(DMBOOT_RENODE_AUDIO_CODEC_DESCRIPTION "wm8994: Sound.WM8994 @ i2c3 0x1A { outputFile: \\\"\\\" }") +set(DMBOOT_RENODE_AUDIO_SAI_DESCRIPTION "sai2: Sound.STM32F7_SAI @ sysbus 0x40015C00 { codec: wm8994; DmaRequestA -> dma2@4; DmaRequestB -> dma2@6 }") diff --git a/configs/board/stm32f746g-disco/flash.dmd b/configs/board/stm32f746g-disco/flash.dmd index 8c664d8..7e336ad 100644 --- a/configs/board/stm32f746g-disco/flash.dmd +++ b/configs/board/stm32f746g-disco/flash.dmd @@ -63,3 +63,14 @@ dmlcdtft driver=board/stm32f746g-disco/lcd.ini # dmi2c dmft5336 driver=board/stm32f746g-disco/touch.ini +# +# Audio: the WM8994 codec - stereo headphone output and the board's digital +# microphone - on SAI2 (PCM, dmsai) with I2C3 for its registers. The config +# joins the touch panel's I2C3 bus node (touch.ini above) instead of +# configuring I2C3 again, so both work at once. The nodes are /dev/wm8994 +# (codec controls and stereo PCM) and /dev/dmsai1 (the raw SAI stream the codec +# drives); an application plays through /dev/wm8994 with the DMDRVI_IOCTL_AUDIO_* +# commands (dmdrvi 2.7). dmsai needs dmclk (SAI clock), dmdma (DMA2) and dmheap. +# +dmsai +dmwm8994 driver=board/stm32f746g-disco/codec-with-touch.ini diff --git a/configs/renode/STM32F7_Audio.cs b/configs/renode/STM32F7_Audio.cs index 213df18..5c487c6 100644 --- a/configs/renode/STM32F7_Audio.cs +++ b/configs/renode/STM32F7_Audio.cs @@ -21,9 +21,9 @@ // LRCLK/BCLK inversion) and writes the DAC1 samples it receives to OutputFile // as raw stereo s16le (silence while DAC1 is muted). // -// Not wired into renode.resc yet. To use it, add to the script before -// LoadELF (the codec node also needs dmsai/dmwm8994 in the firmware and the -// FT5336 touch config left out, since both own I2C3): +// Loaded by renode.resc when DMBOOT_RENODE_AUDIO_CAPTURE names a file (the +// board's board.cmake names the peripherals, scripts/targets.cmake writes the +// script lines): // // include @configs/renode/STM32F7_Audio.cs // sysbus Unregister sysbus.dma2 @@ -31,7 +31,10 @@ // machine LoadPlatformDescriptionFromString "wm8994: Sound.WM8994 @ i2c3 0x1A { outputFile: \"/tmp/codec.pcm\" }" // machine LoadPlatformDescriptionFromString "sai2: Sound.STM32F7_SAI @ sysbus 0x40015C00 { codec: wm8994; DmaRequestA -> dma2@4; DmaRequestB -> dma2@6 }" // -// and convert the capture with e.g. "sox -t raw -r 48000 -e signed -b 16 -c 2 /tmp/codec.pcm out.wav". +// DMA2 has to be replaced before anything that connects to it by name (the SD +// host). The codec shares I2C3 with the touch panel, as on the board +// (flash.dmd). Convert the capture with e.g. +// "sox -t raw -r 48000 -e signed -b 16 -c 2 /tmp/codec.pcm out.wav". // using System; using System.Collections.Generic; diff --git a/configs/renode/renode.resc.in b/configs/renode/renode.resc.in index 3c2492a..959696a 100644 --- a/configs/renode/renode.resc.in +++ b/configs/renode/renode.resc.in @@ -6,6 +6,8 @@ sysbus LoadELF @@CMAKE_BINARY_DIR@/renode_firmware.elf machine StartGdbServer 3333 +@DMBOOT_RENODE_AUDIO_SETUP@ + @DMBOOT_RENODE_SDCARD_SETUP@ @DMBOOT_RENODE_LCD_SETUP@ diff --git a/docs/cmake-options.md b/docs/cmake-options.md index 8dc02f3..89f1c58 100644 --- a/docs/cmake-options.md +++ b/docs/cmake-options.md @@ -29,6 +29,7 @@ These options are defined in the top-level `CMakeLists.txt` and control the over | `DMBOOT_RENODE_TAP` | STRING | *(empty)* | In emulation mode, host TAP interface (e.g. `tap0`) that the board's Ethernet controller (`DMBOOT_RENODE_ETHERNET`, set by the board config, e.g. `sysbus.ethernet` for `stm32f746g-disco`) is connected to through a Renode switch, so the host can reach the board over IP. Needs root and `/dev/net/tun`; empty leaves the Ethernet unconnected. | | `DMBOOT_RENODE_SDCARD` | FILEPATH | *(empty)* | In emulation mode, SD card image (e.g. one built by `scripts/make_sdcard_image.py`) inserted in the board's card slot (`DMBOOT_RENODE_SDMMC`, set by the board config, e.g. `sysbus.sdmmc` for `stm32f746g-disco`). The board's SD host is replaced with its own model (`configs/renode/STM32F7_SDMMC.cs`, compiled by Renode when it loads the script) and the card detect pin is driven low. Writes to the card are discarded when Renode exits; empty leaves the slot empty. | | `DMBOOT_RENODE_LCD_CAPTURE` | FILEPATH | *(empty)* | In emulation mode, file every frame the board's LCD controller (`DMBOOT_RENODE_LCD`, set by the board config, e.g. `sysbus.ltdc` for `stm32f746g-disco`) renders is written to whenever it changes: a header line `DMLCD ` followed by the raw pixels (see `configs/renode/lcd_capture.py.in`). Empty: frames are not captured. | +| `DMBOOT_RENODE_AUDIO_CAPTURE` | FILEPATH | *(empty)* | In emulation mode, file the board's audio codec (`stm32f746g-disco`: WM8994 on I2C3, fed by SAI2) writes what it plays to, as raw stereo 16-bit little-endian samples at the codec's rate (48 kHz). Setting it loads `configs/renode/STM32F7_Audio.cs`, which adds the SAI and the codec and replaces DMA2 with a model that has circular mode and half-transfer interrupts (Renode's has neither; the SD host keeps working). Needs the audio modules from `flash.dmd`. Empty: the audio peripherals are not modelled and the codec does not respond. | ## VFS Options diff --git a/scripts/targets.cmake b/scripts/targets.cmake index b6a2e47..4c64868 100644 --- a/scripts/targets.cmake +++ b/scripts/targets.cmake @@ -51,6 +51,21 @@ if(DMBOOT_RENODE_ETHERNET AND DMBOOT_RENODE_TAP) else() set(DMBOOT_RENODE_NETWORK "") endif() +# Play audio through the board's codec (if it names the models): the DMA2 +# controller is replaced with one that has circular mode and half-transfer +# interrupts, and the SAI and the WM8994 codec are added (see +# DMBOOT_RENODE_AUDIO_MODEL, a C# model Renode compiles when it loads the +# script). What the codec plays is written to a file as raw stereo 16-bit +# little-endian samples. The SD card setup below connects to DMA2 by name, so +# DMA2 has to be replaced first. +set(DMBOOT_RENODE_AUDIO_CAPTURE "" CACHE FILEPATH "File the board's audio codec writes what it plays to in emulation mode (raw stereo s16le); empty: the audio models are not loaded") +if(DMBOOT_RENODE_AUDIO_MODEL AND DMBOOT_RENODE_AUDIO_CAPTURE) + string(REPLACE "" "${DMBOOT_RENODE_AUDIO_CAPTURE}" _AUDIO_CODEC "${DMBOOT_RENODE_AUDIO_CODEC_DESCRIPTION}") + set(DMBOOT_RENODE_AUDIO_SETUP + "include @${DMBOOT_RENODE_AUDIO_MODEL}\nsysbus Unregister ${DMBOOT_RENODE_AUDIO_DMA}\nmachine LoadPlatformDescriptionFromString \"${DMBOOT_RENODE_AUDIO_DMA_DESCRIPTION}\"\nmachine LoadPlatformDescriptionFromString \"${_AUDIO_CODEC}\"\nmachine LoadPlatformDescriptionFromString \"${DMBOOT_RENODE_AUDIO_SAI_DESCRIPTION}\"") +else() + set(DMBOOT_RENODE_AUDIO_SETUP "") +endif() # Insert an SD card image in the board's card slot (if it names one): the # board's SD host is replaced with its model (DMBOOT_RENODE_SDMMC_MODEL, C# # Renode compiles when it loads the script), the image attached to it and the