Initial CHERIoT Soundstream demo Converts the bare-metal soundstream demo app to a hypothetical sencha platform (derived from matcha by replacing the SMC Ibex core by a RISC-V CHERIoT core). This depends on the SEC to boot the SMC from flash and uses DIF support where possible for peripherals. Specific changes: - stick drivers in compartments - use cheriot-rtos interrupts & threads - use capabilities for mmio + shared memory refs - allocate app data from the heap instead of using global vars (temp until toolchain is updated) - hide hard-to-use-directly bits in a compat.h - use cheriot api's where appropriate (e.g. __builtin_unreachable -> panic) - band-aid dif include files to workaround c++ usage NB: interrupt thread stacks are super-sized to support logging Bug: 330741645 Bypass-Presubmit-Reason: no sencha presubmit tests Change-Id: Iea2122422231571eb0a477b31b47eed0cb0c18f7
diff --git a/sw/device/cheriot/soundstream/.gitignore b/sw/device/cheriot/soundstream/.gitignore new file mode 100644 index 0000000..b39c548 --- /dev/null +++ b/sw/device/cheriot/soundstream/.gitignore
@@ -0,0 +1 @@ +*.proto
diff --git a/sw/device/cheriot/soundstream/README.md b/sw/device/cheriot/soundstream/README.md new file mode 100644 index 0000000..12f2f32 --- /dev/null +++ b/sw/device/cheriot/soundstream/README.md
@@ -0,0 +1,144 @@ +Soundstream bare-metal demo example +=================================== + +Cheriot port of the shodan soundstream bare-metal demo. + +If you do not have a shodan repo setup, follow the instructions at +https://spacebeaker.googlesource.com/shodan/docs/+/refs/heads/master/GettingStarted.md. + +Be sure ROOTDIR is set in the environment pointing to a current +shodan repo checkout and the target platform is "sencha"; e.g. +```shell +cd ~/shodan +source build/setup.sh +set-platform sencha +printenv ROOTDIR +/usr/local/google/home/sleffler/shodan +``` + +The first time you setup a "sencha" platform you need to install the +necessary tools: +```shell +set-platform sencha +m tools +``` +(note the tools are platform-dependent and only installed when +the current platform is set to "sencha"). + +You also need a current `xmake` to build cheriot firmware. Note the most +recent prebuilt package is too old so you need to do something like: +```shell +$ sudo apt-get install xmake +Reading package lists... Done +Building dependency tree... Done +Reading state information... Done +The following NEW packages will be installed: + xmake +0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded. +... +Setting up xmake (2.8.6+ds-3) ... +... +$ which xmake +/usr/bin/xmake +$ xmake update +update version v2.9.1 from official source .. + => download https://gitlab.com/tboox/xmake.git .. ok + => install to ~/.local/bin .. ok +``` +(if you use an old xmake you will see this failure: +```shell + xmake build +checking for platform ... cheriot +checking for architecture ... cheriot +error: decode json failed, @programdir/core/base/json.lua:223: invalid json syntax starting at position 63: x2000000, +``` +) + +Build a sencha platform image with the soundstream firmware for the SMC +and run the simulator: + +```shell +$ m simulate +... +export XMAKE_CONFIGDIR=/usr/local/google/home/sleffler/shodan/out/cheriot/sencha/release; \ +cd/usr/local/google/home/sleffler/shodan/hw/matcha/sw/device/cheriot/soundstream && \ + xmake config \ + -o /usr/local/google/home/sleffler/shodan/out/cheriot/sencha/release \ + --sdk=/usr/local/google/home/sleffler/shodan/cache/cheriot-tools \ + --board=sencha \ + --debug-scheduler=true --debug-allocator=true && \ +xmake build +checking for platform ... cheriot +checking for architecture ... cheriot +generating /usr/local/google/home/sleffler/shodan/sw/cheriot-rtos/sdk/firmware.ldscript.in ... ok +[ 31%]: cache compiling.release i2s.cc +[ 31%]: cache compiling.release ../../lib/dif/dif_i2s.c +[ 32%]: cache compiling.release soundstream.cc +[ 32%]: cache compiling.release ../../lib/dif/autogen/dif_i2s_autogen.c +[ 32%]: cache compiling.release ../../../../../../sw/cheriot-rtos/sdk/lib/crt/cz.c +[ 32%]: cache compiling.release ../../../../../../sw/cheriot-rtos/sdk/lib/crt/arith64.c +[ 32%]: cache compiling.release ../../../../../../sw/cheriot-rtos/sdk/core/scheduler/main.cc +[ 32%]: cache compiling.release ../../../../../../sw/cheriot-rtos/sdk/lib/atomic/atomic1.cc +[ 33%]: cache compiling.release encode.cc +[ 34%]: cache compiling.release ../../../../hw/top_matcha/sw/autogen/top_matcha.c +[ 37%]: cache compiling.release ../../../../../../sw/cheriot-rtos/sdk/lib/freestanding/memcmp.c +[ 38%]: cache compiling.release ../../../../../../sw/cheriot-rtos/sdk/lib/freestanding/memcpy.c +[ 38%]: compiling.release ../../../../../../sw/cheriot-rtos/sdk/core/token_library/token_unseal.S +[ 39%]: cache compiling.release ../../../../../../sw/cheriot-rtos/sdk/lib/freestanding/memset.c +[ 40%]: cache compiling.release mailbox.cc +[ 42%]: cache compiling.release ../../lib/dif/dif_tlul_mailbox.c +[ 43%]: cache compiling.release ../../lib/dif/autogen/dif_tlul_mailbox_autogen.c +[ 44%]: compiling.release ../../../../../../sw/cheriot-rtos/sdk/core/switcher/entry.S +[ 45%]: cache compiling.release ../../../../../../sw/cheriot-rtos/sdk/core/loader/boot.cc +[ 46%]: compiling.release ../../../../../../sw/cheriot-rtos/sdk/core/loader/boot.S +[ 46%]: cache compiling.release ../../../../../../sw/cheriot-rtos/sdk/core/software_revoker/revoker.cc +[ 48%]: cache compiling.release ../../../../../../sw/cheriot-rtos/sdk/lib/debug/debug.cc +[ 49%]: cache compiling.release ../../../../../../sw/cheriot-rtos/sdk/core/allocator/main.cc +[ 50%]: cache compiling.release ../../../../../../sw/cheriot-rtos/sdk/lib/compartment_helpers/claim_fast.cc +[ 51%]: cache compiling.release ../../../../../../sw/cheriot-rtos/sdk/lib/compartment_helpers/check_pointer.cc +[ 53%]: cache compiling.release ../../../../../../sw/cheriot-rtos/sdk/lib/locks/locks.cc +[ 54%]: cache compiling.release ../../../../../../sw/cheriot-rtos/sdk/lib/locks/semaphore.cc +[ 55%]: cache compiling.release ../../../../../../sw/cheriot-rtos/sdk/lib/atomic/atomic4.cc +[ 56%]: cache compiling.release ml_top.cc +[ 57%]: cache compiling.release ../../lib/dif/dif_ml_top.c +[ 59%]: cache compiling.release ../../lib/dif/autogen/dif_ml_top_autogen.c +[ 60%]: linking library crt.library +[ 61%]: linking privileged library cheriot.token_library.library +[ 62%]: linking library freestanding.library +[ 65%]: linking compartment i2s.compartment +[ 66%]: linking compartment soundstream.compartment +[ 67%]: linking library atomic1.library +[ 68%]: linking compartment mailbox.compartment +[ 69%]: linking library debug.library +[ 71%]: linking privileged compartment cheriot.software_revoker.compartment +[ 83%]: linking library atomic4.library +[ 85%]: linking library compartment_helpers.library +[ 89%]: linking library locks.library +[ 91%]: linking privileged compartment soundstream-firmware.scheduler.compartment +[ 93%]: linking compartment ml_top.compartment +[ 96%]: linking privileged compartment cheriot.allocator.compartment +[ 98%]: linking firmware ../../../../../../out/cheriot/sencha/release/cheriot/cheriot/release/soundstream-firmware +[ 98%]: Creating firmware report ../../../../../../out/cheriot/sencha/release/cheriot/cheriot/release/soundstream-firmware.json +[ 98%]: Creating firmware dump ../../../../../../out/cheriot/sencha/release/cheriot/cheriot/release/soundstream-firmware.dump +... +mkdir /usr/local/google/home/sleffler/shodan/out/cheriot/sencha/release/tmp +cp -f /usr/local/google/home/sleffler/shodan/out/matcha-bundle-release.elf /usr/local/google/home/sleffler/shodan/out/cheriot/sencha/release/tmp/matcha-tock-bundle +riscv32-unknown-elf-strip /usr/local/google/home/sleffler/shodan/out/cheriot/sencha/release/tmp/matcha-tock-bundle +riscv32-unknown-elf-objcopy -O binary -g /usr/local/google/home/sleffler/shodan/out/cheriot/sencha/release/tmp/matcha-tock-bundle /usr/local/google/home/sleffler/shodan/out/cheriot/sencha/release/tmp/matcha-tock-bundle.bin +ln -sf /usr/local/google/home/sleffler/shodan/out/cheriot/sencha/release/cheriot/cheriot/release/soundstream-firmware /usr/local/google/home/sleffler/shodan/out/cheriot/sencha/release/tmp/kernel +tar -C /usr/local/google/home/sleffler/shodan/out/cheriot/sencha/release/tmp -cvhf /usr/local/google/home/sleffler/shodan/out/cheriot/sencha/release/ext_flash.tar matcha-tock-bundle.bin kernel +matcha-tock-bundle.bin +kernel +cd /usr/local/google/home/sleffler/shodan && /usr/local/google/home/sleffler/shodan/cache/renode/renode --disable-xwt --port 1234 -e "\ + \$repl_file = @sim/config/platforms/sencha.repl; \ + \$tar = @/usr/local/google/home/sleffler/shodan/out/cheriot/sencha/release/ext_flash.tar; \ + \$sc_bin =@/usr/local/google/home/sleffler/shodan/out/cheriot/sencha/release/tmp/matcha-tock-bundle.bin; \ + \$term_port = 3456; \$gdb_port = 3333; i @sim/config/sencha.resc; \ + pause; cpu0 IsHalted false; start" +16:20:42.1702 [INFO] Loaded monitor commands from: /usr/local/google/home/sleffler/shodan/cache/renode/scripts/monitor.py +16:20:42.1895 [INFO] Monitor available in telnet mode on port 1234 +16:20:42.4740 [INFO] Including script: /usr/local/google/home/sleffler/shodan/sim/config/sencha.resc +16:20:42.4910 [INFO] System bus created. +16:20:45.5498 [INFO] Including script: /usr/local/google/home/sleffler/shodan/sim/config/sencha.resc +... +```
diff --git a/sw/device/cheriot/soundstream/compat.h b/sw/device/cheriot/soundstream/compat.h new file mode 100644 index 0000000..70b1193 --- /dev/null +++ b/sw/device/cheriot/soundstream/compat.h
@@ -0,0 +1,89 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef EXAMPLES_SOUNDSTREAM_COMPAT_H_ +#define EXAMPLES_SOUNDSTREAM_COMPAT_H_ + +// XXX maybe uart_write? +// XXX fix var args +#define LOG_ERROR(msg, ...) Debug::log("{}", msg) +#define LOG_INFO(msg, ...) Debug::log("{}", msg) +#define LOG_FATAL(msg, ...) Debug::Assert(false, "{}", msg) + +/** + * Checks that the given condition is true. If the condition is false, this + * function logs and then aborts. + * + * @param condition An expression to check. + * @param ... Arguments to a LOG_* macro, which are evaluated if the check + * fails. + */ +#define CHECK(condition, ...) \ + do { \ + if (!(condition)) { \ + /* NOTE: because the condition in this if \ + statement can be statically determined, \ + only one of the below string constants \ + will be included in the final binary.*/ \ + if (OT_VA_ARGS_COUNT(_, ##__VA_ARGS__) == 0) { \ + LOG_ERROR("CHECK-fail: " #condition); \ + } else { \ + LOG_ERROR("CHECK-fail: " __VA_ARGS__); \ + } \ + /* Currently, this macro will call into \ + the test failure code, which logs \ + "FAIL" and aborts. In the future, \ + we will try to condition on whether \ + or not this is a test.*/ \ + /* XXX fill me in */ \ + } \ + } while (false) + +/** + * Checks that the given DIF call returns kDifOk. If the DIF call returns a + * different dif_result_t value (defined in sw/device/lib/dif/dif_base.h), this + * function logs and then aborts. + * + * @param dif_call DIF call to invoke and check its return value. + * @param ... Arguments to a LOG_* macro, which are evaluated if the check + * fails. + */ +#define CHECK_DIF_OK(dif_call, ...) \ + do { \ + dif_result_t dif_result = dif_call; \ + if (dif_result != kDifOk) { \ + /* NOTE: because the condition in this if \ + statement can be statically determined, \ + only one of the below string constants \ + will be included in the final binary.*/ \ + if (OT_VA_ARGS_COUNT(_, ##__VA_ARGS__) == 0) { \ + LOG_ERROR("DIF-fail: " #dif_call " returns %d", dif_result); \ + } else { \ + LOG_ERROR("DIF-fail: " __VA_ARGS__); \ + } \ + /* Currently, this macro will call into \ + the test failure code, which logs \ + "FAIL" and aborts. In the future, \ + we will try to condition on whether \ + or not this is a test.*/ \ + /* XXX fill me in */ \ + } \ + } while (false) + +// TODO(sleffler): need a scheduler yield kinda call +inline void wait_for_interrupt(void) { asm volatile("wfi"); } + +#endif // EXAMPLES_SOUNDSTREAM_COMPAT_H_
diff --git a/sw/device/cheriot/soundstream/encode.cc b/sw/device/cheriot/soundstream/encode.cc new file mode 100644 index 0000000..da826d1 --- /dev/null +++ b/sw/device/cheriot/soundstream/encode.cc
@@ -0,0 +1,52 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "encode.h" + +const char base64_alphabet[64] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; + +void encode(const uint8_t* const in, size_t in_len, char* out) { + size_t rem = in_len % 3; + size_t out_idx = 0; + for (int i = 0; i < (in_len - rem); i += 3) { + out[out_idx++] = base64_alphabet[(in[0 + i] >> 2)]; + out[out_idx++] = + base64_alphabet[((in[0 + i] & 0x3) << 4) | (in[1 + i] >> 4)]; + out[out_idx++] = + base64_alphabet[((in[1 + i] & 0xf) << 2) | (in[2 + i] >> 6)]; + out[out_idx++] = base64_alphabet[(in[2 + i] & 0x3f)]; + } + + if (rem == 2) { + out[out_idx++] = base64_alphabet[in[in_len - rem] >> 2]; + out[out_idx++] = base64_alphabet[((in[in_len - rem] & 0x3) << 4) | + (in[in_len - rem + 1] >> 4)]; + out[out_idx++] = base64_alphabet[((in[in_len - rem + 1] & 0xf) << 2)]; + out[out_idx++] = '='; + } else if (rem == 1) { + out[out_idx++] = base64_alphabet[in[in_len - rem] >> 2]; + out[out_idx++] = base64_alphabet[((in[in_len - rem] & 0x3) << 4) | + (in[in_len - rem + 1] >> 4)]; + out[out_idx++] = '='; + out[out_idx++] = '='; + } + // NULL-terminate. + out[out_idx] = 0; +}
diff --git a/sw/device/cheriot/soundstream/encode.h b/sw/device/cheriot/soundstream/encode.h new file mode 100644 index 0000000..01f9b3e --- /dev/null +++ b/sw/device/cheriot/soundstream/encode.h
@@ -0,0 +1,27 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef EXAMPLES_SOUNDSTREAM_ENCODE_H_ +#define EXAMPLES_SOUNDSTREAM_ENCODE_H_ + +#include <stddef.h> +#include <stdint.h> + +#define ENCODE_OUT_SIZE(s) ((size_t)((((s) + 2) / 3) * 4 + 1)) + +void encode(const uint8_t* in, size_t in_len, char* out); + +#endif // EXAMPLES_SOUNDSTREAM_ENCODE_H_
diff --git a/sw/device/cheriot/soundstream/i2s.cc b/sw/device/cheriot/soundstream/i2s.cc new file mode 100644 index 0000000..90a04bf --- /dev/null +++ b/sw/device/cheriot/soundstream/i2s.cc
@@ -0,0 +1,143 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Barebones i2s for soundstream. Only supports recording and + * without any buffering. The caller must manage the rx fifo + * carefully to avoid data loss. + */ +#include "i2s.h" + +#include <fail-simulator-on-error.h> +#include <futex.h> +#include <interrupt.h> +#include <locks.h> +#include <thread.h> + +#include <debug.hh> +#include <platform/sencha/platform-i2s.hh> + +#include "hw/top_matcha/sw/autogen/top_matcha.h" +#include "i2s_regs.h" + +/// Expose debugging features unconditionally for this compartment. +using Debug = ConditionalDebug<false, "I2S">; + +#include "compat.h" + +typedef uint32_t i2s_mmio_t[TOP_MATCHA_I2S0_SIZE_BYTES / sizeof(uint32_t)]; + +static dif_i2s_t i2s; +static void i2s_irq_acknowledge(i2s_irq_t irq_id); + +#define CHECK_INIT() CHECK(i2s.base_addr.base != 0) + +static CountingSemaphoreState startup = { 0, 1 }; + +void i2s_init(void) { + CHECK_DIF_OK(dif_i2s_init( + mmio_region_from_addr((uintptr_t)MMIO_CAPABILITY(i2s_mmio_t, i2s)), + &i2s)); + semaphore_put(&startup); +} + +// NB: could be atomic but not needed for our ussage. +volatile bool rx_watermark_seen = false; + +void i2s_isr(void) { + Timeout t = {0, UnlimitedTimeout}; + semaphore_get(&t, &startup); + Debug::log("i2s_isr: i2s {} (Thread {})", i2s.base_addr.base, thread_id_get()); + + const uint32_t *rxWatermarkFutex = interrupt_futex_get( + STATIC_SEALED_VALUE(i2sRxWatermarkInterruptCapability)); + uint32_t last = *rxWatermarkFutex; + for (;;) { + Debug::Assert(futex_wait(rxWatermarkFutex, last) == 0, "futex_wait"); + last = *rxWatermarkFutex; + Debug::log("i2s_isr: i2s {} last {})", i2s.base_addr.base, last); + rx_watermark_seen = true; + // Acknowledge and disable the interrupt; it will be + // re-enabled after the RX FIFO is drained. + i2s_irq_acknowledge(kI2sIrqRxWatermark); + i2s_irq_set_enabled(kI2sIrqRxWatermark, /*enabled=*/false); + CHECK(interrupt_complete( + STATIC_SEALED_VALUE(i2sRxWatermarkInterruptCapability)) == 0); + } +} +bool i2s_rx_watermark_seen(void) { + // return atomic_swap(&rx_watermark_seen, false); + bool was_seen = rx_watermark_seen; + if (was_seen) { + rx_watermark_seen = false; + } + return was_seen; +} + +static void i2s_irq_acknowledge(i2s_irq_t irq_id) { + CHECK_INIT(); + CHECK_DIF_OK(dif_i2s_irq_acknowledge(&i2s, irq_id)); +} + +void i2s_irq_acknowledge_all() { + CHECK_INIT(); + CHECK_DIF_OK(dif_i2s_irq_acknowledge_all(&i2s)); +} + +void i2s_irq_set_enabled(i2s_irq_t irq_id, bool enabled) { + CHECK_INIT(); + CHECK_DIF_OK(dif_i2s_irq_set_enabled( + &i2s, irq_id, enabled ? kDifToggleEnabled : kDifToggleDisabled)); +} + +// Clear RX FIFO +void i2s_rxfifo_clear(void) { + CHECK_INIT(); + uint32_t reg_val; + reg_val = mmio_region_read32(i2s.base_addr, I2S_FIFO_CTRL_REG_OFFSET); + reg_val = bitfield_bit32_write(reg_val, I2S_FIFO_CTRL_RXRST_BIT, 1); + mmio_region_write32(i2s.base_addr, I2S_FIFO_CTRL_REG_OFFSET, reg_val); +} + +bool i2s_rxfifo_is_empty(void) { + CHECK_INIT(); + bool empty; + CHECK_DIF_OK(dif_i2s_rxfifo_empty(&i2s, &empty)); + return empty; +} + +// Configure and enable recording +void i2s_record_begin(void) { + CHECK_INIT(); + uint32_t reg_val = mmio_region_read32(i2s.base_addr, I2S_CTRL_REG_OFFSET); + reg_val = bitfield_bit32_write(reg_val, I2S_CTRL_RX_BIT, 1); + reg_val = bitfield_field32_write(reg_val, I2S_CTRL_NCO_RX_FIELD, + 24); // divide by 24 + mmio_region_write32(i2s.base_addr, I2S_CTRL_REG_OFFSET, reg_val); +} + +uint32_t i2s_get_rdata(void) { + CHECK_INIT(); + return mmio_region_read32(i2s.base_addr, I2S_RDATA_REG_OFFSET); +} + +// Disable recording +void i2s_record_end(void) { + CHECK_INIT(); + uint32_t reg_val = mmio_region_read32(i2s.base_addr, I2S_CTRL_REG_OFFSET); + reg_val = bitfield_bit32_write(reg_val, I2S_CTRL_RX_BIT, 0); + mmio_region_write32(i2s.base_addr, I2S_CTRL_REG_OFFSET, reg_val); +}
diff --git a/sw/device/cheriot/soundstream/i2s.h b/sw/device/cheriot/soundstream/i2s.h new file mode 100644 index 0000000..08f6565 --- /dev/null +++ b/sw/device/cheriot/soundstream/i2s.h
@@ -0,0 +1,41 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef EXAMPLES_SOUNDSTREAM_I2S_H_ +#define EXAMPLES_SOUNDSTREAM_I2S_H_ + +#include <compartment.h> + +#include "sw/device/lib/dif/dif_i2s.h" + +typedef dif_i2s_irq_t i2s_irq_t; +const i2s_irq_t kI2sIrqRxWatermark = kDifI2sIrqRxWatermark; +const i2s_irq_t kI2sIrqTxWatermark = kDifI2sIrqTxWatermark; +const i2s_irq_t kI2sIrqTxEmpty = kDifI2sIrqTxEmpty; + +void __cheri_compartment("i2s") i2s_init(void); +void __cheri_compartment("i2s") i2s_isr(void); +void __cheri_compartment("i2s") i2s_irq_acknowledge_all(void); +void __cheri_compartment("i2s") + i2s_irq_set_enabled(i2s_irq_t irq_id, bool enabled); +bool __cheri_compartment("i2s") i2s_rx_watermark_seen(void); +void __cheri_compartment("i2s") i2s_rxfifo_clear(void); +bool __cheri_compartment("i2s") i2s_rxfifo_is_empty(void); +void __cheri_compartment("i2s") i2s_record_begin(void); +void __cheri_compartment("i2s") i2s_record_end(void); +uint32_t __cheri_compartment("i2s") i2s_get_rdata(void); + +#endif // EXAMPLES_SOUNDSTREAM_I2S_H_
diff --git a/sw/device/cheriot/soundstream/mailbox.cc b/sw/device/cheriot/soundstream/mailbox.cc new file mode 100644 index 0000000..174c17f --- /dev/null +++ b/sw/device/cheriot/soundstream/mailbox.cc
@@ -0,0 +1,102 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Special-purpose mailbox support for soundstream. Assumes a + * companion implementation is running on the security core. + */ +#include "mailbox.h" + +#include <fail-simulator-on-error.h> +#include <futex.h> +#include <interrupt.h> +#include <locks.h> +#include <thread.h> + +#include <debug.hh> +#include <platform/sencha/platform-mailbox.hh> + +#include "hw/top_matcha/sw/autogen/top_matcha.h" + +/// Expose debugging features unconditionally for this compartment. +using Debug = ConditionalDebug<false, "Mailbox">; + +#include "compat.h" + +const uint32_t kMessageGpioPressed = 0xdeadbeef; +const uint32_t kMessageGpioReleased = 0x21524110; +const uint32_t kMessageEnableLed = 0xcafeb0ba; +const uint32_t kMessageDisableLed = 0x35014f45; + +typedef uint32_t + mailbox_mmio_t[TOP_MATCHA_TLUL_MAILBOX_SMC_SIZE_BYTES / sizeof(uint32_t)]; + +static dif_tlul_mailbox_t mailbox; +#define CHECK_INIT() CHECK(mailbox.base_addr.base != 0) + +static CountingSemaphoreState startup = { 0, 1 }; + +void mailbox_init(void) { + CHECK_DIF_OK( + dif_tlul_mailbox_init(mmio_region_from_addr((uintptr_t)MMIO_CAPABILITY( + mailbox_mmio_t, mailbox_smc)), + &mailbox)); + semaphore_put(&startup); +} + +// NB: could be atomic but not needed for our ussage. +volatile bool button_pressed = false; + +void mailbox_isr(void) { + Timeout t = {0, UnlimitedTimeout}; + semaphore_get(&t, &startup); + Debug::log("mailbox_isr: mailbox {} (Thread {})", mailbox.base_addr.base, thread_id_get()); + + const uint32_t *mailboxRtFutex = + interrupt_futex_get(STATIC_SEALED_VALUE(mailboxRtInterruptCapability)); + uint32_t last = *mailboxRtFutex; + for (;;) { + Debug::Assert(futex_wait(mailboxRtFutex, last) == 0, "futex_wait"); + last = *mailboxRtFutex; + Debug::log("mailbox_isr: mailbox {} last {}", mailbox.base_addr.base, last); + interrupt_complete(STATIC_SEALED_VALUE(mailboxRtInterruptCapability)); + + // NB: this is NOT the TockOS mailbox protocol that exchanges + // variable length packets that are encoded by Rust's postcard + // crate. + uint32_t message; + CHECK_DIF_OK(dif_tlul_mailbox_read_message(&mailbox, &message)); + if (message == kMessageGpioPressed) { + button_pressed = true; + } else if (message == kMessageGpioReleased) { + button_pressed = false; + } else { + CHECK(false, "Unknown message"); + } + } +} +bool mailbox_button_pressed(void) { return button_pressed; } + +// XXX temp until gpio's on the SEC can be used (either in simulation or for real) +void mailbox_set_button_pressed(bool pressed) { button_pressed = pressed; } + +void mailbox_set_led(bool enabled) { + CHECK_INIT(); + // NB: this is not the TockOS mailbox protocol. + uint32_t message_enable_led = + enabled ? kMessageEnableLed : kMessageDisableLed; + CHECK_DIF_OK(dif_tlul_mailbox_send_message(&mailbox, &message_enable_led)); +}
diff --git a/sw/device/cheriot/soundstream/mailbox.h b/sw/device/cheriot/soundstream/mailbox.h new file mode 100644 index 0000000..6663fca --- /dev/null +++ b/sw/device/cheriot/soundstream/mailbox.h
@@ -0,0 +1,34 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef EXAMPLES_SOUNDSTREAM_MBOX_H_ +#define EXAMPLES_SOUNDSTREAM_MBOX_H_ + +#include <compartment.h> + +#include "sw/device/lib/dif/dif_tlul_mailbox.h" + +typedef dif_tlul_mailbox_irq_t mailbox_irq_t; +const mailbox_irq_t kMboxIrqRtIrq = kDifTlulMailboxIrqRtirq; + +void __cheri_compartment("mailbox") mailbox_init(void); +void __cheri_compartment("mailbox") mailbox_isr(void); +bool __cheri_compartment("mailbox") mailbox_button_pressed(void); +// XXX temp +void __cheri_compartment("mailbox") mailbox_set_button_pressed(bool); +void __cheri_compartment("mailbox") mailbox_set_led(bool enabled); + +#endif // EXAMPLES_SOUNDSTREAM_MBOX_H_
diff --git a/sw/device/cheriot/soundstream/ml_top.cc b/sw/device/cheriot/soundstream/ml_top.cc new file mode 100644 index 0000000..dc931ce --- /dev/null +++ b/sw/device/cheriot/soundstream/ml_top.cc
@@ -0,0 +1,154 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// NB: override default setup done for DIF code linked into this compartment +#ifdef CHERIOT_NO_AMBIENT_MALLOC +#undef CHERIOT_NO_AMBIENT_MALLOC +#endif + +/* + * ML accelerator support for soundstream. There is no support for + * loading a model; it's assumed done by the security core which + * has access to the flash where model files are placed. + */ +#include "ml_top.h" + +#include <fail-simulator-on-error.h> +#include <futex.h> +#include <locks.h> +#include <multiwaiter.h> +#include <thread.h> + +#include <debug.hh> +#include <platform/sencha/platform-ml_top.hh> + +#include "compat.h" +#include "hw/top_matcha/sw/autogen/top_matcha.h" +#include "ml_top_regs.h" + +typedef uint8_t ml_top_dmem_t[TOP_MATCHA_RAM_ML_DMEM_SIZE_BYTES]; +typedef uint32_t + ml_top_mmio_t[TOP_MATCHA_ML_TOP_CORE_SIZE_BYTES / sizeof(uint32_t)]; + +/// Expose debugging features unconditionally for this compartment. +using Debug = ConditionalDebug<false, "ML_TOP">; + +static dif_ml_top_t ml_top; +#define CHECK_INIT() CHECK(ml_top.base_addr.base != 0) + +static CountingSemaphoreState startup = { 0, 1 }; + +void ml_top_init(void) { + CHECK_DIF_OK(dif_ml_top_init(mmio_region_from_addr((uintptr_t)MMIO_CAPABILITY( + ml_top_mmio_t, ml_top_core)), + &ml_top)); + semaphore_put(&startup); +} + +// NB: could be atomic but not needed for our ussage. +volatile bool finish_done = false; + +void ml_top_isr(void) { + Timeout t = {0, UnlimitedTimeout}; + semaphore_get(&t, &startup); + Debug::log("ml_top_isr: ml_top {} (Thread {})", + ml_top.base_addr.base, thread_id_get()); + + MultiWaiter* mw; + Timeout unlimited{0, UnlimitedTimeout}; + int error = multiwaiter_create(&unlimited, MALLOC_CAPABILITY, &mw, 2); + Debug::Assert(error == 0 && mw != nullptr, + "multiwaiter_create failed: {}", error); + + EventWaiterSource events[2]; + const uint32_t* mlTopFinishFutex = + interrupt_futex_get(STATIC_SEALED_VALUE(mlTopFinishInterruptCapability)); + events[0] = {(void*)mlTopFinishFutex, EventWaiterFutex, *mlTopFinishFutex}; + const uint32_t* mlTopFaultFutex = + interrupt_futex_get(STATIC_SEALED_VALUE(mlTopFaultInterruptCapability)); + events[1] = {(void*)mlTopFaultFutex, EventWaiterFutex, *mlTopFaultFutex}; + + for (;;) { + Debug::Assert(multiwaiter_wait(&unlimited, mw, events, 2) == 0, "multiwaiter_wait"); + Debug::log("ml_top_isr: Finish:{} Fault:{}", events[0].value, events[1].value); + if (events[1].value == 1) { // Fault signaled + LOG_FATAL("ML_TOP fault!"); + abort(); + } + if (events[0].value == 1) { // Finish signaled + finish_done = true; + interrupt_complete(STATIC_SEALED_VALUE(mlTopFinishInterruptCapability)); + CHECK_DIF_OK(dif_ml_top_reset_ctrl_en(&ml_top)); + } + } +} +bool ml_top_finish_done(void) { + // return atomic_swap(&finish_done, false); + bool was_done = finish_done; + if (was_done) { + finish_done = false; + } + return was_done; +} + +void ml_top_irq_acknowledge(ml_top_irq_t irq_id) { + CHECK_INIT(); + CHECK_DIF_OK(dif_ml_top_irq_acknowledge(&ml_top, irq_id)); +} + +void ml_top_irq_acknowledge_all() { + CHECK_INIT(); + CHECK_DIF_OK(dif_ml_top_irq_acknowledge_all(&ml_top)); +} + +void ml_top_irq_set_enabled(ml_top_irq_t irq_id, bool enabled) { + CHECK_INIT(); + CHECK_DIF_OK(dif_ml_top_irq_set_enabled( + &ml_top, irq_id, enabled ? kDifToggleEnabled : kDifToggleDisabled)); +} + +void ml_top_resume_ctrl_en(uint32_t resume_pc) { + CHECK_INIT(); + CHECK_DIF_OK(dif_ml_top_resume_ctrl_en(&ml_top, resume_pc)); +} + +void ml_top_set_input(void* const data, size_t data_len_bytes) { + void* input_ptr = (void*)(MMIO_CAPABILITY(ml_top_dmem_t, ml_top_dmem) + + TOP_MATCHA_RAM_ML_DMEM_SIZE_BYTES - 4096); + memcpy(input_ptr, data, data_len_bytes); +} + +void ml_top_get_output_header(struct output_header* header) { + uint8_t* ml_top_dmem_base = (uint8_t*) MMIO_CAPABILITY(ml_top_dmem_t, ml_top_dmem); + struct output_header* output_header_ptr = (struct output_header*) + (ml_top_dmem_base + (TOP_MATCHA_RAM_ML_DMEM_SIZE_BYTES - 0x40)); + header->return_code = output_header_ptr->return_code; + header->output_ptr = output_header_ptr->output_ptr; + header->length = output_header_ptr->length; + header->resume_pc = output_header_ptr->resume_pc; +#if 0 + Debug::log("return_code: {}", header.return_code); + Debug::log("output_ptr: {}", header.output_ptr); + Debug::log("length: {}", header.length); + Debug::log("resume_pc: {}", header.resume_pc); +#endif +} + +void ml_top_get_output_data(struct output_header* const header, void* buffer) { + const void* src = (const void*)(MMIO_CAPABILITY(ml_top_dmem_t, ml_top_dmem) + + header->output_ptr); + memcpy(buffer, src, header->length); +}
diff --git a/sw/device/cheriot/soundstream/ml_top.h b/sw/device/cheriot/soundstream/ml_top.h new file mode 100644 index 0000000..5555576 --- /dev/null +++ b/sw/device/cheriot/soundstream/ml_top.h
@@ -0,0 +1,48 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef EXAMPLES_SOUNDSTREAM_ML_TOP_H_ +#define EXAMPLES_SOUNDSTREAM_ML_TOP_H_ + +#include <compartment.h> + +#include "sw/device/lib/dif/dif_ml_top.h" + +typedef dif_ml_top_irq_t ml_top_irq_t; +const uint32_t kMlTopIrqFinish = kDifMlTopIrqFinish; + +struct output_header { + uint32_t return_code; // Populated in kelvin_start + uint32_t output_ptr; + uint32_t length; + uint32_t resume_pc; // PC at which to resume for another invocation. +}; + +void __cheri_compartment("ml_top") ml_top_init(void); +void __cheri_compartment("ml_top") ml_top_isr(void); +void __cheri_compartment("ml_top") ml_top_irq_acknowledge_all(void); +void __cheri_compartment("ml_top") + ml_top_irq_set_enabled(ml_top_irq_t irq_id, bool enabled); +bool __cheri_compartment("ml_top") ml_top_finish_done(void); +void __cheri_compartment("ml_top") ml_top_resume_ctrl_en(uint32_t resume_pc); +void __cheri_compartment("ml_top") + ml_top_set_input(void* const data, size_t data_len_bytes); +void __cheri_compartment("ml_top") + ml_top_get_output_header(struct output_header* header); +void __cheri_compartment("ml_top") + ml_top_get_output_data(struct output_header* const header, void* buffer); + +#endif // EXAMPLES_SOUNDSTREAM_ML_TOP_H_
diff --git a/sw/device/cheriot/soundstream/soundstream.cc b/sw/device/cheriot/soundstream/soundstream.cc new file mode 100644 index 0000000..897a921 --- /dev/null +++ b/sw/device/cheriot/soundstream/soundstream.cc
@@ -0,0 +1,212 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Data structures that hold audio samples are allocated on the heap +// because they are too big (for now) for .data/.bss. We use calloc +// for this and need to override the default quota (4K) to satisfy our +// large'ish requests. +//#define kSamples (5 * 16000) +#define kSamples (5000) // NB: reduced sample count for slow renode +#define kFilterSamples (256) +#define roundup(a, b) (((a) + (b) - 1) / (b)) * (b) +#define MALLOC_QUOTA \ + roundup( \ + (kSamples * sizeof(int32_t)) + 2 * (kFilterSamples * sizeof(int16_t)), \ + 4096) + +#include "soundstream.h" + +#include <fail-simulator-on-error.h> +#include <thread.h> + +#include <debug.hh> + +#include "hw/top_matcha/sw/autogen/top_matcha.h" + +/// Expose debugging features unconditionally for this compartment. +using Debug = ConditionalDebug<true, "SOUNDSTREAM">; + +#include "compat.h" + +#define ABS(x) (x > 0 ? x : -x) + +void __cheri_compartment("soundstream") entry(void) { + Debug::log("soundstream (Thread {})", thread_id_get()); + + i2s_init(); + ml_top_init(); + mailbox_init(); + + i2s_rxfifo_clear(); + i2s_irq_acknowledge_all(); + i2s_irq_set_enabled(kI2sIrqRxWatermark, /*enabled=*/true); + + // NB: sample buffers are temporarily allocated from the heap because + // the toolchain does not support large'ish data structures. Support + // has been committed upstream but not yet brought in. + + // XXX calloc returns 0xffffffff (v:0 0xfffffe00-0xfffffe00 l:0x0 o:0x0 p: - + // ------ -- ---) on failure so cannot check against NULL + // int16_t samples_left[kFilterSamples] = {0}; + // int16_t samples_right[kFilterSamples] = {0}; + int16_t* samples_left = (int16_t*)calloc(sizeof(int16_t), kFilterSamples); + Debug::Assert(__builtin_cheri_length_get(samples_left) > 0, + "samples_left allocation failed {}", samples_left); + int16_t* samples_right = (int16_t*)calloc(sizeof(int16_t), kFilterSamples); + Debug::Assert(__builtin_cheri_length_get(samples_right) > 0, + "samples_right allocation failed {}", samples_right); + size_t index_left = 0; + size_t index_right = 0; + int32_t total_left = 0; + int32_t total_right = 0; + + // static int32_t samples[kSamples]; + int32_t* samples = (int32_t*)calloc(sizeof(int32_t), kSamples); + Debug::Assert(__builtin_cheri_length_get(samples) > 0, + "Sample allocation failed {}", samples); + memset(samples, 0xa5, sizeof(int32_t) * kSamples); + + Debug::log("Setup complete"); + + while (true) { + // TODO(sleffler): need custom security core code running and + // a way to toggle the gpio associated with the button; for now + // just force it to appear as though the button has been pressed. + mailbox_set_button_pressed(true); + + // Wait until the record switch is pushed + Debug::log("Wait for button press..."); + while (!mailbox_button_pressed()) { + wait_for_interrupt(); + } + + mailbox_set_led(/*enabled=*/true); + Debug::log("Start recording (max {} samples)...", kSamples); + i2s_record_begin(); + + // Record until our buffer is full or the switch is released. + int sample = 0; + while (sample < kSamples && mailbox_button_pressed()) { + i2s_irq_set_enabled(kI2sIrqRxWatermark, /*enabled=*/true); + while (!i2s_rx_watermark_seen()) { + wait_for_interrupt(); + } + + while (!i2s_rxfifo_is_empty()) { + uint32_t reg_val = i2s_get_rdata(); + // For each sample, split into left and right channel values, + // and update the moving average. + int16_t left = reg_val >> 16; + int16_t right = reg_val & 0xFFFF; + total_left -= samples_left[index_left]; + total_right -= samples_right[index_right]; + total_left += left; + samples_left[index_left] = left; + total_right += right; + samples_right[index_right] = right; + index_left = (index_left + 1) % kFilterSamples; + index_right = (index_right + 1) % kFilterSamples; + int16_t mean_left = total_left / kFilterSamples; + int16_t mean_right = total_right / kFilterSamples; + + // Subtract the moving average from each channel, and repack into the + // sample buffer. + uint32_t offset_reg_val = (((left - mean_left) & 0xFFFF) << 16) | + ((right - mean_right) & 0xFFFF); + samples[sample++] = offset_reg_val; + if (sample == kSamples || !mailbox_button_pressed()) { + break; + } + } + } + + i2s_record_end(); + mailbox_set_led(/*enabled=*/false); + + Debug::log("Done recording {} samples", sample); + int samples_captured = sample; + + // Calculate the min/max of the audio, after correcting DC offsets + int32_t max = INT16_MIN; + int32_t min = INT16_MAX; + for (int i = 1; i < (samples_captured * 2); i += 2) { + int16_t* samples_s16 = (int16_t*)samples; + int32_t sample = samples_s16[i]; + if (sample < min) { + min = sample; + } + if (sample > max) { + max = sample; + } + } + + // Calculate a scaling factor, and use this to scale the waveform + // to a peak of 75% amplitude. + int32_t scale_max = ((int32_t)max * 100) / ((int32_t)INT16_MAX); + int32_t scale_min = ABS(((int32_t)min * 100) / ((int32_t)INT16_MIN)); + int32_t scale = scale_max > scale_min ? scale_max : scale_min; + for (int i = 1; i < (samples_captured * 2); i += 2) { + int16_t* samples_s16 = (int16_t*)samples; + int16_t sample = samples_s16[i]; + int16_t scaled_sample = (int16_t)(((int32_t)sample * 100) / scale); + scaled_sample = (((int32_t)scaled_sample * 75) / 100); + samples_s16[i] = scaled_sample; + } + + Debug::log("Processing recorded audio..."); + + // 320 x int16 + int iterations_to_process = samples_captured / 320; + int16_t process_buffer[320]; + int16_t result_buffer[64]; + char result_buffer_encoded[ENCODE_OUT_SIZE(sizeof(result_buffer))]; + + struct output_header header; + memset(&header, 0, sizeof(header)); // NB: resume_pc = 0 + + for (int i = 0; i < iterations_to_process; ++i) { + Debug::log("Iteration {}", i); + int16_t* samples_s16 = (int16_t*)samples; + // Extract left channel audio + for (int j = 0; j < 320; ++j) { + process_buffer[j] = samples_s16[(i * 320 * 2) + (j * 2) + 1]; + } + ml_top_set_input(process_buffer, sizeof(process_buffer)); + + // Start/resume kelvin + (void) ml_top_finish_done(); // NB: reset state + ml_top_resume_ctrl_en(header.resume_pc); + + // wfi + while (!ml_top_finish_done()) { + wait_for_interrupt(); + } + + ml_top_get_output_header(&header); + CHECK(header.length == sizeof(result_buffer), "Unexpected ML result size"); + ml_top_get_output_data(&header, result_buffer); + + encode((const unsigned char*)result_buffer, sizeof(result_buffer), + result_buffer_encoded); + Debug::log("[sound]::ENCODER:{}", + std::string_view(result_buffer_encoded, sizeof(result_buffer_encoded))); + } + + Debug::log("[sound]::ENCODER: done"); + Debug::log("Done with processing."); + } + panic(); +}
diff --git a/sw/device/cheriot/soundstream/soundstream.h b/sw/device/cheriot/soundstream/soundstream.h new file mode 100644 index 0000000..640834c --- /dev/null +++ b/sw/device/cheriot/soundstream/soundstream.h
@@ -0,0 +1,27 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef EXAMPLES_SOUNDSTREAM_SOUNDSTREAM_H_ +#define EXAMPLES_SOUNDSTREAM_SOUNDSTREAM_H_ + +#include <stdint.h> + +#include "encode.h" +#include "i2s.h" +#include "mailbox.h" +#include "ml_top.h" + +#endif // EXAMPLES_SOUNDSTREAM_SOUNDSTREAM_H_
diff --git a/sw/device/cheriot/soundstream/xmake.lua b/sw/device/cheriot/soundstream/xmake.lua new file mode 100644 index 0000000..3d2d1da --- /dev/null +++ b/sw/device/cheriot/soundstream/xmake.lua
@@ -0,0 +1,111 @@ +-- +-- Copyright 2023 Google LLC +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. + +set_project("CHERIoT soundstream demo") + +local shodan_dir = os.getenv("ROOTDIR") +if shodan_dir == nil or shodan_dir == '' then + raise("ROOTDIR not set") +end +local cheriot_out_dir = os.getenv("CHERIOT_OUT_DIR") +if cheriot_out_dir == nil or cheriot_out_dir == '' then + raise("CHERIOT_OUT_DIR not set") +end + +local sdkdir = path.join(shodan_dir, "sw/cheriot-rtos/sdk") +includes(sdkdir) +set_toolchains("cheriot-clang") + +option("board") + set_default("sencha") + +local matcha_dir = path.join(shodan_dir, "hw/matcha") +local dif_dir = path.join(matcha_dir, "sw/device/lib/dif") +local dif_autogen_dir = path.join(dif_dir, "autogen") +local opentitan_dir = path.join(shodan_dir, "hw/opentitan-upstream") +local matcha_gen_dir = path.join(cheriot_out_dir, "opentitan-gen/include/opentitan") + +-- Support libraries +includes(path.join(sdkdir, "lib")) +includes(path.join(sdkdir, "lib/freestanding")) + +-- Each driver operates in a compartment. +compartment("i2s") + add_files("i2s.cc") + add_files(path.join(dif_dir, "dif_i2s.c"), + path.join(dif_autogen_dir, "dif_i2s_autogen.c")) + add_includedirs(matcha_dir, matcha_gen_dir, opentitan_dir) + add_defines("CHERIOT_NO_AMBIENT_MALLOC") + +compartment("mailbox") + add_files("mailbox.cc") + add_files(path.join(dif_dir, "dif_tlul_mailbox.c"), + path.join(dif_autogen_dir, "dif_tlul_mailbox_autogen.c")) + add_includedirs(matcha_dir, matcha_gen_dir, opentitan_dir) + add_defines("CHERIOT_NO_AMBIENT_MALLOC") + +compartment("ml_top") + add_files("ml_top.cc") + add_files(path.join(dif_dir, "dif_ml_top.c"), + path.join(dif_autogen_dir, "dif_ml_top_autogen.c")) + add_includedirs(matcha_dir, matcha_gen_dir, opentitan_dir) + add_defines("CHERIOT_NO_AMBIENT_MALLOC") + +-- Soundstream application. +compartment("soundstream") + add_files("soundstream.cc", "encode.cc") + add_files(path.join(matcha_dir, "hw/top_matcha/sw/autogen/top_matcha.c")); + add_includedirs(matcha_dir, matcha_gen_dir, opentitan_dir) + +-- Firmware image. +firmware("soundstream-firmware") + add_deps("freestanding") + add_deps("debug", "i2s", "ml_top", "mailbox", "soundstream") + on_load(function(target) + target:values_set("board", "$(board)") + -- NB: trusted_stack_frames is a guess; +1'd for any + -- compartment error handler usage? + target:values_set("threads", { + { + compartment = "soundstream", + priority = 1, + entry_point = "entry", + stack_size = 0x1000, -- 4KB + trusted_stack_frames = 5 + }, + -- NB: stack sizes bumped for logging + { + compartment = "i2s", + priority = 1, + entry_point = "i2s_isr", + stack_size = 0x400, -- 512B + trusted_stack_frames = 3 + }, + { + compartment = "ml_top", + priority = 1, + entry_point = "ml_top_isr", + stack_size = 0x400, -- 512B + trusted_stack_frames = 3 + }, + { + compartment = "mailbox", + priority = 1, + entry_point = "mailbox_isr", + stack_size = 0x400, -- 512B + trusted_stack_frames = 3 + } + }, {expand = false}) + end)
diff --git a/sw/device/lib/dif/dif_i2s.h b/sw/device/lib/dif/dif_i2s.h index 190c454..7d01197 100644 --- a/sw/device/lib/dif/dif_i2s.h +++ b/sw/device/lib/dif/dif_i2s.h
@@ -16,8 +16,6 @@ */ - - #ifndef OPENTITAN_SW_DEVICE_LIB_DIF_DIF_I2S_H_ #define OPENTITAN_SW_DEVICE_LIB_DIF_DIF_I2S_H_ @@ -36,6 +34,8 @@ extern "C" { #endif // __cplusplus +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wextern-c-compat" /** * Runtime configuration for I2S Audio. * @@ -60,6 +60,7 @@ typedef struct dif_i2s_output { // Your fields here. } dif_i2s_output_t; +#pragma clang diagnostic pop /** * Configures I2S Audio with runtime information.
diff --git a/sw/device/lib/dif/dif_ml_top.h b/sw/device/lib/dif/dif_ml_top.h index 57ed19e..684c461 100644 --- a/sw/device/lib/dif/dif_ml_top.h +++ b/sw/device/lib/dif/dif_ml_top.h
@@ -48,6 +48,8 @@ */ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wextern-c-compat" /** * Runtime configuration for ML_Top. * @@ -72,6 +74,7 @@ typedef struct dif_ml_top_output { // Your fields here. } dif_ml_top_output_t; +#pragma clang diagnostic pop /** * Configures ML_Top with runtime information.
diff --git a/sw/device/lib/dif/dif_tlul_mailbox.h b/sw/device/lib/dif/dif_tlul_mailbox.h index 69aaf20..6b09fdf 100644 --- a/sw/device/lib/dif/dif_tlul_mailbox.h +++ b/sw/device/lib/dif/dif_tlul_mailbox.h
@@ -39,6 +39,8 @@ dif_result_t dif_tlul_mailbox_read_message(const dif_tlul_mailbox_t *tlul_mailbox, uint32_t *buf); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wextern-c-compat" /** * Runtime configuration for tlul_mailbox for SC<->SMC communication. * @@ -62,6 +64,7 @@ typedef struct dif_tlul_mailbox_output { // Your fields here. } dif_tlul_mailbox_output_t; +#pragma clang diagnostic pop /** * Configures tlul_mailbox for SC<->SMC communication with runtime information.